package shape import ( "context" "reflect" "testing" "gitea.maximumdirect.net/eric/notarius/internal/core/source" "gitea.maximumdirect.net/eric/notarius/internal/framework/contracts" "gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline" "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd" ) func TestValidatorRequiresSceneShapeAndExactlyOneExtractionRecord(t *testing.T) { valid := list(scene("one", dnd.SceneKindNarrative, "Arrival", "The party arrives.", 1, 1)) validator := New(Options{}) for _, test := range []struct { name string value dnd.SceneDescriptionList stage string approved bool }{ {name: "valid normalization", value: valid, approved: true}, {name: "valid extraction", value: valid, stage: string(pipeline.StageExtract), approved: true}, {name: "multiple extraction records", value: list(valid.Scenes[0], scene("two", dnd.SceneKindMeta, "Rules", "The table checks rules.", 2, 2)), stage: string(pipeline.StageExtract)}, {name: "nil list", value: dnd.SceneDescriptionList{}}, {name: "empty list", value: dnd.SceneDescriptionList{Scenes: []dnd.SceneDescription{}}}, {name: "blank ID", value: list(scene(" ", dnd.SceneKindNarrative, "Arrival", "The party arrives.", 1, 1))}, {name: "unsupported kind", value: list(scene("one", "other", "Arrival", "The party arrives.", 1, 1))}, {name: "untrimmed prose", value: list(scene("one", dnd.SceneKindNarrative, " Arrival ", "The party arrives.", 1, 1))}, {name: "empty source reference", value: dnd.SceneDescriptionList{Scenes: []dnd.SceneDescription{{ID: "one", Kind: dnd.SceneKindNarrative, Title: "Arrival", Summary: "The party arrives."}}}}, } { t.Run(test.name, func(t *testing.T) { result, err := validator.Validate(context.Background(), contracts.TypedValidationRequest[dnd.SceneDescriptionList]{Stage: test.stage, Value: test.value}) if err != nil || result.Approved != test.approved { t.Fatalf("Validate() = %#v, %v; want approved=%t", result, err, test.approved) } }) } } func TestValidatorContractAndRegistration(t *testing.T) { registry := pipeline.NewValidatorRegistry() if err := Register(registry); err != nil { t.Fatalf("Register() error = %v", err) } if _, ok := registry.Spec(Key); !ok { t.Fatalf("registry has no spec for %q", Key) } if _, err := DecodeOptions(map[string]any{"unexpected": true}); err == nil { t.Fatal("DecodeOptions() accepted unknown options") } validator := New(Options{}) if got, want := validator.CheckpointFingerprints(), []pipeline.CheckpointFingerprint{{Name: "policy", Value: policy}}; !reflect.DeepEqual(got, want) { t.Fatalf("fingerprints = %#v, want %#v", got, want) } } func list(scenes ...dnd.SceneDescription) dnd.SceneDescriptionList { return dnd.SceneDescriptionList{Scenes: scenes} } func scene(id string, kind dnd.SceneKind, title, summary string, start, end int) dnd.SceneDescription { return dnd.SceneDescription{ID: id, Kind: kind, Title: title, Summary: summary, SourceRef: source.SourceRef{SourceID: "session", StartUnitID: start, EndUnitID: end}} }