From a1baf4b871ea35eb4add782a07d0ba13afd88b6f Mon Sep 17 00:00:00 2001 From: Steve Dudenhoeffer Date: Sun, 23 Aug 2026 00:16:54 -0400 Subject: [PATCH] Fill: refuse an empty rectangle; list plops whose plant is gone unnamed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- internal/service/ops.go | 12 +++++++++--- internal/service/ops_test.go | 7 +++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/internal/service/ops.go b/internal/service/ops.go index dabb90f..53d2b93 100644 --- a/internal/service/ops.go +++ b/internal/service/ops.go @@ -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)) diff --git a/internal/service/ops_test.go b/internal/service/ops_test.go index 78c1f34..a803ae8 100644 --- a/internal/service/ops_test.go +++ b/internal/service/ops_test.go @@ -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) + } + } }