Address Gadfly review on #7: garden cap, dim validation, shared errors
Build image / build-and-push (push) Successful in 5s
Build image / build-and-push (push) Successful in 5s
Fixes from the PR #26 adversarial review (graded 18 real / 0 false positive). Correctness / security - maxGardenCM fixed to 10_000 (100 m), matching its comment — it was 100_000 cm (1 km), 10x too lax (5 models flagged this). - Dimension validation now rejects NaN/Inf (which slip past naive comparisons) and subnormal-tiny positives, via a finite [1cm, 100m] check. Name (200) and notes (10_000) are length-capped so untrusted input can't balloon storage. - Update version binding is `required,min=1`, so a negative/zero version is a 400, not a 409. Maintainability / performance - One unified writeServiceError (new errors.go) maps every auth + resource sentinel; writeResourceError removed. writeVersionConflict and parseIDParam moved to errors.go (shared, not in the gardens feature file). - Request structs share an embedded gardenFields (one toInput). - CreateGarden uses INSERT ... RETURNING (one round-trip). - ListGardensForOwner has a defensive LIMIT (pagination is post-v1). Tests: name/notes length, NaN/Inf/subnormal dims, dimension-at-cap valid, negative/zero version -> 400. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01JdQpdYYsTgtkJBxbcpAszi
This commit is contained in:
@@ -3,6 +3,8 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gitea.stevedudenhoeffer.com/steve/pansy/internal/domain"
|
||||
@@ -45,17 +47,27 @@ func TestCreateGardenValidation(t *testing.T) {
|
||||
owner := seedUser(t, s, "[email protected]")
|
||||
|
||||
cases := []GardenInput{
|
||||
{Name: " "}, // blank name
|
||||
{Name: "X", UnitPref: "furlongs"}, // bad unit
|
||||
{Name: "X", WidthCM: -5}, // non-positive dim (defaults only fill 0)
|
||||
{Name: "X", WidthCM: maxGardenCM + 1}, // over the cap
|
||||
{Name: "X", HeightCM: maxGardenCM * 10}, // over the cap
|
||||
{Name: " "}, // blank name
|
||||
{Name: strings.Repeat("a", maxGardenNameLen+1)}, // name too long
|
||||
{Name: "X", Notes: strings.Repeat("b", maxGardenNotesLen+1)}, // notes too long
|
||||
{Name: "X", UnitPref: "furlongs"}, // bad unit
|
||||
{Name: "X", WidthCM: -5}, // negative (defaults only fill exact 0)
|
||||
{Name: "X", WidthCM: maxGardenCM + 1}, // over the cap
|
||||
{Name: "X", HeightCM: maxGardenCM * 10}, // over the cap
|
||||
{Name: "X", WidthCM: math.NaN()}, // NaN slips past naive < / >
|
||||
{Name: "X", HeightCM: math.Inf(1)}, // +Inf
|
||||
{Name: "X", WidthCM: math.SmallestNonzeroFloat64}, // subnormal, > 0 but < 1 cm
|
||||
}
|
||||
for i, in := range cases {
|
||||
if _, err := s.CreateGarden(context.Background(), owner, in); !errors.Is(err, domain.ErrInvalidInput) {
|
||||
t.Errorf("case %d: err = %v, want ErrInvalidInput", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
// A dimension exactly at the cap is valid.
|
||||
if _, err := s.CreateGarden(context.Background(), owner, GardenInput{Name: "Big", WidthCM: maxGardenCM, HeightCM: maxGardenCM}); err != nil {
|
||||
t.Errorf("dimension at cap should be valid: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListGardensOwnedOnly(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user