package engine import ( "reflect" "testing" "gitea.maximumdirect.net/eric/ConfigKit/internal/document" ) func TestOverlayDocumentsRecursesMappingsAndReplacesAtomicSubtrees(t *testing.T) { baseRoot := parseCompositionDocument(t, "source-1", `shared: kept: base changed: old sequence: [one, two] nullable: value to_map: scalar to_scalar: {child: old} `) base, diagnostics := composeDocuments(baseRoot) if len(diagnostics) != 0 { t.Fatal(diagnostics) } overlay := parseCompositionDocument(t, "source-2", `shared: changed: new added: overlay sequence: [] nullable: null to_map: {child: new} to_scalar: scalar `) baseBefore := document.Clone(base.root) overlayBefore := document.Clone(overlay) result, diagnostics := overlayDocuments(base, overlay) if len(diagnostics) != 0 { t.Fatalf("overlayDocuments() diagnostics = %#v", diagnostics) } checks := []struct { path Path kind document.Kind text string }{ {path: Keys("shared", "kept"), kind: document.String, text: "base"}, {path: Keys("shared", "changed"), kind: document.String, text: "new"}, {path: Keys("shared", "added"), kind: document.String, text: "overlay"}, {path: Keys("sequence"), kind: document.Sequence}, {path: Keys("nullable"), kind: document.Null}, {path: Keys("to_map"), kind: document.Mapping}, {path: Keys("to_map", "child"), kind: document.String, text: "new"}, {path: Keys("to_scalar"), kind: document.String, text: "scalar"}, } for _, check := range checks { node, ok := documentAtPath(result.root, check.path) if !ok || node.Kind != check.kind || node.Text != check.text { t.Errorf("node %s = %#v, %v", check.path.String(), node, ok) } } if _, exists := result.origins["/sequence/0"]; exists { t.Fatal("replaced empty sequence retained a stale index origin") } if _, exists := result.origins["/to_scalar/child"]; exists { t.Fatal("parent replacement retained a stale child origin") } shared := result.origins["/shared"] if shared.Layer != LayerOverlay || len(shared.Locations) != 2 { t.Fatalf("shared mapping origin = %#v", shared) } if len(result.overlays) != 5 { t.Fatalf("overlay records = %#v, want five replacements", result.overlays) } if !reflect.DeepEqual(base.root, baseBefore) || !reflect.DeepEqual(overlay, overlayBefore) { t.Fatal("overlay mutated an input document") } result.root.Mapping[0].Value.Mapping[0].Value.Text = "changed" if !reflect.DeepEqual(base.root, baseBefore) || !reflect.DeepEqual(overlay, overlayBefore) { t.Fatal("overlay result aliases an input document") } } func TestOwnedOverlayKeepsCompositeContributorsAndAcquiredInputs(t *testing.T) { baseInput := parseCompositionDocument(t, "source-1", "catalog:\n base: true\n") base, diagnostics := composeDocuments(baseInput) if len(diagnostics) != 0 { t.Fatal(diagnostics) } first := parseCompositionDocument(t, "source-2", "catalog:\n first: true\n") second := parseCompositionDocument(t, "source-3", "catalog:\n second: true\n") firstBefore, secondBefore := document.Clone(first), document.Clone(second) incoming, diagnostics := composeDocuments(first, second) if len(diagnostics) != 0 { t.Fatal(diagnostics) } assembly := newAssemblyAccumulator(base) if diagnostics := assembly.applyOverlay(incoming.root, incoming.origins, incoming.applications, LayerOverlay); len(diagnostics) != 0 { t.Fatal(diagnostics) } for _, key := range []string{"base", "first", "second"} { if _, ok := documentAtPath(assembly.current.root, Keys("catalog", key)); !ok { t.Fatalf("catalog is missing %q", key) } } origin := assembly.current.origins[Keys("catalog").String()] if got, want := origin.Locations, []Location{{SourceID: "source-1", Line: 2, Column: 3}, {SourceID: "source-2", Line: 2, Column: 3}, {SourceID: "source-3", Line: 2, Column: 3}}; !reflect.DeepEqual(got, want) { t.Fatalf("catalog origin locations = %#v, want %#v", got, want) } if len(assembly.current.applications) != 3 { t.Fatalf("history applications = %d, want three source contributions", len(assembly.current.applications)) } for _, application := range assembly.current.applications[1:] { if application.layer != LayerOverlay { t.Fatalf("incoming application layer = %q", application.layer) } } assembly.current.root.Mapping[0].Value.Mapping[1].Value.Text = "false" if !reflect.DeepEqual(first, firstBefore) || !reflect.DeepEqual(second, secondBefore) || !reflect.DeepEqual(first, assembly.current.applications[1].value) || !reflect.DeepEqual(second, assembly.current.applications[2].value) { t.Fatal("working tree mutation changed an acquired input") } }