package engine import ( "bytes" "reflect" "strings" "testing" "go.maximumdirect.net/configkit/internal/document" ) func TestSequenceHistoryReplaysAppendAtAssignedIndexes(t *testing.T) { policy := mustCompileSequenceIdentityPolicy(t, []sequenceIdentityRegistration{ {selector: sequenceIdentitySelector{ literalSequenceSelector("collections"), anyMappingSequenceSelector(), literalSequenceSelector("records"), }, identityFields: []string{"id"}}, }) first := parseCompositionDocument(t, "source-1", "collections: {north: {records: [{id: a, value: first}]}, south: {records: [{id: x}]}}\n") second := parseCompositionDocument(t, "source-2", "collections: {north: {records: [{id: b, value: second}]}, south: {records: [{id: y}]}}\n") composed, diagnostics := composeDocumentsWithPolicy(policy, first, second) if len(diagnostics) != 0 { t.Fatalf("compose diagnostics = %#v", diagnostics) } for applicationIndex, application := range composed.applications { for path, want := range map[string]int{"/collections/north/records": 1, "/collections/south/records": 1} { sequence, present := documentAtPath(application.value, mustParsePath(t, path)) if !present || sequence.Kind != document.Sequence || len(sequence.Sequence) != want { t.Errorf("application %d retained %s = %#v, %v; want one source fragment item", applicationIndex, path, sequence, present) } } } working := parseCompositionDocument(t, "defaults", "collections: {north: {records: [{id: default}]}, south: {records: [{id: default}]}}\n") index := new(mappingIndex) for _, application := range composed.applications { applyHistoryApplication(&working, application, index) } for path, want := range map[string][]string{ "/collections/north/records": {"a", "b"}, "/collections/south/records": {"x", "y"}, } { sequence, present := documentAtPath(working, mustParsePath(t, path)) if !present || sequence.Kind != document.Sequence || len(sequence.Sequence) != len(want) { t.Fatalf("replayed %s = %#v", path, sequence) } for itemIndex, id := range want { field := sequence.Sequence[itemIndex].Mapping[0].Value.Text if field != id { t.Errorf("replayed %s[%d].id = %q, want %q", path, itemIndex, field, id) } } } application := composed.applications[1] cloned := cloneHistoryApplications(composed.applications) cloned[1].sequences[0].path.segments[0].key = "mutated" if composed.applications[1].sequences[0].path.String() != "/collections/north/records" { t.Fatal("cloned sequence history path aliases its source") } addedPath := Keys("collections", "north", "records").Index(1).Key("value") if operation, relevant := applicationOperationPath(application, addedPath, nil); !relevant || operation.String() != addedPath.String() { t.Fatalf("appended value operation = %s, %v", operation.String(), relevant) } if _, relevant := applicationOperationPath(application, Keys("collections", "north", "records").Index(0).Key("value"), nil); relevant { t.Fatal("later contribution was reported as changing an earlier record") } origin := historyApplicationOrigin(application, addedPath, nil) if len(origin.Locations) != 1 || origin.Locations[0].SourceID != "source-2" { t.Fatalf("appended value origin = %#v", origin) } overlay := historyApplication{ layer: LayerOverlay, value: parseCompositionDocument(t, "overlay", "collections: {north: {records: []}}\n"), } applyHistoryApplication(&working, overlay, index) replaced, present := documentAtPath(working, Keys("collections", "north", "records")) if !present || replaced.Kind != document.Sequence || len(replaced.Sequence) != 0 { t.Fatalf("ordinary later list replacement = %#v, %v", replaced, present) } } func TestTierSequenceHistoryReplaysSelectedOperation(t *testing.T) { t.Parallel() policy := mustCompileSequenceIdentityPolicy(t, []sequenceIdentityRegistration{{ selector: sequenceIdentitySelector{literalSequenceSelector("records")}, identityFields: []string{"id"}, }}) for _, test := range []struct { name string mode sequenceMode }{{"append", sequenceModeAppend}, {"upsert", sequenceModeUpsert}, {"replace", sequenceModeReplace}} { t.Run(test.name, func(t *testing.T) { mode := test.mode lower, diagnostics := composeDocumentsWithPolicy(policy, parseCompositionDocument(t, "lower", "records: [{id: a, removed: old}, {id: b, value: old, removed: old}]\n")) if len(diagnostics) != 0 { t.Fatal(diagnostics) } secondID := "d" if mode == sequenceModeUpsert { secondID = "b" } declarations := []sequenceDeclarationSpec{{destination: Keys("records"), modePresent: true, mode: mode}} higher, diagnostics := composeDocumentsWithCollectionPolicy(policy, lower.collectionPolicies, declarations, parseCompositionDocument(t, "first-peer", "records: [{id: c, value: first}]\n"), parseCompositionDocument(t, "second-peer", "records: [{id: "+secondID+", value: second}]\n"), ) if len(diagnostics) != 0 { t.Fatal(diagnostics) } assembly := newAssemblyAccumulator(lower) if diagnostics := assembly.applyOverlayWithCollectionPolicy(higher.root, higher.origins, higher.collectionPolicies, higher.applications, LayerMain, policy, declarations); len(diagnostics) != 0 { t.Fatal(diagnostics) } if len(assembly.current.applications) != 3 { t.Fatalf("source applications = %d, want all three sources", len(assembly.current.applications)) } if mode == sequenceModeUpsert { query := Keys("records").Index(1).Key("value") if _, relevant := applicationOperationPath(assembly.current.applications[1], query, nil); relevant { t.Fatal("first peer was attributed to another peer's upserted record") } origin := historyApplicationOrigin(assembly.current.applications[2], query, nil) if len(origin.Locations) != 1 || origin.Locations[0].SourceID != "second-peer" { t.Fatalf("upsert history source = %#v", origin) } } working := parseCompositionDocument(t, "initial", "{}\n") index := new(mappingIndex) for _, application := range assembly.current.applications { applyHistoryApplication(&working, application, index) } if !configurationValuesEqual(working, assembly.current.root) { t.Fatal("tier history differs from structural assembly before decoding") } overlay := parseCompositionDocument(t, "overlay", "records: [{id: c, value: overlay}]\n") declarations[0].mode = sequenceModeUpsert if diagnostics := assembly.applyOverlayWithCollectionPolicy(overlay, nil, collectionPolicyScope{}, nil, LayerOverlay, policy, declarations); len(diagnostics) != 0 { t.Fatal(diagnostics) } applyHistoryApplication(&working, assembly.current.applications[3], index) if !configurationValuesEqual(working, assembly.current.root) { t.Fatal("subsequent overlay history differs from structural assembly") } }) } } func TestSequenceHistoryPreservesTierReplacementAndEmptyLists(t *testing.T) { policy := mustCompileSequenceIdentityPolicy(t, []sequenceIdentityRegistration{ {selector: sequenceIdentitySelector{literalSequenceSelector("records")}, identityFields: []string{"id"}}, {selector: sequenceIdentitySelector{literalSequenceSelector("independent")}, identityFields: []string{"id"}}, }) first, diagnostics := composeDocumentsWithPolicy(policy, parseCompositionDocument(t, "underlay-1a", "records: [{id: shared}]\nindependent: [{id: x}]\n"), parseCompositionDocument(t, "underlay-1b", "records: [{id: lower}]\n"), ) if len(diagnostics) != 0 { t.Fatalf("first tier diagnostics = %#v", diagnostics) } for index := range first.applications { first.applications[index].layer = LayerUnderlay } second, diagnostics := composeDocumentsWithPolicy(policy, parseCompositionDocument(t, "underlay-2", "records: [{id: shared}]\nindependent: []\n"), ) if len(diagnostics) != 0 { t.Fatalf("second tier diagnostics = %#v", diagnostics) } main, diagnostics := composeDocumentsWithPolicy(policy, parseCompositionDocument(t, "main", "records: []\n"), ) if len(diagnostics) != 0 { t.Fatalf("main diagnostics = %#v", diagnostics) } assembly := newAssemblyAccumulator(first) if diagnostics := assembly.applyOverlay(second.root, second.origins, second.applications, LayerUnderlay); len(diagnostics) != 0 { t.Fatalf("second tier handoff diagnostics = %#v", diagnostics) } if diagnostics := assembly.applyOverlay(main.root, main.origins, main.applications, LayerMain); len(diagnostics) != 0 { t.Fatalf("main handoff diagnostics = %#v", diagnostics) } if len(assembly.current.applications) != 4 { t.Fatalf("retained source applications = %d, want 4", len(assembly.current.applications)) } working := parseCompositionDocument(t, "defaults", "records: [{id: default}]\nindependent: [{id: default}]\n") index := new(mappingIndex) for _, application := range assembly.current.applications { applyHistoryApplication(&working, application, index) } for path, want := range map[string][]string{ "/records": nil, "/independent": nil, } { sequence, present := documentAtPath(working, mustParsePath(t, path)) if !present || sequence.Kind != document.Sequence || len(sequence.Sequence) != len(want) { t.Errorf("replayed replacement %s = %#v, want an explicit empty sequence", path, sequence) } } // The same identity may appear in separate tiers, while each tier still // retains its individual source applications for explanation replay. if got := []Layer{assembly.current.applications[0].layer, assembly.current.applications[1].layer, assembly.current.applications[2].layer, assembly.current.applications[3].layer}; !reflect.DeepEqual(got, []Layer{LayerUnderlay, LayerUnderlay, LayerUnderlay, LayerMain}) { t.Fatalf("application layers = %#v", got) } } func TestHistoryMergeSupportsSemanticIndexedPrefixes(t *testing.T) { root := parseCompositionDocument(t, "base", "records: [{id: one, helper: {value: old}}]\n") application := historyApplication{ path: Keys("records").Index(0).Key("helper"), value: parseCompositionDocument(t, "helper", "value: new\n"), } applyHistoryApplication(&root, application, new(mappingIndex)) value, present := documentAtPath(root, Keys("records").Index(0).Key("helper").Key("value")) if !present || value.Text != "new" { t.Fatalf("indexed helper replay = %#v, %v", value, present) } root = parseCompositionDocument(t, "base", "records: [{id: one, helper: {items: [{id: a}]}}]\n") application.value = parseCompositionDocument(t, "helper", "items: [{id: b}]\n") application.sequences = []sequenceHistoryContribution{{path: Keys("items"), mode: sequenceHistoryAppend, offset: 1, length: 1}} application.sequencesByPath = indexSequenceHistoryContributions(application.sequences) applyHistoryApplication(&root, application, new(mappingIndex)) items, present := documentAtPath(root, Keys("records").Index(0).Key("helper").Key("items")) if !present || len(items.Sequence) != 2 { t.Fatalf("indexed helper lost its local sequence operation: %#v", items) } } func TestLayeredUpsertExplanationFollowsStructuralOrder(t *testing.T) { t.Parallel() type config struct { Records []map[string]any `yaml:"records"` } loaded, err := testGroupedLoad(loadRequest[config]{ main: loadInput{source: Bytes("main", []byte("configkit:\n sequences: [{destination: [records], key: id}]\nrecords: [{id: b, value: new-b}, {id: c, value: new-c}, {id: a, value: new-a}]\n"))}, underlays: []loadGroup{{inputs: []loadInput{{source: Bytes("lower", []byte("records: [{id: a, value: old-a}, {id: b, value: old-b, removed: old}]\n"))}}}}, }) if err != nil { t.Fatal(err) } explanation, ok := loaded.Explain(Keys("records")) if !ok || len(explanation.History) == 0 { t.Fatal("missing collection history") } for _, change := range explanation.History { if change.Origin.Operation == ChangeDecoding { t.Fatal("structural upsert was misreported as a decoding reconciliation") } } if last := explanation.History[len(explanation.History)-1]; last.After.YAML != explanation.Effective.YAML || last.Origin.Layer != LayerMain { t.Fatalf("main contribution did not produce the effective collection: %#v", last) } removed, ok := loaded.Explain(Keys("records").Index(1).Key("removed")) if !ok || len(removed.History) == 0 || removed.Effective.Present { t.Fatalf("missing displaced field history: %#v, %v", removed, ok) } if last := removed.History[len(removed.History)-1]; last.After.Present || last.Origin.Layer != LayerMain || last.Origin.Operation == ChangeDecoding { t.Fatalf("upsert removal attribution = %#v", last) } } func TestProjectedSequenceHistoryAgreesAcrossMetadataAndExplanation(t *testing.T) { policy := mustCompileSequenceIdentityPolicy(t, []sequenceIdentityRegistration{{selector: sequenceIdentitySelector{literalSequenceSelector("records")}, identityFields: []string{"id"}}}) composed, diagnostics := composeDocumentsWithPolicy(policy, parseCompositionDocument(t, "first", "records: [{id: first, token: public}]"), parseCompositionDocument(t, "second", "records: [{id: second, token: private}]"), ) if len(diagnostics) != 0 { t.Fatalf("compose diagnostics = %#v", diagnostics) } path := Keys("records").Index(1).Key("token") baseline := &document.Node{Kind: document.Mapping} model := sourceHistoryModelForInspection(baseline, composed.root, composed.origins, composed.applications, composed.overlays) metadata := Metadata{historyModel: model} finalizeEffectiveMetadataPaths(&metadata, composed.root) if !metadata.hasHistoricalPath(path) { t.Fatalf("projected metadata omitted shifted path %s: %#v", path, metadata.HistoricalPaths()) } inspection := &Inspection{metadata: metadata, history: model, final: composed.root} inspection.secrets = classifySecretPaths(nil, func(candidate Path) bool { return reflect.DeepEqual(candidate, path) }, metadata) explanation, ok := inspection.Explain(path) if !ok || !explanation.Effective.Redacted { t.Fatalf("shifted explanation = %#v, %v", explanation, ok) } var foundSecondOrigin bool for _, change := range explanation.History { for _, location := range change.Origin.Locations { foundSecondOrigin = foundSecondOrigin || location.SourceID == "second" } } if !foundSecondOrigin { t.Fatalf("shifted history lost second source attribution: %#v", explanation.History) } var rendered bytes.Buffer if err := inspection.WriteExplanation(&rendered, path); err != nil { t.Fatal(err) } if !strings.Contains(rendered.String(), `path: "/records/1/token"`) || strings.Contains(rendered.String(), "private") { t.Fatalf("shifted explanation rendering is incomplete or unsafe: %s", rendered.String()) } } func TestReplacedProjectedSensitiveRecordRemainsRedacted(t *testing.T) { policy := mustCompileSequenceIdentityPolicy(t, []sequenceIdentityRegistration{{selector: sequenceIdentitySelector{literalSequenceSelector("records")}, identityFields: []string{"id"}}}) composed, diagnostics := composeDocumentsWithPolicy(policy, parseCompositionDocument(t, "first", "records: [{id: first, token: public}]"), parseCompositionDocument(t, "second", "records: [{id: second, token: PRIVATE-RECORD-CANARY}]"), ) if len(diagnostics) != 0 { t.Fatalf("compose diagnostics = %#v", diagnostics) } applications := append([]historyApplication(nil), composed.applications...) applications = append(applications, historyApplication{ layer: LayerOverlay, value: parseCompositionDocument(t, "replacement", "records: [{id: replacement, token: visible}]"), }) final := applications[len(applications)-1].value path := Keys("records").Index(1).Key("token") baseline := &document.Node{Kind: document.Mapping} model := sourceHistoryModelForInspection(baseline, final, nil, applications, nil) metadata := Metadata{historyModel: model} finalizeEffectiveMetadataPaths(&metadata, final) inspection := &Inspection{metadata: metadata, history: model, final: final} inspection.secrets = classifySecretPaths(nil, func(candidate Path) bool { return reflect.DeepEqual(candidate, path) }, metadata) explanation, ok := inspection.Explain(path) if !ok || explanation.Effective.Present { t.Fatalf("replaced shifted record explanation = %#v, %v", explanation, ok) } var rendered bytes.Buffer if err := inspection.WriteExplanation(&rendered, path); err != nil { t.Fatal(err) } if strings.Contains(rendered.String(), "PRIVATE-RECORD-CANARY") || !strings.Contains(rendered.String(), "[REDACTED]") { t.Fatalf("replaced sensitive history leaked or was not redacted: %s", rendered.String()) } } func sourceHistoryModelForInspection(baseline, final *document.Node, origins map[string]Origin, applications []historyApplication, records []overlayRecord) *historyModel { builder := newHistoryBuilder() builder.observeBaseline(baseline) builder.setScratch(baseline) builder.observeSourceEvidence(final, origins, applications, records) builder.model.final = final return builder.freeze() } func TestMappedUpsertProjectsSensitiveReplacementEvidence(t *testing.T) { lowerRoot := parseCompositionDocument(t, "lower", `records: - id: first - id: middle - id: last token: MAPPED-SECRET-CANARY `) higherRoot := parseCompositionDocument(t, "higher", `records: - id: last token: visible - id: first - id: added `) lower, _ := documentAtPath(lowerRoot, Keys("records")) higher, _ := documentAtPath(higherRoot, Keys("records")) combined, diagnostics := combineSequence( lower, higher, sequenceCombinationPolicy(sequenceModeUpsert, "id"), sequenceCombinationContext{destination: Keys("records")}, ) if len(diagnostics) != 0 { t.Fatalf("combine diagnostics = %#v", diagnostics) } secretPath := Keys("records").Index(2).Key("token") secretNode, present := documentAtPath(lowerRoot, Keys("records").Index(2).Key("token")) if !present { t.Fatal("sensitive source token is missing") } secretNode.Sensitive = true application := historyApplication{ layer: LayerOverlay, value: higherRoot, sequences: []sequenceHistoryContribution{{ path: Keys("records"), mode: sequenceHistoryMapped, items: combined.higherItems, replacements: combined.replacements, }}, } application.sequencesByPath = indexSequenceHistoryContributions(application.sequences) applications := []historyApplication{{value: lowerRoot}, application} replayed := &document.Node{Kind: document.Mapping} for _, current := range applications { applyHistoryApplication(&replayed, current, new(mappingIndex)) } replayedRecords, _ := documentAtPath(replayed, Keys("records")) if !reflect.DeepEqual(replayedRecords.Sequence, combined.value.Sequence) { t.Fatalf("replayed value = %#v, want combination = %#v", replayedRecords.Sequence, combined.value.Sequence) } finalRoot := document.Clone(lowerRoot) finalRecords, _ := documentAtPath(finalRoot, Keys("records")) finalRecords.Sequence = document.Clone(combined.value).Sequence baseline := &document.Node{Kind: document.Mapping} model := sourceHistoryModelForInspection(baseline, finalRoot, nil, applications, nil) metadata := Metadata{historyModel: model} finalizeEffectiveMetadataPaths(&metadata, finalRoot) inspection := &Inspection{metadata: metadata, history: model, final: finalRoot} secretRule := func(candidate Path) bool { return reflect.DeepEqual(candidate, secretPath) } inspection.secrets = classifySecretPaths(nil, secretRule, metadata) explanation, ok := inspection.Explain(secretPath) if !ok || !explanation.Effective.Redacted { t.Fatalf("mapped replacement explanation = %#v, %v", explanation, ok) } var rendered bytes.Buffer if err := inspection.WriteExplanation(&rendered, secretPath); err != nil { t.Fatal(err) } if strings.Contains(rendered.String(), "MAPPED-SECRET-CANARY") || !strings.Contains(rendered.String(), "[REDACTED]") { t.Fatalf("mapped replacement history leaked or was not redacted: %s", rendered.String()) } } func TestSequenceHistoryReplaysCombinationEvidence(t *testing.T) { tests := []struct { name string operation sequenceMode lower string higher string }{ { name: "append", operation: sequenceModeAppend, lower: "records: [{id: first}, {id: last}]\n", higher: "records: [{id: added}]\n", }, { name: "upsert", operation: sequenceModeUpsert, lower: `records: - id: first label: same nested: {removed: lower} - id: middle - id: last nested: {removed: lower} `, higher: `records: - id: last nested: {kept: higher} - id: first label: same - id: added `, }, { name: "replace", operation: sequenceModeReplace, lower: "records: [{id: discarded}]\n", higher: "records: [{id: replacement}]\n", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { lowerRoot := parseCompositionDocument(t, "lower", test.lower) higherRoot := parseCompositionDocument(t, "higher", test.higher) lower, _ := documentAtPath(lowerRoot, Keys("records")) higher, _ := documentAtPath(higherRoot, Keys("records")) fields := []string(nil) if test.operation != sequenceModeReplace { fields = []string{"id"} } combined, diagnostics := combineSequence(lower, higher, sequenceCombinationPolicy(test.operation, fields...), sequenceCombinationContext{destination: Keys("records")}) if len(diagnostics) != 0 { t.Fatalf("combine diagnostics = %#v", diagnostics) } application := historyApplication{ layer: LayerOverlay, value: higherRoot, sequences: []sequenceHistoryContribution{ sequenceHistoryContributionForCombination(Keys("records"), test.operation, higher, combined), }, } if len(combined.higherItems) != 0 { combined.higherItems[0].outputIndex++ } if len(combined.replacements) != 0 { combined.replacements[0].outputIndex++ } application.sequencesByPath = indexSequenceHistoryContributions(application.sequences) replayed := document.Clone(lowerRoot) applyHistoryApplication(&replayed, application, new(mappingIndex)) got, present := documentAtPath(replayed, Keys("records")) if !present || !reflect.DeepEqual(got.Sequence, combined.value.Sequence) { t.Fatalf("replayed sequence = %#v, want combination = %#v", got, combined.value.Sequence) } }) } } func TestMappedSequenceHistoryReplacesWholeRecordsAndKeepsDisplacedEvidence(t *testing.T) { lowerRoot := parseCompositionDocument(t, "lower", `records: - id: first label: same - id: middle - id: last nested: {removed: LOWER-CANARY} `) higherRoot := parseCompositionDocument(t, "higher", `records: - id: last nested: {kept: higher} - id: first label: same - id: added `) lower, _ := documentAtPath(lowerRoot, Keys("records")) higher, _ := documentAtPath(higherRoot, Keys("records")) combined, diagnostics := combineSequence( lower, higher, sequenceCombinationPolicy(sequenceModeUpsert, "id"), sequenceCombinationContext{destination: Keys("records")}, ) if len(diagnostics) != 0 { t.Fatal(diagnostics) } application := historyApplication{ layer: LayerOverlay, value: higherRoot, sequences: []sequenceHistoryContribution{{ path: Keys("records"), mode: sequenceHistoryMapped, items: combined.higherItems, replacements: combined.replacements, }}, } application.sequencesByPath = indexSequenceHistoryContributions(application.sequences) clonedApplications := cloneHistoryApplications([]historyApplication{application}) clonedApplications[0].sequences[0].items[0].outputIndex = 99 clonedApplications[0].sequences[0].replacements[0].outputIndex = 99 if application.sequences[0].items[0].outputIndex == 99 || application.sequences[0].replacements[0].outputIndex == 99 { t.Fatal("cloned mapped history aliases its source projections") } query := Keys("records").Index(2).Key("nested").Key("removed") operationPath, relevant := applicationOperationPath(application, query, nil) if !relevant || operationPath.String() != "/records/2" { t.Fatalf("removed descendant operation = %s, %v", operationPath.String(), relevant) } localPath, ok := historyApplicationLocalPath(application, Keys("records").Index(2).Key("nested").Key("kept")) if !ok || localPath.String() != "/records/0/nested/kept" { t.Fatalf("mapped effective path localizes to %s, %v", localPath.String(), ok) } origin := historyApplicationOrigin(application, operationPath, nil) if len(origin.Locations) != 1 || origin.Locations[0].SourceID != "higher" { t.Fatalf("replacement operation origin = %#v", origin) } var walkedMappedPath bool walkHistoryApplication(application, func(local, effective Path, _ *document.Node) { if local.String() == "/records/0/nested/kept" && effective.String() == "/records/2/nested/kept" { walkedMappedPath = true } }) if !walkedMappedPath { t.Fatal("history walk did not project a noncontiguous incoming item") } replayed := document.Clone(lowerRoot) applyHistoryApplication(&replayed, application, new(mappingIndex)) sequence, _ := documentAtPath(replayed, Keys("records")) if _, present := documentAtPath(sequence.Sequence[2], Keys("nested", "removed")); present { t.Fatal("whole-record replacement retained an omitted descendant") } if value, present := documentAtPath(sequence.Sequence[2], Keys("nested", "kept")); !present || value.Text != "higher" { t.Fatalf("replacement descendant = %#v, %v", value, present) } if sequence.Sequence[1].Mapping[0].Value.Text != "middle" { t.Fatal("unchanged neighboring record moved or changed") } if value, present := documentAtPath(lowerRoot, Keys("records").Index(2).Key("nested").Key("removed")); !present || value.Text != "LOWER-CANARY" { t.Fatalf("displaced lower contribution was not retained as raw history: %#v, %v", value, present) } if got := application.sequences[0].replacements; !reflect.DeepEqual(got, combined.replacements) || len(got) != 2 { t.Fatalf("replacement evidence lost equal-valued or noncontiguous events: %#v", got) } } func TestHistorySourceSequenceApplicationsUseAssignedCoordinates(t *testing.T) { t.Parallel() policy := mustCompileSequenceIdentityPolicy(t, []sequenceIdentityRegistration{{ selector: sequenceIdentitySelector{literalSequenceSelector("collections"), anyMappingSequenceSelector(), literalSequenceSelector("records")}, identityFields: []string{"id"}, }}) first := parseCompositionDocument(t, "source-1", "collections: {north: {records: [{id: a, value: first}]}}\n") second := parseCompositionDocument(t, "source-2", "collections: {north: {records: [{id: b, value: second}]}}\n") composed, diagnostics := composeDocumentsWithPolicy(policy, first, second) if len(diagnostics) != 0 { t.Fatalf("compose diagnostics = %#v", diagnostics) } builder := newHistoryBuilder() builder.observeSourceEvidence(composed.root, composed.origins, composed.applications, composed.overlays) model := builder.freeze() appendedPath := Keys("collections", "north", "records").Index(1).Key("value") selections := model.selectHistory(appendedPath) foundSecond := false for _, selection := range selections { if selection.role == historyRoleContribution && selection.attribution.origin.Locations[0].SourceID == "source-2" { foundSecond = true } } if !foundSecond { t.Fatalf("appended source was not attributed at its assigned index: %#v", selections) } for _, selection := range model.selectHistory(Keys("collections", "north", "records").Index(0).Key("value")) { if selection.role == historyRoleContribution && selection.attribution.origin.Locations[0].SourceID == "source-2" { t.Fatalf("later source acquired an earlier append index: %#v", selection) } } local, ok := historyApplicationLocalPath(composed.applications[1], appendedPath) if !ok || local.String() != "/collections/north/records/0/value" { t.Fatalf("assigned append path localizes to %s, %v", local.String(), ok) } } func TestHistorySourceSequenceReplacementTracksMappedAndWholeSides(t *testing.T) { t.Parallel() lowerRoot := parseCompositionDocument(t, "lower", `records: - id: first - id: middle - id: last nested: {removed: lower} `) higherRoot := parseCompositionDocument(t, "higher", `records: - id: last nested: {kept: higher} - id: first - id: added `) lower, _ := documentAtPath(lowerRoot, Keys("records")) higher, _ := documentAtPath(higherRoot, Keys("records")) combined, diagnostics := combineSequence( lower, higher, sequenceCombinationPolicy(sequenceModeUpsert, "id"), sequenceCombinationContext{destination: Keys("records")}, ) if len(diagnostics) != 0 { t.Fatal(diagnostics) } finalRoot := document.Clone(lowerRoot) finalSequence, _ := documentAtPath(finalRoot, Keys("records")) *finalSequence = *document.Clone(combined.value) lowerOrigins := make(map[string]Origin) collectOverlayOrigins(lowerOrigins, lowerRoot, Path{}, LayerMain) higherOrigins := make(map[string]Origin) collectOverlayOrigins(higherOrigins, higherRoot, Path{}, LayerOverlay) assembly := newAssemblyAccumulator(&composedDocument{root: finalRoot, origins: lowerOrigins}) applyOverlaySequenceEvidence( assembly, Keys("records"), lower, higher, combined, collectionPolicyResolution{operation: sequenceModeUpsert}, higherOrigins, collectionPolicyScope{}, LayerOverlay, ) higherApplication := historyApplication{ layer: LayerOverlay, value: higherRoot, sequences: []sequenceHistoryContribution{{ path: Keys("records"), mode: sequenceHistoryMapped, items: combined.higherItems, replacements: combined.replacements, }}, } higherApplication.sequencesByPath = indexSequenceHistoryContributions(higherApplication.sequences) applications := []historyApplication{ {layer: LayerMain, value: lowerRoot}, higherApplication, } builder := newHistoryBuilder() builder.observeSourceEvidence(finalRoot, assembly.current.origins, applications, assembly.current.overlays) model := builder.freeze() removedPath := Keys("records").Index(2).Key("nested").Key("removed") removed := model.selectHistory(removedPath) var lowerContribution, removal bool for _, selection := range removed { if selection.role == historyRoleContribution && selection.attribution.origin.Locations[0].SourceID == "lower" { lowerContribution = true } if selection.role == historyRoleOperation { removal = selection.before && !selection.after } } if !lowerContribution || !removal { t.Fatalf("omitted descendant evidence = %#v", removed) } introducedPath := Keys("records").Index(2).Key("nested").Key("kept") introduced := model.selectHistory(introducedPath) var higherContribution, introduction bool for _, selection := range introduced { if selection.role == historyRoleContribution && selection.attribution.origin.Locations[0].SourceID == "higher" { higherContribution = true } if selection.role == historyRoleOperation { introduction = !selection.before && selection.after } } if !higherContribution || !introduction { t.Fatalf("mapped introduced descendant evidence = %#v", introduced) } if local, ok := historyApplicationLocalPath(higherApplication, introducedPath); !ok || local.String() != "/records/0/nested/kept" { t.Fatalf("mapped path localizes to %s, %v", local.String(), ok) } if selections := model.selectHistory(Keys("records").Index(2).Key("nested").Key("removed")); len(selections) > 0 { for _, selection := range selections { if selection.role == historyRoleContribution && selection.attribution.origin.Locations[0].SourceID == "higher" { t.Fatalf("unaddressed source acquired removed-record attribution: %#v", selections) } } } wholeLower := parseCompositionDocument(t, "whole-lower", "records: [{id: old, nested: {removed: old}}]\n") wholeHigher := parseCompositionDocument(t, "whole-higher", "records: [{id: new, nested: {added: new}}]\n") wholeLowerSequence, _ := documentAtPath(wholeLower, Keys("records")) wholeHigherSequence, _ := documentAtPath(wholeHigher, Keys("records")) whole, diagnostics := combineSequence( wholeLowerSequence, wholeHigherSequence, sequenceCombinationPolicy(sequenceModeReplace), sequenceCombinationContext{destination: Keys("records")}, ) if len(diagnostics) != 0 { t.Fatal(diagnostics) } wholeFinal := document.Clone(wholeLower) wholeFinalSequence, _ := documentAtPath(wholeFinal, Keys("records")) *wholeFinalSequence = *document.Clone(whole.value) wholeLowerOrigins := make(map[string]Origin) collectOverlayOrigins(wholeLowerOrigins, wholeLower, Path{}, LayerMain) wholeHigherOrigins := make(map[string]Origin) collectOverlayOrigins(wholeHigherOrigins, wholeHigher, Path{}, LayerOverlay) wholeAssembly := newAssemblyAccumulator(&composedDocument{root: wholeFinal, origins: wholeLowerOrigins}) applyOverlaySequenceEvidence( wholeAssembly, Keys("records"), wholeLowerSequence, wholeHigherSequence, whole, collectionPolicyResolution{operation: sequenceModeReplace}, wholeHigherOrigins, collectionPolicyScope{}, LayerOverlay, ) wholeHigherApplication := historyApplication{ layer: LayerOverlay, value: wholeHigher, sequences: []sequenceHistoryContribution{sequenceHistoryContributionForCombination(Keys("records"), sequenceModeReplace, wholeHigherSequence, whole)}, } wholeHigherApplication.sequencesByPath = indexSequenceHistoryContributions(wholeHigherApplication.sequences) wholeBuilder := newHistoryBuilder() wholeBuilder.observeSourceEvidence(wholeFinal, wholeAssembly.current.origins, []historyApplication{ {layer: LayerMain, value: wholeLower}, wholeHigherApplication, }, wholeAssembly.current.overlays) wholeModel := wholeBuilder.freeze() if len(wholeAssembly.current.overlays) != 1 { t.Fatalf("whole-sequence replacement records = %#v", wholeAssembly.current.overlays) } wholeIntroductions := wholeAssembly.current.overlays[0].introduced if len(wholeIntroductions) != 5 { t.Fatalf("whole-sequence introduced paths = %#v", wholeIntroductions) } var introducedNested bool for _, fact := range wholeIntroductions { if sameConcreteHistoryPath(fact.path, Keys("records").Index(0).Key("nested").Key("added")) { introducedNested = true } } if !introducedNested { t.Fatalf("whole-sequence nested introduction missing: %#v", wholeIntroductions) } oldWholePath := Keys("records").Index(0).Key("nested").Key("removed") newWholePath := Keys("records").Index(0).Key("nested").Key("added") oldWhole := wholeModel.selectHistory(oldWholePath) newWhole := wholeModel.selectHistory(newWholePath) if len(oldWhole) < 2 || oldWhole[len(oldWhole)-1].role != historyRoleOperation || !oldWhole[len(oldWhole)-1].before || oldWhole[len(oldWhole)-1].after { t.Fatalf("whole-sequence removed descendant selections = %#v", oldWhole) } if len(newWhole) < 2 || newWhole[len(newWhole)-1].role != historyRoleOperation || newWhole[len(newWhole)-1].before || !newWhole[len(newWhole)-1].after { t.Fatalf("whole-sequence introduced descendant selections = %#v", newWhole) } } func mustParsePath(t *testing.T, value string) Path { t.Helper() path, err := ParsePath(value) if err != nil { t.Fatal(err) } return path }