package app import ( "os" "path/filepath" "strings" "testing" ) func TestCleanValidateScopedDirAndFile(t *testing.T) { root := t.TempDir() dirTarget := filepath.Join(root, "runs", "run-1") fileTarget := filepath.Join(root, "cache", "a.flac") if err := os.MkdirAll(dirTarget, 0o755); err != nil { t.Fatalf("MkdirAll(dirTarget) error = %v", err) } if err := os.MkdirAll(filepath.Dir(fileTarget), 0o755); err != nil { t.Fatalf("MkdirAll(file parent) error = %v", err) } if err := os.WriteFile(fileTarget, []byte("audio"), 0o644); err != nil { t.Fatalf("WriteFile(fileTarget) error = %v", err) } if _, err := validateScopedDir(root, dirTarget, "test.dir"); err != nil { t.Fatalf("validateScopedDir() error = %v", err) } if _, err := validateScopedFile(root, fileTarget, "test.file"); err != nil { t.Fatalf("validateScopedFile() error = %v", err) } } func TestCleanValidateScopedTargetSafetyRules(t *testing.T) { root := t.TempDir() outside := t.TempDir() target := filepath.Join(root, "runs", "run-1") if err := os.MkdirAll(target, 0o755); err != nil { t.Fatalf("MkdirAll(target) error = %v", err) } fileTarget := filepath.Join(root, "cache", "a.flac") if err := os.MkdirAll(filepath.Dir(fileTarget), 0o755); err != nil { t.Fatalf("MkdirAll(file parent) error = %v", err) } if err := os.WriteFile(fileTarget, []byte("audio"), 0o644); err != nil { t.Fatalf("WriteFile(fileTarget) error = %v", err) } symlinkTarget := filepath.Join(root, "symlink") if err := os.Symlink(target, symlinkTarget); err != nil { t.Fatalf("Symlink() error = %v", err) } if _, err := validateScopedDir(root, root, "test.root"); err == nil || !strings.Contains(err.Error(), "refusing to delete root directory") { t.Fatalf("validateScopedDir(root) error = %v, want root deletion rejection", err) } if _, err := validateScopedDir(root, filepath.Join(outside, "x"), "test.outside"); err == nil || !strings.Contains(err.Error(), "outside root") { t.Fatalf("validateScopedDir(outside) error = %v, want outside-root rejection", err) } if _, err := validateScopedDir(root, fileTarget, "test.file-as-dir"); err == nil || !strings.Contains(err.Error(), "is not a directory") { t.Fatalf("validateScopedDir(file) error = %v, want not-a-directory rejection", err) } if _, err := validateScopedFile(root, target, "test.dir-as-file"); err == nil || !strings.Contains(err.Error(), "is a directory") { t.Fatalf("validateScopedFile(dir) error = %v, want is-a-directory rejection", err) } if _, err := validateScopedDir(root, symlinkTarget, "test.symlink"); err == nil || !strings.Contains(err.Error(), "refusing to delete symlink path") { t.Fatalf("validateScopedDir(symlink) error = %v, want symlink rejection", err) } } func TestCleanableRootChildrenRejectsSymlinkChild(t *testing.T) { root := t.TempDir() realChild := filepath.Join(root, "runs") if err := os.MkdirAll(realChild, 0o755); err != nil { t.Fatalf("MkdirAll(realChild) error = %v", err) } if err := os.Symlink(realChild, filepath.Join(root, "link")); err != nil { t.Fatalf("Symlink() error = %v", err) } _, _, err := cleanableRootChildren(root, "test.root.children") if err == nil || !strings.Contains(err.Error(), "refusing to delete symlink path") { t.Fatalf("cleanableRootChildren() error = %v, want symlink rejection", err) } } func TestCleanValidateScopedTargetMissing(t *testing.T) { root := t.TempDir() missingDir := filepath.Join(root, "runs", "missing") got, err := validateScopedDir(root, missingDir, "test.missing") if err != nil { t.Fatalf("validateScopedDir(missing) error = %v", err) } if got.Exists { t.Fatalf("validateScopedDir(missing).Exists = true, want false") } missingFile := filepath.Join(root, "cache", "missing.flac") got, err = validateScopedFile(root, missingFile, "test.missing.file") if err != nil { t.Fatalf("validateScopedFile(missing) error = %v", err) } if got.Exists { t.Fatalf("validateScopedFile(missing).Exists = true, want false") } }