package engine import ( "errors" "io/fs" "os" "path/filepath" "runtime" "strings" "testing" "testing/fstest" ) func TestResourceContextsPreserveLiteralBackslashes(t *testing.T) { const directory = `assets\literal` const name = directory + `/text\file` check := func(t *testing.T, filesystem fs.FS) { t.Helper() data, err := fs.ReadFile(filesystem, name) if err != nil || string(data) != "content" { t.Fatalf("read = %q, %v", data, err) } entries, err := fs.ReadDir(filesystem, directory) if err != nil || len(entries) != 1 || entries[0].Name() != `text\file` { t.Fatalf("entries = %v, %v", entries, err) } if _, err := filesystem.Open("../outside"); !errors.Is(err, fs.ErrInvalid) { t.Fatalf("escape = %v", err) } } t.Run("native", func(t *testing.T) { check(t, rootedNativeFS{filesystem: fstest.MapFS{"bundle/" + name: &fstest.MapFile{Data: []byte("content")}}, root: "bundle"}) }) t.Run("host", func(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("backslash is a host separator on Windows; native case remains covered") } root := t.TempDir() if err := os.Mkdir(filepath.Join(root, directory), 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(root, filepath.FromSlash(name)), []byte("content"), 0o600); err != nil { t.Fatal(err) } check(t, rootedHostFS{root: root}) }) } type trackedHostRoot struct { root *os.Root closed *int failOpen bool } func (r *trackedHostRoot) Open(name string) (*os.File, error) { if r.failOpen { return nil, fs.ErrPermission } return r.root.Open(name) } func (r *trackedHostRoot) Close() error { (*r.closed)++ return r.root.Close() } func TestRootedHostFSClosesRootOnSuccessfulAndFailedOpen(t *testing.T) { rootPath := t.TempDir() if err := os.WriteFile(filepath.Join(rootPath, "resource.txt"), []byte("resource"), 0o600); err != nil { t.Fatal(err) } opened, closed := 0, 0 failOpen := false filesystem := rootedHostFS{root: rootPath, openRoot: func(name string) (hostResourceRoot, error) { root, err := os.OpenRoot(name) if err != nil { return nil, err } opened++ return &trackedHostRoot{root: root, closed: &closed, failOpen: failOpen}, nil }} if _, err := filesystem.Open("../resource.txt"); !errors.Is(err, fs.ErrInvalid) || opened != 0 { t.Fatalf("invalid path = %v; roots opened %d", err, opened) } if _, err := filesystem.Open("missing.txt"); !errors.Is(err, fs.ErrNotExist) || opened != 1 || closed != 1 { t.Fatalf("missing resource = %v; opened %d, closed %d", err, opened, closed) } failOpen = true if _, err := filesystem.Open("resource.txt"); !errors.Is(err, fs.ErrPermission) || opened != 2 || closed != 2 { t.Fatalf("failed inner open = %v; opened %d, closed %d", err, opened, closed) } failOpen = false file, err := filesystem.Open("resource.txt") if err != nil || opened != 3 || closed != 2 { t.Fatalf("resource open = %v; opened %d, closed %d", err, opened, closed) } if err := file.Close(); err != nil || closed != 3 { t.Fatalf("resource close = %v; opened %d, closed %d", err, opened, closed) } if _, err := file.Read(make([]byte, 1)); err == nil { t.Fatal("closed resource file remained readable") } directory, err := filesystem.Open(".") if err != nil { t.Fatal(err) } reader, ok := directory.(fs.ReadDirFile) if !ok { t.Fatal("rooted host directory did not expose ReadDirFile") } entries, err := reader.ReadDir(-1) if err != nil || len(entries) != 1 || entries[0].Name() != "resource.txt" { t.Fatalf("directory entries = %#v, %v", entries, err) } if err := directory.Close(); err != nil || opened != 4 || closed != 4 { t.Fatalf("directory close = %v; opened %d, closed %d", err, opened, closed) } } func TestNativeResourceViewIsBorrowedAndRooted(t *testing.T) { filesystem := &observedFilesystem{files: fstest.MapFS{ "bundle/config/resource.txt": &fstest.MapFile{Data: []byte("content")}, }} view := rootedNativeFS{filesystem: filesystem, root: "bundle"} if filesystem.opened != 0 { t.Fatal("constructing a native resource view opened files") } data, err := fs.ReadFile(view, "config/resource.txt") if err != nil || string(data) != "content" { t.Fatalf("resource = %q, %v", data, err) } filesystem.assertClosed(t) if _, err := fs.ReadFile(view, "../outside.txt"); !errors.Is(err, fs.ErrInvalid) { t.Fatalf("resource escape = %v", err) } } func TestLoadingClosesNativeFilesAcrossPipelineOutcomes(t *testing.T) { type config struct { Value string `yaml:"value"` Version string `yaml:"version"` } for _, test := range []struct { name string main string child string helper string failRead string options Options[config] wantErr bool }{ {name: "success", main: "value: retained\n"}, {name: "acquisition", main: "value: retained\n", failRead: "main.yml", wantErr: true}, {name: "parse", main: "value: [broken\n", wantErr: true}, {name: "composition", main: "configkit: {imports: [{path: child.yml}]}\nvalue: first\n", child: "value: second\n", options: Options[config]{Imports: true}, wantErr: true}, { name: "nested item composition", main: "configkit: {imports: [{path: child.yml, destination: [children], placement: sequence-item}]}\nid: parent\n", child: "configkit: {imports: [{path: helper.yml}]}\nid: child\nvalue: child\n", helper: "value: helper\n", options: Options[config]{ Imports: true, Destination: Keys("groups"), PlacementMode: PlacementModeSequenceItem, }, wantErr: true, }, {name: "version", main: "version: wrong\n", options: Options[config]{Definition: Definition[config]{Version: &VersionRule{Path: Keys("version"), Check: func(Version, *Validation) error { return fs.ErrInvalid }}}}, wantErr: true}, {name: "validation", main: "value: retained\n", options: Options[config]{Definition: Definition[config]{Validate: func(_ *config, validation *Validation) { validation.Error(Keys("value"), "rejected") }}}, wantErr: true}, } { t.Run(test.name, func(t *testing.T) { files := fstest.MapFS{"main.yml": &fstest.MapFile{Data: []byte(test.main)}} if test.child != "" { files["child.yml"] = &fstest.MapFile{Data: []byte(test.child)} } if test.helper != "" { files["helper.yml"] = &fstest.MapFile{Data: []byte(test.helper)} } filesystem := &observedFilesystem{files: files, failRead: test.failRead} _, err := testLoad(filesystemFile(newNativeNamespace("bundle", filesystem), "main.yml"), test.options) if (err != nil) != test.wantErr { t.Fatalf("load error = %v, want failure %v", err, test.wantErr) } filesystem.assertClosed(t) }) } } func TestDirectorySelectorsFailOnPostOpenNotExistErrors(t *testing.T) { const canary = "DIRECTORY_FAILURE_CANARY" readFailure := errors.Join(fs.ErrNotExist, errors.New(canary)) closeFailure := errors.Join(fs.ErrNotExist, errors.New(canary)) for _, test := range []struct { name string selector string fallback bool readError bool noProgress bool closeError bool fallbackErr bool }{ {name: "directory batched read with entries", selector: "directory", readError: true}, {name: "directory close", selector: "directory", closeError: true}, {name: "directory fallback with entries", selector: "directory", fallback: true, fallbackErr: true}, {name: "directory no progress", selector: "directory", noProgress: true}, {name: "glob batched read with entries", selector: "glob", readError: true}, {name: "glob close", selector: "glob", closeError: true}, {name: "glob fallback with entries", selector: "glob", fallback: true, fallbackErr: true}, {name: "glob no progress", selector: "glob", noProgress: true}, } { t.Run(test.name, func(t *testing.T) { main := "configkit:\n imports:\n" if test.selector == "directory" { main += " - dir: [broken, good]\n pattern: '*.yml'\n" } else { main += " - glob: 'broken/*.yml'\n - glob: 'good/*.yml'\n" } filesystem := &observedFilesystem{ files: fstest.MapFS{ "main.yml": &fstest.MapFile{Data: []byte(main)}, "broken": &fstest.MapFile{Mode: fs.ModeDir | 0o700}, "broken/bad.yml": &fstest.MapFile{Data: []byte("value: bad\n")}, "good": &fstest.MapFile{Mode: fs.ModeDir | 0o700}, "good/good.yml": &fstest.MapFile{Data: []byte("value: good\n")}, }, fallback: test.fallback, failReadDir: "broken", readDirErr: readFailure, failNoProgress: "broken", failClose: "broken", closeErr: closeFailure, failFallbackDir: "broken", fallbackErr: readFailure, } if !test.readError { filesystem.failReadDir = "" } if !test.noProgress { filesystem.failNoProgress = "" } if !test.closeError { filesystem.failClose = "" } if !test.fallbackErr { filesystem.failFallbackDir = "" } result, err := testLoad[map[string]any](filesystemFile(newNativeNamespace("bundle", filesystem), "main.yml"), Options[map[string]any]{Imports: true}) if result != nil { t.Fatalf("load returned a result after directory failure: %#v", result) } var loadError *LoadError if !errors.As(err, &loadError) { t.Fatalf("load error = %#v, want structured LoadError", err) } diagnostics := loadError.Diagnostics() if len(diagnostics) != 1 { t.Fatalf("diagnostics = %#v, want one directory failure", diagnostics) } diagnostic := diagnostics[0] wantCode := CodeImportInvalid if test.selector == "glob" { wantCode = CodeImportRead } if diagnostic.Code != wantCode || diagnostic.Primary == nil || diagnostic.Primary.SourceID != "source-1" { t.Fatalf("directory diagnostic = %#v, want %q at source-1", diagnostic, wantCode) } var rendered strings.Builder if err := WriteDiagnostics(&rendered, diagnostics); err != nil { t.Fatal(err) } if strings.Contains(err.Error()+diagnostic.Message+rendered.String(), canary) { t.Fatalf("backing error canary escaped: error=%q diagnostic=%#v rendered=%q", err, diagnostic, rendered.String()) } if filesystem.fallbackBeforeClose { t.Fatal("fallback directory enumeration began before the opened handle was closed") } filesystem.assertClosed(t) }) } } func TestReadDirectoryPreservesReadErrorOverCloseError(t *testing.T) { readFailure := errors.New("read failure") closeFailure := errors.New("close failure") filesystem := &observedFilesystem{ files: fstest.MapFS{ "broken": &fstest.MapFile{Mode: fs.ModeDir | 0o700}, "broken/entry": &fstest.MapFile{Data: []byte("entry")}, }, failReadDir: "broken", readDirErr: readFailure, failClose: "broken", closeErr: closeFailure, } access := newNativeAccess(filesystem, ".", &namespaceToken{}) visited := 0 err := access.readDirectory("broken", func(fs.DirEntry) bool { visited++ return true }) if !errors.Is(err, readFailure) || errors.Is(err, closeFailure) || visited != 1 { t.Fatalf("directory read = %v, visited %d; want entries plus primary read failure", err, visited) } if missingDirectoryOpen(err) { t.Fatal("post-open read failure was classified as missing-on-open") } filesystem.assertClosed(t) } func TestReadDirectoryVisitStopCloseNotExistIsPostOpen(t *testing.T) { filesystem := &observedFilesystem{ files: fstest.MapFS{ "broken": &fstest.MapFile{Mode: fs.ModeDir | 0o700}, "broken/entry": &fstest.MapFile{Data: []byte("entry")}, }, failClose: "broken", } access := newNativeAccess(filesystem, ".", &namespaceToken{}) err := access.readDirectory("broken", func(fs.DirEntry) bool { return false }) if !errors.Is(err, fs.ErrNotExist) || missingDirectoryOpen(err) { t.Fatalf("visit-stop close error = %v, missing-on-open=%t", err, missingDirectoryOpen(err)) } filesystem.assertClosed(t) } func TestDirectorySelectorsKeepMissingRootsOptional(t *testing.T) { for _, selector := range []string{"directory", "glob"} { t.Run(selector, func(t *testing.T) { main := "configkit:\n imports:\n" if selector == "directory" { main += " - dir: [missing, good]\n pattern: '*.yml'\n required: true\n" } else { main += " - glob: 'missing/*.yml'\n - glob: 'good/*.yml'\n required: true\n" } filesystem := &observedFilesystem{files: fstest.MapFS{ "main.yml": &fstest.MapFile{Data: []byte(main)}, "good": &fstest.MapFile{Mode: fs.ModeDir | 0o700}, "good/good.yml": &fstest.MapFile{Data: []byte("value: good\n")}, }} result, err := testLoad[map[string]any](filesystemFile(newNativeNamespace("bundle", filesystem), "main.yml"), Options[map[string]any]{Imports: true}) if err != nil || result == nil { t.Fatalf("load with missing search root = %#v, %v", result, err) } filesystem.assertClosed(t) }) } } func TestDirectorySelectorsTreatMissingOnOpenAsOptional(t *testing.T) { for _, selector := range []string{"directory", "glob"} { t.Run(selector, func(t *testing.T) { main := "configkit:\n imports:\n" if selector == "directory" { main += " - dir: [broken, good]\n pattern: '*.yml'\n required: true\n" } else { main += " - glob: 'broken/*.yml'\n - glob: 'good/*.yml'\n required: true\n" } filesystem := &observedFilesystem{ files: fstest.MapFS{ "main.yml": &fstest.MapFile{Data: []byte(main)}, "broken": &fstest.MapFile{Mode: fs.ModeDir | 0o700}, "good": &fstest.MapFile{Mode: fs.ModeDir | 0o700}, "good/good.yml": &fstest.MapFile{Data: []byte("value: good\n")}, }, failOpen: "broken", } result, err := testLoad[map[string]any](filesystemFile(newNativeNamespace("bundle", filesystem), "main.yml"), Options[map[string]any]{Imports: true}) if err != nil || result == nil { t.Fatalf("load after missing-on-open root = %#v, %v", result, err) } filesystem.assertClosed(t) }) } }