Fill: refuse an empty rectangle; list plops whose plant is gone unnamed
Build image / build-and-push (push) Successful in 12s

A blank region name with a zero-area Region reached hexCenters, whose
tiny-region rule plants one plop in the middle — a caller that said nothing
about where got a plop at the centre. ListObjectPlantings also failed the whole
listing if one plop's plant no longer existed; it now lists that plop unnamed.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
2026-08-23 00:16:54 -04:00
co-authored by Claude Fable 5
parent bc14bbed0d
commit a1baf4b871
2 changed files with 16 additions and 3 deletions
+9 -3
View File
@@ -218,6 +218,10 @@ func (s *Service) Fill(ctx context.Context, actorID, objectID int64, spec FillSp
if region, err = NamedRegion(o, spec.RegionName); err != nil {
return nil, err
}
} else if !(region.MinX < region.MaxX && region.MinY < region.MaxY) {
// A zero or inverted rectangle is a caller that said nothing about where
// — not a request for the one plop hexCenters would put at its middle.
return nil, fmt.Errorf("%w: the fill rectangle is empty", domain.ErrInvalidInput)
}
return s.fillLoaded(ctx, actorID, o, region, spec)
}
@@ -673,11 +677,13 @@ func (s *Service) ListObjectPlantings(ctx context.Context, actorID, objectID int
plant, ok := plants[pl.PlantID]
if !ok {
p, err := s.store.GetPlant(ctx, pl.PlantID)
if err != nil {
if err != nil && !errors.Is(err, domain.ErrNotFound) {
return nil, err
}
plant = *p
plants[pl.PlantID] = plant
if p != nil {
plant = *p
}
plants[pl.PlantID] = plant // a plant that no longer exists lists unnamed, not as an error
}
pl.DerivedCount = derivedCount(pl.RadiusCM, plant.SpacingCM)
out = append(out, describePlanting(pl, plant.Name))
+7
View File
@@ -790,4 +790,11 @@ func TestFillByRectangleAttributesSeed(t *testing.T) {
if _, err := s.Fill(ctx, owner, bed.ID, FillSpec{RegionName: "all", PlantID: garlic.ID, SeedLotID: &lot.ID}); err == nil {
t.Error("a fill charged to a lot of a different plant succeeded")
}
// No name and no rectangle is "nowhere", not "one plop in the middle" (which
// is what hexCenters makes of a zero-area region).
for _, r := range []Region{{}, {MinX: 10, MinY: -10, MaxX: 10, MaxY: 10}, {MinX: 20, MinY: 0, MaxX: -20, MaxY: 10}} {
if _, err := s.Fill(ctx, owner, bed.ID, FillSpec{Region: r, PlantID: garlic.ID}); !errors.Is(err, domain.ErrInvalidInput) {
t.Errorf("empty rectangle %+v: err = %v, want ErrInvalidInput", r, err)
}
}
}