Borrow lower collection policies during strict composition

This commit is contained in:
2026-10-25 22:59:03 +00:00
parent 68383f34de
commit 3a1b6ff355
5 changed files with 169 additions and 29 deletions

View File

@@ -489,6 +489,96 @@ func TestComposeDocumentsUsesLowerIdentityForStrictPeers(t *testing.T) {
}
}
func TestComposeDocumentsReturnsSparseCollectionPolicies(t *testing.T) {
itemsPath := Keys("items")
unrelatedPath := Keys("unrelated")
var lower collectionPolicyScope
lower.set(itemsPath, collectionPolicy{identityFields: []string{"id"}, identityPresent: true})
unrelated := collectionPolicy{identityFields: []string{"name"}, identityPresent: true}
unrelated.setRecordPolicy(newCollectionRecordPolicy())
lower.set(unrelatedPath, unrelated)
composed, diagnostics := composeDocumentsWithCollectionPolicy(
sequencePolicy{}, lower, nil,
parseCompositionDocument(t, "incoming", "items: [{id: current}]\n"),
)
if len(diagnostics) != 0 {
t.Fatalf("compose incoming sequence: %#v", diagnostics)
}
if len(composed.collectionPolicies.collections) != 1 {
t.Fatalf("incoming collection policies = %#v; want only touched destinations", composed.collectionPolicies.collections)
}
if policy, found := composed.collectionPolicies.at(itemsPath); !found || !policy.identityPresent || !reflect.DeepEqual(policy.identityFields, []string{"id"}) {
t.Fatalf("resolved incoming policy = %#v, found %t", policy, found)
}
if _, found := composed.collectionPolicies.at(unrelatedPath); found {
t.Fatal("incoming composition copied an unrelated lower policy")
}
if policy, found := lower.at(unrelatedPath); !found || !policy.identityPresent || !reflect.DeepEqual(policy.identityFields, []string{"name"}) || len(policy.recordOrder) != 1 {
t.Fatalf("borrowed lower policy changed during composition: %#v, found %t", policy, found)
}
composed.collectionPolicies.collections[itemsPath.String()].policy.identityFields[0] = "changed"
if policy, _ := lower.at(itemsPath); !reflect.DeepEqual(policy.identityFields, []string{"id"}) {
t.Fatalf("incoming policy shares identity storage with lower scope: %#v", policy)
}
}
func TestStrictCompositionMasksLowerIdentityForReplacedAncestor(t *testing.T) {
itemsPath := Keys("catalog", "items")
var lower collectionPolicyScope
lower.set(itemsPath, collectionPolicy{identityFields: []string{"id"}, identityPresent: true})
declaration := sequenceDeclarationSpec{destination: Keys("catalog"), mode: sequenceModeReplace, modePresent: true}
_, diagnostics := composeDocumentsWithCollectionPolicy(
sequencePolicy{}, lower, []sequenceDeclarationSpec{declaration},
parseCompositionDocument(t, "source-1", "catalog: {items: [{name: first}]}\n"),
parseCompositionDocument(t, "source-2", "catalog: {items: [{name: second}]}\n"),
)
if len(diagnostics) != 1 || diagnostics[0].Code != CodeComposeConflict || diagnostics[0].Path.String() != "/catalog/items" {
t.Fatalf("strict peers used identity masked by replaced ancestor: %#v", diagnostics)
}
if policy, found := lower.at(itemsPath); !found || !policy.identityPresent || !reflect.DeepEqual(policy.identityFields, []string{"id"}) {
t.Fatalf("strict resolution mutated borrowed lower identity: %#v, found %t", policy, found)
}
}
func TestComposeDocumentsDeclarationWithoutValueRetainsOwnedRecordPolicy(t *testing.T) {
path := Keys("items")
nestedPath := Keys("prompts")
var nested collectionPolicyScope
nested.set(nestedPath, collectionPolicy{identityFields: []string{"name"}, identityPresent: true})
record := collectionRecordPolicy{owner: newCollectionRecordPolicy().owner, scope: nested}
lowerPolicy := collectionPolicy{
identityFields: []string{"id"}, identityPresent: true,
explicitMode: sequenceModeAppend, explicitModePresent: true,
}
lowerPolicy.setRecordPolicy(record)
var lower collectionPolicyScope
lower.set(path, lowerPolicy)
declaration := sequenceDeclarationSpec{destination: path, mode: sequenceModeReplace, modePresent: true}
composed, diagnostics := composeDocumentsWithCollectionPolicy(
sequencePolicy{}, lower, []sequenceDeclarationSpec{declaration},
parseCompositionDocument(t, "metadata", "other: value\n"),
)
if len(diagnostics) != 0 {
t.Fatalf("compose declaration without a value: %#v", diagnostics)
}
resolved, found := composed.collectionPolicies.at(path)
if !found || !resolved.identityPresent || !reflect.DeepEqual(resolved.identityFields, []string{"id"}) || resolved.explicitMode != sequenceModeReplace || !resolved.explicitModePresent {
t.Fatalf("declaration-only result changed retained collection policy: %#v, found %t", resolved, found)
}
if len(resolved.recordOrder) != 1 || len(resolved.recordPolicies) != 1 {
t.Fatalf("declaration-only result lost record ownership: %#v", resolved)
}
owner := resolved.recordOrder[0]
resolved.recordPolicies[owner].collections[nestedPath.String()].policy.identityFields[0] = "changed"
retained, _ := lower.at(path)
if nestedPolicy, found := retained.recordPolicies[owner].at(nestedPath); !found || !reflect.DeepEqual(nestedPolicy.identityFields, []string{"name"}) {
t.Fatalf("declaration-only result borrowed nested record policy: %#v, found %t", nestedPolicy, found)
}
}
func TestComposeDocumentsResolvesDeclarationsBeforePeerComposition(t *testing.T) {
path := Keys("records")
declaration := func(source string, line int, fields ...string) sequenceDeclarationSpec {