package engine import ( "bytes" "errors" "strings" "testing" "gitea.maximumdirect.net/eric/ConfigKit/internal/document" ) type diffIntegrationConfig struct { Defaulted string `yaml:"defaulted"` Dynamic map[string]any `yaml:"dynamic"` Credential Secret `yaml:"credential" configkit:"secret"` Derived string `yaml:"derived"` } type diffOptionalSecretConfig struct { Optional *Secret `yaml:"optional" configkit:"secret"` } func TestDiffUsesFinalValuesAndDependencyDerivedSensitivity(t *testing.T) { var resolveCalls int options := Options[diffIntegrationConfig]{Definition: Definition[diffIntegrationConfig]{ Defaults: func() diffIntegrationConfig { return diffIntegrationConfig{Defaulted: "from-default", Dynamic: map[string]any{"defaulted": "retained"}} }, Resolve: func(configuration *diffIntegrationConfig, resolution *Resolution) error { resolveCalls++ configuration.Derived = configuration.Dynamic["visible"].(string) resolution.Record(Keys("derived"), ChangeNormalization, "derive from effective values", Keys("credential")) return nil }, }} input := func(value string) Source { return Bytes("effective.yml", []byte("dynamic: {visible: "+value+"}\ncredential: DIFF_ENGINE_SECRET\n")) } t.Setenv("DIFF_ENGINE_SECRET", "credential-left-canary") left, err := testLoad(input("before"), options) if err != nil { t.Fatal(err) } t.Setenv("DIFF_ENGINE_SECRET", "credential-right-canary") right, err := testLoad(input("after"), options) if err != nil { t.Fatal(err) } if left.Config.Defaulted != "from-default" || right.Config.Defaulted != "from-default" { t.Fatalf("defaulted values = %q, %q", left.Config.Defaulted, right.Config.Defaulted) } if !left.Inspection.secrets["/credential"] || !left.Inspection.secrets["/derived"] || !right.Inspection.secrets["/credential"] || !right.Inspection.secrets["/derived"] { t.Fatalf("typed or dependency-derived sensitivity was not retained: left=%#v right=%#v", left.Inspection.secrets, right.Inspection.secrets) } if node, present := documentAtPathWithLookup(left.Inspection.final, Keys("derived"), left.Inspection.immutableLookup); !present || node.Text != "before" { t.Fatalf("left frozen derived value = %#v, present=%v", node, present) } differences, err := Diff(left.Inspection, right.Inspection) if err != nil { t.Fatal(err) } if len(differences) != 2 || differences[0].Path.String() != "/derived" || differences[1].Path.String() != "/dynamic/visible" { t.Fatalf("differences = %#v", differences) } if !differences[0].Left.Redacted || !differences[0].Right.Redacted || !differences[0].HiddenChanged || differences[0].Left.YAML != "'[REDACTED]'\n" || differences[0].Right.YAML != "'[REDACTED]'\n" { t.Fatalf("dependency-derived difference = %#v", differences[0]) } if differences[1].Left.Redacted || differences[1].Right.Redacted || differences[1].HiddenChanged || differences[1].Left.YAML != "before\n" || differences[1].Right.YAML != "after\n" { t.Fatalf("public dynamic difference = %#v", differences[1]) } for _, value := range differences { if strings.Contains(value.Left.YAML+value.Right.YAML, "credential-") { t.Fatal("diff snapshot exposed a resolved credential") } } var effective bytes.Buffer if err := left.Inspection.WriteEffective(&effective); err != nil { t.Fatal(err) } if strings.Contains(effective.String(), "credential-left-canary") { t.Fatal("standalone effective rendering exposed a credential after diff") } explanation, ok := left.Inspection.Explain(Keys("derived")) if !ok || !explanation.Effective.Redacted || explanation.Effective.YAML != "'[REDACTED]'\n" { t.Fatalf("explanation changed after diff: %#v, ok=%v", explanation, ok) } if _, ok := left.Inspection.Metadata().Lookup(Keys("dynamic", "visible")); !ok { t.Fatal("metadata lookup failed after diff") } if resolveCalls != 2 { t.Fatalf("resolution callbacks ran during diff: %d calls", resolveCalls) } // Returned records can be modified without affecting a later comparison. differences[0].Path.segments[0].key = "changed-by-caller" differences[0].Left.YAML = "changed-by-caller" t.Setenv("DIFF_ENGINE_SECRET", "credential-after-load-canary") repeated, err := Diff(left.Inspection, right.Inspection) if err != nil || len(repeated) != 2 || repeated[0].Path.String() != "/derived" || repeated[0].Left.YAML != "'[REDACTED]'\n" { t.Fatalf("repeated diff = %#v, err = %v", repeated, err) } if resolveCalls != 2 { t.Fatalf("repeated diff reran resolution: %d calls", resolveCalls) } independent, err := testLoad(input("before"), options) if err != nil { t.Fatal(err) } if resolveCalls != 3 { t.Fatalf("independent load did not retain normal resolution behavior: %d calls", resolveCalls) } if _, err := Diff(left.Inspection, independent.Inspection); err != nil { t.Fatalf("independent load comparison = %v", err) } } func TestDiffUsesReferenceNamesAndFrozenSecretState(t *testing.T) { loadReference := func(name string) *testResult[diffOptionalSecretConfig] { t.Helper() result, err := testLoad[diffOptionalSecretConfig](Bytes("secret-reference.yml", []byte("optional: "+name+"\n"))) if err != nil { t.Fatal(err) } return result } t.Setenv("DIFF_ROTATING_SECRET", "first-credential-canary") left := loadReference("DIFF_ROTATING_SECRET") t.Setenv("DIFF_ROTATING_SECRET", "second-credential-canary") right := loadReference("DIFF_ROTATING_SECRET") t.Setenv("DIFF_ROTATING_SECRET", "changed-after-load-canary") differences, err := Diff(left.Inspection, right.Inspection) if err != nil || len(differences) != 0 { t.Fatalf("credential rotation behind one reference = %#v, err = %v", differences, err) } if node, present := documentAtPathWithLookup(left.Inspection.final, Keys("optional"), left.Inspection.immutableLookup); !present || node.Text != "DIFF_ROTATING_SECRET" { t.Fatalf("frozen reference = %#v, present=%v", node, present) } if node, present := documentAtPathWithLookup(right.Inspection.final, Keys("optional"), right.Inspection.immutableLookup); !present || node.Text != "DIFF_ROTATING_SECRET" { t.Fatalf("right frozen reference = %#v, present=%v", node, present) } t.Setenv("DIFF_OTHER_SECRET", "other-credential-canary") other := loadReference("DIFF_OTHER_SECRET") differences, err = Diff(left.Inspection, other.Inspection) if err != nil || len(differences) != 1 || differences[0].Path.String() != "/optional" { t.Fatalf("reference-name change = %#v, err = %v", differences, err) } if !differences[0].Left.Present || !differences[0].Right.Present || !differences[0].Left.Redacted || !differences[0].Right.Redacted || !differences[0].HiddenChanged || differences[0].Left.YAML != "'[REDACTED]'\n" || differences[0].Right.YAML != "'[REDACTED]'\n" { t.Fatalf("classified reference-name diff = %#v", differences[0]) } absent, err := testLoad[diffOptionalSecretConfig](Bytes("absent-secret.yml", []byte("{}\n"))) if err != nil { t.Fatal(err) } if absent.Config.Optional != nil { t.Fatalf("missing optional reference decoded as %#v", absent.Config.Optional) } if node, present := documentAtPathWithLookup(absent.Inspection.final, Keys("optional"), absent.Inspection.immutableLookup); !present || node.Kind != document.Null { t.Fatalf("optional nil projection = %#v, present=%v", node, present) } differences, err = Diff(absent.Inspection, left.Inspection) if err != nil || len(differences) != 1 || differences[0].Path.String() != "/optional" { t.Fatalf("optional absence diff = %#v, err = %v", differences, err) } if !differences[0].Left.Present || !differences[0].Left.Redacted || !differences[0].Right.Present || !differences[0].Right.Redacted || !differences[0].HiddenChanged { t.Fatalf("optional reference change snapshots = %#v", differences[0]) } for _, value := range differences { if strings.Contains(value.Left.YAML+value.Right.YAML, "credential-canary") { t.Fatal("reference diff exposed a resolved credential") } } } func TestDiffProtectsHistoricalSensitiveRemovalAndReplacement(t *testing.T) { type configuration struct { Dynamic map[string]any `yaml:"dynamic"` } secretPath := Keys("dynamic", "token") secretSelector := func(path Path) bool { return path.String() == secretPath.String() } load := func(input string, resolve func(*configuration, *Resolution) error) *testResult[configuration] { t.Helper() result, err := testLoad(Bytes("historical-secret.yml", []byte(input)), Options[configuration]{Definition: Definition[configuration]{ Secret: secretSelector, Resolve: resolve, }}) if err != nil { t.Fatal(err) } return result } t.Run("removed historical value", func(t *testing.T) { left := load("dynamic: {token: removed-canary}\n", func(configuration *configuration, resolution *Resolution) error { delete(configuration.Dynamic, "token") resolution.Record(secretPath, ChangeNormalization, "remove token") return nil }) right := load("dynamic: {token: current-canary}\n", nil) if !left.Inspection.secrets[secretPath.String()] { t.Fatal("removed historical selector was not retained") } if _, present := documentAtPathWithLookup(left.Inspection.final, secretPath, left.Inspection.immutableLookup); present { t.Fatal("removed token remains in the effective left tree") } differences, err := Diff(left.Inspection, right.Inspection) if err != nil || len(differences) != 1 || differences[0].Path.String() != secretPath.String() { t.Fatalf("historical removal diff = %#v, err = %v", differences, err) } if differences[0].Left.Present || !differences[0].Right.Present || !differences[0].Right.Redacted || !differences[0].HiddenChanged { t.Fatalf("historical removal snapshots = %#v", differences[0]) } if strings.Contains(differences[0].Left.YAML+differences[0].Right.YAML, "canary") { t.Fatal("historical removal diff exposed a sensitive value") } }) t.Run("replaced historical value", func(t *testing.T) { left := load("dynamic: {token: original-canary}\n", func(configuration *configuration, resolution *Resolution) error { configuration.Dynamic["token"] = "replacement-canary" resolution.Record(secretPath, ChangeNormalization, "replace token") return nil }) right := load("dynamic: {token: other-canary}\n", nil) if !left.Inspection.secrets[secretPath.String()] { t.Fatal("replaced historical selector was not retained") } differences, err := Diff(left.Inspection, right.Inspection) if err != nil || len(differences) != 1 || differences[0].Path.String() != secretPath.String() { t.Fatalf("historical replacement diff = %#v, err = %v", differences, err) } if !differences[0].Left.Present || !differences[0].Right.Present || !differences[0].Left.Redacted || !differences[0].Right.Redacted || !differences[0].HiddenChanged { t.Fatalf("historical replacement snapshots = %#v", differences[0]) } if strings.Contains(differences[0].Left.YAML+differences[0].Right.YAML, "canary") { t.Fatal("historical replacement diff exposed a sensitive value") } }) } func TestDiffReturnsSafeErrorsWithoutRecords(t *testing.T) { valid := diffTestInspection(&document.Node{Kind: document.String, Text: "safe"}) for _, test := range []struct { name string left, right *Inspection }{ {name: "nil left", right: valid}, {name: "nil right", left: valid}, {name: "missing final tree", left: &Inspection{}, right: valid}, } { t.Run(test.name, func(t *testing.T) { differences, err := Diff(test.left, test.right) if differences != nil || err == nil || err != errDiffInspectionUnavailable || strings.Contains(err.Error(), "safe") { t.Fatalf("differences, err = %#v, %v", differences, err) } }) } } func TestDiffSerializationErrorDoesNotExposeWriterDetails(t *testing.T) { err := writeDiffDocument(diffErrorWriter{}, &document.Node{Kind: document.String, Text: "safe"}) if err != errDiffSnapshotUnavailable || strings.Contains(err.Error(), "CANARY") { t.Fatalf("serialization error = %v", err) } } type diffErrorWriter struct{} func (diffErrorWriter) Write([]byte) (int, error) { return 0, errors.New("CANARY_WRITER_ERROR") }