package postgres import ( "database/sql" "encoding/json" "errors" "os" "testing" "gardomatic.kleiax.de/internal/storage" ) func TestLocationAndPlantLocationModelsEnforceGardenBoundary(t *testing.T) { dsn := os.Getenv("GARDOMATIC_TEST_DB_DSN") if dsn == "" { t.Skip("set GARDOMATIC_TEST_DB_DSN to run PostgreSQL integration tests") } db, err := sql.Open("postgres", dsn) if err != nil { t.Fatal(err) } t.Cleanup(func() { _ = db.Close() }) if err := db.Ping(); err != nil { t.Fatalf("connect to PostgreSQL: %v", err) } var gardenID, foreignGardenID int if err := db.QueryRow(`INSERT INTO gardens (name) VALUES ('Location integration A') RETURNING id`).Scan(&gardenID); err != nil { t.Fatal(err) } if err := db.QueryRow(`INSERT INTO gardens (name) VALUES ('Location integration B') RETURNING id`).Scan(&foreignGardenID); err != nil { t.Fatal(err) } t.Cleanup(func() { _, _ = db.Exec(`DELETE FROM gardens WHERE id IN ($1, $2)`, gardenID, foreignGardenID) }) locations := LocationModel{DB: db} local, err := locations.Insert(storage.Location{GardenID: gardenID, Name: "Beet", Attributes: json.RawMessage(`{}`)}) if err != nil { t.Fatalf("insert local location: %v", err) } foreign, err := locations.Insert(storage.Location{GardenID: foreignGardenID, Name: "Fremdes Beet", Attributes: json.RawMessage(`{}`)}) if err != nil { t.Fatalf("insert foreign location: %v", err) } if _, err := locations.Get(gardenID, foreign.ID); !errors.Is(err, storage.ErrRecordNotFound) { t.Fatalf("foreign location lookup: got %v, want ErrRecordNotFound", err) } var plantID int if err := db.QueryRow(`INSERT INTO plants (garden_id, name) VALUES ($1, 'Tomate') RETURNING id`, gardenID).Scan(&plantID); err != nil { t.Fatal(err) } plants, err := (PlantModel{DB: db}).GetAllForGarden(gardenID) if err != nil { t.Fatalf("list plants with planter join: %v", err) } if len(plants) != 1 || plants[0].ID != plantID { t.Fatalf("listed plants: got %+v, want plant %d", plants, plantID) } assignments := PlantLocationModel{DB: db} created, err := assignments.Insert(gardenID, storage.PlantLocation{PlantID: plantID, LocationID: local.ID, Quantity: 2}) if err != nil { t.Fatalf("insert local assignment: %v", err) } if created.ID == 0 { t.Fatal("local assignment has no id") } if _, err := assignments.Insert(gardenID, storage.PlantLocation{PlantID: plantID, LocationID: foreign.ID, Quantity: 1}); !errors.Is(err, storage.ErrRecordNotFound) { t.Fatalf("foreign assignment: got %v, want ErrRecordNotFound", err) } listed, err := assignments.GetAllForPlant(gardenID, plantID) if err != nil || len(listed) != 1 { t.Fatalf("list assignments: values=%+v err=%v", listed, err) } }