Verify accumulated import resource accounting
This commit is contained in:
@@ -270,7 +270,7 @@ root-scoped upper tiers. No limitations.
|
||||
|
||||
## Stage 5 — Verify accumulated resource accounting
|
||||
|
||||
Status: planned. Scope: limit regressions and any necessary narrow correction.
|
||||
Status: complete. Scope: limit regressions and any necessary narrow correction.
|
||||
|
||||
1. Audit `placeDocument`, `destinationWrapperNodeCount`, `importAccounting`,
|
||||
and composition totals. Each body uses the full effective destination for
|
||||
@@ -290,6 +290,16 @@ Status: planned. Scope: limit regressions and any necessary narrow correction.
|
||||
Validate: focused import/destination/limit/override tests and everyday check.
|
||||
Done: accumulation cannot bypass current bounds or falsely charge absent input.
|
||||
|
||||
Evidence (2026-10-14): based on `430985c6293ca8f9151149f1af34481ac325eed3`,
|
||||
using `go1.26.8 linux/amd64`. The focused engine import/glob/destination/grouped/
|
||||
overlay/override/history/inheritance suite and `./scripts/check.sh` passed.
|
||||
Exact, below, and above node-budget fixtures cover nested empty imports and
|
||||
multiple glob matches using their accumulated destinations. Additional cases
|
||||
verify accumulated depth bounds and that absent optional path/glob contributions
|
||||
at unused depth add no source or wrapper charge. Existing
|
||||
`TestSelectedOverlayAndCLIUsePlacementNodeCharges` continues to cover later
|
||||
overlay/CLI accounting. No limitations.
|
||||
|
||||
## Stage 6 — Preserve diagnostics, source inspection, and resource contexts
|
||||
|
||||
Status: planned. Scope: coordinates, detached records, and resource ownership.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
|
||||
@@ -120,6 +122,207 @@ func TestImportedEmptyBodiesChargeEachDestinationWrapper(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportedEmptyBodiesChargeFullRelativeDestinations(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
directory := t.TempDir()
|
||||
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
|
||||
imports:
|
||||
- path: empty.yml
|
||||
destination: [groups, empty]
|
||||
`)
|
||||
fragmentPath := writeImportFile(t, directory, "empty.yml", "{}\n")
|
||||
main := parseImportMain(t, mainPath)
|
||||
fragment, diagnostics := parseSource(File(fragmentPath), "source-2", defaultParseLimits())
|
||||
if len(diagnostics) != 0 {
|
||||
t.Fatalf("empty fragment diagnostics = %#v", diagnostics)
|
||||
}
|
||||
entryDestination := Keys("collections", "entry")
|
||||
importDestination := appendDestination(entryDestination, Keys("groups", "empty"))
|
||||
wantNodes := countInputNodes(main.document) + countInputNodes(fragment.document) +
|
||||
destinationWrapperNodeCount(entryDestination) + destinationWrapperNodeCount(importDestination)
|
||||
limits := defaultImportLimits()
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
delta int
|
||||
wantError bool
|
||||
}{
|
||||
{name: "below aggregate bound", delta: -1, wantError: true},
|
||||
{name: "at aggregate bound"},
|
||||
{name: "above aggregate bound", delta: 1},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
currentLimits := limits
|
||||
currentLimits.nodes = wantNodes + test.delta
|
||||
accounting := &importAccounting{}
|
||||
composed, sources, diagnostics := loadImportedDocumentsTracked(
|
||||
File(mainPath), nil, main, entryDestination, nil, currentLimits, accounting, nil, nil,
|
||||
)
|
||||
if test.wantError {
|
||||
if composed != nil || len(diagnostics) != 1 || diagnostics[0].Code != CodeImportLimitExceeded {
|
||||
t.Fatalf("below-bound relative placement = %#v, %#v", composed, diagnostics)
|
||||
}
|
||||
return
|
||||
}
|
||||
if len(diagnostics) != 0 || composed == nil || len(sources) != 2 {
|
||||
t.Fatalf("bounded relative placement = %#v, %#v, %#v", composed, sources, diagnostics)
|
||||
}
|
||||
if composed.inputNodes != wantNodes || accounting.nodes != wantNodes {
|
||||
t.Fatalf("relative placement node charge = %d/%d, want %d", composed.inputNodes, accounting.nodes, wantNodes)
|
||||
}
|
||||
if sources[1].Destination.String() != importDestination.String() {
|
||||
t.Fatalf("empty import destination = %q, want %q", sources[1].Destination.String(), importDestination.String())
|
||||
}
|
||||
if node, present := documentAtPath(composed.root, importDestination); !present || node.Kind != document.Mapping || len(node.Mapping) != 0 {
|
||||
t.Fatalf("empty relative import body = %#v, present %v", node, present)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGlobMatchesChargeTheirSharedRelativeDestination(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
directory := t.TempDir()
|
||||
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
|
||||
imports:
|
||||
- glob: fragments/*.yml
|
||||
destination: [groups, empty]
|
||||
`)
|
||||
fragmentPaths := []string{
|
||||
writeImportFile(t, directory, "fragments/a.yml", "{}\n"),
|
||||
writeImportFile(t, directory, "fragments/b.yml", "{}\n"),
|
||||
writeImportFile(t, directory, "fragments/c.yml", "{}\n"),
|
||||
}
|
||||
main := parseImportMain(t, mainPath)
|
||||
fragment, diagnostics := parseSource(File(fragmentPaths[0]), "source-fragment", defaultParseLimits())
|
||||
if len(diagnostics) != 0 {
|
||||
t.Fatalf("glob fragment diagnostics = %#v", diagnostics)
|
||||
}
|
||||
entryDestination := Keys("collections", "entry")
|
||||
importDestination := appendDestination(entryDestination, Keys("groups", "empty"))
|
||||
wantNodes := countInputNodes(main.document) + len(fragmentPaths)*countInputNodes(fragment.document) +
|
||||
destinationWrapperNodeCount(entryDestination) + len(fragmentPaths)*destinationWrapperNodeCount(importDestination)
|
||||
limits := defaultImportLimits()
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
delta int
|
||||
wantError bool
|
||||
}{
|
||||
{name: "below aggregate bound", delta: -1, wantError: true},
|
||||
{name: "at aggregate bound"},
|
||||
{name: "above aggregate bound", delta: 1},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
currentLimits := limits
|
||||
currentLimits.nodes = wantNodes + test.delta
|
||||
accounting := &importAccounting{}
|
||||
composed, sources, diagnostics := loadImportedDocumentsTracked(
|
||||
File(mainPath), nil, main, entryDestination, nil, currentLimits, accounting, nil, nil,
|
||||
)
|
||||
if test.wantError {
|
||||
if composed != nil || len(diagnostics) != 1 || diagnostics[0].Code != CodeImportLimitExceeded {
|
||||
t.Fatalf("below-bound glob placement = %#v, %#v", composed, diagnostics)
|
||||
}
|
||||
return
|
||||
}
|
||||
if len(diagnostics) != 0 || composed == nil || len(sources) != len(fragmentPaths)+1 {
|
||||
t.Fatalf("bounded glob placement = %#v, %#v, %#v", composed, sources, diagnostics)
|
||||
}
|
||||
if composed.inputNodes != wantNodes || accounting.nodes != wantNodes {
|
||||
t.Fatalf("glob relative placement node charge = %d/%d, want %d", composed.inputNodes, accounting.nodes, wantNodes)
|
||||
}
|
||||
for index, source := range sources[1:] {
|
||||
if source.Destination.String() != importDestination.String() {
|
||||
t.Errorf("glob source %d destination = %q, want %q", index, source.Destination.String(), importDestination.String())
|
||||
}
|
||||
}
|
||||
if node, present := documentAtPath(composed.root, importDestination); !present || node.Kind != document.Mapping || len(node.Mapping) != 0 {
|
||||
t.Fatalf("empty glob contributions = %#v, present %v", node, present)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRelativeImportDestinationUsesAccumulatedDocumentDepth(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
directory := t.TempDir()
|
||||
mainPath := writeImportFile(t, directory, "main.yml", "configkit: {imports: [{path: fragment.yml, destination: [extra]}]}\n")
|
||||
writeImportFile(t, directory, "fragment.yml", "value: nested\n")
|
||||
destination := Path{segments: make([]pathSegment, maxDocumentDepth)}
|
||||
for index := range destination.segments {
|
||||
destination.segments[index] = pathSegment{kind: pathSegmentKey, key: "entry"}
|
||||
}
|
||||
_, err := LoadLayered(
|
||||
Input{Source: File(mainPath), Imports: true, Destination: destination}, testSecretBinding(),
|
||||
LayeredOptions[map[string]any]{},
|
||||
)
|
||||
loadError, ok := err.(*LoadError)
|
||||
if !ok || len(loadError.Diagnostics()) != 1 || loadError.Diagnostics()[0].Code != CodeImportLimitExceeded {
|
||||
t.Fatalf("accumulated depth result = %v", err)
|
||||
}
|
||||
sources := loadError.Sources()
|
||||
if len(sources) != 2 || len(sources[0].Destination.segments) != maxDocumentDepth || len(sources[1].Destination.segments) != maxDocumentDepth+1 {
|
||||
t.Fatalf("depth failure source destinations = %#v", sources)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAbsentOptionalImportsDoNotChargeUnusedDeepDestinations(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
destination := Path{segments: make([]pathSegment, maxDocumentDepth)}
|
||||
for index := range destination.segments {
|
||||
destination.segments[index] = pathSegment{kind: pathSegmentKey, key: "entry"}
|
||||
}
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
body string
|
||||
setup func(*testing.T, string)
|
||||
}{
|
||||
{
|
||||
name: "optional missing path",
|
||||
body: "configkit: {imports: [{path: missing.yml, optional: true, destination: [unused]}]}\n",
|
||||
},
|
||||
{
|
||||
name: "optional unmatched glob",
|
||||
body: "configkit: {imports: [{glob: empty/*.yml, optional: true, destination: [unused]}]}\n",
|
||||
setup: func(t *testing.T, directory string) {
|
||||
if err := os.Mkdir(filepath.Join(directory, "empty"), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
directory := t.TempDir()
|
||||
if test.setup != nil {
|
||||
test.setup(t, directory)
|
||||
}
|
||||
mainPath := writeImportFile(t, directory, "main.yml", test.body)
|
||||
main := parseImportMain(t, mainPath)
|
||||
limits := defaultImportLimits()
|
||||
accounting := &importAccounting{}
|
||||
composed, sources, diagnostics := loadImportedDocumentsTracked(
|
||||
File(mainPath), nil, main, destination, nil, limits, accounting, nil, nil,
|
||||
)
|
||||
if len(diagnostics) != 0 || composed == nil || len(sources) != 1 {
|
||||
t.Fatalf("absent optional import = %#v, %#v, %#v", composed, sources, diagnostics)
|
||||
}
|
||||
wantNodes := countInputNodes(main.document) + destinationWrapperNodeCount(destination)
|
||||
if composed.inputNodes != wantNodes || accounting.nodes != wantNodes {
|
||||
t.Fatalf("absent optional node charge = %d/%d, want %d", composed.inputNodes, accounting.nodes, wantNodes)
|
||||
}
|
||||
if node, present := documentAtPath(composed.root, destination); !present || node.Kind != document.Mapping || len(node.Mapping) != 0 {
|
||||
t.Fatalf("empty entry body = %#v, present %v", node, present)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoSourceAndRootPlacementAddNoWrapperCharges(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user