package config import ( "os" "path/filepath" "reflect" "strings" "testing" ) func TestPipelineConfigKitImportsOverlayAndBaseSelection(t *testing.T) { dir := t.TempDir() writePipelineFixture(t, dir, "pipeline.yml", `configkit: imports: - path: config/workspace.yml - path: config/artifact.yml whisperx: transcribe_url: https://transcription.example.test/transcribe `) writePipelineFixture(t, dir, "config/workspace.yml", "workspace:\n root: /srv/base\n") writePipelineFixture(t, dir, "config/artifact.yml", `scriptorium: artifacts: recap: enabled: false output_path: artifacts/recap.md `) writePipelineFixture(t, dir, "overlays/testing.yml", "workspace:\n root: /srv/testing\n") writePipelineFixture(t, dir, "overlays/identical.yml", "workspace:\n root: /srv/base\n") writePipelineFixture(t, dir, "overlays/unselected.yml", "unknown: [\n") root := filepath.Join(dir, "pipeline.yml") var baseDigest string for _, profile := range []*string{nil, stringPointer(""), stringPointer("default")} { cfg, err := LoadPipelineWithOptions(root, PipelineLoadOptions{Overlay: profile}) if err != nil { t.Fatalf("load base selection %#v: %v", profile, err) } if cfg.Workspace.Root != "/srv/base" { t.Fatalf("base workspace = %q", cfg.Workspace.Root) } if baseDigest == "" { baseDigest = EffectivePipelineDigest(cfg) } else if EffectivePipelineDigest(cfg) != baseDigest { t.Fatal("equivalent base selections changed effective digest") } } identical := "identical" unchanged, err := LoadPipelineWithOptions(root, PipelineLoadOptions{Overlay: &identical}) if err != nil { t.Fatal(err) } if EffectivePipelineDigest(unchanged) != baseDigest { t.Fatal("equivalent overlay changed effective digest") } selected := "testing" cfg, err := LoadPipelineWithOptions(root, PipelineLoadOptions{Overlay: &selected}) if err != nil { t.Fatal(err) } if cfg.Workspace.Root != "/srv/testing" || len(EffectivePipelineImports(cfg)) != 2 { t.Fatalf("profiled pipeline = %#v, imports=%#v", cfg.Workspace, EffectivePipelineImports(cfg)) } profile, ok := SelectedPipelineOverlay(cfg) if !ok || profile.Name != "testing" || profile.OverlayPath != filepath.Join(dir, "overlays", "testing.yml") { t.Fatalf("selected profile = %#v, %v", profile, ok) } } func TestPipelineOverlayDoesNotDiscoverLegacyDirectory(t *testing.T) { dir := t.TempDir() writePipelineFixture(t, dir, "pipeline.yml", "configkit:\n imports:\n - path: profiles/shared.yml\n") writePipelineFixture(t, dir, "profiles/shared.yml", "workspace:\n root: /srv/base\n") writePipelineFixture(t, dir, "profiles/testing.yml", "workspace:\n root: /srv/old\n") if base, err := LoadPipeline(filepath.Join(dir, "pipeline.yml")); err != nil || base.Workspace.Root != "/srv/base" { t.Fatalf("explicit import from profiles directory = %#v, %v", base, err) } selected := "testing" if _, err := LoadPipelineWithOptions(filepath.Join(dir, "pipeline.yml"), PipelineLoadOptions{Overlay: &selected}); err == nil || !strings.Contains(err.Error(), "overlay.missing") { t.Fatalf("legacy selected file result = %v, want missing overlay", err) } } func TestPipelineConfigKitRejectsDuplicateBaseDeclarations(t *testing.T) { for _, test := range []struct { name, first, second, clearedContainer string }{ {name: "artifact", first: "scriptorium:\n artifacts:\n recap:\n prompt_id: recap\n", second: "scriptorium:\n artifacts:\n recap:\n output_path: artifacts/recap.md\n", clearedContainer: "scriptorium:\n artifacts: null\n"}, {name: "artifact family", first: "scriptorium:\n artifact_families:\n recap:\n for_each: party.characters\n", second: "scriptorium:\n artifact_families:\n recap:\n output_path_pattern: artifacts/{character_id}.md\n", clearedContainer: "scriptorium:\n artifact_families: null\n"}, } { t.Run(test.name, func(t *testing.T) { dir := t.TempDir() writePipelineFixture(t, dir, "pipeline.yml", `configkit: imports: - path: first.yml - path: second.yml whisperx: transcribe_url: https://transcription.example.test/transcribe `) writePipelineFixture(t, dir, "first.yml", test.first) writePipelineFixture(t, dir, "second.yml", test.second) writePipelineFixture(t, dir, "overlays/clear-container.yml", test.clearedContainer) writePipelineFixture(t, dir, "overlays/clear-scriptorium.yml", "scriptorium: null\n") for _, profile := range []string{"", "clear-container", "clear-scriptorium"} { t.Run(profile, func(t *testing.T) { options := PipelineLoadOptions{} if profile != "" { options.Overlay = &profile } _, err := LoadPipelineWithOptions(filepath.Join(dir, "pipeline.yml"), options) if err == nil || !strings.Contains(err.Error(), "declared by multiple base files") || !strings.Contains(err.Error(), "first.yml") || !strings.Contains(err.Error(), "second.yml") { t.Fatalf("duplicate declaration error = %v", err) } }) } }) } } func TestPipelineOverlayDoesNotRequireUnusedBaseCredentials(t *testing.T) { const production = "NARRATIO_PROFILE_UNUSED_PROD_KEY" const testingKey = "NARRATIO_PROFILE_TEST_KEY" t.Setenv(production, "") if err := os.Unsetenv(production); err != nil { t.Fatal(err) } t.Setenv(testingKey, "synthetic-test-key") for _, credential := range []string{testingKey, "null"} { t.Run(credential, func(t *testing.T) { dir := t.TempDir() root := writePipelineFixture(t, dir, "pipeline.yml", "whisperx:\n transcribe_url: https://transcription.example.test/transcribe\naudita:\n llm_api_key: "+production+"\n") writePipelineFixture(t, dir, "overlays/testing.yml", "audita:\n llm_api_key: "+credential+"\n") profile := "testing" cfg, err := LoadPipelineWithOptions(root, PipelineLoadOptions{Overlay: &profile}) if err != nil { t.Fatal(err) } if credential == "null" { if cfg.Audita.LLMAPIKey != nil { t.Fatal("profile did not remove the base credential") } } else if cfg.Audita.LLMAPIKey == nil || cfg.Audita.LLMAPIKey.EnvName() != testingKey || !cfg.Audita.LLMAPIKey.IsResolved() { t.Fatal("profile credential was not resolved") } }) } } func TestNotariusCredentialProfileReplacementAndProvenance(t *testing.T) { const baseKey = "NARRATIO_NOTARIUS_UNUSED_BASE_KEY" const profileKey = "NARRATIO_NOTARIUS_PROFILE_KEY" if err := os.Unsetenv(baseKey); err != nil { t.Fatal(err) } t.Setenv(profileKey, "synthetic-notarius-profile-key") for _, credential := range []string{profileKey, "null"} { t.Run(credential, func(t *testing.T) { dir := t.TempDir() root := writePipelineFixture(t, dir, "pipeline.yml", "whisperx:\n transcribe_url: https://transcription.example.test/transcribe\nnotarius:\n enabled: false\n llm_api_key: "+baseKey+"\n") overlay := writePipelineFixture(t, dir, "overlays/testing.yml", "notarius:\n llm_api_key: "+credential+"\n") profile := "testing" cfg, err := LoadPipelineWithOptions(root, PipelineLoadOptions{Overlay: &profile}) if err != nil { t.Fatal(err) } if credential == "null" { if cfg.Notarius.LLMAPIKey != nil { t.Fatal("profile did not remove the base credential") } return } if cfg.Notarius.LLMAPIKey == nil || cfg.Notarius.LLMAPIKey.EnvName() != profileKey || !cfg.Notarius.LLMAPIKey.IsResolved() { t.Fatal("profile credential was not resolved") } sources, err := EffectivePipelineSources(cfg, ResolvedParty{}) if err != nil { t.Fatal(err) } found := false for _, source := range sources { if source.Path == "notarius.llm_api_key" { found = source.Source == overlay } } if !found { t.Fatal("Notarius credential provenance did not identify the selected profile") } }) } } func TestNotariusConfigProfileComposition(t *testing.T) { tests := []struct { name string baseValue string overlayName string overlay string want *string }{ {name: "omitted"}, {name: "explicit empty", baseValue: `""`, want: stringPointer("")}, {name: "explicit default", baseValue: "default", want: stringPointer("default")}, {name: "base named selection", baseValue: "production", want: stringPointer("production")}, {name: "profile replaces named selection", baseValue: "production", overlayName: "testing", overlay: "notarius:\n config_profile: inexpensive\n", want: stringPointer("inexpensive")}, {name: "profile clears named selection", baseValue: "production", overlayName: "testing", overlay: "notarius:\n config_profile: null\n"}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { dir := t.TempDir() rootYAML := "whisperx:\n transcribe_url: https://transcription.example.test/transcribe\nnotarius:\n enabled: false\n" if test.baseValue != "" { rootYAML += " config_profile: " + test.baseValue + "\n" } root := writePipelineFixture(t, dir, "pipeline.yml", rootYAML) options := PipelineLoadOptions{} if test.overlayName != "" { writePipelineFixture(t, dir, "overlays/"+test.overlayName+".yml", test.overlay) options.Overlay = &test.overlayName } cfg, err := LoadPipelineWithOptions(root, options) if err != nil { t.Fatal(err) } if cfg.Notarius == nil || !reflect.DeepEqual(cfg.Notarius.ConfigProfile, test.want) { t.Fatalf("config profile = %#v, want %#v", cfg.Notarius.ConfigProfile, test.want) } }) } } func TestNotariusCredentialRootProvenance(t *testing.T) { const envName = "NARRATIO_NOTARIUS_ROOT_KEY" t.Setenv(envName, "synthetic-notarius-root-key") dir := t.TempDir() root := writePipelineFixture(t, dir, "pipeline.yml", "whisperx:\n transcribe_url: https://transcription.example.test/transcribe\nnotarius:\n enabled: false\n llm_api_key: "+envName+"\n") cfg, err := LoadPipeline(root) if err != nil { t.Fatal(err) } sources, err := EffectivePipelineSources(cfg, ResolvedParty{}) if err != nil { t.Fatal(err) } for _, source := range sources { if source.Path == "notarius.llm_api_key" && source.Source == root { return } } t.Fatal("Notarius credential provenance did not identify the root pipeline") } func TestNotariusCacheControlProfileReplacementAndNullReset(t *testing.T) { tests := []struct { name string overlay string wantResume bool wantMode string wantSource bool wantNil bool }{ {name: "replacement", overlay: "notarius:\n resume: true\n chunk_cache: refresh\n", wantResume: true, wantMode: "refresh", wantSource: true}, {name: "null reset", overlay: "notarius: null\n", wantNil: true}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { dir := t.TempDir() root := writePipelineFixture(t, dir, "pipeline.yml", "whisperx:\n transcribe_url: https://transcription.example.test/transcribe\nnotarius:\n enabled: false\n resume: false\n chunk_cache: bypass\n") overlay := writePipelineFixture(t, dir, "overlays/testing.yml", test.overlay) profile := "testing" cfg, err := LoadPipelineWithOptions(root, PipelineLoadOptions{Overlay: &profile}) if err != nil { t.Fatal(err) } if test.wantNil { if cfg.Notarius != nil { t.Fatalf("Notarius = %#v, want null reset", cfg.Notarius) } return } if cfg.Notarius == nil || cfg.Notarius.Resume != test.wantResume || cfg.Notarius.ChunkCache != test.wantMode { t.Fatalf("cache controls = resume %v, mode %q; want resume %v, mode %q", cfg.Notarius.Resume, cfg.Notarius.ChunkCache, test.wantResume, test.wantMode) } if !test.wantSource { return } sources, err := EffectivePipelineSources(cfg, ResolvedParty{}) if err != nil { t.Fatal(err) } found := map[string]bool{} for _, source := range sources { if source.Path == "notarius.resume" || source.Path == "notarius.chunk_cache" { found[source.Path] = source.Source == overlay } } if !found["notarius.resume"] || !found["notarius.chunk_cache"] { t.Fatalf("cache-control provenance did not identify profile: %#v", found) } }) } } func TestPipelineConfigKitAllowsOneBaseDeclarationWithProfileOverride(t *testing.T) { dir := t.TempDir() writePipelineFixture(t, dir, "pipeline.yml", `configkit: imports: - path: artifact.yml whisperx: transcribe_url: https://transcription.example.test/transcribe `) writePipelineFixture(t, dir, "artifact.yml", "scriptorium:\n artifacts:\n recap:\n prompt_id: base\n output_path: artifacts/recap.md\n") writePipelineFixture(t, dir, "overlays/testing.yml", "scriptorium:\n artifacts:\n recap:\n prompt_id: testing\n") profile := "testing" cfg, err := LoadPipelineWithOptions(filepath.Join(dir, "pipeline.yml"), PipelineLoadOptions{Overlay: &profile}) if err != nil { t.Fatal(err) } if got := cfg.Scriptorium.Artifacts["recap"].PromptID; got != "testing" { t.Fatalf("profile prompt = %q, want testing", got) } } func TestPipelineConfigKitRejectsUnknownDirectiveAlias(t *testing.T) { dir := t.TempDir() path := writePipelineFixture(t, dir, "pipeline.yml", "composition:\n imports: []\n") _, err := LoadPipeline(path) if err == nil || !strings.Contains(err.Error(), "decode.unknown_field") { t.Fatalf("composition alias error = %v", err) } } func writePipelineFixture(t *testing.T, root, relative, contents string) string { t.Helper() path := filepath.Join(root, filepath.FromSlash(relative)) if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(path, []byte(contents), 0o644); err != nil { t.Fatal(err) } return path } func stringPointer(value string) *string { return &value }