Verify mounted provenance and resource contexts
This commit is contained in:
@@ -342,7 +342,7 @@ budgets and introduces no unrelated caps.
|
|||||||
|
|
||||||
## Stage 8 — Preserve provenance, history, and source resource contexts
|
## Stage 8 — Preserve provenance, history, and source resource contexts
|
||||||
|
|
||||||
Status: planned. Scope: engine inspection and retained evidence.
|
Status: complete. Scope: engine inspection and retained evidence.
|
||||||
|
|
||||||
1. Audit `compositionOrigins`, `completeCompositionOrigins`, application replay,
|
1. Audit `compositionOrigins`, `completeCompositionOrigins`, application replay,
|
||||||
metadata construction/finalization, and source-record cloning against placed
|
metadata construction/finalization, and source-record cloning against placed
|
||||||
@@ -359,9 +359,18 @@ Status: planned. Scope: engine inspection and retained evidence.
|
|||||||
inspect each source record, and verify independent destination paths. Repeat
|
inspect each source record, and verify independent destination paths. Repeat
|
||||||
inspection after comparison and independent loads to detect retained mutation.
|
inspection after comparison and independent loads to detect retained mutation.
|
||||||
|
|
||||||
Validate: destination/metadata/provenance/history/resource tests,
|
Validated with `go test ./internal/engine -run 'Destination|Metadata|Provenance|History|Resource|SourceContext'`,
|
||||||
`go test -race ./internal/engine`, and everyday check. Done: inspection explains
|
`go test -race ./internal/engine`, and `./scripts/check.sh` (go1.26.8
|
||||||
effective placement and physical origin without consumer reconstruction.
|
linux/amd64); all passed. `TestLayeredDestinationsRetainOriginsHistoryAndDetachedRecords`
|
||||||
|
checks synthetic-container fallback locations, authored leaf coordinates,
|
||||||
|
composite origins, removed-descendant history, and the same host file retained
|
||||||
|
at distinct destinations in separate tiers before and after comparison and an
|
||||||
|
independent load. `TestLayeredImportedInheritedValuesRetainResourceContexts`
|
||||||
|
uses real temporary files to verify resolution-time and completed-result
|
||||||
|
relative-resource lookup through an imported surviving parent and its inherited
|
||||||
|
child. Existing metadata and resource-context tests continue to cover detached
|
||||||
|
records and loading-handle closure. Done: inspection explains effective
|
||||||
|
placement and physical origin without consumer reconstruction.
|
||||||
|
|
||||||
## Stage 9 — Verify final inheritance, sensitivity, and lifecycle integration
|
## Stage 9 — Verify final inheritance, sensitivity, and lifecycle integration
|
||||||
|
|
||||||
|
|||||||
181
internal/engine/destination_provenance_test.go
Normal file
181
internal/engine/destination_provenance_test.go
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io/fs"
|
||||||
|
"path"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLayeredDestinationsRetainOriginsHistoryAndDetachedRecords(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
directory := t.TempDir()
|
||||||
|
sharedPath := writeImportFile(t, directory, "shared.yml", "section:\n shared: lower\n branch:\n child: old\n kept: retained\n")
|
||||||
|
shared := File(sharedPath)
|
||||||
|
load := func() (*Loaded[map[string]any], error) {
|
||||||
|
return LoadLayered(
|
||||||
|
Input{Source: NoSource()}, testSecretBinding(),
|
||||||
|
LayeredOptions[map[string]any]{Underlays: []Underlay{
|
||||||
|
{Inputs: []Input{
|
||||||
|
{Source: shared, Destination: Keys("catalog")},
|
||||||
|
{Source: Bytes("peer", []byte("section:\n peer: present\n")), Destination: Keys("catalog")},
|
||||||
|
}},
|
||||||
|
{Inputs: []Input{{Source: shared, Destination: Keys("backup")}}},
|
||||||
|
{Inputs: []Input{{Source: Bytes("replacement", []byte("section:\n branch: replaced\n")), Destination: Keys("catalog")}}},
|
||||||
|
}},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
loaded, err := load()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("LoadLayered: %v, diagnostics %#v", err, err.(*LoadError).Diagnostics())
|
||||||
|
}
|
||||||
|
result := testLoaded(loaded)
|
||||||
|
metadata := result.Metadata()
|
||||||
|
|
||||||
|
container, ok := metadata.Lookup(Keys("catalog"))
|
||||||
|
if !ok || !container.Explicit || len(container.Origin.Locations) < 3 {
|
||||||
|
t.Fatalf("mounted container origin = %#v, %v", container, ok)
|
||||||
|
}
|
||||||
|
containerSources := make(map[string]bool)
|
||||||
|
for _, location := range container.Origin.Locations {
|
||||||
|
containerSources[location.SourceID] = true
|
||||||
|
if location.Line < 1 {
|
||||||
|
t.Fatalf("introduced container lacks its source-root fallback: %#v", location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, sourceID := range []string{"source-1", "source-2", "source-4"} {
|
||||||
|
if !containerSources[sourceID] {
|
||||||
|
t.Fatalf("container contributors = %#v, missing %s", container.Origin.Locations, sourceID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedField, ok := metadata.Lookup(Keys("catalog", "section", "shared"))
|
||||||
|
if !ok || len(sharedField.Origin.Locations) != 1 || sharedField.Origin.Locations[0].SourceID != "source-1" || sharedField.Origin.Locations[0].Line != 2 {
|
||||||
|
t.Fatalf("authored leaf origin = %#v, %v", sharedField, ok)
|
||||||
|
}
|
||||||
|
peerField, ok := metadata.Lookup(Keys("catalog", "section", "peer"))
|
||||||
|
if !ok || len(peerField.Origin.Locations) != 1 || peerField.Origin.Locations[0].SourceID != "source-2" {
|
||||||
|
t.Fatalf("peer leaf origin = %#v, %v", peerField, ok)
|
||||||
|
}
|
||||||
|
if _, ok := metadata.Lookup(Keys("catalog", "section", "branch", "child")); ok {
|
||||||
|
t.Fatal("replaced descendant remains effective")
|
||||||
|
}
|
||||||
|
removed := metadata.History(Keys("catalog", "section", "branch", "child"))
|
||||||
|
if len(removed) < 2 || removed[0].Origin.Locations[0].SourceID != "source-1" {
|
||||||
|
t.Fatalf("removed descendant history = %#v", removed)
|
||||||
|
}
|
||||||
|
|
||||||
|
backup, ok := metadata.Lookup(Keys("backup", "section", "shared"))
|
||||||
|
if !ok || len(backup.Origin.Locations) != 1 || backup.Origin.Locations[0].SourceID != "source-3" {
|
||||||
|
t.Fatalf("second-tier leaf origin = %#v, %v", backup, ok)
|
||||||
|
}
|
||||||
|
sources := result.Sources()
|
||||||
|
if len(sources) != 4 || sources[0].Path != sources[2].Path || sources[0].Destination.String() != "/catalog" || sources[2].Destination.String() != "/backup" {
|
||||||
|
t.Fatalf("same source in separate tiers = %#v", sources)
|
||||||
|
}
|
||||||
|
if sources[0].Layer != LayerUnderlay || sources[0].UnderlayIndex != 1 || sources[2].Layer != LayerUnderlay || sources[2].UnderlayIndex != 2 {
|
||||||
|
t.Fatalf("tier source records = %#v", sources)
|
||||||
|
}
|
||||||
|
|
||||||
|
other, err := load()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("independent load: %v", err)
|
||||||
|
}
|
||||||
|
differences, err := Diff(loaded.Inspection, other.Inspection)
|
||||||
|
if err != nil || len(differences) != 0 {
|
||||||
|
t.Fatalf("equivalent inspection diff = %#v, %v", differences, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sources[0].Destination.segments[0].key = "mutated"
|
||||||
|
sharedField.Origin.Locations[0].SourceID = "mutated"
|
||||||
|
removed[0].Path.segments[0].key = "mutated"
|
||||||
|
if got := result.Sources()[0].Destination.String(); got != "/catalog" {
|
||||||
|
t.Fatalf("source record copy changed after inspection: %q", got)
|
||||||
|
}
|
||||||
|
fresh, ok := result.Metadata().Lookup(Keys("catalog", "section", "shared"))
|
||||||
|
if !ok || fresh.Origin.Locations[0].SourceID != "source-1" {
|
||||||
|
t.Fatalf("effective origin changed after mutating returned records: %#v, %v", fresh, ok)
|
||||||
|
}
|
||||||
|
if got := result.Metadata().History(Keys("catalog", "section", "branch", "child")); len(got) < 2 || got[0].Path.String() != "/catalog/section/branch/child" {
|
||||||
|
t.Fatalf("historical path changed after mutating returned records: %#v", got)
|
||||||
|
}
|
||||||
|
if got := other.Inspection.sources[0].Destination.String(); got != "/catalog" {
|
||||||
|
t.Fatalf("independent load destination changed: %q", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLayeredImportedInheritedValuesRetainResourceContexts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type entry struct {
|
||||||
|
Extends string `yaml:"extends"`
|
||||||
|
Resource string `yaml:"resource"`
|
||||||
|
Count int `yaml:"count"`
|
||||||
|
}
|
||||||
|
type config struct {
|
||||||
|
Catalog struct {
|
||||||
|
Entries map[string]entry `yaml:"entries"`
|
||||||
|
} `yaml:"catalog"`
|
||||||
|
}
|
||||||
|
directory := t.TempDir()
|
||||||
|
mainPath := writeImportFile(t, directory, "config/main.yml", "child:\n extends: parent\n")
|
||||||
|
inputPath := writeImportFile(t, directory, "shared/input.yml", "configkit: {imports: [{path: parent.yml}]}\n")
|
||||||
|
writeImportFile(t, directory, "shared/parent.yml", "parent:\n resource: icon.txt\n count: 1\n")
|
||||||
|
writeImportFile(t, directory, "shared/icon.txt", "parent-resource")
|
||||||
|
var resolutionContext ResourceContext
|
||||||
|
loaded, err := LoadLayered(
|
||||||
|
Input{Source: File(mainPath), Destination: Keys("catalog", "entries")},
|
||||||
|
testSecretBinding(),
|
||||||
|
LayeredOptions[config]{
|
||||||
|
Underlays: []Underlay{{Inputs: []Input{{
|
||||||
|
Source: File(inputPath), Imports: true, Destination: Keys("catalog", "entries"),
|
||||||
|
}}}},
|
||||||
|
Definition: Definition[config]{Resolve: func(configuration *config, resolution *Resolution) error {
|
||||||
|
info, ok := resolution.InputInfo(Keys("catalog", "entries", "child", "resource"))
|
||||||
|
if !ok || len(info.Origin.Locations) != 1 || len(info.Origin.Inheritance) != 1 {
|
||||||
|
return errors.New("inherited resource lacks its source origin")
|
||||||
|
}
|
||||||
|
var found bool
|
||||||
|
resolutionContext, found = resolution.state.input.sourceContext(info.Origin.Locations[0].SourceID)
|
||||||
|
if !found || resolutionContext.BaseDir != "." || resolutionContext.FS == nil {
|
||||||
|
return errors.New("resolver could not find the declaring resource context")
|
||||||
|
}
|
||||||
|
data, err := fs.ReadFile(resolutionContext.FS, path.Join(resolutionContext.BaseDir, configuration.Catalog.Entries["child"].Resource))
|
||||||
|
if err != nil || string(data) != "parent-resource" {
|
||||||
|
return errors.New("resolver could not read the inherited relative resource")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("LoadLayered: %v, diagnostics %#v", err, err.(*LoadError).Diagnostics())
|
||||||
|
}
|
||||||
|
result := testLoaded(loaded)
|
||||||
|
child := result.Config.Catalog.Entries["child"]
|
||||||
|
if child.Resource != "icon.txt" || child.Count != 1 || child.Extends != "" {
|
||||||
|
t.Fatalf("inherited child = %#v", child)
|
||||||
|
}
|
||||||
|
field, ok := result.Metadata().Lookup(Keys("catalog", "entries", "child", "resource"))
|
||||||
|
if !ok || len(field.Origin.Locations) != 1 || len(field.Origin.Inheritance) != 1 {
|
||||||
|
t.Fatalf("inherited resource origin = %#v, %v", field, ok)
|
||||||
|
}
|
||||||
|
context, ok := result.Inspection.SourceContext(field.Origin.Locations[0].SourceID)
|
||||||
|
if !ok || context.BaseDir != "." || context.FS == nil || resolutionContext.BaseDir != context.BaseDir {
|
||||||
|
t.Fatalf("completed resource contexts = %#v, %#v, %v", resolutionContext, context, ok)
|
||||||
|
}
|
||||||
|
data, err := fs.ReadFile(context.FS, path.Join(context.BaseDir, child.Resource))
|
||||||
|
if err != nil || string(data) != "parent-resource" {
|
||||||
|
t.Fatalf("completed source-relative resource = %q, %v", data, err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(result.Config.Catalog.Entries["parent"], entry{Resource: "icon.txt", Count: 1}) {
|
||||||
|
t.Fatalf("surviving lower-tier parent = %#v", result.Config.Catalog.Entries["parent"])
|
||||||
|
}
|
||||||
|
for _, source := range result.Sources() {
|
||||||
|
if source.Layer == LayerUnderlay && source.Destination.String() != "/catalog/entries" {
|
||||||
|
t.Errorf("underlay destination = %#v", source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user