test(05-06): add lagoon.Fill fuzz test
- FuzzFill seeds extra and server-owned keys against a fixture struct - Non-allow-listed fields stay at their pre-Fill values with zero panics
This commit is contained in:
68
lagoon/fill_fuzz_test.go
Normal file
68
lagoon/fill_fuzz_test.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package lagoon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// fuzzFillFixture is the D-07 framework-level target: only name/notes are
|
||||||
|
// allow-listed. collection_id, secret, and id must stay at their pre-Fill values.
|
||||||
|
type fuzzFillFixture struct {
|
||||||
|
ID uint `gorm:"column:id"`
|
||||||
|
Name string `gorm:"column:name"`
|
||||||
|
Notes *string `gorm:"column:notes"`
|
||||||
|
CollectionID uint `gorm:"column:collection_id"`
|
||||||
|
Secret string `gorm:"column:secret"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzFill(f *testing.F) {
|
||||||
|
f.Add(`{"name":"ok","notes":"n"}`)
|
||||||
|
f.Add(`{"name":"ok","collection_id":9,"secret":"leak","id":99}`)
|
||||||
|
f.Add(`{"name":null,"notes":null}`)
|
||||||
|
f.Add(`{"collection_id":1,"unknown":true}`)
|
||||||
|
f.Add(`{"name":{"nested":true},"notes":[1,2]}`)
|
||||||
|
f.Add(`{"name":1,"notes":false}`)
|
||||||
|
f.Add(`[]`)
|
||||||
|
f.Add(``)
|
||||||
|
f.Add(`null`)
|
||||||
|
f.Add(`{"name":"x","extra":{"deep":{"x":1}}}`)
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, raw string) {
|
||||||
|
var requested map[string]any
|
||||||
|
_ = json.Unmarshal([]byte(raw), &requested)
|
||||||
|
if requested == nil {
|
||||||
|
requested = map[string]any{}
|
||||||
|
}
|
||||||
|
requested["collection_id"] = float64(99)
|
||||||
|
requested["secret"] = "pwned"
|
||||||
|
requested["id"] = float64(7)
|
||||||
|
|
||||||
|
row := fuzzFillFixture{ID: 1, CollectionID: 3, Secret: "keep"}
|
||||||
|
if err := Fill(&row, []string{"name", "notes"}, requested, true); err != nil {
|
||||||
|
// Type mismatches on allow-listed keys are errors, not panics.
|
||||||
|
// Non-allow-listed fields must still be untouched.
|
||||||
|
}
|
||||||
|
if row.CollectionID != 3 {
|
||||||
|
t.Fatalf("collection_id mutated to %d", row.CollectionID)
|
||||||
|
}
|
||||||
|
if row.Secret != "keep" {
|
||||||
|
t.Fatalf("secret mutated to %q", row.Secret)
|
||||||
|
}
|
||||||
|
if row.ID != 1 {
|
||||||
|
t.Fatalf("id mutated to %d", row.ID)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuzzFillNilAndEmptyNeverPanic(t *testing.T) {
|
||||||
|
row := fuzzFillFixture{CollectionID: 3, Secret: "keep"}
|
||||||
|
if err := Fill(&row, []string{"name"}, nil, true); err != nil {
|
||||||
|
t.Fatalf("nil map: %v", err)
|
||||||
|
}
|
||||||
|
if err := Fill(&row, []string{"name"}, map[string]any{}, true); err != nil {
|
||||||
|
t.Fatalf("empty map: %v", err)
|
||||||
|
}
|
||||||
|
if row.CollectionID != 3 || row.Secret != "keep" {
|
||||||
|
t.Fatalf("zero-input fill mutated %+v", row)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user