- Fill allow-list, once-per-key dropped log, production silence - BeforeCreate fires on real Postgres; cascade rollback on error
106 lines
2.7 KiB
Go
106 lines
2.7 KiB
Go
package lagoon
|
|
|
|
import (
|
|
"bytes"
|
|
"log/slog"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
type fillFixture struct {
|
|
Name string `gorm:"column:name"`
|
|
CollectionID uint `gorm:"column:collection_id"`
|
|
Notes *string `gorm:"column:notes"`
|
|
}
|
|
|
|
func (fillFixture) Fillable() []string { return []string{"name", "notes"} }
|
|
func (fillFixture) Hidden() []string { return []string{"collection_id"} }
|
|
|
|
var (
|
|
_ HasFillable = fillFixture{}
|
|
_ HasHidden = fillFixture{}
|
|
)
|
|
|
|
func TestFillAllowList(t *testing.T) {
|
|
var row fillFixture
|
|
row.CollectionID = 3
|
|
err := Fill(&row, []string{"name"}, map[string]any{
|
|
"name": "x",
|
|
"collection_id": uint(9),
|
|
}, true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if row.Name != "x" {
|
|
t.Fatalf("name = %q", row.Name)
|
|
}
|
|
if row.CollectionID != 3 {
|
|
t.Fatalf("collection_id mutated to %d", row.CollectionID)
|
|
}
|
|
}
|
|
|
|
func TestFillDroppedKeyLogsOnce(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
prev := slog.Default()
|
|
slog.SetDefault(slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelWarn})))
|
|
defer slog.SetDefault(prev)
|
|
|
|
var row fillFixture
|
|
requested := map[string]any{"name": "once", "collection_id": uint(1)}
|
|
if err := Fill(&row, []string{"name"}, requested, false); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := Fill(&row, []string{"name"}, requested, false); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
log := buf.String()
|
|
if strings.Count(log, "collection_id") != 1 {
|
|
t.Fatalf("dropped key should log once, got %q", log)
|
|
}
|
|
if !strings.Contains(log, "lagoon: dropped non-fillable key") {
|
|
t.Fatalf("missing warn message: %q", log)
|
|
}
|
|
}
|
|
|
|
func TestFillProductionSilent(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
prev := slog.Default()
|
|
slog.SetDefault(slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelWarn})))
|
|
defer slog.SetDefault(prev)
|
|
|
|
var row fillFixture
|
|
if err := Fill(&row, []string{"name"}, map[string]any{
|
|
"name": "prod",
|
|
"unknown_field": true,
|
|
}, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if buf.Len() != 0 {
|
|
t.Fatalf("production fill must be silent, got %q", buf.String())
|
|
}
|
|
if row.Name != "prod" {
|
|
t.Fatalf("name = %q", row.Name)
|
|
}
|
|
}
|
|
|
|
func TestFillNilPointerClearsNullable(t *testing.T) {
|
|
existing := "keep"
|
|
row := fillFixture{Notes: &existing}
|
|
if err := Fill(&row, []string{"notes"}, map[string]any{"notes": nil}, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if row.Notes != nil {
|
|
t.Fatalf("notes = %v, want nil", row.Notes)
|
|
}
|
|
}
|
|
|
|
func TestFillDroppedKeyNeverErrors(t *testing.T) {
|
|
var row fillFixture
|
|
if err := Fill(&row, []string{"name"}, map[string]any{"collection_id": uint(9), "nope": 1}, true); err != nil {
|
|
t.Fatalf("dropped keys must not error: %v", err)
|
|
}
|
|
if row.CollectionID != 0 {
|
|
t.Fatalf("collection_id = %d", row.CollectionID)
|
|
}
|
|
}
|