package pipeline import ( "strings" "testing" "gitea.maximumdirect.net/eric/notarius/internal/framework/contracts" ) func TestValidateModuleSpecAllowsReferenceSlotsForEligibleStages(t *testing.T) { tests := []struct { name string kind string stage ModuleStage }{ {name: "chunker", kind: "chunker", stage: StageChunk}, {name: "extractor", kind: "extractor", stage: StageExtract}, {name: "normalizer", kind: "normalizer", stage: StageNormalize}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { spec := normalizeModuleSpec(ModuleSpec{ Key: "module", Stage: test.stage, ReferenceSlots: []contracts.ReferenceSlot{ {Name: "roster", Description: "Character roster", MaxBytes: 1024}, }, }) err := validateModuleSpec(test.kind, test.stage, spec) if err != nil { t.Fatalf("validateModuleSpec() error = %v, want nil", err) } }) } } func TestValidateModuleSpecRejectsReferenceSlotsForIneligibleStages(t *testing.T) { tests := []struct { name string kind string stage ModuleStage }{ {name: "input", kind: "input adapter", stage: StageInput}, {name: "merge", kind: "merger", stage: StageMerge}, {name: "validate", kind: "validator", stage: StageValidate}, {name: "output", kind: "output encoder", stage: StageOutput}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { spec := normalizeModuleSpec(ModuleSpec{ Key: "module", Stage: test.stage, ReferenceSlots: []contracts.ReferenceSlot{ {Name: "roster"}, }, }) err := validateModuleSpec(test.kind, test.stage, spec) if err == nil { t.Fatal("validateModuleSpec() error = nil, want error") } if !strings.Contains(err.Error(), "reference slots") { t.Fatalf("validateModuleSpec() error = %q, want reference slots context", err.Error()) } }) } } func TestValidateModuleSpecRejectsInvalidReferenceSlotsForEligibleStages(t *testing.T) { invalidSlots := []struct { name string slots []contracts.ReferenceSlot want string }{ { name: "empty name", slots: []contracts.ReferenceSlot{{Name: " "}}, want: "name", }, { name: "duplicate name after trim", slots: []contracts.ReferenceSlot{ {Name: "roster"}, {Name: " roster "}, }, want: "duplicated", }, { name: "negative max bytes", slots: []contracts.ReferenceSlot{{Name: "roster", MaxBytes: -1}}, want: "max_bytes", }, } eligibleStages := []struct { name string kind string stage ModuleStage }{ {name: "chunk", kind: "chunker", stage: StageChunk}, {name: "extract", kind: "extractor", stage: StageExtract}, {name: "normalize", kind: "normalizer", stage: StageNormalize}, } for _, stage := range eligibleStages { for _, invalid := range invalidSlots { t.Run(stage.name+"/"+invalid.name, func(t *testing.T) { spec := normalizeModuleSpec(ModuleSpec{ Key: "module", Stage: stage.stage, ReferenceSlots: invalid.slots, }) err := validateModuleSpec(stage.kind, stage.stage, spec) if err == nil { t.Fatal("validateModuleSpec() error = nil, want error") } if !strings.Contains(err.Error(), invalid.want) { t.Fatalf("validateModuleSpec() error = %q, want %q", err.Error(), invalid.want) } }) } } }