Preserve inherited source origins and transfer evidence
This commit is contained in:
141
internal/engine/inheritance_origin_test.go
Normal file
141
internal/engine/inheritance_origin_test.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
|
||||
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
||||
)
|
||||
|
||||
func TestInheritedOriginsKeepSourcePathsPresenceAndIndependentReplacement(t *testing.T) {
|
||||
parent := parseCompositionDocument(t, "source-1", `parent:
|
||||
enabled: false
|
||||
count: 0
|
||||
optional: null
|
||||
items: [{file: message.txt}]
|
||||
settings: {shared: parent}
|
||||
`)
|
||||
child := parseCompositionDocument(t, "source-2", `child:
|
||||
extends: parent
|
||||
count: 3
|
||||
settings: {own: child}
|
||||
`)
|
||||
composed, diagnostics := composeDocuments(parent, child)
|
||||
if len(diagnostics) != 0 {
|
||||
t.Fatal(diagnostics)
|
||||
}
|
||||
result, diagnostics := expandInheritanceWithOrigins(composed.root, composed.origins, defaultProjectionLimits())
|
||||
if len(diagnostics) != 0 {
|
||||
t.Fatal(diagnostics)
|
||||
}
|
||||
for _, key := range []string{"enabled", "optional"} {
|
||||
path := Keys("child", key)
|
||||
value, found := documentAtPath(result.root, path)
|
||||
origin, hasOrigin := result.origins[path.String()]
|
||||
if !found || !hasOrigin || value == nil || len(origin.Locations) != 1 || origin.Locations[0].SourceID != "source-1" ||
|
||||
len(projectInheritanceSteps(origin)) != 1 {
|
||||
t.Fatalf("inherited %s = %#v, %#v", key, value, origin)
|
||||
}
|
||||
}
|
||||
count := result.origins["/child/count"]
|
||||
if len(count.Locations) != 1 || count.Locations[0].SourceID != "source-2" || len(projectInheritanceSteps(count)) != 0 {
|
||||
t.Fatalf("direct replacement origin = %#v", count)
|
||||
}
|
||||
if len(result.replacements) != 1 || result.replacements[0].path.String() != "/child/count" ||
|
||||
result.replacements[0].before.Text != "0" || result.replacements[0].after.Text != "3" ||
|
||||
len(result.replacements[0].previous) != 1 ||
|
||||
len(projectInheritanceSteps(result.replacements[0].previous[0].origin)) != 1 {
|
||||
t.Fatalf("displaced inheritance = %#v", result.replacements)
|
||||
}
|
||||
settings := result.origins["/child/settings"]
|
||||
if len(settings.Locations) != 2 || settings.Locations[0].SourceID != "source-1" ||
|
||||
settings.Locations[1].SourceID != "source-2" || len(projectInheritanceSteps(settings)) != 1 {
|
||||
t.Fatalf("composite mapping origin = %#v", settings)
|
||||
}
|
||||
if originContainedBy(result.origins["/child/enabled"], result.origins["/parent/enabled"]) ||
|
||||
originContainedBy(result.origins["/parent/enabled"], result.origins["/child/enabled"]) {
|
||||
t.Fatal("direct and inherited evidence collapsed despite matching source coordinates")
|
||||
}
|
||||
original := result.origins["/child/enabled"]
|
||||
detached := cloneOrigin(original)
|
||||
detached.Locations[0].SourceID = "changed"
|
||||
detached.contributors[0].source.segments[0].key = "changed"
|
||||
stepsCopy := projectInheritanceSteps(original)
|
||||
stepsCopy[0].Locations[0].SourceID = "changed"
|
||||
if original.Locations[0].SourceID != "source-1" ||
|
||||
original.contributors[0].source.String() != "/parent/enabled" ||
|
||||
projectInheritanceSteps(original)[0].Locations[0].SourceID != "source-2" {
|
||||
t.Fatal("cloned origin or projected steps changed retained evidence")
|
||||
}
|
||||
resource := result.origins["/child/items/0/file"]
|
||||
steps := projectInheritanceSteps(resource)
|
||||
if len(steps) != 1 || steps[0].From.String() != "/parent/items/0/file" ||
|
||||
steps[0].To.String() != "/child/items/0/file" || steps[0].Reference.String() != "/child/extends" ||
|
||||
len(resource.Locations) != 1 || resource.Locations[0].SourceID != "source-1" {
|
||||
t.Fatalf("sequence resource origin = %#v, steps = %#v", resource, steps)
|
||||
}
|
||||
contexts := map[string]fs.FS{
|
||||
"source-1": fstest.MapFS{"message.txt": &fstest.MapFile{Data: []byte("from parent")}},
|
||||
"source-2": fstest.MapFS{"message.txt": &fstest.MapFile{Data: []byte("from child")}},
|
||||
}
|
||||
data, err := fs.ReadFile(contexts[resource.Locations[0].SourceID], "message.txt")
|
||||
if err != nil || string(data) != "from parent" {
|
||||
t.Fatalf("inherited resource = %q, %v", data, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInheritedOriginsRetainChainsAndNestedDestinations(t *testing.T) {
|
||||
first := parseCompositionDocument(t, "source-1", `first:
|
||||
enabled: false
|
||||
settings:
|
||||
primary: {value: original}
|
||||
secondary: {extends: primary}
|
||||
`)
|
||||
second := parseCompositionDocument(t, "source-2", `first: {other: present}
|
||||
middle: {extends: first}
|
||||
last:
|
||||
extends: middle
|
||||
settings:
|
||||
primary: {value: replaced}
|
||||
`)
|
||||
composed, diagnostics := composeDocuments(first, second)
|
||||
if len(diagnostics) != 0 {
|
||||
t.Fatal(diagnostics)
|
||||
}
|
||||
result, diagnostics := expandInheritanceWithOrigins(composed.root, composed.origins, defaultProjectionLimits())
|
||||
if len(diagnostics) != 0 {
|
||||
t.Fatal(diagnostics)
|
||||
}
|
||||
chain := projectInheritanceSteps(result.origins["/last/enabled"])
|
||||
if len(chain) != 2 || chain[0].From.String() != "/first/enabled" ||
|
||||
chain[0].To.String() != "/middle/enabled" || chain[1].From.String() != "/middle/enabled" ||
|
||||
chain[1].To.String() != "/last/enabled" {
|
||||
t.Fatalf("chain = %#v", chain)
|
||||
}
|
||||
composite := result.origins["/last"]
|
||||
if len(composite.Locations) < 2 || len(composite.contributors) != len(composite.Locations) {
|
||||
t.Fatalf("composite contributors = %#v", composite)
|
||||
}
|
||||
seenSources := make(map[string]bool)
|
||||
for _, contributor := range composite.contributors {
|
||||
seenSources[contributor.location.SourceID] = true
|
||||
}
|
||||
if !seenSources["source-1"] || !seenSources["source-2"] {
|
||||
t.Fatalf("same-layer source contributors = %#v", composite.contributors)
|
||||
}
|
||||
nested := projectInheritanceSteps(result.origins["/last/settings/secondary/value"])
|
||||
if len(nested) != 1 || nested[0].From.String() != "/last/settings/primary/value" ||
|
||||
nested[0].To.String() != "/last/settings/secondary/value" {
|
||||
t.Fatalf("destination-specific nested transfer = %#v", nested)
|
||||
}
|
||||
if got := result.origins["/last/settings/secondary/value"].Locations[0].SourceID; got != "source-2" {
|
||||
t.Fatalf("nested replacement source = %q", got)
|
||||
}
|
||||
if original, found := documentAtPath(first, Keys("first", "settings", "primary", "value")); !found || original.Text != "original" {
|
||||
t.Fatal("expansion changed its input")
|
||||
}
|
||||
if inherited, found := documentAtPath(result.root, Keys("last", "settings", "secondary", "value")); !found || inherited.Kind != document.String || inherited.Text != "replaced" {
|
||||
t.Fatalf("nested inherited value = %#v, %v", inherited, found)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user