package engine import ( "errors" "strings" "testing" ) func TestResolutionAuditsOrdinaryListFields(t *testing.T) { type item struct { Count int `yaml:"count"` Enabled bool `yaml:"enabled"` Nested []*int `yaml:"nested"` } type config struct { Items []item `yaml:"items"` } for _, test := range []struct { name string other, resize bool wantPath string }{ {name: "covered field"}, {name: "uncovered field", other: true, wantPath: "/items/0/enabled"}, {name: "length change", resize: true, wantPath: "/items"}, } { t.Run(test.name, func(t *testing.T) { result, err := testLoad(Bytes("items", []byte("items: [{count: 3, enabled: true, nested: [null]}]\n")), Options[config]{Definition: Definition[config]{ Resolve: func(value *config, r *Resolution) error { value.Items[0].Count = 0 r.RecordNormalization(Keys("items").Index(0).Key("count"), "clear count") if test.other { value.Items[0].Enabled = false } if test.resize { value.Items = append(value.Items, item{}) } return nil }, }}) if test.wantPath == "" { if err != nil || result.Config.Items[0].Count != 0 { t.Fatalf("result = %#v, error = %v", result, err) } return } var failure *LoadError if !errors.As(err, &failure) { t.Fatalf("error = %v", err) } found := false for _, diagnostic := range failure.Diagnostics() { if diagnostic.Code == CodeResolveUnrecordedChange && diagnostic.Path.String() == test.wantPath { found = true } } if !found { t.Fatalf("diagnostics = %#v", failure.Diagnostics()) } }) } } func TestResolutionComparisonKeepsDiffAtomic(t *testing.T) { before := parseCompositionDocument(t, "before", "items: [{values: [true, null]}]") after := parseCompositionDocument(t, "after", "items: [{values: [false, 0]}]") deltas := compareResolutionValues(before, after) if len(deltas) != 2 || deltas[0].path.String() != "/items/0/values/0" || deltas[1].path.String() != "/items/0/values/1" { t.Fatalf("resolution deltas = %#v", deltas) } atomic := compareConfigurationValues(before, after) if len(atomic) != 1 || atomic[0].path.String() != "/items" { t.Fatalf("atomic deltas = %#v", atomic) } } func TestResolutionAuditsRegisteredCollectionChanges(t *testing.T) { type item struct { ID string `yaml:"id"` Value string `yaml:"value"` } type config struct { Items []item `yaml:"items"` } for _, test := range []struct { name string move, container, rename bool wantPath string }{ {name: "field edit"}, {name: "uncovered move", move: true, wantPath: "/items"}, {name: "covered move", move: true, container: true}, {name: "identity replacement", rename: true, wantPath: "/items"}, } { t.Run(test.name, func(t *testing.T) { definition := Definition[config]{Resolve: func(value *config, r *Resolution) error { value.Items[0].Value = "changed" if test.move { value.Items[0], value.Items[1] = value.Items[1], value.Items[0] } if test.rename { value.Items[0].ID = "new" } index := 0 if test.move { index = 1 } r.RecordNormalization(Keys("items").Index(index).Key("value"), "change value") if test.container { r.RecordNormalization(Keys("items"), "reorder") } return nil }} definition = DefinitionWithSequenceIdentityRegistrations(definition, []SequenceIdentityRegistrationInput{{ExactPath: pathPointer(Keys("items")), IdentityFields: []string{"id"}}}) result, err := testLoad(Bytes("items", []byte("items: [{id: a, value: old}, {id: b, value: other}]\n")), Options[config]{Definition: definition}) if test.wantPath == "" { if err != nil { t.Fatal(err) } if test.move { correspondence := result.history.resolutionPayload.correspondence entry, ok := correspondence.entryPath(Keys("items").Index(1).Key("value")) if !ok || entry.String() != "/items/0/value" { t.Fatalf("retained entry = %s, %t", entry.String(), ok) } for _, snapshot := range result.history.resolutionPayload.snapshots { if snapshot.change.Path.String() == "/items/1/value" && (snapshot.before == nil || snapshot.before.Text != "old" || !snapshot.hasEntry || snapshot.entryPath.String() != "/items/0/value") { t.Fatalf("paired before = %#v", snapshot.before) } if snapshot.before != nil { found := false for _, root := range result.history.documentRoots() { if root == snapshot.before { found = true } } if !found { t.Fatal("paired snapshot omitted from retained roots") } } } } return } var failure *LoadError if !errors.As(err, &failure) { t.Fatalf("error = %v", err) } found := false for _, diagnostic := range failure.Diagnostics() { if diagnostic.Code == CodeResolveUnrecordedChange && diagnostic.Path.String() == test.wantPath { found = true } } if !found { t.Fatalf("diagnostics = %#v", failure.Diagnostics()) } }) } } func TestRegisteredResolutionFallbackAndHelperTarget(t *testing.T) { type item struct { ID string `yaml:"id"` Value string `yaml:"value"` } type config struct { Items []item `yaml:"items"` } registration := []SequenceIdentityRegistrationInput{{ExactPath: pathPointer(Keys("items")), IdentityFields: []string{"id"}}} makeDefinition := func(resolve func(*config, *Resolution) error) Definition[config] { return DefinitionWithSequenceIdentityRegistrations(Definition[config]{Defaults: func() config { return config{Items: []item{{ID: "a"}, {ID: "a"}}} }, Resolve: resolve}, registration) } _, err := testLoad(NoSource(), Options[config]{Definition: makeDefinition(func(value *config, r *Resolution) error { value.Items[1].ID = "b" r.RecordNormalization(Keys("items"), "repair identities") return nil })}) if err != nil { t.Fatalf("repair rejected: %v", err) } _, err = testLoad(NoSource(), Options[config]{Definition: makeDefinition(func(*config, *Resolution) error { return nil })}) if err == nil { t.Fatal("invalid final identities accepted") } definition := DefinitionWithSequenceIdentityRegistrations(Definition[config]{Resolve: func(value *config, r *Resolution) error { Normalize(r, Keys("items").Index(0).Key("value"), &value.Items[0].Value, "changed", "normalize") value.Items[0], value.Items[1] = value.Items[1], value.Items[0] r.RecordNormalization(Keys("items"), "reorder") return nil }}, registration) _, err = testLoad(Bytes("items", []byte("items: [{id: a, value: first}, {id: b, value: second}]\n")), Options[config]{Definition: definition}) var failure *LoadError if !errors.As(err, &failure) { t.Fatalf("helper error = %v", err) } found := false for _, diagnostic := range failure.Diagnostics() { if diagnostic.Code == CodeResolveInvalidTarget && diagnostic.Path.String() == "/items/0/value" { found = true } } if !found { t.Fatalf("diagnostics = %#v", failure.Diagnostics()) } } func TestRegisteredResolutionMetadataFollowsEntryIdentity(t *testing.T) { type item struct { ID string `yaml:"id"` Enabled bool `yaml:"enabled"` Count int `yaml:"count"` } type config struct { Items []item `yaml:"items"` } entryFixed := false definition := DefinitionWithSequenceIdentityRegistrations(Definition[config]{Resolve: func(value *config, r *Resolution) error { entryFixed = r.IsExplicit(Keys("items").Index(0).Key("enabled")) && !r.IsExplicit(Keys("items").Index(1).Key("enabled")) value.Items[0], value.Items[1] = value.Items[1], value.Items[0] value.Items = append(value.Items, item{ID: "new"}) r.RecordNormalization(Keys("items"), "reorder and add") return nil }}, []SequenceIdentityRegistrationInput{{ExactPath: pathPointer(Keys("items")), IdentityFields: []string{"id"}}}) result, err := testLoad(Bytes("records", []byte("items: [{id: a, enabled: false}, {id: b, count: 0}]\n")), Options[config]{Definition: definition}) if err != nil || !entryFixed { t.Fatalf("load = %#v, fixed entry = %t, error = %v", result, entryFixed, err) } for _, check := range []struct { index int key string explicit bool }{{0, "enabled", false}, {0, "count", true}, {1, "enabled", true}, {1, "count", false}, {2, "id", false}} { info, ok := result.Metadata().Lookup(Keys("items").Index(check.index).Key(check.key)) if !ok || info.Explicit != check.explicit || info.Origin.Layer != LayerResolution { t.Errorf("item %d %s = %#v, %t", check.index, check.key, info, ok) } } } func TestRegisteredResolutionValidationLocationFollowsMovedRecord(t *testing.T) { type item struct { ID string `yaml:"id"` Value string `yaml:"value"` } type config struct { Items []item `yaml:"items"` } definition := DefinitionWithSequenceIdentityRegistrations(Definition[config]{ Resolve: func(value *config, r *Resolution) error { value.Items[0], value.Items[1] = value.Items[1], value.Items[0] r.RecordNormalization(Keys("items"), "reorder") return nil }, Validate: func(_ *config, v *Validation) { v.Error(Keys("items").Index(0).Key("value"), "invalid") }, }, []SequenceIdentityRegistrationInput{{ExactPath: pathPointer(Keys("items")), IdentityFields: []string{"id"}}}) _, err := testLoad(Bytes("records", []byte("items:\n - id: a\n value: first\n - id: b\n value: second\n")), Options[config]{Definition: definition}) var failure *LoadError if !errors.As(err, &failure) { t.Fatalf("error = %v", err) } for _, diagnostic := range failure.Diagnostics() { if diagnostic.Path.String() == "/items/0/value" && diagnostic.Primary != nil && diagnostic.Primary.Line == 5 { return } } t.Fatalf("diagnostics = %#v", failure.Diagnostics()) } func TestRegisteredResolutionHistoryFollowsMatchedSource(t *testing.T) { type item struct { ID string `yaml:"id"` Value string `yaml:"value"` } type config struct { Items []item `yaml:"items"` } definition := DefinitionWithSequenceIdentityRegistrations(Definition[config]{Resolve: func(value *config, r *Resolution) error { value.Items[0], value.Items[1] = value.Items[1], value.Items[0] value.Items = append(value.Items, item{ID: "new", Value: "added"}) r.RecordNormalization(Keys("items"), "reorder and add") return nil }}, []SequenceIdentityRegistrationInput{{ExactPath: pathPointer(Keys("items")), IdentityFields: []string{"id"}}}) result, err := testLoad(Bytes("records", []byte("items:\n - id: a\n value: first\n - id: b\n value: second\n")), Options[config]{Definition: definition}) if err != nil { t.Fatal(err) } for _, check := range []struct{ index, line int }{{0, 5}, {1, 3}} { path := Keys("items").Index(check.index).Key("value") if location, ok := result.history.latestHistoricalLocation(path); !ok || location.Line != check.line { t.Errorf("historical location for %s = %#v, %t", path.String(), location, ok) } history := result.Metadata().History(path) found := false for _, change := range history { for _, location := range change.Origin.Locations { if location.Line == check.line { found = true } else if location.Line == 3 || location.Line == 5 { t.Errorf("wrong record in %s: %#v", path.String(), history) } } } if !found { t.Errorf("missing source line %d in %#v", check.line, history) } explanation, ok := result.Inspection.Explain(path) if !ok { t.Fatalf("missing explanation for %s", path.String()) } want := "second\n" if check.index == 1 { want = "first\n" } foundValue := false for _, change := range explanation.History { if change.Origin.Layer == LayerMain && change.After.YAML == want { foundValue = true } } if !foundValue { t.Errorf("missing matched input %q in %#v", want, explanation.History) } } for _, change := range result.Metadata().History(Keys("items").Index(2).Key("value")) { if change.Origin.Layer == LayerMain { t.Fatalf("added record inherited input: %#v", change) } } } func TestMovedRecordExplanationIncludesInheritedValue(t *testing.T) { type item struct { ID string `yaml:"id"` Value string `yaml:"value"` } type config struct { Items []item `yaml:"items"` } definition := DefinitionWithSequenceIdentityRegistrations(Definition[config]{Resolve: func(value *config, r *Resolution) error { value.Items[0], value.Items[1] = value.Items[1], value.Items[0] r.RecordNormalization(Keys("items"), "reorder") return nil }}, []SequenceIdentityRegistrationInput{{ExactPath: pathPointer(Keys("items")), IdentityFields: []string{"id"}}}) result, err := testLoad(Bytes("records", []byte("items: [{id: parent, value: inherited}, {id: child, extends: {id: parent}}]\n")), Options[config]{Definition: definition}) if err != nil { t.Fatal(err) } explanation, ok := result.Inspection.Explain(Keys("items").Index(0).Key("value")) if !ok { t.Fatal("missing explanation") } found := false for _, change := range explanation.History { if change.After.YAML == "inherited\n" { found = true } } if !found { t.Fatalf("inherited history = %#v", explanation.History) } } func TestMovedRecordExplanationIncludesCLIValue(t *testing.T) { type item struct { ID string `yaml:"id"` Value string `yaml:"value"` } type config struct { Items []item `yaml:"items"` } definition := DefinitionWithSequenceIdentityRegistrations(Definition[config]{Resolve: func(value *config, r *Resolution) error { value.Items[0], value.Items[1] = value.Items[1], value.Items[0] r.RecordNormalization(Keys("items"), "reorder") return nil }}, []SequenceIdentityRegistrationInput{{ExactPath: pathPointer(Keys("items")), IdentityFields: []string{"id"}}}) result, err := testLoad(NoSource(), Options[config]{Definition: definition, Overrides: []Override{{ Path: Keys("items"), Flag: "--items", Value: []any{map[string]any{"id": "a", "value": "first"}, map[string]any{"id": "b", "value": "second"}}, }}}) if err != nil { t.Fatal(err) } explanation, ok := result.Inspection.Explain(Keys("items").Index(1).Key("value")) if !ok { t.Fatal("missing explanation") } for _, change := range explanation.History { if change.Origin.Layer == LayerCLI && change.After.YAML == "first\n" { return } } t.Fatalf("CLI history = %#v", explanation.History) } func TestRegisteredResolutionExplainsRemovalAndReusedPositions(t *testing.T) { type item struct { ID string `yaml:"id"` Value string `yaml:"value"` } type config struct { Items []item `yaml:"items"` } definition := DefinitionWithSequenceIdentityRegistrations(Definition[config]{Resolve: func(value *config, r *Resolution) error { value.Items = value.Items[1:] r.RecordNormalization(Keys("items"), "remove first") return nil }}, []SequenceIdentityRegistrationInput{{ExactPath: pathPointer(Keys("items")), IdentityFields: []string{"id"}}}) result, err := testLoad(Bytes("records", []byte("items:\n - id: reports\n value: report\n - id: invoices\n value: invoice\n")), Options[config]{Definition: definition}) if err != nil { t.Fatal(err) } path := Keys("items").Index(0).Key("value") explanation, ok := result.Inspection.Explain(path) if !ok { t.Fatal("survivor explanation missing") } found := false for _, change := range explanation.History { if change.Origin.Layer == LayerResolution { if change.Before.YAML != "invoice\n" || change.After.YAML != "invoice\n" { t.Fatalf("survivor operation = %#v", change) } found = true } if change.Origin.Layer == LayerMain && change.After.YAML == "report\n" { t.Fatalf("removed occupant leaked into survivor: %#v", change) } } if !found { t.Fatal("missing collection operation") } container, ok := result.Inspection.Explain(Keys("items")) if !ok { t.Fatal("collection explanation missing") } found = false for _, change := range container.History { if change.Origin.Layer == LayerResolution && strings.Contains(change.Before.YAML, "reports") && !strings.Contains(change.After.YAML, "reports") { found = true } } if !found { t.Fatalf("missing removed collection evidence: %#v", container.History) } mainEvidence := false for _, change := range result.Metadata().History(Keys("items")) { if change.Origin.Layer == LayerMain && len(change.Origin.Locations) != 0 { mainEvidence = true } } if !mainEvidence { t.Fatal("collection history lost source attribution") } if _, ok := result.Inspection.Explain(Keys("items").Index(1)); !ok { t.Fatal("absent historical position missing") } if _, ok := result.Inspection.Explain(Keys("items").Index(8)); ok { t.Fatal("unknown position reported") } } func TestRegisteredResolutionExplainsAddedAndChangedIdentities(t *testing.T) { type item struct { ID string `yaml:"id"` Value string `yaml:"value"` } type config struct { Items []item `yaml:"items"` } for _, replace := range []bool{false, true} { definition := DefinitionWithSequenceIdentityRegistrations(Definition[config]{Resolve: func(value *config, r *Resolution) error { if replace { value.Items[0] = item{ID: "new", Value: "added"} } else { value.Items = append([]item{{ID: "new", Value: "added"}}, value.Items...) } r.RecordNormalization(Keys("items"), "update collection") return nil }}, []SequenceIdentityRegistrationInput{{ExactPath: pathPointer(Keys("items")), IdentityFields: []string{"id"}}}) result, err := testLoad(Bytes("records", []byte("items: [{id: old, value: original}]\n")), Options[config]{Definition: definition}) if err != nil { t.Fatal(err) } explanation, ok := result.Inspection.Explain(Keys("items").Index(0).Key("value")) if !ok { t.Fatal("added explanation missing") } for _, change := range explanation.History { if change.Origin.Layer == LayerMain || (change.Origin.Layer == LayerResolution && change.Before.Present) { t.Fatalf("added record inherited previous value: %#v", explanation.History) } } if !replace { moved, ok := result.Inspection.Explain(Keys("items").Index(1).Key("value")) if !ok { t.Fatal("shifted survivor missing") } found := false for _, change := range moved.History { if change.Origin.Layer == LayerResolution && change.Before.YAML == "original\n" && change.After.YAML == "original\n" { found = true } } if !found { t.Fatalf("shifted survivor = %#v", moved.History) } } } } func TestRegisteredResolutionExplainsRemovedFieldOnMovedRecord(t *testing.T) { type config struct { Items []map[string]any `yaml:"items"` } definition := DefinitionWithSequenceIdentityRegistrations(Definition[config]{Resolve: func(value *config, r *Resolution) error { delete(value.Items[0], "extra") value.Items[0], value.Items[1] = value.Items[1], value.Items[0] r.RecordNormalization(Keys("items"), "remove and reorder") return nil }}, []SequenceIdentityRegistrationInput{{ExactPath: pathPointer(Keys("items")), IdentityFields: []string{"id"}}}) result, err := testLoad(Bytes("records", []byte("items: [{id: a, extra: own}, {id: b, extra: other}]\n")), Options[config]{Definition: definition}) if err != nil { t.Fatal(err) } explanation, ok := result.Inspection.Explain(Keys("items").Index(1).Key("extra")) if !ok { t.Fatal("removed field explanation missing") } for _, change := range explanation.History { if change.Origin.Layer == LayerResolution && change.Before.YAML == "own\n" && !change.After.Present { return } } t.Fatalf("removed field history = %#v", explanation.History) } func TestRegisteredResolutionTransfersSensitivityFromEntry(t *testing.T) { type item struct { ID string `yaml:"id"` Value string `yaml:"value"` } type config struct { Items []item `yaml:"items"` Derived string `yaml:"derived"` } definition := DefinitionWithSequenceIdentityRegistrations(Definition[config]{ Secret: func(path Path) bool { return path.String() == "/items/0/value" }, Resolve: func(value *config, r *Resolution) error { value.Items[0], value.Items[1] = value.Items[1], value.Items[0] value.Derived = value.Items[1].Value r.RecordNormalization(Keys("items"), "reorder") r.RecordNormalization(Keys("derived"), "derive", Keys("items").Index(1).Key("value")) return nil }, }, []SequenceIdentityRegistrationInput{{ExactPath: pathPointer(Keys("items")), IdentityFields: []string{"id"}}}) result, err := testLoad(Bytes("records", []byte("items: [{id: a, value: confidential}, {id: b, value: visible}]\n")), Options[config]{Definition: definition}) if err != nil { t.Fatal(err) } for _, path := range []Path{Keys("items").Index(0).Key("value"), Keys("items").Index(1).Key("value"), Keys("derived")} { if !pathIsSecret(result.Inspection.secrets, path) { t.Errorf("sensitivity lost at %s", path.String()) } } } func TestRegisteredResolutionKeepsRemovedSensitivity(t *testing.T) { type item struct { ID string `yaml:"id"` Value string `yaml:"value"` } type config struct { Items []item `yaml:"items"` } definition := DefinitionWithSequenceIdentityRegistrations(Definition[config]{ Secret: func(path Path) bool { return path.String() == "/items/0/value" }, Resolve: func(value *config, r *Resolution) error { value.Items = value.Items[1:] r.RecordNormalization(Keys("items"), "remove record") return nil }, }, []SequenceIdentityRegistrationInput{{ExactPath: pathPointer(Keys("items")), IdentityFields: []string{"id"}}}) result, err := testLoad(Bytes("records", []byte("items: [{id: a, value: confidential}, {id: b, value: visible}]\n")), Options[config]{Definition: definition}) if err != nil { t.Fatal(err) } if !pathIsSecret(result.Inspection.secrets, Keys("items").Index(0).Key("value")) { t.Fatal("removed historical sensitivity lost") } } func TestMovedAndRemovedSensitiveValuesStayHidden(t *testing.T) { type item struct { ID string `yaml:"id"` Value string `yaml:"value"` } type config struct { Items []item `yaml:"items"` Derived string `yaml:"derived"` } for _, remove := range []bool{false, true} { definition := DefinitionWithSequenceIdentityRegistrations(Definition[config]{ Secret: func(path Path) bool { return path.String() == "/items/1/value" }, Resolve: func(value *config, r *Resolution) error { value.Items[0], value.Items[1] = value.Items[1], value.Items[0] value.Derived = value.Items[1].Value r.RecordNormalization(Keys("items").Index(1).Key("value"), "retain value", Keys("derived")) if remove { value.Items = value.Items[:1] } r.RecordNormalization(Keys("items"), "update records") r.RecordNormalization(Keys("derived"), "derive", Keys("items").Index(1).Key("value")) return nil }, }, []SequenceIdentityRegistrationInput{{ExactPath: pathPointer(Keys("items")), IdentityFields: []string{"id"}}}) result, err := testLoad(Bytes("records", []byte("items: [{id: a, value: PRIVATE_CANARY}, {id: b, value: other}]\n")), Options[config]{Definition: definition}) if err != nil { t.Fatal(err) } for _, path := range []Path{Keys("items"), Keys("items").Index(0), Keys("items").Index(1), Keys("derived")} { explanation, ok := result.Inspection.Explain(path) if !ok { continue } if strings.Contains(explanation.Effective.YAML, "PRIVATE_CANARY") { t.Fatalf("effective leaked at %s", path.String()) } for _, change := range explanation.History { if strings.Contains(change.Before.YAML, "PRIVATE_CANARY") || strings.Contains(change.After.YAML, "PRIVATE_CANARY") { t.Fatalf("history leaked at %s: %#v", path.String(), change) } } } var output strings.Builder if err := result.Inspection.WriteEffective(&output); err != nil || strings.Contains(output.String(), "PRIVATE_CANARY") { t.Fatalf("effective output leaked: %q, %v", output.String(), err) } } } func TestMovedDynamicTaggedValueStaysHidden(t *testing.T) { type sensitive struct { ID string `yaml:"id"` Token string `yaml:"token" configkit:"secret"` } type config struct { Items []any `yaml:"items"` } definition := DefinitionWithSequenceIdentityRegistrations(Definition[config]{ Defaults: func() config { return config{Items: []any{sensitive{ID: "a", Token: "DYNAMIC_PRIVATE_CANARY"}, sensitive{ID: "b", Token: "other"}}} }, Resolve: func(value *config, r *Resolution) error { value.Items[0], value.Items[1] = value.Items[1], value.Items[0] r.RecordNormalization(Keys("items"), "reorder") return nil }, }, []SequenceIdentityRegistrationInput{{ExactPath: pathPointer(Keys("items")), IdentityFields: []string{"id"}}}) result, err := testLoad(NoSource(), Options[config]{Definition: definition}) if err != nil { t.Fatal(err) } for _, path := range []Path{Keys("items"), Keys("items").Index(1).Key("token")} { explanation, ok := result.Inspection.Explain(path) if !ok { t.Fatalf("missing explanation %s", path.String()) } if strings.Contains(explanation.Effective.YAML, "DYNAMIC_PRIVATE_CANARY") { t.Fatal("effective value leaked") } for _, change := range explanation.History { if strings.Contains(change.Before.YAML, "DYNAMIC_PRIVATE_CANARY") || strings.Contains(change.After.YAML, "DYNAMIC_PRIVATE_CANARY") { t.Fatal("history leaked") } } } } func TestMovedDependencyProtectsEarlierMatchedValue(t *testing.T) { type item struct { ID string `yaml:"id"` Secret string `yaml:"secret"` Value string `yaml:"value"` } type config struct { Items []item `yaml:"items"` } definition := DefinitionWithSequenceIdentityRegistrations(Definition[config]{ Secret: func(path Path) bool { return path.String() == "/items/0/secret" }, Resolve: func(value *config, r *Resolution) error { value.Items[0], value.Items[1] = value.Items[1], value.Items[0] value.Items[0].Value = value.Items[1].Secret r.RecordNormalization(Keys("items"), "reorder") r.RecordNormalization(Keys("items").Index(0).Key("value"), "derive", Keys("items").Index(1).Key("secret")) return nil }, }, []SequenceIdentityRegistrationInput{{ExactPath: pathPointer(Keys("items")), IdentityFields: []string{"id"}}}) result, err := testLoad(Bytes("records", []byte("items: [{id: a, secret: HIDDEN_A, value: public}, {id: b, value: EARLIER_PRIVATE}]\n")), Options[config]{Definition: definition}) if err != nil { t.Fatal(err) } explanation, ok := result.Inspection.Explain(Keys("items").Index(0).Key("value")) if !ok { t.Fatal("missing explanation") } for _, change := range explanation.History { if strings.Contains(change.Before.YAML, "EARLIER_PRIVATE") || strings.Contains(change.After.YAML, "EARLIER_PRIVATE") || strings.Contains(change.After.YAML, "HIDDEN_A") { t.Fatalf("sensitive lineage leaked: %#v", change) } } }