ZDDC/zddc/internal/zddc/field_codes_test.go
ZDDC 875827d484 fix(records): validate folder_fields at load time + cache field-code patterns
- Add RecordRule.UnmarshalYAML so a misconfigured folder_fields fails
  when the .zddc is parsed, not as a 500 on the first record write. A
  negative parent-distance is now rejected with a message naming the
  field. Mirrors FieldCode.UnmarshalYAML's raw-alias pattern.
- Memoize anchored field-code pattern regexes in a package-level
  sync.Map (compileFieldPattern), used by both the unmarshal-time
  validation and FieldCode.Validate — replacing the per-call
  regexp.Compile that the old comment flagged as cache-if-it-shows-up.

Tests: negative distance rejected (standalone + nested in a records:
map), valid distance round-trips, pattern field code matches anchored.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 15:28:35 -05:00

67 lines
2.4 KiB
Go

package zddc
import (
"strings"
"testing"
"gopkg.in/yaml.v3"
)
// TestRecordRule_RejectsNegativeFolderDistance — a folder_fields entry
// with a negative parent-distance must fail at .zddc parse time (via
// RecordRule.UnmarshalYAML), not surface as a 500 on the first record
// write.
func TestRecordRule_RejectsNegativeFolderDistance(t *testing.T) {
src := "folder_fields:\n originator: -1\nfilename_format: \"{originator}-{sequence}\"\n"
var rr RecordRule
err := yaml.Unmarshal([]byte(src), &rr)
if err == nil {
t.Fatal("expected error for negative folder_fields distance, got nil")
}
if !strings.Contains(err.Error(), "folder_fields") || !strings.Contains(err.Error(), "originator") {
t.Errorf("error should name folder_fields + the field: %v", err)
}
}
// TestRecordRule_AcceptsValidFolderFields — a non-negative distance
// unmarshals and round-trips.
func TestRecordRule_AcceptsValidFolderFields(t *testing.T) {
src := "folder_fields:\n originator: 1\nfilename_format: \"{originator}-{sequence}\"\n"
var rr RecordRule
if err := yaml.Unmarshal([]byte(src), &rr); err != nil {
t.Fatalf("valid folder_fields should unmarshal: %v", err)
}
if rr.FolderFields["originator"] != 1 {
t.Errorf("folder_fields[originator]=%d want 1", rr.FolderFields["originator"])
}
}
// TestRecordRule_NegativeDistanceInRecordsMap — the same rejection
// applies when the rule is nested in a records: map (the real cascade
// shape: map[string]RecordRule decodes each value via UnmarshalYAML).
func TestRecordRule_NegativeDistanceInRecordsMap(t *testing.T) {
src := "\"*.yaml\":\n folder_fields:\n originator: -2\n"
var rules map[string]RecordRule
if err := yaml.Unmarshal([]byte(src), &rules); err == nil {
t.Fatal("expected error for negative distance in records map, got nil")
}
}
// TestFieldCode_PatternValidate — pattern field codes match values
// against the anchored regex (exercises the compiled-pattern cache).
func TestFieldCode_PatternValidate(t *testing.T) {
src := "kind: pattern\npattern: \"[0-9]{4}\"\n"
var fc FieldCode
if err := yaml.Unmarshal([]byte(src), &fc); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if err := fc.Validate("0042"); err != nil {
t.Errorf("0042 should match [0-9]{4}: %v", err)
}
if err := fc.Validate("42"); err == nil {
t.Error("42 should NOT match [0-9]{4} (anchored, 4 digits)")
}
if err := fc.Validate("0042x"); err == nil {
t.Error("0042x should NOT match (anchored)")
}
}