372 lines
13 KiB
Go
372 lines
13 KiB
Go
package engine
|
|
|
|
import (
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"testing/fstest"
|
|
)
|
|
|
|
func TestSelectShallowDirectoriesUnionsRootsAndPatterns(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
filesystem := fstest.MapFS{
|
|
"one": &fstest.MapFile{Mode: fs.ModeDir | 0o700},
|
|
"one/a.yaml": &fstest.MapFile{Data: []byte("a: 1\n")},
|
|
"one/README": &fstest.MapFile{Data: []byte("readme\n")},
|
|
"one/.hidden": &fstest.MapFile{Data: []byte("hidden\n")},
|
|
"one/draft.draft.yaml": &fstest.MapFile{Data: []byte("draft\n")},
|
|
"one/nested/deep.yaml": &fstest.MapFile{Data: []byte("deep: 1\n")},
|
|
"two": &fstest.MapFile{Mode: fs.ModeDir | 0o700},
|
|
"two/a.yaml": &fstest.MapFile{Data: []byte("a: 2\n")},
|
|
"three": &fstest.MapFile{Mode: fs.ModeDir | 0o700},
|
|
"three/extensionless": &fstest.MapFile{Data: []byte("plain\n")},
|
|
}
|
|
access := newNativeAccess(filesystem, ".", &namespaceToken{})
|
|
specs, diagnostics := parseImportSpecsYAML(t, `
|
|
- dir: [one, one, missing, two]
|
|
pattern: ['*.yaml', 'a.*', 'README']
|
|
exclude: '*.draft.yaml'
|
|
required: true
|
|
`)
|
|
if len(diagnostics) != 0 {
|
|
t.Fatal(diagnostics)
|
|
}
|
|
policy, diagnostics := resolveImportPolicy(specs[0], importDefaults{})
|
|
if len(diagnostics) != 0 {
|
|
t.Fatal(diagnostics)
|
|
}
|
|
accounting := &importAccounting{}
|
|
selection, diagnostics := selectDirectoryImportCandidates(access, ".", specs[0], policy, defaultImportLimits(), accounting)
|
|
if len(diagnostics) != 0 {
|
|
t.Fatalf("selection diagnostics = %#v", diagnostics)
|
|
}
|
|
if selection.outcome != importSelectionHasCandidates || len(selection.candidates) != 3 {
|
|
t.Fatalf("selection = %#v, want three unioned matches", selection)
|
|
}
|
|
if selection.candidates[0].path != "one/README" || selection.candidates[1].path != "one/a.yaml" || selection.candidates[2].path != "two/a.yaml" {
|
|
t.Fatalf("selected paths = %#v", selection.candidates)
|
|
}
|
|
if accounting.entries != 6 {
|
|
t.Fatalf("entry visits = %d, want 6 entries across distinct roots", accounting.entries)
|
|
}
|
|
}
|
|
|
|
func TestSelectShallowDirectoriesIncludeUnrestrictedHiddenAndExtensionlessFiles(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
root := t.TempDir()
|
|
for name, value := range map[string]string{
|
|
"a.yaml": "a: 1\n",
|
|
".private": "private\n",
|
|
"README": "readme\n",
|
|
"nested/deep": "deep\n",
|
|
} {
|
|
filename := filepath.Join(root, filepath.FromSlash(name))
|
|
if err := os.MkdirAll(filepath.Dir(filename), 0o700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filename, []byte(value), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
access, err := openHostAccess(root)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer access.close()
|
|
|
|
specs, diagnostics := parseImportSpecsYAML(t, " - dir: .\n")
|
|
if len(diagnostics) != 0 {
|
|
t.Fatal(diagnostics)
|
|
}
|
|
policy, diagnostics := resolveImportPolicy(specs[0], importDefaults{})
|
|
if len(diagnostics) != 0 {
|
|
t.Fatal(diagnostics)
|
|
}
|
|
selection, diagnostics := selectDirectoryImportCandidates(access, root, specs[0], policy, defaultImportLimits(), &importAccounting{})
|
|
if len(diagnostics) != 0 {
|
|
t.Fatalf("selection diagnostics = %#v", diagnostics)
|
|
}
|
|
if len(selection.candidates) != 3 {
|
|
t.Fatalf("selected files = %#v, want yaml, hidden, and extensionless files", selection.candidates)
|
|
}
|
|
for _, candidate := range selection.candidates {
|
|
if filepath.Base(candidate.path) == "deep" {
|
|
t.Fatalf("shallow search descended into a child directory: %#v", selection.candidates)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSelectShallowDirectoriesApplyRequirednessToFinalUnion(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
filesystem := fstest.MapFS{
|
|
"available": &fstest.MapFile{Mode: fs.ModeDir | 0o700},
|
|
"available/match.yaml": &fstest.MapFile{Data: []byte("match: true\n")},
|
|
"available/excluded.yaml": &fstest.MapFile{Data: []byte("excluded: true\n")},
|
|
}
|
|
access := newNativeAccess(filesystem, ".", &namespaceToken{})
|
|
for _, test := range []struct {
|
|
name string
|
|
yaml string
|
|
wantError bool
|
|
wantCount int
|
|
}{
|
|
{
|
|
name: "missing root alongside a match",
|
|
yaml: " - dir: [missing, available]\n pattern: '*.yaml'\n exclude: excluded.yaml\n required: true\n",
|
|
wantCount: 1,
|
|
},
|
|
{
|
|
name: "all matches excluded",
|
|
yaml: " - dir: available\n pattern: '*.yaml'\n exclude: '*.yaml'\n required: true\n",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "explicit empty patterns select nothing",
|
|
yaml: " - dir: available\n pattern: []\n",
|
|
wantCount: 0,
|
|
},
|
|
{
|
|
name: "empty search roots",
|
|
yaml: " - dir: []\n required: true\n",
|
|
wantError: true,
|
|
},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
specs, diagnostics := parseImportSpecsYAML(t, test.yaml)
|
|
if len(diagnostics) != 0 {
|
|
t.Fatal(diagnostics)
|
|
}
|
|
policy, diagnostics := resolveImportPolicy(specs[0], importDefaults{})
|
|
if len(diagnostics) != 0 {
|
|
t.Fatal(diagnostics)
|
|
}
|
|
selection, diagnostics := selectDirectoryImportCandidates(access, ".", specs[0], policy, defaultImportLimits(), &importAccounting{})
|
|
if test.wantError {
|
|
if len(diagnostics) != 1 || diagnostics[0].Code != CodeImportMissing {
|
|
t.Fatalf("diagnostics = %#v, want required empty-union error", diagnostics)
|
|
}
|
|
return
|
|
}
|
|
if len(diagnostics) != 0 || len(selection.candidates) != test.wantCount {
|
|
t.Fatalf("selection, diagnostics = %#v, %#v", selection, diagnostics)
|
|
}
|
|
if test.wantCount == 1 && selection.candidates[0].path != "available/match.yaml" {
|
|
t.Fatalf("selected path = %q, want available/match.yaml", selection.candidates[0].path)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSelectShallowDirectoryRejectsInvalidRootInsteadOfTreatingItAsMissing(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
filesystem := fstest.MapFS{
|
|
"file.yaml": &fstest.MapFile{Data: []byte("not a directory\n")},
|
|
}
|
|
access := newNativeAccess(filesystem, ".", &namespaceToken{})
|
|
specs, diagnostics := parseImportSpecsYAML(t, " - dir: file.yaml\n")
|
|
if len(diagnostics) != 0 {
|
|
t.Fatal(diagnostics)
|
|
}
|
|
policy, diagnostics := resolveImportPolicy(specs[0], importDefaults{})
|
|
if len(diagnostics) != 0 {
|
|
t.Fatal(diagnostics)
|
|
}
|
|
_, diagnostics = selectDirectoryImportCandidates(access, ".", specs[0], policy, defaultImportLimits(), &importAccounting{})
|
|
if len(diagnostics) != 1 || diagnostics[0].Code != CodeImportInvalid {
|
|
t.Fatalf("invalid-root diagnostics = %#v", diagnostics)
|
|
}
|
|
|
|
denied := &statOnlyImportFilesystem{statErrors: map[string]error{"denied": fs.ErrPermission}}
|
|
access = newNativeAccess(denied, ".", &namespaceToken{})
|
|
specs, diagnostics = parseImportSpecsYAML(t, " - dir: denied\n")
|
|
if len(diagnostics) != 0 {
|
|
t.Fatal(diagnostics)
|
|
}
|
|
policy, diagnostics = resolveImportPolicy(specs[0], importDefaults{})
|
|
if len(diagnostics) != 0 {
|
|
t.Fatal(diagnostics)
|
|
}
|
|
_, diagnostics = selectDirectoryImportCandidates(access, ".", specs[0], policy, defaultImportLimits(), &importAccounting{})
|
|
if len(diagnostics) != 1 || diagnostics[0].Code != CodeImportInvalid {
|
|
t.Fatalf("inaccessible-root diagnostics = %#v", diagnostics)
|
|
}
|
|
}
|
|
|
|
func TestSelectShallowDirectoryFiltersSymlinkAliasSpelling(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
root := t.TempDir()
|
|
if err := os.WriteFile(filepath.Join(root, "welcome.yaml"), []byte("welcome: true\n"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.Symlink("welcome.yaml", filepath.Join(root, "welcome.draft.yaml")); err != nil {
|
|
t.Skipf("symlinks are unavailable: %v", err)
|
|
}
|
|
access, err := openHostAccess(root)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer access.close()
|
|
|
|
specs, diagnostics := parseImportSpecsYAML(t, " - dir: .\n pattern: '*.yaml'\n exclude: '*.draft.yaml'\n")
|
|
if len(diagnostics) != 0 {
|
|
t.Fatal(diagnostics)
|
|
}
|
|
policy, diagnostics := resolveImportPolicy(specs[0], importDefaults{})
|
|
if len(diagnostics) != 0 {
|
|
t.Fatal(diagnostics)
|
|
}
|
|
selection, diagnostics := selectDirectoryImportCandidates(access, root, specs[0], policy, defaultImportLimits(), &importAccounting{})
|
|
if len(diagnostics) != 0 || len(selection.candidates) != 1 || filepath.Base(selection.candidates[0].path) != "welcome.yaml" {
|
|
t.Fatalf("alias filtering selection, diagnostics = %#v, %#v", selection, diagnostics)
|
|
}
|
|
}
|
|
|
|
func TestSelectDirectoriesApplyRecursiveDepthToFileLevels(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
root := t.TempDir()
|
|
for name := range map[string]struct{}{
|
|
"direct.yaml": {},
|
|
"child/child.yaml": {},
|
|
"child/grand/grand.yaml": {},
|
|
} {
|
|
filename := filepath.Join(root, filepath.FromSlash(name))
|
|
if err := os.MkdirAll(filepath.Dir(filename), 0o700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filename, []byte("value: true\n"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
access, err := openHostAccess(root)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer access.close()
|
|
|
|
for _, test := range []struct {
|
|
name string
|
|
yaml string
|
|
defaults importDefaults
|
|
wantPaths []string
|
|
}{
|
|
{
|
|
name: "depth one includes direct files",
|
|
yaml: " - dir: .\n recursive: true\n depth: 1\n pattern: '*.yaml'\n",
|
|
wantPaths: []string{"direct.yaml"},
|
|
},
|
|
{
|
|
name: "depth two includes one descendant level",
|
|
yaml: " - dir: .\n recursive: true\n depth: 2\n pattern: '*.yaml'\n",
|
|
wantPaths: []string{"child.yaml", "direct.yaml"},
|
|
},
|
|
{
|
|
name: "zero depth is unlimited",
|
|
yaml: " - dir: .\n recursive: true\n depth: 0\n pattern: '*.yaml'\n",
|
|
wantPaths: []string{"child.yaml", "grand.yaml", "direct.yaml"},
|
|
},
|
|
{
|
|
name: "recursion stays disabled by default",
|
|
yaml: " - dir: .\n pattern: '*.yaml'\n",
|
|
wantPaths: []string{"direct.yaml"},
|
|
},
|
|
{
|
|
name: "explicit false overrides application recursion",
|
|
yaml: " - dir: .\n recursive: false\n pattern: '*.yaml'\n",
|
|
defaults: importDefaults{recursive: boolPointer(true)},
|
|
wantPaths: []string{"direct.yaml"},
|
|
},
|
|
{
|
|
name: "application default enables recursion for depth",
|
|
yaml: " - dir: .\n depth: 2\n pattern: '*.yaml'\n",
|
|
defaults: importDefaults{recursive: boolPointer(true)},
|
|
wantPaths: []string{"child.yaml", "direct.yaml"},
|
|
},
|
|
{
|
|
name: "large depth remains a valid unlimited traversal bound",
|
|
yaml: " - dir: .\n recursive: true\n depth: 999999999999999999999999999999\n pattern: '*.yaml'\n",
|
|
wantPaths: []string{"child.yaml", "grand.yaml", "direct.yaml"},
|
|
},
|
|
{
|
|
name: "filename filter does not prune directories",
|
|
yaml: " - dir: .\n recursive: true\n depth: 3\n pattern: grand.yaml\n",
|
|
wantPaths: []string{"grand.yaml"},
|
|
},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
specs, diagnostics := parseImportSpecsYAML(t, test.yaml)
|
|
if len(diagnostics) != 0 {
|
|
t.Fatal(diagnostics)
|
|
}
|
|
policy, diagnostics := resolveImportPolicy(specs[0], test.defaults)
|
|
if len(diagnostics) != 0 {
|
|
t.Fatal(diagnostics)
|
|
}
|
|
selection, diagnostics := selectDirectoryImportCandidates(access, root, specs[0], policy, defaultImportLimits(), &importAccounting{})
|
|
if len(diagnostics) != 0 {
|
|
t.Fatalf("selection diagnostics = %#v", diagnostics)
|
|
}
|
|
var got []string
|
|
for _, candidate := range selection.candidates {
|
|
got = append(got, filepath.Base(candidate.path))
|
|
}
|
|
if strings.Join(got, ",") != strings.Join(test.wantPaths, ",") {
|
|
t.Fatalf("selected basenames = %#v, want %#v", got, test.wantPaths)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSelectDirectoriesRetainsEmptySearchAndDirectoryVisitLimits(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
root := t.TempDir()
|
|
for _, name := range []string{"direct.yaml", "child/child.yaml", "child/grand/grand.yaml"} {
|
|
filename := filepath.Join(root, filepath.FromSlash(name))
|
|
if err := os.MkdirAll(filepath.Dir(filename), 0o700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filename, []byte("value: true\n"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
access, err := openHostAccess(root)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer access.close()
|
|
|
|
specs, diagnostics := parseImportSpecsYAML(t, " - dir: .\n recursive: true\n pattern: []\n")
|
|
if len(diagnostics) != 0 {
|
|
t.Fatal(diagnostics)
|
|
}
|
|
policy, diagnostics := resolveImportPolicy(specs[0], importDefaults{})
|
|
if len(diagnostics) != 0 {
|
|
t.Fatal(diagnostics)
|
|
}
|
|
accounting := &importAccounting{}
|
|
selection, diagnostics := selectDirectoryImportCandidates(access, root, specs[0], policy, defaultImportLimits(), accounting)
|
|
if len(diagnostics) != 0 || selection.outcome != importSelectionNone || len(selection.candidates) != 0 {
|
|
t.Fatalf("empty recursive selection, diagnostics = %#v, %#v", selection, diagnostics)
|
|
}
|
|
if accounting.entries != 5 {
|
|
t.Fatalf("empty search visited %d entries, want all 5 within the recursive search", accounting.entries)
|
|
}
|
|
|
|
limited := defaultImportLimits()
|
|
limited.entries = 2
|
|
_, diagnostics = selectDirectoryImportCandidates(access, root, specs[0], policy, limited, &importAccounting{})
|
|
if len(diagnostics) != 1 || diagnostics[0].Code != CodeImportLimitExceeded {
|
|
t.Fatalf("directory limit diagnostics = %#v", diagnostics)
|
|
}
|
|
}
|
|
|
|
func boolPointer(value bool) *bool { return &value }
|