package engine import ( "testing" "gitea.maximumdirect.net/eric/ConfigKit/internal/document" ) func TestInheritanceCombinesIdentifiedSequencesAtDestination(t *testing.T) { root := parseItemPolicyDocument(t, "inheritance-values", `base: items: - id: shared value: parent child: extends: base items: - id: shared value: child `) modeLocation := Location{SourceID: "base-policy", Line: 4, Column: 5} policy := collectionPolicy{ identityFields: []string{"id"}, identityPresent: true, explicitMode: sequenceModeAppend, explicitModePresent: true, modeLocation: &modeLocation, } policies := collectionPolicyScope{} policies.set(Keys("base", "items"), policy) expanded, diagnostics := expandInheritanceWithCollectionRules( root, nil, policies, sequencePolicy{}, defaultProjectionLimits(), ) if len(diagnostics) != 0 { t.Fatalf("expand identified inheritance: %#v", diagnostics) } items, found := documentAtPath(expanded.root, Keys("child", "items")) if !found || items.Kind != document.Sequence || len(items.Sequence) != 1 { t.Fatalf("child items = %#v, found %t; parent mode should not force an append", items, found) } if value, exists := documentAtPath(items.Sequence[0], Keys("value")); !exists || value.Text != "child" { t.Fatalf("matched record = %#v, want complete child replacement", items.Sequence[0]) } childPolicy, found := expanded.collectionPolicies.at(Keys("child", "items")) if !found || !childPolicy.identityPresent || childPolicy.identityFields[0] != "id" { t.Fatalf("inherited destination identity = %#v, found %t; scope=%#v", childPolicy, found, expanded.collectionPolicies.collections) } if childPolicy.explicitModePresent { t.Fatalf("parent explicit mode leaked to a different destination: %#v", childPolicy) } var transfer *inheritanceOperation for index := range expanded.operations { if expanded.operations[index].transfer != nil && expanded.operations[index].transfer.to.String() == "/child" { transfer = &expanded.operations[index] break } } if transfer == nil || len(transfer.sequences) != 1 || transfer.sequences[0].path.String() != "/child/items" || len(transfer.sequences[0].replacements) != 1 { t.Fatalf("inheritance sequence projection = %#v, want one mapped replacement", transfer) } if origin, exists := expanded.origins["/child/items/0/value"]; !exists || len(origin.Locations) == 0 || origin.Locations[0].SourceID != "inheritance-values" { t.Fatalf("replacement origin lost the child's source context: %#v", origin) } } func TestLoadAppliesYamlCollectionIdentityDuringSiblingInheritance(t *testing.T) { input := `configkit: sequences: - destination: [base, items] key: id mode: append base: items: - id: shared value: parent child: extends: base items: - id: shared value: child ` loaded, err := Load[map[string]any](Bytes("inheritance-policy", []byte(input)), testSecretBinding()) if err != nil { if loadError, ok := err.(*LoadError); ok { t.Fatalf("load inherited collection policy: %v; diagnostics=%#v", err, loadError.Diagnostics()) } t.Fatal(err) } value, found := loaded.Inspection.Explain(Keys("child", "items").Index(0).Key("value")) if !found || value.Effective.YAML != "child\n" { t.Fatalf("inherited sequence value = %#v, found %t", value, found) } if len(value.History) == 0 { t.Fatal("inherited sequence replacement has no inspection history") } parentValueVisible, childValueVisible := false, false for _, change := range value.History { parentValueVisible = parentValueVisible || change.Before.YAML == "parent\n" || change.After.YAML == "parent\n" childValueVisible = childValueVisible || change.After.YAML == "child\n" } if !parentValueVisible || !childValueVisible { t.Fatalf("sequence replacement history omitted its parent or child value: %#v", value.History) } } func TestInheritanceUsesDestinationExplicitModeAndApplicationConstraint(t *testing.T) { t.Run("destination append", func(t *testing.T) { root := parseItemPolicyDocument(t, "append", `base: items: - id: base child: extends: base items: - id: child `) policies := collectionPolicyScope{} policies.set(Keys("base", "items"), collectionPolicy{identityFields: []string{"id"}, identityPresent: true}) childLocation := Location{SourceID: "child-policy", Line: 4, Column: 5} policies.set(Keys("child", "items"), collectionPolicy{ identityFields: []string{"id"}, identityPresent: true, explicitMode: sequenceModeAppend, explicitModePresent: true, modeLocation: &childLocation, }) expanded, diagnostics := expandInheritanceWithCollectionRules(root, nil, policies, sequencePolicy{}, defaultProjectionLimits()) if len(diagnostics) != 0 { t.Fatalf("expand with destination append: %#v", diagnostics) } items, _ := documentAtPath(expanded.root, Keys("child", "items")) if len(items.Sequence) != 2 || items.Sequence[0].Mapping[0].Value.Text != "base" || items.Sequence[1].Mapping[0].Value.Text != "child" { t.Fatalf("appended items = %#v", items) } }) t.Run("new matching namespace", func(t *testing.T) { root := parseItemPolicyDocument(t, "dynamic", `base: enabled: true child: extends: base extensions: - id: duplicate - id: duplicate `) applicationPolicy, diagnostics := compileSequenceIdentityPolicy([]sequenceIdentityRegistration{{ selector: sequenceIdentitySelector{ {kind: sequenceSelectorLiteral, key: "child"}, {kind: sequenceSelectorLiteral, key: "extensions"}, }, identityFields: []string{"id"}, }}) if len(diagnostics) != 0 { t.Fatalf("compile identity constraint: %#v", diagnostics) } _, diagnostics = expandInheritanceWithCollectionRules(root, nil, collectionPolicyScope{}, applicationPolicy, defaultProjectionLimits()) if len(diagnostics) != 1 || diagnostics[0].Code != CodeSequenceConflict || diagnostics[0].Path.String() != "/child/extensions" { t.Fatalf("dynamic application constraint diagnostics = %#v", diagnostics) } }) } func TestInheritanceReplacementModesAndCancellation(t *testing.T) { for _, test := range []struct { name string value string kind document.Kind }{ {name: "empty sequence", value: "[]", kind: document.Sequence}, {name: "null", value: "null", kind: document.Null}, } { t.Run(test.name, func(t *testing.T) { root := parseItemPolicyDocument(t, "replacement", "base:\n items:\n - id: prior\nchild:\n extends: base\n items: "+test.value+"\n") policies := collectionPolicyScope{} policies.set(Keys("base", "items"), collectionPolicy{identityFields: []string{"id"}, identityPresent: true}) policies.set(Keys("child", "items"), collectionPolicy{ identityFields: []string{"id"}, identityPresent: true, explicitMode: sequenceModeReplace, explicitModePresent: true, }) expanded, diagnostics := expandInheritanceWithCollectionRules(root, nil, policies, sequencePolicy{}, defaultProjectionLimits()) if len(diagnostics) != 0 { t.Fatalf("expand replacement: %#v", diagnostics) } items, found := documentAtPath(expanded.root, Keys("child", "items")) if !found || items.Kind != test.kind || (items.Kind == document.Sequence && len(items.Sequence) != 0) { t.Fatalf("replacement value = %#v, found %t", items, found) } }) } for _, value := range []string{"null", "{}"} { t.Run("append rejects "+value, func(t *testing.T) { root := parseItemPolicyDocument(t, "append-kind", "base:\n items: [{id: prior}]\nchild:\n extends: base\n items: "+value+"\n") policies := collectionPolicyScope{} policies.set(Keys("base", "items"), collectionPolicy{identityFields: []string{"id"}, identityPresent: true}) policies.set(Keys("child", "items"), collectionPolicy{ identityFields: []string{"id"}, identityPresent: true, explicitMode: sequenceModeAppend, explicitModePresent: true, }) _, diagnostics := expandInheritanceWithCollectionRules(root, nil, policies, sequencePolicy{}, defaultProjectionLimits()) if len(diagnostics) != 1 || diagnostics[0].Code != CodeSequenceInvalid || diagnostics[0].Path.String() != "/child/items" { t.Fatalf("append kind diagnostics = %#v", diagnostics) } }) } t.Run("incompatible inherited identity", func(t *testing.T) { root := parseItemPolicyDocument(t, "identity", `base: items: [{id: one}] child: extends: base items: [{id: two}] `) policies := collectionPolicyScope{} policies.set(Keys("base", "items"), collectionPolicy{identityFields: []string{"id"}, identityPresent: true}) policies.set(Keys("child", "items"), collectionPolicy{identityFields: []string{"slug"}, identityPresent: true}) _, diagnostics := expandInheritanceWithCollectionRules(root, nil, policies, sequencePolicy{}, defaultProjectionLimits()) if len(diagnostics) != 1 || diagnostics[0].Code != CodeSequenceConflict || diagnostics[0].Path.String() != "/child/items" { t.Fatalf("identity conflict diagnostics = %#v", diagnostics) } }) t.Run("cancelled relationship", func(t *testing.T) { root := parseItemPolicyDocument(t, "cancel", `base: items: [{id: parent}] child: extends: null items: [{id: local}] `) policies := collectionPolicyScope{} policies.set(Keys("base", "items"), collectionPolicy{identityFields: []string{"id"}, identityPresent: true}) expanded, diagnostics := expandInheritanceWithCollectionRules(root, nil, policies, sequencePolicy{}, defaultProjectionLimits()) if len(diagnostics) != 0 { t.Fatalf("expand cancelled relationship: %#v", diagnostics) } if _, found := expanded.collectionPolicies.at(Keys("child", "items")); found { t.Fatal("cancelled relationship transferred the parent's collection policy") } }) } func TestInheritanceReplacementRetiresRecordLocalCollectionPolicy(t *testing.T) { root := parseItemPolicyDocument(t, "record-scope", `base: items: - id: shared children: - key: old child: extends: base items: - id: shared children: - key: new `) nested := collectionPolicy{identityFields: []string{"key"}, identityPresent: true, explicitMode: sequenceModeAppend, explicitModePresent: true} recordScope := collectionPolicyScope{} recordScope.set(Keys("children"), nested) outer := collectionPolicy{identityFields: []string{"id"}, identityPresent: true} outer.setRecordPolicyAt(0, newCollectionRecordPolicy()) owner := outer.recordOrder[0] outer.recordPolicies[owner] = recordScope policies := collectionPolicyScope{} policies.set(Keys("base", "items"), outer) expanded, diagnostics := expandInheritanceWithCollectionRules(root, nil, policies, sequencePolicy{}, defaultProjectionLimits()) if len(diagnostics) != 0 { t.Fatalf("expand replacement record: %#v", diagnostics) } children, found := documentAtPath(expanded.root, Keys("child", "items").Index(0).Key("children")) if !found || children.Kind != document.Sequence || len(children.Sequence) != 1 { t.Fatalf("replacement children = %#v, found %t", children, found) } if key, exists := documentAtPath(children.Sequence[0], Keys("key")); !exists || key.Text != "new" { t.Fatalf("replacement child = %#v, want only new record", children.Sequence[0]) } childPolicy, exists := expanded.collectionPolicies.at(Keys("child", "items")) if !exists { t.Fatal("inherited outer collection policy is missing") } record, found := childPolicy.recordPolicyAt(0) if !found { t.Fatal("replacement item has no independent record owner") } if nestedPolicy, exists := record.scope.at(Keys("children")); exists && nestedPolicy.explicitModePresent { t.Fatalf("replaced record retained its former nested operation: %#v", nestedPolicy) } }