|
| 1 | +import * as fs from "fs" |
| 2 | +import * as os from "os" |
| 3 | +import * as path from "path" |
| 4 | + |
| 5 | +import { isPathOutsideWorkspace } from "../pathUtils" |
| 6 | + |
| 7 | +// Mutable workspaceFolders the mocked vscode module reads from. |
| 8 | +const { mockWorkspace } = vi.hoisted(() => ({ |
| 9 | + mockWorkspace: { folders: [] as Array<{ uri: { fsPath: string } }> }, |
| 10 | +})) |
| 11 | + |
| 12 | +vi.mock("vscode", () => ({ |
| 13 | + workspace: { |
| 14 | + get workspaceFolders() { |
| 15 | + return mockWorkspace.folders.length > 0 ? mockWorkspace.folders : undefined |
| 16 | + }, |
| 17 | + }, |
| 18 | +})) |
| 19 | + |
| 20 | +describe("isPathOutsideWorkspace", () => { |
| 21 | + let tmpRoot: string |
| 22 | + let workspaceDir: string |
| 23 | + let outsideDir: string |
| 24 | + |
| 25 | + beforeEach(() => { |
| 26 | + // realpath the tmp dir because macOS resolves /var -> /private/var |
| 27 | + tmpRoot = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), "zoo-pathutils-"))) |
| 28 | + workspaceDir = path.join(tmpRoot, "workspace") |
| 29 | + outsideDir = path.join(tmpRoot, "outside") |
| 30 | + fs.mkdirSync(workspaceDir) |
| 31 | + fs.mkdirSync(outsideDir) |
| 32 | + mockWorkspace.folders = [{ uri: { fsPath: workspaceDir } }] |
| 33 | + }) |
| 34 | + |
| 35 | + afterEach(() => { |
| 36 | + mockWorkspace.folders = [] |
| 37 | + fs.rmSync(tmpRoot, { recursive: true, force: true }) |
| 38 | + }) |
| 39 | + |
| 40 | + it("treats a real file inside the workspace as inside", () => { |
| 41 | + const inside = path.join(workspaceDir, "file.ts") |
| 42 | + fs.writeFileSync(inside, "x") |
| 43 | + expect(isPathOutsideWorkspace(inside)).toBe(false) |
| 44 | + }) |
| 45 | + |
| 46 | + it("treats a real file outside the workspace as outside", () => { |
| 47 | + const outside = path.join(outsideDir, "secret.txt") |
| 48 | + fs.writeFileSync(outside, "secret") |
| 49 | + expect(isPathOutsideWorkspace(outside)).toBe(true) |
| 50 | + }) |
| 51 | + |
| 52 | + it("treats a not-yet-existing file inside the workspace as inside", () => { |
| 53 | + // File about to be created — realpath of the parent (workspace) still resolves. |
| 54 | + expect(isPathOutsideWorkspace(path.join(workspaceDir, "new-file.ts"))).toBe(false) |
| 55 | + }) |
| 56 | + |
| 57 | + it("treats a symlink inside the workspace that points outside as OUTSIDE (#169)", () => { |
| 58 | + const secret = path.join(outsideDir, "secret.txt") |
| 59 | + fs.writeFileSync(secret, "secret") |
| 60 | + const link = path.join(workspaceDir, "link-to-secret.txt") |
| 61 | + fs.symlinkSync(secret, link) |
| 62 | + |
| 63 | + // Lexically the link lives inside the workspace, but it resolves outside. |
| 64 | + expect(isPathOutsideWorkspace(link)).toBe(true) |
| 65 | + }) |
| 66 | + |
| 67 | + it("treats a symlinked directory inside the workspace that points outside as OUTSIDE (#169)", () => { |
| 68 | + fs.writeFileSync(path.join(outsideDir, "deep.txt"), "secret") |
| 69 | + const linkDir = path.join(workspaceDir, "linked-dir") |
| 70 | + fs.symlinkSync(outsideDir, linkDir) |
| 71 | + |
| 72 | + expect(isPathOutsideWorkspace(path.join(linkDir, "deep.txt"))).toBe(true) |
| 73 | + }) |
| 74 | + |
| 75 | + it("returns true when there are no workspace folders", () => { |
| 76 | + mockWorkspace.folders = [] |
| 77 | + expect(isPathOutsideWorkspace(path.join(workspaceDir, "file.ts"))).toBe(true) |
| 78 | + }) |
| 79 | +}) |
0 commit comments