package engine import ( "strings" "testing" "gitea.maximumdirect.net/eric/ConfigKit/internal/document" ) func parseIdentifiedInheritanceSequence(t *testing.T, sourceID, input string) *document.Node { t.Helper() var indented strings.Builder indented.WriteString("jobs:\n") for _, line := range strings.Split(strings.TrimSuffix(input, "\n"), "\n") { indented.WriteString(" ") indented.WriteString(line) indented.WriteByte('\n') } root := parseItemPolicyDocument(t, sourceID, indented.String()) sequence, found := documentAtPath(root, Keys("jobs")) if !found || sequence.Kind != document.Sequence { t.Fatalf("identified sequence missing from test document: %#v", root) } return sequence } func TestIdentifiedInheritanceGraphSchedulesForwardReferencesInPlace(t *testing.T) { sequence := parseIdentifiedInheritanceSequence(t, "identified-graph", `- id: 2 tenant: acme extends: {id: 1.0, tenant: acme} - id: 1e0 tenant: acme extends: {id: 0, tenant: acme} - id: 0 tenant: acme - id: 3 tenant: acme extends: null `) path := Keys("jobs") graph, diagnostics := analyzeIdentifiedInheritanceGraph(sequence, path, []string{"tenant", "id"}) if len(diagnostics) != 0 { t.Fatalf("analyze identified graph: %#v", diagnostics) } if len(graph.edges) != 2 { t.Fatalf("edges = %#v, want the two links in the inheritance chain", graph.edges) } order, diagnostics := graph.schedule() if len(diagnostics) != 0 { t.Fatalf("schedule identified graph: %#v", diagnostics) } if len(order) != 4 || order[0] != 2 || order[1] != 1 || order[2] != 0 { t.Fatalf("schedule = %v, want the forward parent chain before its children", order) } if graph.nodes[0].path.String() != "/jobs/0" || graph.nodes[0].item != sequence.Sequence[0] || !graph.nodes[3].cancelled || len(graph.nodes[3].directivePath.segments) == 0 { t.Fatalf("graph nodes changed authored positions or cancellation: %#v", graph.nodes) } if _, found := graph.byIdentity[graph.nodes[1].tuple]; !found { t.Fatal("identity index does not contain the parent record") } } func TestIdentifiedInheritanceScopeAdapterReusesTemplateExecution(t *testing.T) { sequence := parseIdentifiedInheritanceSequence(t, "identified-scope", `- id: child-a tenant: team-a extends: {id: parent-a, tenant: team-a} settings: {child: a} - id: parent-a tenant: team-a settings: {parent: a} - id: child-b tenant: team-b extends: {id: parent-b, tenant: team-b} settings: {child: b} - id: parent-b tenant: team-b settings: {parent: b} `) path := Keys("jobs") graph, diagnostics := analyzeIdentifiedInheritanceGraph(sequence, path, []string{"tenant", "id"}) if len(diagnostics) != 0 { t.Fatalf("analyze identified graph: %#v", diagnostics) } policies := collectionPolicyScope{} policies.set(path.Index(1).Key("steps"), collectionPolicy{ identityFields: []string{"step-id"}, identityPresent: true, }) result, diagnostics := applyIdentifiedInheritanceTemplates(graph, defaultProjectionLimits(), nil, policies, sequencePolicy{}) if len(diagnostics) != 0 { t.Fatalf("apply identified templates: %#v", diagnostics) } if got := len(result.root.Sequence); got != 4 { t.Fatalf("result sequence length = %d, want 4", got) } for index, want := range []string{"child-a", "parent-a", "child-b", "parent-b"} { id, found := documentAtPath(result.root.Sequence[index], Path{}.Key("id")) if !found || id.Text != want { t.Fatalf("result identity at index %d = %#v, found %t; want %q", index, id, found, want) } } for index, want := range []string{"a", "b"} { value, found := documentAtPath(result.root.Sequence[index*2], Path{}.Key("settings").Key("parent")) if !found || value.Text != want { t.Fatalf("inherited value at record %d = %#v, found %t; want %q", index*2, value, found, want) } } if len(result.transfers) != 2 || result.transfers[0].from.String() != "/jobs/1" || result.transfers[0].to.String() != "/jobs/0" || result.transfers[1].from.String() != "/jobs/3" || result.transfers[1].to.String() != "/jobs/2" { t.Fatalf("record transfers = %#v, want independent transfers at authored positions", result.transfers) } if len(result.controls) != 2 || result.controls[0].path.String() != "/jobs/0/extends" || result.controls[1].path.String() != "/jobs/2/extends" { t.Fatalf("relationship controls = %#v, want paths from child positions", result.controls) } if len(result.operations) != 4 || result.operations[1].transfer == nil || result.operations[1].transfer.to.String() != "/jobs/0" || result.operations[3].transfer == nil || result.operations[3].transfer.to.String() != "/jobs/2" { t.Fatalf("relationship history operations = %#v, want transfer evidence at child positions", result.operations) } if _, found := result.origins[path.Index(0).Key("id").String()]; !found { t.Fatal("resolved sequence output did not retain the child identity origin path") } projectedPolicy, found := result.collectionPolicies.at(path.Index(0).Key("steps")) if !found || !projectedPolicy.identityPresent || len(projectedPolicy.identityFields) != 1 || projectedPolicy.identityFields[0] != "step-id" { t.Fatalf("inherited nested collection policy = %#v, found %t", projectedPolicy, found) } adapter := identifiedSequenceInheritanceScope{graph: graph, policies: policies} fields := adapter.templates()[0].identityFields if len(fields) != 2 || fields[0] != "id" || fields[1] != "tenant" { t.Fatalf("payload identity fields = %v, want [id tenant]", fields) } templates := adapter.templates() parentPolicy, found := templates[1].collectionPolicies.at(Keys("steps")) if !found || len(parentPolicy.identityFields) != 1 || parentPolicy.identityFields[0] != "step-id" { t.Fatalf("parent nested policy = %#v, found %t", parentPolicy, found) } if _, found := templates[0].collectionPolicies.at(Keys("steps")); found { t.Fatal("nested policy from one record leaked into a sibling template") } parentPolicy.identityFields[0] = "changed" templates[1].collectionPolicies.set(Keys("steps"), parentPolicy) reused, found := adapter.templates()[1].collectionPolicies.at(Keys("steps")) if !found || len(reused.identityFields) != 1 || reused.identityFields[0] != "step-id" { t.Fatal("reusing the adapter observed a mutation to a prior template snapshot") } if sequence.Sequence[0].Mapping[0].Value.Text != "child-a" { t.Fatal("executing the sequence adapter mutated its source scope") } } func TestIdentifiedInheritanceResolvesNestedCollectionsAtEachDestination(t *testing.T) { root := parseItemPolicyDocument(t, "identified-nested", `jobs: - id: child extends: {id: parent} - id: parent extends: {id: base} steps: - id: child-step extends: {id: parent-step} value: child-value - id: parent-step value: parent-value - id: base steps: - id: base-step value: base-value `) identity := collectionPolicy{identityFields: []string{"id"}, identityPresent: true} policies := collectionPolicyScope{} policies.set(Keys("jobs"), identity) for index := 0; index < 3; index++ { policies.set(Keys("jobs").Index(index).Key("steps"), identity) } expanded, diagnostics := expandInheritanceWithCollectionRules(root, nil, policies, sequencePolicy{}, defaultProjectionLimits()) if len(diagnostics) != 0 { t.Fatalf("expand nested identified inheritance: %#v", diagnostics) } steps, found := documentAtPath(expanded.root, Keys("jobs").Index(0).Key("steps")) if !found || steps.Kind != document.Sequence || len(steps.Sequence) != 3 { t.Fatalf("child nested steps = %#v, found %t", steps, found) } for index, want := range []string{"base-step", "child-step", "parent-step"} { id, exists := documentAtPath(steps.Sequence[index], Keys("id")) if !exists || id.Text != want { t.Fatalf("child step %d identity = %#v, found %t; want %q", index, id, exists, want) } } value, found := documentAtPath(steps.Sequence[1], Keys("value")) if !found || value.Text != "child-value" { t.Fatalf("nested record inheritance = %#v, found %t", value, found) } if containsInheritanceDirective(expanded.root) { t.Fatal("a nested record relationship remained after destination-local expansion") } childIdentity, found := expanded.origins["/jobs/0/id"] if !found || len(childIdentity.Locations) == 0 || childIdentity.Locations[0].SourceID != "identified-nested" { t.Fatalf("child identity origin = %#v, found %t", childIdentity, found) } if _, found := expanded.origins["/jobs/0/steps/1/value"]; !found { t.Fatal("nested inherited value did not retain its destination origin") } } func TestIdentifiedInheritanceRespectsOutputNodeBudget(t *testing.T) { root := parseItemPolicyDocument(t, "identified-budget", `jobs: - id: child extends: {id: parent} - id: parent value: inherited `) policies := collectionPolicyScope{} policies.set(Keys("jobs"), collectionPolicy{identityFields: []string{"id"}, identityPresent: true}) baseline, diagnostics := expandInheritanceWithCollectionRules(root, nil, policies, sequencePolicy{}, defaultProjectionLimits()) if len(diagnostics) != 0 { t.Fatalf("unbounded identified inheritance: %#v", diagnostics) } outputNodes, within := inheritanceLowerBoundCount(baseline.root, -1) if !within || outputNodes < 2 { t.Fatalf("identified output node count = %d, within %t", outputNodes, within) } limits := defaultProjectionLimits() limits.nodes = outputNodes if result, diagnostics := expandInheritanceWithCollectionRules(root, nil, policies, sequencePolicy{}, limits); result == nil || len(diagnostics) != 0 { t.Fatalf("at output-node boundary: result = %#v, diagnostics = %#v", result, diagnostics) } limits.nodes = outputNodes - 1 if result, diagnostics := expandInheritanceWithCollectionRules(root, nil, policies, sequencePolicy{}, limits); result != nil || len(diagnostics) != 1 || diagnostics[0].Code != CodeSchemaProjectionLimit { t.Fatalf("below output-node boundary: result = %#v, diagnostics = %#v", result, diagnostics) } } func TestIdentifiedInheritanceRequiresChildIdentityBeforeParentLookup(t *testing.T) { sequence := parseIdentifiedInheritanceSequence(t, "missing-child-field", `- id: child extends: {id: parent, tenant: acme} - id: parent tenant: acme `) _, diagnostics := analyzeIdentifiedInheritanceGraph(sequence, Keys("jobs"), []string{"id", "tenant"}) if len(diagnostics) != 1 || diagnostics[0].Code != CodeSequenceInvalid || diagnostics[0].Path.String() != "/jobs/0/tenant" { t.Fatalf("missing child identity diagnostics = %#v", diagnostics) } } func TestIdentifiedInheritanceRejectsMalformedAndMissingReferences(t *testing.T) { tests := []struct { name string value string wantCode Code }{ {name: "string shorthand", value: "extends: parent\n", wantCode: CodeInheritanceInvalid}, {name: "sequence shorthand", value: "extends: [{id: parent, tenant: acme}]\n", wantCode: CodeInheritanceInvalid}, {name: "missing component", value: "extends: {id: parent}\n", wantCode: CodeInheritanceInvalid}, {name: "extra component", value: "extends: {id: parent, tenant: acme, extra: value}\n", wantCode: CodeInheritanceInvalid}, {name: "mapping component", value: "extends: {id: {nested: value}, tenant: acme}\n", wantCode: CodeInheritanceInvalid}, {name: "missing parent", value: "extends: {id: missing, tenant: acme}\n", wantCode: CodeInheritanceMissing}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { sequence := parseIdentifiedInheritanceSequence(t, "bad-reference", "- id: child\n tenant: acme\n "+test.value) _, diagnostics := analyzeIdentifiedInheritanceGraph(sequence, Keys("jobs"), []string{"id", "tenant"}) if len(diagnostics) != 1 || diagnostics[0].Code != test.wantCode || diagnostics[0].Path.String() != "/jobs/0/extends" && diagnostics[0].Code != CodeInheritanceInvalid { t.Fatalf("diagnostics = %#v", diagnostics) } if diagnostics[0].Code == CodeInheritanceMissing && diagnostics[0].Path.String() != "/jobs/0/extends" { t.Fatalf("missing-parent path = %s", diagnostics[0].Path) } }) } } func TestIdentifiedInheritanceGraphFindsSelfAndLongerCycles(t *testing.T) { for _, input := range []string{ `- id: self tenant: acme extends: {id: self, tenant: acme} `, `- id: alpha tenant: acme extends: {id: beta, tenant: acme} - id: beta tenant: acme extends: {id: gamma, tenant: acme} - id: gamma tenant: acme extends: {id: alpha, tenant: acme} `, } { graph, diagnostics := analyzeIdentifiedInheritanceGraph(parseIdentifiedInheritanceSequence(t, "cycle", input), Keys("jobs"), []string{"id", "tenant"}) if len(diagnostics) != 0 { t.Fatalf("analyze cycle graph: %#v", diagnostics) } _, diagnostics = graph.schedule() if len(diagnostics) != 1 || diagnostics[0].Code != CodeInheritanceCycle || diagnostics[0].Path.String() == "" { t.Fatalf("cycle diagnostics = %#v", diagnostics) } if diagnostics[0].Primary == nil || diagnostics[0].Primary.SourceID != "cycle" { t.Fatalf("cycle primary location = %#v", diagnostics[0].Primary) } } } func TestIdentifiedInheritanceScopesCannotReferenceOtherCollections(t *testing.T) { other := parseIdentifiedInheritanceSequence(t, "other-collection", `- id: parent tenant: acme `) _, diagnostics := analyzeIdentifiedInheritanceGraph(other, Keys("other"), []string{"id", "tenant"}) if len(diagnostics) != 0 { t.Fatal(diagnostics) } first := parseIdentifiedInheritanceSequence(t, "first-collection", `- id: child tenant: acme extends: {id: parent, tenant: acme} `) _, diagnostics = analyzeIdentifiedInheritanceGraph(first, Keys("first"), []string{"id", "tenant"}) if len(diagnostics) != 1 || diagnostics[0].Code != CodeInheritanceMissing { t.Fatalf("cross-collection reference diagnostics = %#v", diagnostics) } } func TestIdentifiedInheritanceAcceptsStrictlyComposedParentReference(t *testing.T) { left := parseCompositionDocument(t, "reference-left", "extends: {id: parent}\n") right := parseCompositionDocument(t, "reference-right", "extends: {tenant: acme}\n") reference, diagnostics := composeDocuments(left, right) if len(diagnostics) != 0 { t.Fatalf("compose disjoint reference helpers: %#v", diagnostics) } child, diagnostics := composeDocuments(parseCompositionDocument(t, "child-fields", "id: child\ntenant: acme\n"), reference.root) if len(diagnostics) != 0 { t.Fatalf("compose child fields and reference: %#v", diagnostics) } parent := parseCompositionDocument(t, "parent", "id: parent\ntenant: acme\n") sequence := &document.Node{Kind: document.Sequence, Sequence: []*document.Node{child.root, parent}} graph, diagnostics := analyzeIdentifiedInheritanceGraph(sequence, Keys("jobs"), []string{"id", "tenant"}) if len(diagnostics) != 0 { t.Fatalf("analyze composed reference: %#v", diagnostics) } if len(graph.edges) != 1 || graph.edges[0] != (dependencyEdge{prerequisite: 1, dependent: 0}) { t.Fatalf("composed reference edge = %#v", graph.edges) } _, diagnostics = composeDocuments( parseCompositionDocument(t, "overlap-a", "extends: {id: first}\n"), parseCompositionDocument(t, "overlap-b", "extends: {id: second}\n"), ) if len(diagnostics) != 1 || diagnostics[0].Code != CodeComposeConflict { t.Fatalf("overlapping reference fields should conflict during strict composition: %#v", diagnostics) } } func TestIdentifiedInheritanceValidatesOnlySurvivingRelationships(t *testing.T) { lower := parseIdentifiedInheritanceSequence(t, "superseded", `- id: shared extends: {id: absent, tenant: acme} `) higher := parseIdentifiedInheritanceSequence(t, "cancelled", `- id: shared extends: null `) resolution := collectionPolicyResolution{ operation: sequenceModeUpsert, effectiveIdentityFields: []string{"id"}, } combined, diagnostics := combineSequence(lower, higher, resolution, sequenceCombinationContext{destination: Keys("jobs")}) if len(diagnostics) != 0 { t.Fatalf("combine surviving reference: %#v", diagnostics) } graph, diagnostics := analyzeIdentifiedInheritanceGraph(combined.value, Keys("jobs"), []string{"id"}) if len(diagnostics) != 0 || len(graph.edges) != 0 || !graph.nodes[0].cancelled { t.Fatalf("superseded missing reference was validated: graph=%#v diagnostics=%#v", graph, diagnostics) } }