package engine import ( "bytes" "strings" "testing" "gitea.maximumdirect.net/eric/ConfigKit/internal/document" ) func inheritanceInspectionForTest(t *testing.T, sources ...*document.Node) (*Inspection, *inheritanceScope) { t.Helper() composed, diagnostics := composeDocuments(sources...) if len(diagnostics) != 0 { t.Fatal(diagnostics) } return inheritanceInspectionFromComposition(t, composed) } func inheritanceInspectionFromComposition(t *testing.T, composed *composedDocument) (*Inspection, *inheritanceScope) { t.Helper() expanded, diagnostics := expandInheritanceWithOrigins(composed.root, composed.origins, defaultProjectionLimits()) if len(diagnostics) != 0 { t.Fatal(diagnostics) } baseline := &document.Node{Kind: document.Mapping} presence := make(map[string]inputPresence) collectPresence(expanded.root, Path{}, presence) decoded := decodedConfiguration{ baseline: baseline, final: expanded.root, presence: presence, origins: expanded.origins, } metadata := metadataFromDecoded(decoded) applyInheritanceMetadata(&metadata, composed.root, composed.origins, composed.overlays, expanded) finalizeMetadataPaths(&metadata, expanded.root, composed.applications, baseline, composed.root) return &Inspection{ metadata: metadata, baseline: baseline, input: expanded.root, inputOrigins: expanded.origins, final: expanded.root, applications: composed.applications, inheritance: expanded.operations, }, expanded } func TestInheritanceHistoryFollowsFinalParentAfterSourceOverlay(t *testing.T) { base, diagnostics := composeDocuments(parseCompositionDocument(t, "source-1", "parent: {value: original}\nchild: {extends: parent}\n")) if len(diagnostics) != 0 { t.Fatal(diagnostics) } assembly := newAssemblyAccumulator(base) if diagnostics := assembly.applyOverlay(parseCompositionDocument(t, "source-2", "parent: {value: latest}\n"), nil, nil, LayerOverlay); len(diagnostics) != 0 { t.Fatal(diagnostics) } inspection, _ := inheritanceInspectionFromComposition(t, assembly.current) path := Keys("child", "value") explanation, found := inspection.Explain(path) if !found || explanation.Effective.YAML != "latest\n" || len(explanation.History) == 0 || explanation.History[len(explanation.History)-1].Origin.Locations[0].SourceID != "source-2" { t.Fatalf("overlaid parent contribution = %#v, %v", explanation, found) } parentHistory := inspection.Metadata().History(Keys("parent", "value")) if len(parentHistory) < 2 || parentHistory[0].Origin.Layer != LayerBase || parentHistory[len(parentHistory)-1].Origin.Layer != LayerOverlay { t.Fatalf("parent source history = %#v", parentHistory) } } func TestInheritanceHistoryPreservesCancellationAndSupersededDirective(t *testing.T) { base, diagnostics := composeDocuments(parseCompositionDocument(t, "source-1", "parent: {value: one}\nchild: {extends: missing, value: own}\n")) if len(diagnostics) != 0 { t.Fatal(diagnostics) } assembly := newAssemblyAccumulator(base) if diagnostics := assembly.applyOverlay(parseCompositionDocument(t, "source-2", "child: {extends: null}\n"), nil, nil, LayerOverlay); len(diagnostics) != 0 { t.Fatal(diagnostics) } inspection, _ := inheritanceInspectionFromComposition(t, assembly.current) control := Keys("child", inheritanceKey) history := inspection.Metadata().History(control) if len(history) < 3 || history[0].Origin.Locations[0].SourceID != "source-1" || history[1].Origin.Locations[0].SourceID != "source-2" || history[len(history)-1].Origin.Operation != ChangeInheritance { t.Fatalf("superseded and cancelled directive history = %#v", history) } explanation, found := inspection.Explain(control) if !found || explanation.Effective.Present || len(explanation.History) < 3 || explanation.History[len(explanation.History)-1].After.Present { t.Fatalf("cancelled directive explanation = %#v, %v", explanation, found) } value, found := inspection.Explain(Keys("child", "value")) if !found || value.Effective.YAML != "own\n" { t.Fatalf("cancelled relationship changed own value = %#v, %v", value, found) } } func TestInheritanceHistoryProjectsNestedDestinationValues(t *testing.T) { inspection, _ := inheritanceInspectionForTest(t, parseCompositionDocument(t, "source-1", `base: settings: primary: {value: old} secondary: {extends: primary} custom: extends: base settings: primary: {value: new} `)) path := Keys("custom", "settings", "secondary", "value") history := inspection.Metadata().History(path) structural := 0 for _, change := range history { if change.Origin.Operation == ChangeInheritance { structural++ } } if structural == 0 { t.Fatalf("nested destination history = %#v", history) } explanation, found := inspection.Explain(path) if !found || explanation.Effective.YAML != "new\n" || len(explanation.Origin.Inheritance) == 0 || explanation.Origin.Inheritance[len(explanation.Origin.Inheritance)-1].To.String() != path.String() { t.Fatalf("nested destination explanation = %#v, %v", explanation, found) } for _, change := range explanation.History { if change.Origin.Operation == ChangeDecoding { t.Fatalf("nested inheritance was attributed to decoding: %#v", explanation.History) } } } func TestInheritanceHistoryShowsParentAndEqualChildContribution(t *testing.T) { inspection, _ := inheritanceInspectionForTest(t, parseCompositionDocument(t, "source-1", "parent: {value: same}\n"), parseCompositionDocument(t, "source-2", "child: {extends: parent, value: same}\n")) path := Keys("child", "value") history := inspection.Metadata().History(path) var structural []Change for _, change := range history { if change.Origin.Operation == ChangeInheritance { structural = append(structural, change) } } if len(structural) != 2 || structural[0].Origin.Locations[0].SourceID != "source-1" || structural[1].Origin.Locations[0].SourceID != "source-2" || len(structural[0].Dependencies) != 2 || structural[0].Dependencies[0].String() != "/parent" || len(structural[0].Origin.Inheritance) != 1 { t.Fatalf("equal-valued structural history = %#v", history) } explanation, found := inspection.Explain(path) if !found || explanation.Effective.YAML != "same\n" { t.Fatalf("explanation = %#v, %v", explanation, found) } var equalEvents int for _, change := range explanation.History { if change.Origin.Operation == ChangeDecoding { t.Fatalf("inheritance was attributed to decoding: %#v", explanation.History) } if change.Origin.Operation == ChangeInheritance && change.Before.YAML == change.After.YAML { equalEvents++ } } if equalEvents != 2 { t.Fatalf("equal parent and child operations = %#v", explanation.History) } parentHistory := inspection.Metadata().History(Keys("parent", "value")) for _, change := range parentHistory { if change.Origin.Operation == ChangeInheritance { t.Fatalf("source definition acquired a destination operation: %#v", parentHistory) } } } func TestInheritanceHistoryRetainsControlsAndDisplacedPaths(t *testing.T) { inspection, _ := inheritanceInspectionForTest(t, parseCompositionDocument(t, "source-1", "parent: {branch: {detail: inherited}}\n"), parseCompositionDocument(t, "source-2", "child: {extends: parent, branch: replaced}\n")) control := Keys("child", inheritanceKey) controlHistory := inspection.Metadata().History(control) if len(controlHistory) < 2 || controlHistory[len(controlHistory)-1].Origin.Operation != ChangeInheritance { t.Fatalf("control history = %#v", controlHistory) } controlExplanation, found := inspection.Explain(control) if !found || controlExplanation.Effective.Present || controlExplanation.History[len(controlExplanation.History)-1].After.Present { t.Fatalf("consumed control = %#v, %v", controlExplanation, found) } path := Keys("child", "branch", "detail") if _, found := inspection.Metadata().Lookup(path); found { t.Fatal("displaced descendant remained effective") } listed := false for _, historical := range inspection.Metadata().HistoricalPaths() { if historical.String() == path.String() { listed = true } } if !listed { t.Fatal("displaced inherited descendant was not enumerable") } history := inspection.Metadata().History(path) if len(history) < 2 || history[len(history)-1].Origin.Operation != ChangeInheritance { t.Fatalf("displaced descendant history = %#v", history) } explanation, found := inspection.Explain(path) if !found || explanation.Effective.Present { t.Fatalf("displaced descendant explanation = %#v, %v", explanation, found) } var sawInherited, sawRemoval bool for _, change := range explanation.History { if change.Origin.Operation != ChangeInheritance { continue } sawInherited = sawInherited || change.After.Present sawRemoval = sawRemoval || (change.Before.Present && !change.After.Present) } if !sawInherited || !sawRemoval { t.Fatalf("displaced value sequence = %#v", explanation.History) } var rendered bytes.Buffer if err := inspection.WriteExplanation(&rendered, path); err != nil || !strings.Contains(rendered.String(), "structural inheritance") { t.Fatalf("rendered structural explanation = %q, %v", rendered.String(), err) } } func TestInheritanceHistoryPrecedesApplicationResolution(t *testing.T) { inspection, _ := inheritanceInspectionForTest(t, parseCompositionDocument(t, "source-1", "parent: {value: original}\nchild: {extends: parent}\n")) path := Keys("child", "value") replacement := &document.Node{Kind: document.String, Text: "derived"} snapshot := resolutionSnapshot{ change: Change{Path: path, Origin: Origin{Layer: LayerResolution, Operation: ChangeNormalization}}, after: replacement, } final := document.Clone(inspection.final) replaceHistoryPath(&final, path, replacement) inspection.final = final inspection.resolution = []resolutionSnapshot{snapshot} inspection.metadata.history[path.String()] = append(inspection.metadata.history[path.String()], snapshot.change) inspection.metadata.fields[path.String()] = FieldInfo{Explicit: true, Origin: snapshot.change.Origin} history := inspection.Metadata().History(path) if len(history) < 2 || history[len(history)-2].Origin.Operation != ChangeInheritance || history[len(history)-1].Origin.Operation != ChangeNormalization { t.Fatalf("resolution order = %#v", history) } explanation, found := inspection.Explain(path) if !found || explanation.Effective.YAML != "derived\n" || explanation.History[len(explanation.History)-1].Origin.Operation != ChangeNormalization { t.Fatalf("resolved explanation = %#v, %v", explanation, found) } } func TestHistoryNodeBudgetCountsSharedInheritanceEvidenceOnce(t *testing.T) { shared := &document.Node{Kind: document.String, Text: "shared"} extra := &document.Node{Kind: document.String, Text: "extra"} expansion := &inheritanceScope{ root: shared, operations: []inheritanceOperation{{parent: shared, effective: shared}}, replacements: []inheritanceReplacement{{before: shared, after: extra}}, } if !historyNodesWithin(2, nil, nil, nil, nil, nil, expansion) || historyNodesWithin(1, nil, nil, nil, nil, nil, expansion) { t.Fatal("shared evidence node accounting is incorrect") } } func TestInheritanceExpansionEnforcesRetainedHistoryBudget(t *testing.T) { input := parseCompositionDocument(t, "source-1", "parent: {value: original}\nchild: {extends: parent, value: replacement}\n") if _, diagnostics := expandInheritanceWithBudgets(input, nil, defaultProjectionLimits(), 2); len(diagnostics) != 1 || diagnostics[0].Code != CodeExplainLimitExceeded { t.Fatalf("retained history limit = %#v", diagnostics) } if _, diagnostics := expandInheritanceWithBudgets(input, nil, defaultProjectionLimits(), 100); len(diagnostics) != 0 { t.Fatalf("sufficient retained history budget = %#v", diagnostics) } }