package engine import "testing" func TestResolutionCollectionCorrespondence(t *testing.T) { policy, diagnostics := compileSequenceIdentityPolicy([]sequenceIdentityRegistration{{ selector: sequenceIdentitySelector{literalSequenceSelector("items")}, identityFields: []string{"id"}, }}) if len(diagnostics) != 0 { t.Fatal(diagnostics) } for _, test := range []struct { name, before, after string eligible, structural bool finalIndex, entryIndex int matched bool }{ {"swap", "items: [{id: a}, {id: b}]", "items: [{id: b}, {id: a}]", true, true, 0, 1, true}, {"insert", "items: [{id: a}]", "items: [{id: b}, {id: a}]", true, true, 0, 0, false}, {"remove", "items: [{id: a}, {id: b}]", "items: [{id: b}]", true, true, 0, 1, true}, {"same", "items: [{id: a}]", "items: [{id: a}]", true, false, 0, 0, true}, {"duplicate", "items: [{id: a}, {id: a}]", "items: [{id: a}]", false, false, 0, 0, true}, {"missing", "items: [{name: a}]", "items: [{id: a}]", false, false, 0, 0, true}, {"identity replacement", "items: [{id: a}]", "items: [{id: b}]", true, true, 0, 0, false}, {"invalid final", "items: [{id: a}]", "items: [bad]", false, false, 0, 0, true}, } { t.Run(test.name, func(t *testing.T) { before := parseCompositionDocument(t, "before", test.before) after := parseCompositionDocument(t, "after", test.after) matching := buildResolutionCorrespondence(before, after, policy) collection := matching.collections["/items"] if collection.eligible != test.eligible || collection.structural != test.structural { t.Fatalf("collection = %#v", collection) } entry, ok := matching.entryPath(Keys("items").Index(test.finalIndex).Key("value")) if ok != test.matched || (ok && entry.String() != Keys("items").Index(test.entryIndex).Key("value").String()) { t.Fatalf("entry = %s, matched = %t", entry.String(), ok) } }) } } func TestResolutionCorrespondenceDynamicCompoundIdentities(t *testing.T) { policy, diagnostics := compileSequenceIdentityPolicy([]sequenceIdentityRegistration{{ selector: sequenceIdentitySelector{literalSequenceSelector("groups"), anyMappingSequenceSelector(), literalSequenceSelector("items")}, identityFields: []string{"id", "kind"}, }}) if len(diagnostics) != 0 { t.Fatal(diagnostics) } before := parseCompositionDocument(t, "before", "groups: {alpha: {items: [{id: 1, kind: x}, {id: '1', kind: x}]}, empty: {items: []}}") after := parseCompositionDocument(t, "after", "groups: {alpha: {items: [{id: '1', kind: x}, {id: 1.0, kind: x}]}, empty: {items: []}}") matching := buildResolutionCorrespondence(before, after, policy) retained := matching.clone() changed := matching.collections["/groups/alpha/items"] changed.finalToEntry[0] = 0 matching.collections["/groups/alpha/items"] = changed if entry, ok := retained.entryPath(Keys("groups", "alpha", "items").Index(0)); !ok || entry.String() != "/groups/alpha/items/1" { t.Fatalf("retained correspondence shares mutable indexes: %s, %t", entry.String(), ok) } matching = retained if empty := matching.collections["/groups/empty/items"]; !empty.eligible || empty.structural { t.Fatalf("empty = %#v", empty) } path, ok := matching.entryPath(Keys("groups", "alpha", "items").Index(0).Key("missing")) if !ok || path.String() != "/groups/alpha/items/1/missing" { t.Fatalf("entry = %s, %t", path.String(), ok) } wrongShape, ok := matching.entryPath(Keys("groups").Index(0).Key("items").Index(0)) if !ok || wrongShape.String() != "/groups/0/items/0" { t.Fatalf("mapping shape = %s, %t", wrongShape.String(), ok) } }