97 lines
3.1 KiB
Go
97 lines
3.1 KiB
Go
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")
|
|
}
|
|
}
|