2026-10-24 14:13:52 +00:00
|
|
|
package engine
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"reflect"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
|
2026-11-01 14:19:37 +00:00
|
|
|
"go.maximumdirect.net/configkit/internal/document"
|
2026-10-24 14:13:52 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestMetadataLocationsAcrossBroadAuthoredAndDefaultMappings(t *testing.T) {
|
|
|
|
|
type config map[string]map[string]string
|
|
|
|
|
defaults := config{}
|
|
|
|
|
var body strings.Builder
|
|
|
|
|
for index := range 96 {
|
|
|
|
|
name := fmt.Sprintf("authored-%03d", index)
|
|
|
|
|
fmt.Fprintf(&body, "%s: {present: source}\n", name)
|
|
|
|
|
defaults[name] = map[string]string{"absent": "default"}
|
|
|
|
|
defaults[fmt.Sprintf("default-%03d", index)] = map[string]string{"absent": "default"}
|
|
|
|
|
}
|
|
|
|
|
input := parseDocumentForDecode(t, body.String())
|
|
|
|
|
decoded, issues := decodeConfiguration(mustSchemaFor[config](t), reflect.ValueOf(defaults), input)
|
|
|
|
|
if len(issues) != 0 {
|
|
|
|
|
t.Fatal(issues)
|
|
|
|
|
}
|
|
|
|
|
located := documentWithMetadataLocations(decoded.final, metadataFromDecoded(decoded), input)
|
|
|
|
|
inputIndex := buildDocumentLookup(input)
|
|
|
|
|
locatedIndex := buildDocumentLookup(located)
|
|
|
|
|
for index := range 96 {
|
|
|
|
|
authored := Keys(fmt.Sprintf("authored-%03d", index))
|
|
|
|
|
mapping, _ := documentAtPathWithLookup(input, authored, inputIndex)
|
|
|
|
|
present, _ := documentAtPathWithLookup(input, authored.Key("present"), inputIndex)
|
|
|
|
|
for _, test := range []struct {
|
|
|
|
|
path Path
|
|
|
|
|
want document.Location
|
|
|
|
|
}{
|
|
|
|
|
{authored.Key("present"), present.Location},
|
|
|
|
|
{authored.Key("absent"), mapping.Location},
|
|
|
|
|
{Keys(fmt.Sprintf("default-%03d", index), "absent"), input.Location},
|
|
|
|
|
} {
|
|
|
|
|
node, found := documentAtPathWithLookup(located, test.path, locatedIndex)
|
|
|
|
|
if !found || node.Location != test.want {
|
|
|
|
|
t.Fatalf("location at %s = %#v, want %#v", test.path, node, test.want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
walkDocument(located, Path{}, func(_ Path, node *document.Node) {
|
|
|
|
|
for _, entry := range node.Mapping {
|
|
|
|
|
if entry.KeyLocation != entry.Value.Location || entry.ValueLocation != entry.Value.Location {
|
|
|
|
|
t.Fatal("entry and value locations disagree")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
before := decoded.final.Mapping[0].Value.Mapping[0].Value.Text
|
|
|
|
|
located.Mapping[0].Value.Mapping[0].Value.Text = "changed snapshot"
|
|
|
|
|
if decoded.final.Mapping[0].Value.Mapping[0].Value.Text != before || input.Mapping[0].Value.Mapping[0].Value.Text != "source" {
|
|
|
|
|
t.Fatal("location projection changed a borrowed input")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestIndexedNearestLocationsPreserveMissingAndSyntheticCoordinates(t *testing.T) {
|
|
|
|
|
root := parseDocumentForDecode(t, "mapping: {'0': value}\nitems: [{value: source}]\nscalar: stop\n")
|
|
|
|
|
lookup := buildDocumentLookup(root)
|
|
|
|
|
for _, path := range []Path{Path{}, Keys("missing"), Keys("mapping", "missing"), Keys("mapping", "0"), Keys("mapping").Index(0), Keys("items").Index(4), Keys("items").Index(0).Key("missing"), Keys("scalar", "child"), Path{}.Index(-1)} {
|
|
|
|
|
want, wantOK := nearestDocumentLocation(root, path)
|
|
|
|
|
got, gotOK := nearestDocumentLocationWithLookup(root, path, lookup)
|
|
|
|
|
if got != want || gotOK != wantOK {
|
|
|
|
|
t.Fatalf("nearest location changed for %s: %#v/%t, want %#v/%t", path, got, gotOK, want, wantOK)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
final := parseDocumentForDecode(t, "value: cli\n")
|
|
|
|
|
synthetic := Location{SourceID: "cli-1"}
|
2026-10-26 17:15:30 +00:00
|
|
|
metadata := Metadata{
|
|
|
|
|
effectivePaths: effectivePathIndex(final),
|
|
|
|
|
fields: map[string]FieldInfo{"/value": {Origin: Origin{Layer: LayerCLI, Locations: []Location{synthetic}}}},
|
|
|
|
|
}
|
2026-10-24 14:13:52 +00:00
|
|
|
located := documentWithMetadataLocations(final, metadata, root)
|
|
|
|
|
if location := located.Mapping[0].Value.Location; location.SourceID != "cli-1" || location.Line != 0 || location.Column != 0 {
|
|
|
|
|
t.Fatalf("synthetic CLI location was replaced by fallback: %#v", location)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-10-26 17:15:30 +00:00
|
|
|
|
|
|
|
|
func TestMetadataLocationFallbackDoesNotCrossKeyIndexShapes(t *testing.T) {
|
|
|
|
|
fallback := parseDocumentForDecode(t, "settings:\n - value:\n child: input\n")
|
|
|
|
|
final := parseDocumentForDecode(t, "settings:\n '0':\n value: final\n")
|
|
|
|
|
metadata := Metadata{
|
|
|
|
|
effectivePaths: effectivePathIndex(final),
|
|
|
|
|
fields: map[string]FieldInfo{"/settings/0/value": {Origin: Origin{Layer: LayerDefault}}},
|
|
|
|
|
}
|
|
|
|
|
located := documentWithMetadataLocations(final, metadata, fallback)
|
|
|
|
|
settings, _ := documentAtPath(located, Keys("settings"))
|
|
|
|
|
value, _ := documentAtPath(located, Keys("settings", "0", "value"))
|
|
|
|
|
if settings == nil || value == nil || value.Location != settings.Location {
|
|
|
|
|
t.Fatalf("fallback crossed from a mapping key to an input sequence item: settings %#v, value %#v", settings, value)
|
|
|
|
|
}
|
|
|
|
|
inputChild, _ := documentAtPath(fallback, Keys("settings").Index(0).Key("value").Key("child"))
|
|
|
|
|
if inputChild == nil || value.Location == inputChild.Location {
|
|
|
|
|
t.Fatalf("fallback fabricated a key location from an index: input child %#v, value %#v", inputChild, value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestHistoricalLocationSelectionSeparatesNumericKeyAndIndex(t *testing.T) {
|
|
|
|
|
mappingRoot := &document.Node{Kind: document.Mapping, Mapping: []document.Entry{{
|
|
|
|
|
Key: "shape", Value: &document.Node{Kind: document.Mapping, Mapping: []document.Entry{{
|
|
|
|
|
Key: "0", Value: &document.Node{Kind: document.Mapping, Mapping: []document.Entry{{
|
|
|
|
|
Key: "value", Value: &document.Node{Kind: document.String, Text: "mapping", Location: document.Location{SourceID: "mapping", Line: 3, Column: 5}},
|
|
|
|
|
}}},
|
|
|
|
|
}}},
|
|
|
|
|
}}}
|
|
|
|
|
sequenceRoot := &document.Node{Kind: document.Mapping, Mapping: []document.Entry{{
|
|
|
|
|
Key: "shape", Value: &document.Node{Kind: document.Sequence, Sequence: []*document.Node{{
|
|
|
|
|
Kind: document.Mapping, Mapping: []document.Entry{{
|
|
|
|
|
Key: "value", Value: &document.Node{Kind: document.String, Text: "sequence", Location: document.Location{SourceID: "sequence", Line: 3, Column: 5}},
|
|
|
|
|
}},
|
|
|
|
|
}}},
|
|
|
|
|
}}}
|
|
|
|
|
builder := newHistoryBuilder()
|
|
|
|
|
builder.observeSourceEvidence(sequenceRoot, nil, []historyApplication{
|
2026-10-27 15:15:39 +00:00
|
|
|
{layer: LayerMain, value: mappingRoot},
|
2026-10-26 17:15:30 +00:00
|
|
|
{layer: LayerOverlay, value: sequenceRoot},
|
|
|
|
|
}, nil)
|
|
|
|
|
model := builder.freeze()
|
|
|
|
|
metadata := Metadata{historyModel: model}
|
|
|
|
|
resolutionOrigin := Origin{Layer: LayerResolution, Operation: ChangeNormalization}
|
|
|
|
|
for _, test := range []struct {
|
|
|
|
|
path Path
|
|
|
|
|
want string
|
|
|
|
|
}{
|
|
|
|
|
{Keys("shape", "0", "value"), "mapping"},
|
|
|
|
|
{Keys("shape").Index(0).Key("value"), "sequence"},
|
|
|
|
|
} {
|
|
|
|
|
location, ok := metadataFieldLocation(metadata, test.path, resolutionOrigin)
|
|
|
|
|
if !ok || location.SourceID != test.want {
|
|
|
|
|
t.Fatalf("historical location for %s = %#v, %v; want %q", test.path, location, ok, test.want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|