133 lines
6.2 KiB
Go
133 lines
6.2 KiB
Go
package cli
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.maximumdirect.net/eric/notarius/internal/core/config"
|
|
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
|
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
|
|
npcextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/npcs"
|
|
npcnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/npcs"
|
|
)
|
|
|
|
func TestProductionNPCConfigurationResolvesTypedLane(t *testing.T) {
|
|
components := productionTestComponents(t)
|
|
catalog := catalogFromRegistries(components.registries)
|
|
configPath := repositoryPath("examples", "dnd-npcs.config.yml")
|
|
cfg := loadMaintainedExample(t, configPath)
|
|
effective, err := cfg.Resolve(config.ResolveInput{PipelineID: "dnd-session", Catalog: catalog})
|
|
if err != nil {
|
|
t.Fatalf("Resolve() error = %v, want nil", err)
|
|
}
|
|
if effective.ResolvedPipeline.Chunk.Module != pipeline.DefaultChunkModule {
|
|
t.Fatalf("chunk module = %q, want %q", effective.ResolvedPipeline.Chunk.Module, pipeline.DefaultChunkModule)
|
|
}
|
|
if len(effective.ResolvedPipeline.ArtifactLanes) != 1 {
|
|
t.Fatalf("artifact lanes = %#v, want one NPC lane", effective.ResolvedPipeline.ArtifactLanes)
|
|
}
|
|
lane := effective.ResolvedPipeline.ArtifactLanes[0]
|
|
if lane.ID != "npcs" || lane.ArtifactKind != dnd.NPCListKind || lane.Extract.Module != npcextract.Key || lane.Extract.Retries != 2 || lane.Merge.Module != pipeline.DefaultMergeModule || lane.Normalize.Module != npcnormalize.Key {
|
|
t.Fatalf("resolved NPC lane = %#v, want typed production composition", lane)
|
|
}
|
|
if len(lane.ExtractReferences.Bindings) != 0 || len(lane.NormalizeReferences.Bindings) != 0 {
|
|
t.Fatalf("unbound NPC references = %#v / %#v, want none", lane.ExtractReferences, lane.NormalizeReferences)
|
|
}
|
|
|
|
extractSpec, ok := catalog.Extractors.Spec(npcextract.Key)
|
|
if !ok || !reflect.DeepEqual(extractSpec.Requires, []string{"chunks", "source.transcript"}) || !reflect.DeepEqual(extractSpec.Provides, []string{"dnd.npcs"}) {
|
|
t.Fatalf("NPC extractor spec = %#v, want source and artifact capabilities", extractSpec)
|
|
}
|
|
mergeSpec, ok := catalog.Mergers.SpecForArtifact(pipeline.DefaultMergeModule, dnd.NPCListKind)
|
|
if !ok || !reflect.DeepEqual(mergeSpec.Provides, []string{"merged"}) {
|
|
t.Fatalf("NPC merger spec = %#v, want merged capability", mergeSpec)
|
|
}
|
|
normalizeSpec, ok := catalog.Normalizers.SpecForArtifact(npcnormalize.Key, dnd.NPCListKind)
|
|
if !ok || !reflect.DeepEqual(normalizeSpec.Requires, []string{"merged"}) || !reflect.DeepEqual(normalizeSpec.Provides, []string{"normalized"}) {
|
|
t.Fatalf("NPC normalizer spec = %#v, want merged/normalized capabilities", normalizeSpec)
|
|
}
|
|
|
|
wantExtractChain := []pipeline.ModuleBinding{
|
|
pipeline.Binding("generic/valid_json"),
|
|
pipeline.Binding("generic/valid_json_schema"),
|
|
pipeline.Binding("extract/dnd/npcs/shape"),
|
|
pipeline.Binding("extract/dnd/npcs/source_refs"),
|
|
pipeline.Binding("extract/dnd/npcs/source_relatedness"),
|
|
}
|
|
wantNormalizeChain := []pipeline.ModuleBinding{
|
|
pipeline.Binding("generic/valid_json"),
|
|
pipeline.Binding("generic/valid_json_schema"),
|
|
pipeline.Binding("extract/dnd/npcs/shape"),
|
|
pipeline.Binding("normalize/dnd/npcs/identity"),
|
|
pipeline.Binding("extract/dnd/npcs/source_refs"),
|
|
pipeline.Binding("extract/dnd/npcs/source_relatedness"),
|
|
}
|
|
if got := validatorChain(effective.ResolvedPipeline, pipeline.StageExtract, npcextract.Key); !reflect.DeepEqual(got, wantExtractChain) {
|
|
t.Fatalf("NPC extract chain = %#v, want %#v", got, wantExtractChain)
|
|
}
|
|
if got := validatorChain(effective.ResolvedPipeline, pipeline.StageNormalize, npcnormalize.Key); !reflect.DeepEqual(got, wantNormalizeChain) {
|
|
t.Fatalf("NPC normalize chain = %#v, want %#v", got, wantNormalizeChain)
|
|
}
|
|
if got := validatorChain(effective.ResolvedPipeline, pipeline.StageMerge, pipeline.DefaultMergeModule); len(got) != 0 {
|
|
t.Fatalf("NPC merge chain = %#v, want empty", got)
|
|
}
|
|
}
|
|
|
|
func TestProductionNPCConfigurationValidatesOptionsReferencesAndPlacement(t *testing.T) {
|
|
components := productionTestComponents(t)
|
|
configPath := repositoryPath("examples", "dnd-npcs.config.yml")
|
|
resolve := func(mutate func(*pipeline.PipelineProfile)) error {
|
|
cfg := loadMaintainedExample(t, configPath)
|
|
profile := cfg.Pipelines["dnd-session"]
|
|
mutate(&profile)
|
|
cfg.Pipelines["dnd-session"] = profile
|
|
_, err := cfg.Resolve(config.ResolveInput{PipelineID: "dnd-session", Catalog: catalogFromRegistries(components.registries)})
|
|
return err
|
|
}
|
|
|
|
if err := resolve(func(profile *pipeline.PipelineProfile) {
|
|
lane := profile.Artifacts["npcs"]
|
|
lane.Extract.Options = map[string]any{"unexpected": true}
|
|
profile.Artifacts["npcs"] = lane
|
|
}); err == nil || !strings.Contains(err.Error(), "unknown option") {
|
|
t.Fatalf("unknown extractor option error = %v, want strict option rejection", err)
|
|
}
|
|
if err := resolve(func(profile *pipeline.PipelineProfile) {
|
|
lane := profile.Artifacts["npcs"]
|
|
lane.Normalize.Options = map[string]any{"unexpected": true}
|
|
profile.Artifacts["npcs"] = lane
|
|
}); err == nil || !strings.Contains(err.Error(), "unknown option") {
|
|
t.Fatalf("unknown normalizer option error = %v, want strict option rejection", err)
|
|
}
|
|
if err := resolve(func(profile *pipeline.PipelineProfile) {
|
|
profile.References = map[string]string{
|
|
"players": "players.txt",
|
|
"party": "party.txt",
|
|
"glossary": "glossary.txt",
|
|
}
|
|
}); err != nil {
|
|
t.Fatalf("optional NPC references error = %v, want resolution success", err)
|
|
}
|
|
if err := resolve(func(profile *pipeline.PipelineProfile) {
|
|
lane := profile.Artifacts["npcs"]
|
|
lane.Validators = []pipeline.ModuleBinding{pipeline.Binding("normalize/dnd/npcs/identity")}
|
|
profile.Artifacts["npcs"] = lane
|
|
}); err == nil || !strings.Contains(err.Error(), "artifact lane level") {
|
|
t.Fatalf("lane-level validator error = %v, want invalid placement rejection", err)
|
|
}
|
|
}
|
|
|
|
func validatorChain(resolved pipeline.ResolvedPipeline, stage pipeline.ModuleStage, module string) []pipeline.ModuleBinding {
|
|
for _, chain := range resolved.ValidatorChains {
|
|
if chain.Stage == stage && chain.ModuleKey == module {
|
|
bindings := make([]pipeline.ModuleBinding, len(chain.Validators))
|
|
for index, validator := range chain.Validators {
|
|
bindings[index] = validator.Binding
|
|
}
|
|
return bindings
|
|
}
|
|
}
|
|
return nil
|
|
}
|