package engine import ( "bytes" "encoding/json" "errors" "fmt" "io" "os" "path/filepath" "reflect" "strings" "testing" "gitea.maximumdirect.net/eric/ConfigKit/internal/document" ) type explanationConcurrency struct { Transcription struct { Concurrency int `yaml:"concurrency"` } `yaml:"transcription"` } func TestExplainShowsDefaultBaseAndEffectiveOverlayValue(t *testing.T) { directory := t.TempDir() mainPath := writeImportFile(t, directory, "config.yml", "configkit:\n imports: [{path: transcription.yml}]\n") writeImportFile(t, directory, "transcription.yml", "transcription:\n concurrency: 8\n") writeImportFile(t, directory, filepath.Join("overlays", "testing.yml"), "transcription:\n concurrency: 1\n") result, err := testLoad[explanationConcurrency](File(mainPath), Options[explanationConcurrency]{ Imports: true, Overlay: "testing", Definition: Definition[explanationConcurrency]{Defaults: func() explanationConcurrency { value := explanationConcurrency{} value.Transcription.Concurrency = 4 return value }}, }) if err != nil { t.Fatal(err) } explanation, ok := result.Explain(Keys("transcription", "concurrency")) if !ok { t.Fatal("known path was not explained") } if got := explanationValues(explanation); !reflect.DeepEqual(got, []string{"4\n", "8\n", "1\n"}) { t.Fatalf("history values = %#v", got) } if explanation.Effective.YAML != "1\n" || explanation.Origin.Layer != LayerOverlay || len(explanation.Origin.Locations) != 1 || explanation.Origin.Locations[0].SourceID != "source-3" || explanation.Origin.Locations[0].Line != 2 { t.Fatalf("effective explanation = %#v", explanation) } var rendered bytes.Buffer if err := result.WriteExplanation(&rendered, Keys("transcription", "concurrency")); err != nil || !strings.Contains(rendered.String(), `layer="overlay"`) || !strings.Contains(rendered.String(), `label="overlays/testing.yml"`) { t.Fatalf("rendered explanation = %v\n%s", err, rendered.String()) } } type explanationConfig struct { Concurrency int `yaml:"concurrency"` Equal string `yaml:"equal"` Limit int `yaml:"limit"` Items []string `yaml:"items"` Labels map[string]string `yaml:"labels"` Dynamic any `yaml:"dynamic"` Maybe *string `yaml:"maybe"` Empty []string `yaml:"empty"` Credential Secret `yaml:"credential"` Classified Secret `yaml:"classified"` DerivedA string `yaml:"derived_a"` DerivedB string `yaml:"derived_b"` } func TestExplainReplaysCLIResolutionContainersAndSafeHistoricalValues(t *testing.T) { directory := t.TempDir() mainPath := writeImportFile(t, directory, "config.yml", `concurrency: 8 equal: same limit: wrong-before-overlay items: [base, removed] labels: {base: retained} dynamic: {retired: HISTORICAL_EXPLAIN_CANARY, public: visible} credential: APP_SECRET classified: APP_SECRET `) writeImportFile(t, directory, filepath.Join("overlays", "testing.yml"), `concurrency: 1 equal: same limit: 7 items: [overlay] labels: {overlay: retained} dynamic: selected `) result, err := testLoadWithSecretLookup(File(mainPath), func(name string) (string, bool) { if name != "APP_SECRET" { t.Fatalf("secret lookup = %q", name) } return "RESOLVED_EXPLAIN_CANARY", true }, Options[explanationConfig]{ Overlay: "testing", Overrides: []Override{ {Path: Keys("concurrency"), Value: 2, Flag: "--concurrency"}, {Path: Keys("equal"), Value: "same", Flag: "--equal"}, {Path: Keys("items"), Value: []any{"cli", "restored"}, Flag: "--items"}, {Path: Keys("labels"), Value: map[string]any{"cli": "retained"}, Flag: "--label"}, }, Definition: Definition[explanationConfig]{ Defaults: func() explanationConfig { return explanationConfig{ Concurrency: 4, Equal: "same", Labels: map[string]string{"default": "retained"}, Maybe: nil, Empty: []string{}, Credential: SecretEnv("APP_SECRET"), Classified: SecretEnv("APP_SECRET"), } }, Secret: func(path Path) bool { return path.String() == "/dynamic/retired" || path.String() == "/classified" }, Resolve: func(configuration *explanationConfig, resolution *Resolution) error { Normalize(resolution, Keys("concurrency"), &configuration.Concurrency, 3, "normalized\nconcurrency", Keys("labels")) Normalize(resolution, Keys("equal"), &configuration.Equal, "same", "record equal value") Normalize(resolution, Keys("derived_a"), &configuration.DerivedA, "DERIVED_EXPLAIN_A", "derived cycle a", Keys("derived_b")) Normalize(resolution, Keys("derived_b"), &configuration.DerivedB, "DERIVED_EXPLAIN_B", "derived cycle b", Keys("derived_a"), Keys("dynamic")) return nil }, }, }) if err != nil { t.Fatal(err) } concurrency, ok := result.Explain(Keys("concurrency")) if !ok { t.Fatal("concurrency was not explained") } if got := explanationValues(concurrency); !reflect.DeepEqual(got, []string{"4\n", "8\n", "1\n", "2\n", "3\n"}) { t.Fatalf("concurrency history = %#v", got) } if got := historyValueLayers(concurrency); !reflect.DeepEqual(got, []Layer{LayerDefault, LayerBase, LayerOverlay, LayerCLI, LayerResolution}) { t.Fatalf("concurrency layers = %#v", got) } if concurrency.History[4].Before.YAML != "2\n" || concurrency.History[4].Reasons[0] != "normalized\nconcurrency" || concurrency.History[4].Dependencies[0].String() != "/labels" { t.Fatalf("resolution history = %#v", concurrency.History[4]) } equal, _ := result.Explain(Keys("equal")) if len(equal.History) != 5 { t.Fatalf("equal writes were lost: %#v", equal.History) } for _, change := range equal.History[1:] { if change.Before.YAML != "same\n" || change.After.YAML != "same\n" { t.Fatalf("equal replacement = %#v", change) } } limit, _ := result.Explain(Keys("limit")) if got := explanationValues(limit); !reflect.DeepEqual(got, []string{"0\n", "wrong-before-overlay\n", "7\n"}) { t.Fatalf("overridden invalid input was not retained: %#v", got) } removed, ok := result.Explain(Keys("dynamic", "retired")) if !ok || removed.Effective.Present || len(removed.History) < 2 { t.Fatalf("removed path = %#v, %v", removed, ok) } for _, change := range removed.History { if strings.Contains(change.Before.YAML+change.After.YAML, "HISTORICAL_EXPLAIN_CANARY") { t.Fatalf("historical secret escaped redaction: %#v", change) } } if !removed.History[0].After.Redacted || removed.History[len(removed.History)-1].After.Present { t.Fatalf("removed secret history = %#v", removed.History) } dynamic, _ := result.Explain(Keys("dynamic")) if len(dynamic.History) < 3 || !dynamic.History[1].After.Redacted || strings.Contains(dynamic.History[1].After.YAML, "HISTORICAL_EXPLAIN_CANARY") { t.Fatalf("container redaction history = %#v", dynamic.History) } items, _ := result.Explain(Keys("items")) if len(items.History) != 4 || !strings.Contains(items.History[1].After.YAML, "removed") || strings.Contains(items.History[2].After.YAML, "removed") || !strings.Contains(items.History[3].After.YAML, "restored") { t.Fatalf("atomic sequence history = %#v", items.History) } removedIndex, ok := result.Explain(Keys("items").Index(1)) if !ok || !removedIndex.History[1].Before.Present || removedIndex.History[1].After.Present || !removedIndex.History[2].After.Present { t.Fatalf("sequence descendant history = %#v", removedIndex.History) } labels, _ := result.Explain(Keys("labels")) if len(labels.History) != 4 || !strings.Contains(labels.Effective.YAML, "default: retained") || !strings.Contains(labels.Effective.YAML, "overlay: retained") || !strings.Contains(labels.Effective.YAML, "cli: retained") { t.Fatalf("composite mapping history = %#v", labels) } if labels.Origin.Layer != LayerCLI || len(labels.Origin.Locations) != 3 { t.Fatalf("composite effective origin = %#v", labels.Origin) } maybe, _ := result.Explain(Keys("maybe")) empty, _ := result.Explain(Keys("empty")) if !maybe.Effective.Present || maybe.Effective.YAML != "null\n" || !empty.Effective.Present || empty.Effective.YAML != "[]\n" { t.Fatalf("null/empty snapshots = maybe %#v empty %#v", maybe.Effective, empty.Effective) } credential, _ := result.Explain(Keys("credential")) if credential.SecretReference == nil || credential.SecretReference.Name != "APP_SECRET" || credential.Effective.YAML != "APP_SECRET\n" { t.Fatalf("secret reference = %#v", credential) } classified, _ := result.Explain(Keys("classified")) if classified.SecretReference != nil || !classified.Effective.Redacted || strings.Contains(classified.Effective.YAML, "APP_SECRET") { t.Fatalf("classified secret reference = %#v", classified) } var derivedExplanations []Explanation for _, path := range []Path{Keys("derived_a"), Keys("derived_b")} { derived, _ := result.Explain(path) if !derived.Effective.Redacted || strings.Contains(derived.Effective.YAML, "DERIVED_EXPLAIN") { t.Fatalf("dependency closure at %s = %#v", path.String(), derived) } derivedExplanations = append(derivedExplanations, derived) } formatted := fmt.Sprintf("%v %#v %v", removed, credential, derivedExplanations) encoded, marshalErr := json.Marshal(append([]Explanation{removed, credential, classified}, derivedExplanations...)) if marshalErr != nil || strings.Contains(formatted+string(encoded), "RESOLVED_EXPLAIN_CANARY") || strings.Contains(formatted+string(encoded), "HISTORICAL_EXPLAIN_CANARY") || strings.Contains(formatted+string(encoded), "DERIVED_EXPLAIN") { t.Fatalf("public records disclosed a canary: %s %s", formatted, encoded) } concurrency.Path.segments[0].key = "mutated" concurrency.History[4].Reasons[0] = "mutated" concurrency.History[4].Dependencies[0].segments[0].key = "mutated" result.Config.Concurrency = 99 again, _ := result.Explain(Keys("concurrency")) if again.Path.String() != "/concurrency" || again.Effective.YAML != "3\n" || again.History[4].Reasons[0] != "normalized\nconcurrency" || again.History[4].Dependencies[0].String() != "/labels" { t.Fatalf("returned explanation aliased internal history: %#v", again) } var first, second bytes.Buffer if err := result.WriteExplanation(&first, Keys("concurrency")); err != nil { t.Fatal(err) } if err := result.WriteExplanation(&second, Keys("concurrency")); err != nil || first.String() != second.String() { t.Fatalf("nondeterministic rendering: %v\n%s\n%s", err, first.String(), second.String()) } if strings.Contains(first.String(), "\nconcurrency\"") || !strings.Contains(first.String(), `"normalized\nconcurrency"`) { t.Fatalf("reason controls were not escaped: %q", first.String()) } unknownOutput := bytes.NewBufferString("unchanged") if err := result.WriteExplanation(unknownOutput, Keys("unknown")); !errors.Is(err, errExplanationUnknownPath) || unknownOutput.String() != "unchanged" { t.Fatalf("unknown writer = %q, %v", unknownOutput.String(), err) } if _, ok := result.Explain(Keys("items").Index(-1)); ok { t.Fatal("malformed path was explained") } wantWriteError := errors.New("write failed") if err := result.WriteExplanation(errorWriter{err: wantWriteError}, Keys("concurrency")); !errors.Is(err, wantWriteError) { t.Fatalf("writer error = %v", err) } if err := result.WriteExplanation(shortWriter{}, Keys("concurrency")); !errors.Is(err, io.ErrShortWrite) { t.Fatalf("short writer error = %v", err) } var invalidInput bytes.Buffer if err := result.WriteExplanation(&invalidInput, Keys("limit")); err != nil || !strings.Contains(invalidInput.String(), "policy-validated input") || strings.Contains(invalidInput.String(), "wrong-before-overlay\n source: layer=\"base\"\n before (canonical typed)") { t.Fatalf("historical representation rendering = %v\n%s", err, invalidInput.String()) } } func TestSecretsBootstrapLoadAndExplanationRemainSeparateAndSafe(t *testing.T) { const name = "CONFIGKIT_EXPLAIN_BOOTSTRAP_TEST" previous, present := os.LookupEnv(name) if err := os.Unsetenv(name); err != nil { t.Fatal(err) } t.Cleanup(func() { if present { _ = os.Setenv(name, previous) } else { _ = os.Unsetenv(name) } }) directory := t.TempDir() writeSecretTestFile(t, directory, name, "BOOTSTRAP_EXPLAIN_CANARY\n") report, err := LoadSecretsDir(directory) if err != nil || len(report.Entries()) != 1 || report.Entries()[0].Status != SecretFileLoaded { t.Fatalf("bootstrap = %#v, %v", report, err) } type config struct { Credential Secret `yaml:"credential"` } result, err := testLoad[config](Bytes("config.yml", []byte("credential: "+name+"\n"))) if err != nil { t.Fatal(err) } var output bytes.Buffer if err := result.WriteExplanation(&output, Keys("credential")); err != nil { t.Fatal(err) } if !strings.Contains(output.String(), name) || strings.Contains(output.String(), "BOOTSTRAP_EXPLAIN_CANARY") { t.Fatalf("explanation mixed bootstrap value into reference report: %s", output.String()) } } func TestHistoryNodeBudgetCountsSharedTreesOnce(t *testing.T) { leaf := &document.Node{Kind: document.String, Text: "value"} root := &document.Node{Kind: document.Mapping, Mapping: []document.Entry{{Key: "value", Value: leaf}}} applications := []historyApplication{{layer: LayerBase, value: root}} if !historyNodesWithin(2, root, root, root, applications, nil) { t.Fatal("structurally shared history was counted more than once") } other := document.Clone(root) if historyNodesWithin(3, root, other, root, applications, nil) { t.Fatal("distinct retained nodes did not consume the budget") } } func explanationValues(explanation Explanation) []string { values := make([]string, len(explanation.History)) for index, change := range explanation.History { values[index] = change.After.YAML } return values } func historyValueLayers(explanation Explanation) []Layer { layers := make([]Layer, len(explanation.History)) for index, change := range explanation.History { layers[index] = change.Origin.Layer } return layers } func TestExplanationReconcilesDefaultsAfterInputReplacement(t *testing.T) { type cfg struct { M map[string]int `yaml:"m"` } for _, base := range []string{"null", "replaced-text"} { for _, cli := range []bool{false, true} { t.Run(fmt.Sprintf("%s/cli=%v", base, cli), func(t *testing.T) { root := t.TempDir() main := writeImportFile(t, root, "main.yml", "m: "+base+"\n") opts := Options[cfg]{Definition: Definition[cfg]{Defaults: func() cfg { return cfg{M: map[string]int{"a": 1}} }}} if cli { opts.Overrides = []Override{{Path: Keys("m"), Value: map[string]any{"b": 2}, Flag: "--m"}} } else { writeImportFile(t, root, "overlays/p.yml", "m: {b: 2}\n") opts.Overlay = "p" } result, err := testLoad(File(main), opts) if err != nil { t.Fatal(err) } for _, path := range []Path{Keys("m"), Keys("m", "a")} { explanation, ok := result.Explain(path) if !ok { t.Fatal("missing explanation") } last := explanation.History[len(explanation.History)-1] if last.After != explanation.Effective || last.Origin.Operation != ChangeDecoding { t.Fatalf("history does not reconcile final value: %#v", explanation) } info, _ := result.Metadata().Lookup(path) if last.Origin.Layer != info.Origin.Layer { t.Fatal("decoding lost effective origin") } } }) } } } func TestIntegerVersionInspectionRetainsInputAndReconcilesTypedValue(t *testing.T) { type config struct { Version string `yaml:"version"` } rule := &VersionRule{Path: Keys("version"), Check: func(version Version, _ *Validation) error { if version.Text != "4" { t.Errorf("version = %#v", version) } return nil }} options := Options[config]{Definition: Definition[config]{ Defaults: func() config { return config{Version: "4"} }, Version: rule, }} first, err := testLoad(Bytes("version.yml", []byte("version: 4\n")), options) if err != nil { t.Fatal(err) } second, err := testLoad(Bytes("version.yml", []byte("version: 4\n")), options) if err != nil { t.Fatal(err) } if first.Config.Version != "4" || second.Config.Version != "4" { t.Fatalf("configurations = %#v / %#v", first.Config, second.Config) } inputVersion, inputOK := documentAtPath(first.applications[0].value, Keys("version")) finalVersion, finalOK := documentAtPath(first.final, Keys("version")) if !inputOK || inputVersion.Kind != document.Number || inputVersion.Text != "4" || !finalOK || finalVersion.Kind != document.String || finalVersion.Text != "4" { t.Fatalf("input/final = %#v / %#v", inputVersion, finalVersion) } info, ok := first.Metadata().Lookup(Keys("version")) if !ok || !info.Explicit || info.Null || info.Origin.Layer != LayerBase || len(info.Origin.Locations) != 1 || info.Origin.Locations[0].SourceID != "source-1" || info.Origin.Locations[0].Line != 1 { t.Fatalf("version metadata = %#v, %v", info, ok) } explanation, ok := first.Explain(Keys("version")) if !ok || explanation.Effective.YAML != "'4'\n" { t.Fatalf("version explanation = %#v, %v", explanation, ok) } foundInput, foundDecoding := false, false for _, change := range explanation.History { if change.After.YAML == "4\n" && change.Origin.Layer == LayerBase { foundInput = true } if change.Before.YAML == "4\n" && change.After.YAML == "'4'\n" && change.Origin.Operation == ChangeDecoding { foundDecoding = true } if change.Origin.Layer == LayerResolution { t.Fatalf("conversion recorded as resolution: %#v", change) } } if !foundInput || !foundDecoding { t.Fatalf("version history = %#v", explanation.History) } paths := first.Metadata().HistoricalPaths() foundPath := false for _, path := range paths { if path.String() == "/version" { foundPath = true } } if !foundPath || len(first.Metadata().History(Keys("version"))) < 2 { t.Fatalf("historical version paths/history = %#v / %#v", paths, first.Metadata().History(Keys("version"))) } var effective strings.Builder if err := first.WriteEffective(&effective); err != nil || effective.String() != "version: '4'\n" { t.Fatalf("effective output = %q, %v", effective.String(), err) } first.Config.Version = "mutated" effective.Reset() if err := first.WriteEffective(&effective); err != nil || effective.String() != "version: '4'\n" || second.Config.Version != "4" { t.Fatalf("detached results = %q / %#v, %v", effective.String(), second.Config, err) } } func TestSensitiveIntegerVersionStaysRedactedWithoutSecretLookup(t *testing.T) { type config struct { Version string `yaml:"version"` } lookups := 0 result, err := testLoadWithSecretLookup(Bytes("version.yml", []byte("version: 987654321\n")), func(string) (string, bool) { lookups++ return "CREDENTIAL_CANARY", true }, Options[config]{Definition: Definition[config]{ Version: &VersionRule{Path: Keys("version"), Check: func(Version, *Validation) error { return nil }}, Secret: func(path Path) bool { return path.String() == "/version" }, }}) if err != nil || result.Config.Version != "987654321" || lookups != 0 { t.Fatalf("result = %#v, lookups = %d, err = %v", result, lookups, err) } var effective strings.Builder if err := result.WriteEffective(&effective); err != nil { t.Fatal(err) } explanation, ok := result.Explain(Keys("version")) formatted := effective.String() + fmt.Sprintf("%#v %#v", explanation, result.Metadata().History(Keys("version"))) if !ok || !explanation.Effective.Redacted || strings.Contains(formatted, "987654321") || strings.Contains(formatted, "CREDENTIAL_CANARY") { t.Fatalf("sensitive inspection = %q / %#v", effective.String(), explanation) } } func TestExplanationReplaysResolutionAdditionsAndOverlappingIntent(t *testing.T) { type cfg struct { M map[string]any `yaml:"m"` } for _, overlap := range []bool{false, true} { t.Run(fmt.Sprint(overlap), func(t *testing.T) { result, err := testLoad(Bytes("base", []byte("m: {old: 1}\n")), Options[cfg]{Definition: Definition[cfg]{Resolve: func(c *cfg, r *Resolution) error { c.M["new"] = map[string]any{"child": int64(7)} delete(c.M, "old") r.Record(Keys("m", "new"), ChangeDerivedDefault, "add nested", Keys("m", "old")) r.Record(Keys("m", "old"), ChangeNormalization, "remove old") if overlap { r.Record(Keys("m"), ChangeNormalization, "update container") } return nil }}}) if err != nil { t.Fatal(err) } for _, path := range []Path{Keys("m"), Keys("m", "new"), Keys("m", "new", "child"), Keys("m", "old")} { x, ok := result.Explain(path) if !ok { t.Fatal("missing known history") } var count int for _, v := range x.History { if v.Origin.Layer == LayerResolution { count++ if overlap && len(v.Reasons) != 3 { t.Fatal("lost overlapping intent") } } } if overlap && count != 1 { t.Fatalf("overlapping records applied %d times", count) } last := x.History[len(x.History)-1] if last.After != x.Effective { t.Fatalf("incorrect replay for %s: %#v", path, x) } } }) } } func TestExplanationReplayDescriptorsRemainDetachedAcrossQueries(t *testing.T) { type config struct { Count int `yaml:"count"` } result, err := testLoad(Bytes("resolution-explanation.yml", []byte("count: 1\n")), Options[config]{ Definition: Definition[config]{Resolve: func(value *config, resolution *Resolution) error { value.Count = 2 resolution.Record(Keys("count"), ChangeNormalization, "normalized", Keys("count")) return nil }}, }) if err != nil { t.Fatal(err) } first, ok := result.Explain(Keys("count")) if !ok { t.Fatal("missing explanation") } var firstResolution *ValueChange for index := range first.History { if first.History[index].Origin.Layer == LayerResolution { firstResolution = &first.History[index] break } } if firstResolution == nil || len(firstResolution.Reasons) != 1 || len(firstResolution.Dependencies) != 1 { t.Fatalf("resolution history = %#v", first.History) } firstResolution.Reasons[0] = "mutated" firstResolution.Dependencies[0].segments[0].key = "mutated" second, ok := result.Explain(Keys("count")) if !ok { t.Fatal("missing repeated explanation") } for _, change := range second.History { if change.Origin.Layer == LayerResolution { if !reflect.DeepEqual(change.Reasons, []string{"normalized"}) || len(change.Dependencies) != 1 || change.Dependencies[0].String() != "/count" { t.Fatalf("repeated explanation observed caller mutation: %#v", change) } return } } t.Fatalf("resolution change missing from repeated explanation: %#v", second.History) }