Keep complete paths in missing version evidence

This commit is contained in:
2026-10-15 22:20:20 +00:00
parent 0a2246bdbe
commit e69ad0f32d
5 changed files with 105 additions and 7 deletions

View File

@@ -265,21 +265,23 @@ func (evidence *sourceVersionEvidence) redactText() {
}
func resolveSourceVersionPath(root *document.Node, path Path) sourceVersionPath {
resolved := sourceVersionPath{absence: sourceVersionPathInvalid}
// Refine only segments whose container shape is known, keeping the full
// requested suffix when lookup stops before the declaration.
resolved := sourceVersionPath{absence: sourceVersionPathInvalid, resolvedPath: path.clone()}
if root == nil || !path.valid() {
return resolved
}
current := root
resolved.nearest = current.Location
resolved.ancestors = append(resolved.ancestors, sourceVersionAncestor{kind: current.Kind})
for _, segment := range path.segments {
for position, segment := range path.segments {
switch current.Kind {
case document.Mapping:
if segment.kind == pathSegmentIndex {
resolved.absence = sourceVersionPathObstructed
return resolved
}
resolved.resolvedPath = resolved.resolvedPath.Key(segment.key)
resolved.resolvedPath.segments[position] = pathSegment{kind: pathSegmentKey, key: segment.key}
found := false
for _, entry := range current.Mapping {
if entry.Key == segment.key {
@@ -298,7 +300,7 @@ func resolveSourceVersionPath(root *document.Node, path Path) sourceVersionPath
resolved.absence = sourceVersionIndexUnavailable
return resolved
}
resolved.resolvedPath = resolved.resolvedPath.Index(index)
resolved.resolvedPath.segments[position] = pathSegment{kind: pathSegmentIndex, index: index}
if index >= len(current.Sequence) {
resolved.absence = sourceVersionIndexUnavailable
return resolved
@@ -310,7 +312,7 @@ func resolveSourceVersionPath(root *document.Node, path Path) sourceVersionPath
}
resolved.nearest = current.Location
resolved.ancestors = append(resolved.ancestors, sourceVersionAncestor{
path: resolved.resolvedPath.clone(), kind: current.Kind,
path: Path{segments: clonePathSegments(resolved.resolvedPath.segments[:position+1], 0)}, kind: current.Kind,
})
}
resolved.node = current

View File

@@ -3,11 +3,40 @@ package engine
import (
"errors"
"path/filepath"
"reflect"
"testing"
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
)
func TestSourceVersionAbsentPathKeepsUnresolvedSegmentIntent(t *testing.T) {
t.Parallel()
for _, test := range []struct {
name string
body string
path string
want []pathSegment
}{
{"missing member", "{}\n", "/meta/version", []pathSegment{{kind: pathSegmentKey, key: "meta"}, {kind: pathSegmentParsed, key: "version"}}},
{"scalar obstruction", "meta: false\n", "/meta/version", []pathSegment{{kind: pathSegmentKey, key: "meta"}, {kind: pathSegmentParsed, key: "version"}}},
{"unavailable index", "meta: []\n", "/meta/3/version", []pathSegment{{kind: pathSegmentKey, key: "meta"}, {kind: pathSegmentIndex, index: 3}, {kind: pathSegmentParsed, key: "version"}}},
} {
t.Run(test.name, func(t *testing.T) {
path, err := ParsePath(test.path)
if err != nil {
t.Fatal(err)
}
resolved := resolveSourceVersionPath(sourceVersionTestDocument(t, test.body), path)
if resolved.present || !reflect.DeepEqual(resolved.resolvedPath.segments, test.want) {
t.Fatalf("absent path = %#v, want %#v", resolved.resolvedPath, test.want)
}
if !reflect.DeepEqual(path.segments, mustParseSourceVersionPath(t, test.path).segments) {
t.Fatal("lookup mutated the requested path")
}
})
}
}
type sourceVersionMountedService struct {
Version string `yaml:"version"`
}