This commit is contained in:
mats committed 2026-06-28 17:52:49 +02:00
1 parent e706f7d907
commit 21bcb63440
42 files changed
+5903

No files matched your search

+96
View File
@@ -0,0 +1,96 @@
package catalog
import (
"encoding/json"
"testing"
)
// TestSchemaRoundTrip verifies a Schema can be JSON-encoded and decoded
// without losing operator-tunable fields. Round-trip is the contract
// LoadFile relies on.
func TestSchemaRoundTrip(t *testing.T) {
original := BygglagretSchema()
original.TrueValues = []string{"true", "ja"} // tenant-specific
raw, err := json.Marshal(original)
if err != nil {
t.Fatalf("marshal: %v", err)
}
var decoded Schema
if err := json.Unmarshal(raw, &decoded); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if decoded.SKUField != original.SKUField {
t.Errorf("SKUField: got %q want %q", decoded.SKUField, original.SKUField)
}
if decoded.PriceScale != original.PriceScale {
t.Errorf("PriceScale: got %d want %d", decoded.PriceScale, original.PriceScale)
}
if decoded.UpdatedAtSource != original.UpdatedAtSource {
t.Errorf("UpdatedAtSource: got %v want %v", decoded.UpdatedAtSource, original.UpdatedAtSource)
}
if decoded.UpdatedAtField != original.UpdatedAtField {
t.Errorf("UpdatedAtField: got %q want %q", decoded.UpdatedAtField, original.UpdatedAtField)
}
if len(decoded.TrueValues) != 2 || decoded.TrueValues[1] != "ja" {
t.Errorf("TrueValues lost on round-trip: %v", decoded.TrueValues)
}
}
// TestDefaultSchemaConsensusShape pins the field-name contract for the
// zero-config tenant (the operator-facing shape).
func TestDefaultSchemaConsensusShape(t *testing.T) {
s := DefaultSchema()
if s.Tenant != "default" {
t.Errorf("DefaultSchema().Tenant = %q, want %q", s.Tenant, "default")
}
if s.SKUField != "sku" || s.PriceField != "price_inc_vat" {
t.Errorf("consensus shape rotated: SKUField=%q PriceField=%q", s.SKUField, s.PriceField)
}
if s.PriceScale != 1 || s.IDPrefix != "sku:" {
t.Errorf("consensus defaults wrong: PriceScale=%d IDPrefix=%q", s.PriceScale, s.IDPrefix)
}
if s.UpdatedAtSource != UseSourceField {
t.Errorf("consensus updated_at_source = %v, want UseSourceField "+
"(foot-gun 3: stamp-now'ing on rebuild invalidates every consumer cache)", s.UpdatedAtSource)
}
if len(s.TrueValues) == 0 {
t.Errorf("Truth set should not be empty in consensus")
}
}
// TestTrueValueCaseInsensitive pins the lower-cased match used by boolOf.
func TestTrueValueCaseInsensitive(t *testing.T) {
truthy := DefaultTrueValues()
for _, s := range []string{"TRUE", "Yes", "ON", "1"} {
// Re-implement the matching here to avoid coupling to builder.go's
// internals, but flag if it ever drifts.
low := ""
for _, c := range s {
if c >= 'A' && c <= 'Z' {
low += string(c + 32)
} else {
low += string(c)
}
}
found := false
for _, t := range truthy {
if low == t {
found = true
break
}
}
if !found {
t.Errorf("truthy set is missing lower-cased %q", s)
}
}
}
// TestSchemaZeroValueInvalid documents that the zero value of Schema is
// explicitly NOT a usable schema — Apply validates and refuses it.
func TestSchemaZeroValueInvalid(t *testing.T) {
_, err := Apply(Schema{}, map[string]any{"sku": "ABC", "price_inc_vat": 1})
if err == nil {
t.Errorf("zero Schema should be rejected by Apply")
}
}