151 lines
5.6 KiB
Go
151 lines
5.6 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
"testing"
|
|
|
|
"gardomatic.kleiax.de/internal/storage"
|
|
)
|
|
|
|
type locationTestModel struct {
|
|
items map[int]storage.Location
|
|
nextID int
|
|
}
|
|
|
|
func (m *locationTestModel) Insert(value storage.Location) (storage.Location, error) {
|
|
m.nextID++
|
|
value.ID = m.nextID
|
|
value.Version = 1
|
|
m.items[value.ID] = value
|
|
return value, nil
|
|
}
|
|
func (m *locationTestModel) Get(gardenID, id int) (storage.Location, error) {
|
|
value, ok := m.items[id]
|
|
if !ok || value.GardenID != gardenID {
|
|
return storage.Location{}, storage.ErrRecordNotFound
|
|
}
|
|
return value, nil
|
|
}
|
|
func (m *locationTestModel) GetAllForGarden(gardenID int) ([]storage.Location, error) {
|
|
result := []storage.Location{}
|
|
for _, value := range m.items {
|
|
if value.GardenID == gardenID {
|
|
result = append(result, value)
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
func (m *locationTestModel) Update(gardenID int, value storage.Location) (storage.Location, error) {
|
|
if _, err := m.Get(gardenID, value.ID); err != nil {
|
|
return storage.Location{}, err
|
|
}
|
|
value.Version++
|
|
m.items[value.ID] = value
|
|
return value, nil
|
|
}
|
|
func (m *locationTestModel) Delete(gardenID, id int) error {
|
|
if _, err := m.Get(gardenID, id); err != nil {
|
|
return err
|
|
}
|
|
delete(m.items, id)
|
|
return nil
|
|
}
|
|
|
|
type plantLocationTestModel struct {
|
|
items map[int]storage.PlantLocation
|
|
nextID int
|
|
}
|
|
|
|
func (m *plantLocationTestModel) Insert(_ int, value storage.PlantLocation) (storage.PlantLocation, error) {
|
|
m.nextID++
|
|
value.ID = m.nextID
|
|
value.Version = 1
|
|
m.items[value.ID] = value
|
|
return value, nil
|
|
}
|
|
func (m *plantLocationTestModel) Get(_ int, id int) (storage.PlantLocation, error) {
|
|
value, ok := m.items[id]
|
|
if !ok {
|
|
return storage.PlantLocation{}, storage.ErrRecordNotFound
|
|
}
|
|
return value, nil
|
|
}
|
|
func (m *plantLocationTestModel) GetAllForPlant(_ int, plantID int) ([]storage.PlantLocation, error) {
|
|
result := []storage.PlantLocation{}
|
|
for _, value := range m.items {
|
|
if value.PlantID == plantID {
|
|
result = append(result, value)
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
func (m *plantLocationTestModel) GetAllForLocation(_ int, locationID int) ([]storage.PlantLocation, error) {
|
|
result := []storage.PlantLocation{}
|
|
for _, value := range m.items {
|
|
if value.LocationID == locationID {
|
|
result = append(result, value)
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
func (m *plantLocationTestModel) Update(_ int, value storage.PlantLocation) (storage.PlantLocation, error) {
|
|
value.Version++
|
|
m.items[value.ID] = value
|
|
return value, nil
|
|
}
|
|
func (m *plantLocationTestModel) Delete(_ int, id int) error {
|
|
if _, ok := m.items[id]; !ok {
|
|
return storage.ErrRecordNotFound
|
|
}
|
|
delete(m.items, id)
|
|
return nil
|
|
}
|
|
|
|
func TestLocationsRejectForeignParentsAndCycles(t *testing.T) {
|
|
app, _, members := newGardenTestApplication()
|
|
user := storage.User{ID: 7, Activated: true}
|
|
members.members[[2]int{3, user.ID}] = storage.GardenMember{GardenID: 3, UserID: user.ID, Role: storage.GardenRoleMember}
|
|
rootID := 1
|
|
locations := &locationTestModel{items: map[int]storage.Location{1: {ID: 1, GardenID: 3, Name: "Beet", CreatedBy: user.ID, Version: 1}, 2: {ID: 2, GardenID: 3, ParentID: &rootID, Name: "Reihe", CreatedBy: user.ID, Version: 1}, 9: {ID: 9, GardenID: 4, Name: "Fremd", Version: 1}}, nextID: 9}
|
|
app.models.Locations = locations
|
|
|
|
foreign := serveResourceRequest(app, user, http.MethodPost, "/v1/gardens/3/locations", []byte(`{"name":"Topf","parent_id":9}`))
|
|
if foreign.Code != http.StatusUnprocessableEntity {
|
|
t.Fatalf("foreign parent: got %d, want %d; %s", foreign.Code, http.StatusUnprocessableEntity, foreign.Body.String())
|
|
}
|
|
cycle := serveResourceRequest(app, user, http.MethodPatch, "/v1/gardens/3/locations/1", []byte(`{"parent_id":2}`))
|
|
if cycle.Code != http.StatusUnprocessableEntity {
|
|
t.Fatalf("cycle: got %d, want %d; %s", cycle.Code, http.StatusUnprocessableEntity, cycle.Body.String())
|
|
}
|
|
foreignRead := serveResourceRequest(app, user, http.MethodGet, "/v1/gardens/3/locations/9", nil)
|
|
if foreignRead.Code != http.StatusNotFound {
|
|
t.Fatalf("foreign read: got %d, want %d", foreignRead.Code, http.StatusNotFound)
|
|
}
|
|
cleared := serveResourceRequest(app, user, http.MethodPatch, "/v1/gardens/3/locations/2", []byte(`{"clear_parent_id":true}`))
|
|
if cleared.Code != http.StatusOK || locations.items[2].ParentID != nil {
|
|
t.Fatalf("clear parent: status=%d body=%s", cleared.Code, cleared.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestPlantLocationIsScopedToGarden(t *testing.T) {
|
|
app, _, members := newGardenTestApplication()
|
|
user := storage.User{ID: 8, Activated: true}
|
|
members.members[[2]int{3, user.ID}] = storage.GardenMember{GardenID: 3, UserID: user.ID, Role: storage.GardenRoleMember}
|
|
app.models.Plants = &plantTestModel{items: map[int]storage.Plant{5: {ID: 5, GardenID: 3, Name: "Tomate", Status: "active"}}}
|
|
app.models.Locations = &locationTestModel{items: map[int]storage.Location{6: {ID: 6, GardenID: 3, Name: "Beet"}, 9: {ID: 9, GardenID: 4, Name: "Fremd"}}}
|
|
assignments := &plantLocationTestModel{items: map[int]storage.PlantLocation{}, nextID: 10}
|
|
app.models.PlantLocations = assignments
|
|
|
|
foreign := serveResourceRequest(app, user, http.MethodPost, "/v1/gardens/3/plants/5/locations", []byte(`{"location_id":9,"quantity":1}`))
|
|
if foreign.Code != http.StatusUnprocessableEntity {
|
|
t.Fatalf("foreign assignment: got %d, want %d; %s", foreign.Code, http.StatusUnprocessableEntity, foreign.Body.String())
|
|
}
|
|
created := serveResourceRequest(app, user, http.MethodPost, "/v1/gardens/3/plants/5/locations", []byte(`{"location_id":6,"quantity":3}`))
|
|
if created.Code != http.StatusCreated {
|
|
t.Fatalf("assignment: got %d, want %d; %s", created.Code, http.StatusCreated, created.Body.String())
|
|
}
|
|
if got := assignments.items[11].Quantity; got != 3 {
|
|
t.Errorf("quantity: got %d, want 3", got)
|
|
}
|
|
}
|