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) } } }