package config import ( "testing" "gitea.maximumdirect.net/eric/notarius/internal/framework/contracts" "gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline" ) func TestRedactedConfigRemovesAPIKeyValues(t *testing.T) { cfg := Default() cfg.LLMProfiles[pipeline.DefaultLLMProfile] = LLMProfile{ Provider: "openai-compatible", BaseURL: "https://example.invalid/v1", Model: "test-model", APIKey: "secret", APIKeyEnv: "NOTARIUS_TEST_API_KEY", TimeoutSeconds: 600, MaxRetries: 3, MaxConcurrency: 1, } cfg.LLMProfiles["other"] = LLMProfile{APIKey: "other-secret", Model: "other-model"} redacted := cfg.Redacted() if redacted.LLMProfiles[pipeline.DefaultLLMProfile].APIKey != redactedSecret { t.Fatalf("expected default API key redacted, got %+v", redacted.LLMProfiles[pipeline.DefaultLLMProfile]) } if redacted.LLMProfiles["other"].APIKey != redactedSecret { t.Fatalf("expected other API key redacted, got %+v", redacted.LLMProfiles["other"]) } if redacted.LLMProfiles[pipeline.DefaultLLMProfile].Model != "test-model" { t.Fatalf("expected non-secret fields preserved, got %+v", redacted.LLMProfiles[pipeline.DefaultLLMProfile]) } if cfg.LLMProfiles[pipeline.DefaultLLMProfile].APIKey != "secret" { t.Fatalf("redaction mutated original config") } } func TestConfigRedactedDiagnosticsPayloadRedactsAPIKeys(t *testing.T) { cfg := Default() profile := cfg.LLMProfiles[pipeline.DefaultLLMProfile] profile.APIKey = "secret" profile.Model = "test-model" cfg.LLMProfiles[pipeline.DefaultLLMProfile] = profile payload, ok := cfg.RedactedDiagnosticsPayload().(Config) if !ok { t.Fatalf("expected Config payload, got %T", cfg.RedactedDiagnosticsPayload()) } if payload.LLMProfiles[pipeline.DefaultLLMProfile].APIKey != redactedSecret { t.Fatalf("expected API key redacted, got %+v", payload.LLMProfiles[pipeline.DefaultLLMProfile]) } if payload.LLMProfiles[pipeline.DefaultLLMProfile].Model != "test-model" { t.Fatalf("expected non-secret fields preserved, got %+v", payload.LLMProfiles[pipeline.DefaultLLMProfile]) } if cfg.LLMProfiles[pipeline.DefaultLLMProfile].APIKey != "secret" { t.Fatalf("redacted diagnostics payload mutated original config") } } func TestEffectiveConfigRedactedDiagnosticsPayloadRedactsAndCopies(t *testing.T) { cfg := validConfig() profile := cfg.LLMProfiles[pipeline.DefaultLLMProfile] profile.APIKey = "secret" cfg.LLMProfiles[pipeline.DefaultLLMProfile] = profile lane := cfg.Pipelines["example"].Artifacts["events"] lane.Extract.Options = map[string]any{"temperature": 0.2} lane.References = map[string]string{"roster": "./roster.yml"} lane.Extract.References = map[string]string{"glossary": "./glossary.md"} lane.Normalize.References = map[string]string{"notes": "./normalize.md"} cfg.Pipelines["example"].Artifacts["events"] = lane pipelineProfile := cfg.Pipelines["example"] pipelineProfile.Chunk.References = map[string]string{"scene_guide": "./scene.md"} cfg.Pipelines["example"] = pipelineProfile effective, err := cfg.Resolve(ResolveInput{ PipelineID: "example", Only: []string{"events"}, Catalog: fakeCatalog(t, pipeline.ModuleSpec{ Key: "generic", Stage: pipeline.StageChunk, Requires: []string{"source"}, Provides: []string{"chunks"}, ReferenceSlots: []contracts.ReferenceSlot{ {Name: "scene_guide"}, }, }, pipeline.ModuleSpec{ Key: "fake/extract", Stage: pipeline.StageExtract, Requires: []string{"chunks"}, Provides: []string{"artifact"}, ReferenceSlots: []contracts.ReferenceSlot{ {Name: "glossary"}, {Name: "roster"}, }, }, pipeline.ModuleSpec{ Key: "noop", Stage: pipeline.StageNormalize, Requires: []string{"merged"}, Provides: []string{"normalized"}, ReferenceSlots: []contracts.ReferenceSlot{ {Name: "notes"}, }, }, ), }) if err != nil { t.Fatalf("Resolve: %v", err) } payload, ok := effective.RedactedDiagnosticsPayload().(EffectiveConfig) if !ok { t.Fatalf("expected EffectiveConfig payload, got %T", effective.RedactedDiagnosticsPayload()) } if payload.Config.LLMProfiles[pipeline.DefaultLLMProfile].APIKey != redactedSecret { t.Fatalf("expected nested API key redacted, got %+v", payload.Config.LLMProfiles[pipeline.DefaultLLMProfile]) } if cfg.LLMProfiles[pipeline.DefaultLLMProfile].APIKey != "secret" { t.Fatalf("redacted diagnostics payload mutated source config") } if payload.PipelineID != effective.PipelineID || payload.ResolvedPipeline.Digest != effective.ResolvedPipeline.Digest { t.Fatalf("expected pipeline metadata preserved, got %+v", payload) } payload.Only[0] = "changed" if effective.Only[0] != "events" { t.Fatalf("expected only lanes to be copied") } payload.ResolvedPipeline.ArtifactLanes[0].Extract.Options["temperature"] = 1.0 if effective.ResolvedPipeline.ArtifactLanes[0].Extract.Options["temperature"] != 0.2 { t.Fatalf("expected resolved pipeline options to be copied") } payload.ResolvedPipeline.ArtifactLanes[0].ExtractReferences.Bindings[0].Source = "./changed.yml" if referenceBindingSource(effective.ResolvedPipeline.ArtifactLanes[0].ExtractReferences.Bindings, "roster") != "./roster.yml" { t.Fatalf("expected resolved pipeline references to be copied") } payload.ResolvedPipeline.Chunk.References["scene_guide"] = "./changed-scene.md" if effective.ResolvedPipeline.Chunk.References["scene_guide"] != "./scene.md" { t.Fatalf("expected chunk references to be copied") } payload.ResolvedPipeline.ArtifactLanes[0].Extract.References["glossary"] = "./changed-glossary.md" if effective.ResolvedPipeline.ArtifactLanes[0].Extract.References["glossary"] != "./glossary.md" { t.Fatalf("expected extract references to be copied") } payload.ResolvedPipeline.ArtifactLanes[0].Normalize.References["notes"] = "./changed-normalize.md" if effective.ResolvedPipeline.ArtifactLanes[0].Normalize.References["notes"] != "./normalize.md" { t.Fatalf("expected normalize references to be copied") } effective.ResolvedPipeline.ArtifactLanes[0].ExtractReferences.ReferenceSet = contracts.ReferenceSet{ Slots: map[string]contracts.ResolvedReferenceSlot{ "roster": { Slot: contracts.ReferenceSlot{Name: "roster"}, Items: []contracts.ReferenceItem{ { SlotName: "roster", Content: []byte("reference content"), }, }, }, }, } payload, ok = effective.RedactedDiagnosticsPayload().(EffectiveConfig) if !ok { t.Fatalf("expected EffectiveConfig payload, got %T", effective.RedactedDiagnosticsPayload()) } payload.ResolvedPipeline.ArtifactLanes[0].ExtractReferences.ReferenceSet.Slots["roster"].Items[0].Content[0] = 'X' got := effective.ResolvedPipeline.ArtifactLanes[0].ExtractReferences.ReferenceSet.Slots["roster"].Items[0].Content if string(got) != "reference content" { t.Fatalf("expected materialized reference content to be copied, got %q", got) } } func referenceBindingSource(bindings []pipeline.ReferenceBinding, slotName string) string { for _, binding := range bindings { if binding.SlotName == slotName { return binding.Source } } return "" }