Address second round of Gadfly findings on #76
Build image / build-and-push (push) Successful in 5s
Build image / build-and-push (push) Successful in 5s
- FillRegion's doc still said "half-pitch inset", left over from the first draft of the fix; the inset is radius - spacing/2. Two docs on the same function disagreeing is worse than either being terse. - Reject non-finite region bounds. They survive clamping and the inverted- region guard (NaN compares false both ways). Nothing corrupt reached the table — SQLite stores NaN as NULL and NOT NULL refuses it — but NaN surfaced as a raw store error and +Inf as a silent zero-plop success. - TestHexCentersTinyRegion used a region symmetric about the origin, so it could not distinguish "the middle of the region" from "the origin" and would have passed for an implementation that just returned (0,0). Added an off-centre case. Not taken: the finding that `make(..., rows*cols)` over-allocates ~12% because staggered rows hold cols-1. True, but the slice is capped at maxFillPlops (5000) and the exact count needs a ceil/floor split for no measurable gain. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01H3zbym8Doka2d7D48maSgZ
This commit is contained in:
@@ -154,10 +154,55 @@ func TestHexCentersEdgeInset(t *testing.T) {
|
||||
|
||||
// TestHexCentersTinyRegion covers a region too small to hold a plop at the
|
||||
// half-pitch inset: planting one in the middle beats refusing to plant at all.
|
||||
//
|
||||
// The off-centre case earns its place — a region symmetric about the origin
|
||||
// can't tell "the middle of the region" from "the origin", so on its own it
|
||||
// would pass for an implementation that just returned (0,0).
|
||||
func TestHexCentersTinyRegion(t *testing.T) {
|
||||
pts := hexCenters(rect(-5, -5, 5, 5), 15, 10)
|
||||
if len(pts) != 1 || pts[0].x != 0 || pts[0].y != 0 {
|
||||
t.Errorf("tiny region gave %+v, want a single centered plop", pts)
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
r Region
|
||||
wantX, wantY float64
|
||||
}{
|
||||
{"centred on the origin", rect(-5, -5, 5, 5), 0, 0},
|
||||
{"off in a corner", rect(20, -40, 30, -30), 25, -35},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
pts := hexCenters(tc.r, 15, 10)
|
||||
if len(pts) != 1 || pts[0].x != tc.wantX || pts[0].y != tc.wantY {
|
||||
t.Errorf("got %+v, want one plop at (%v,%v)", pts, tc.wantX, tc.wantY)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestFillRegionRejectsNonFiniteRegion: non-finite bounds survive clamping and
|
||||
// the inverted-region guard (NaN compares false both ways). Without the explicit
|
||||
// check, NaN surfaced as a raw store error ("NOT NULL constraint failed") and
|
||||
// +Inf as a silent success that planted nothing — neither of which tells the
|
||||
// caller what it actually did wrong.
|
||||
func TestFillRegionRejectsNonFiniteRegion(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
s := newTestService(t, openConfig())
|
||||
owner := seedUser(t, s, "[email protected]")
|
||||
g, _ := s.CreateGarden(ctx, owner, GardenInput{Name: "Big", WidthCM: 2000, HeightCM: 2000})
|
||||
bed := seedFillBed(t, s, owner, g.ID, 100, 100)
|
||||
plant := seedOwnPlant(t, s, owner, 10)
|
||||
|
||||
nan := math.NaN()
|
||||
for _, r := range []Region{
|
||||
{MinX: nan, MinY: -50, MaxX: 50, MaxY: 50},
|
||||
{MinX: -50, MinY: -50, MaxX: 50, MaxY: math.Inf(1)},
|
||||
} {
|
||||
created, err := s.FillRegion(ctx, owner, bed.ID, r, plant.ID, nil)
|
||||
if !errors.Is(err, domain.ErrInvalidInput) {
|
||||
t.Errorf("FillRegion(%+v) err = %v, want ErrInvalidInput", r, err)
|
||||
}
|
||||
for _, p := range created {
|
||||
if !isFinite(p.XCM) || !isFinite(p.YCM) {
|
||||
t.Errorf("persisted a plop with non-finite coordinates: %+v", p)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user