package engine import ( "path/filepath" "testing" "testing/fstest" ) type importDestinationConfig struct { Mounted map[string]string `yaml:"mounted"` Collections map[string]map[string]string `yaml:"collections"` } func TestLayeredHostImportsInheritInputDestinationTransitively(t *testing.T) { t.Parallel() directory := t.TempDir() mainPath := writeImportFile(t, directory, "main.yml", `configkit: imports: - path: first.yml - path: second.yml root: main `) firstPath := writeImportFile(t, directory, "first.yml", `configkit: imports: - path: nested/third.yml first: one `) thirdPath := writeImportFile(t, directory, "nested/third.yml", "nested: three\n") secondPath := writeImportFile(t, directory, "second.yml", "second: two\n") loaded, err := LoadLayered( Input{Source: File(mainPath), Imports: true, Destination: Keys("mounted")}, testSecretBinding(), LayeredOptions[importDestinationConfig]{}, ) if err != nil { if loadError, ok := err.(*LoadError); ok { t.Fatalf("LoadLayered diagnostics: %#v", loadError.Diagnostics()) } t.Fatalf("LoadLayered: %v", err) } result := testLoaded(loaded) want := map[string]string{"root": "main", "first": "one", "nested": "three", "second": "two"} if len(result.Config.Mounted) != len(want) { t.Fatalf("mounted values = %#v", result.Config.Mounted) } for key, value := range want { if result.Config.Mounted[key] != value { t.Errorf("mounted[%q] = %q, want %q", key, result.Config.Mounted[key], value) } } if _, ok := result.Metadata().Lookup(Keys("mounted", "configkit")); ok { t.Fatal("source-root import directive leaked into application content") } sources := result.Sources() wantPaths := []string{canonicalTestPath(t, mainPath), canonicalTestPath(t, firstPath), canonicalTestPath(t, thirdPath), canonicalTestPath(t, secondPath)} if len(sources) != len(wantPaths) { t.Fatalf("sources = %#v", sources) } for index, source := range sources { if source.Destination.String() != "/mounted" { t.Errorf("source %d destination = %q", index, source.Destination.String()) } if source.Path != wantPaths[index] { t.Errorf("source %d path = %q, want %q", index, source.Path, wantPaths[index]) } if index > 0 && source.ImportedFrom == nil { t.Errorf("imported source %d has no physical import location", index) } } } func TestLayeredNativeFilesystemImportsInheritInputDestination(t *testing.T) { t.Parallel() native := fstest.MapFS{ "main.yml": &fstest.MapFile{Data: []byte("configkit:\n imports:\n - path: first.yml\nroot: main\n")}, "first.yml": &fstest.MapFile{Data: []byte("configkit: {imports: [{path: nested.yml}]}\nfirst: imported\n")}, "nested.yml": &fstest.MapFile{Data: []byte("nested: imported\n")}, } namespace := newNativeNamespace("bundle", native) loaded, err := LoadLayered( Input{Source: filesystemFile(namespace, "main.yml"), Imports: true, Destination: Keys("mounted")}, testSecretBinding(), LayeredOptions[importDestinationConfig]{}, ) if err != nil { if loadError, ok := err.(*LoadError); ok { t.Fatalf("LoadLayered diagnostics: %#v", loadError.Diagnostics()) } t.Fatalf("LoadLayered: %v", err) } result := testLoaded(loaded) if result.Config.Mounted["root"] != "main" || result.Config.Mounted["first"] != "imported" || result.Config.Mounted["nested"] != "imported" { t.Fatalf("mounted native filesystem values = %#v", result.Config.Mounted) } sources := result.Sources() if len(sources) != 3 || sources[0].Destination.String() != "/mounted" || sources[1].Destination.String() != "/mounted" || sources[2].Destination.String() != "/mounted" { t.Fatalf("native source destinations = %#v", sources) } } func TestLayeredImportOnlyInputMaterializesDestination(t *testing.T) { t.Parallel() directory := t.TempDir() mainPath := writeImportFile(t, directory, "main.yml", `configkit: imports: - path: optional.yml optional: true `) loaded, err := LoadLayered( Input{Source: File(mainPath), Imports: true, Destination: Keys("collections", "empty")}, testSecretBinding(), LayeredOptions[importDestinationConfig]{}, ) if err != nil { t.Fatalf("LoadLayered: %v", err) } result := testLoaded(loaded) info, ok := result.Metadata().Lookup(Keys("collections", "empty")) if !ok || !info.Explicit { t.Fatalf("import-only body destination = %#v, %v", info, ok) } sources := result.Sources() if len(sources) != 1 || sources[0].Destination.String() != "/collections/empty" { t.Fatalf("sources = %#v", sources) } } func TestLayeredImportedVersionClaimsUseEffectiveDestination(t *testing.T) { t.Parallel() for _, test := range []struct { name string rulePath Path destination Path wantCode Code }{ {name: "protected effective path", rulePath: Keys("mounted", "version"), destination: Keys("mounted"), wantCode: CodeImportVersion}, {name: "ordinary nested domain version", rulePath: Keys("version"), destination: Keys("collections", "entry")}, } { t.Run(test.name, func(t *testing.T) { t.Parallel() directory := t.TempDir() writeImportFile(t, directory, "main.yml", "configkit: {imports: [{path: fragment.yml}]}\n") writeImportFile(t, directory, "fragment.yml", "version: v2\n") rule := &VersionRule{Path: test.rulePath, Check: func(Version, *Validation) error { return nil }} loaded, err := LoadLayered( Input{Source: NoSource()}, testSecretBinding(), LayeredOptions[importDestinationConfig]{ Definition: Definition[importDestinationConfig]{Version: rule}, Underlays: []Underlay{{Inputs: []Input{{ Source: File(filepath.Join(directory, "main.yml")), Imports: true, Destination: test.destination, }}}}, }, ) if test.wantCode != "" { if err == nil { t.Fatal("protected imported version claim was accepted") } loadError, ok := err.(*LoadError) if !ok || len(loadError.Diagnostics()) == 0 || loadError.Diagnostics()[0].Code != test.wantCode { t.Fatalf("load error = %#v", err) } if test.name == "protected effective path" { sources := loadError.Sources() if len(sources) != 2 || sources[0].Destination.String() != "/mounted" || sources[1].Destination.String() != "/mounted" { t.Fatalf("error source destinations = %#v", sources) } } return } if err != nil { t.Fatalf("ordinary nested version was rejected: %v", err) } result := testLoaded(loaded) if got := result.Config.Collections["entry"]["version"]; got != "v2" { t.Fatalf("nested version = %q", got) } }) } } func TestLayeredDestinationsDoNotChangeRepeatedSourceIdentity(t *testing.T) { t.Parallel() directory := t.TempDir() writeImportFile(t, directory, "shared.yml", "value: shared\n") importerPath := writeImportFile(t, directory, "importer.yml", "configkit: {imports: [{path: shared.yml}]}\n") _, err := LoadLayered( Input{Source: NoSource()}, testSecretBinding(), LayeredOptions[importDestinationConfig]{Underlays: []Underlay{{Inputs: []Input{ {Source: File(importerPath), Imports: true, Destination: Keys("imported")}, {Source: File(filepath.Join(directory, "shared.yml")), Destination: Keys("direct")}, }}}}, ) if err == nil { t.Fatal("the same physical source was accepted at two destinations in one group") } loadError, ok := err.(*LoadError) if !ok || len(loadError.Diagnostics()) == 0 || loadError.Diagnostics()[0].Code != CodeImportRepeated { t.Fatalf("load error = %#v", err) } }