diff --git a/src/shared/git-worktree/collect-git-diff-stats.test.ts b/src/shared/git-worktree/collect-git-diff-stats.test.ts
index 334d15f4a..659c42343 100644
--- a/src/shared/git-worktree/collect-git-diff-stats.test.ts
+++ b/src/shared/git-worktree/collect-git-diff-stats.test.ts
@@ -1,79 +1,79 @@
///
-import { describe, expect, mock, test } from "bun:test"
-
-const execSyncMock = mock(() => {
- throw new Error("execSync should not be called")
-})
-
-const execFileSyncMock = mock((file: string, args: string[], _opts: { cwd?: string }) => {
- if (file !== "git") throw new Error(`unexpected file: ${file}`)
- const subcommand = args[0]
-
- if (subcommand === "diff") {
- return "1\t2\tfile.ts\n"
- }
-
- if (subcommand === "status") {
- return " M file.ts\n?? new-file.ts\n"
- }
-
- if (subcommand === "ls-files") {
- return "new-file.ts\n"
- }
-
- throw new Error(`unexpected args: ${args.join(" ")}`)
-})
-
-const readFileSyncMock = mock((_path: string, _encoding: string) => {
- return "line1\nline2\nline3\nline4\nline5\nline6\nline7\nline8\nline9\nline10\n"
-})
-
-mock.module("node:child_process", () => ({
- execSync: execSyncMock,
- execFileSync: execFileSyncMock,
-}))
-
-mock.module("node:fs", () => ({
- readFileSync: readFileSyncMock,
-}))
-
-const { collectGitDiffStats } = await import("./collect-git-diff-stats")
+import { describe, expect, test, spyOn, beforeEach, afterEach } from "bun:test"
+import * as childProcess from "node:child_process"
+import * as fs from "node:fs"
describe("collectGitDiffStats", () => {
- test("uses execFileSync with arg arrays (no shell injection)", () => {
+ let execFileSyncSpy: ReturnType
+ let execSyncSpy: ReturnType
+ let readFileSyncSpy: ReturnType
+
+ beforeEach(() => {
+ execSyncSpy = spyOn(childProcess, "execSync").mockImplementation(() => {
+ throw new Error("execSync should not be called")
+ })
+
+ execFileSyncSpy = spyOn(childProcess, "execFileSync").mockImplementation(
+ ((file: string, args: string[], _opts: { cwd?: string }) => {
+ if (file !== "git") throw new Error(`unexpected file: ${file}`)
+ const subcommand = args[0]
+
+ if (subcommand === "diff") return "1\t2\tfile.ts\n"
+ if (subcommand === "status") return " M file.ts\n?? new-file.ts\n"
+ if (subcommand === "ls-files") return "new-file.ts\n"
+
+ throw new Error(`unexpected args: ${args.join(" ")}`)
+ }) as typeof childProcess.execFileSync
+ )
+
+ readFileSyncSpy = spyOn(fs, "readFileSync").mockImplementation(
+ ((_path: unknown, _encoding: unknown) => {
+ return "line1\nline2\nline3\nline4\nline5\nline6\nline7\nline8\nline9\nline10\n"
+ }) as typeof fs.readFileSync
+ )
+ })
+
+ afterEach(() => {
+ execSyncSpy.mockRestore()
+ execFileSyncSpy.mockRestore()
+ readFileSyncSpy.mockRestore()
+ })
+
+ test("uses execFileSync with arg arrays (no shell injection)", async () => {
//#given
+ const { collectGitDiffStats } = await import("./collect-git-diff-stats")
const directory = "/tmp/safe-repo;touch /tmp/pwn"
//#when
const result = collectGitDiffStats(directory)
//#then
- expect(execSyncMock).not.toHaveBeenCalled()
- expect(execFileSyncMock).toHaveBeenCalledTimes(3)
+ expect(execSyncSpy).not.toHaveBeenCalled()
+ expect(execFileSyncSpy).toHaveBeenCalledTimes(3)
- const [firstCallFile, firstCallArgs, firstCallOpts] = execFileSyncMock.mock
+ const [firstCallFile, firstCallArgs, firstCallOpts] = execFileSyncSpy.mock
.calls[0]! as unknown as [string, string[], { cwd?: string }]
expect(firstCallFile).toBe("git")
expect(firstCallArgs).toEqual(["diff", "--numstat", "HEAD"])
expect(firstCallOpts.cwd).toBe(directory)
expect(firstCallArgs.join(" ")).not.toContain(directory)
- const [secondCallFile, secondCallArgs, secondCallOpts] = execFileSyncMock.mock
+ const [secondCallFile, secondCallArgs, secondCallOpts] = execFileSyncSpy.mock
.calls[1]! as unknown as [string, string[], { cwd?: string }]
expect(secondCallFile).toBe("git")
expect(secondCallArgs).toEqual(["status", "--porcelain"])
expect(secondCallOpts.cwd).toBe(directory)
expect(secondCallArgs.join(" ")).not.toContain(directory)
- const [thirdCallFile, thirdCallArgs, thirdCallOpts] = execFileSyncMock.mock
+ const [thirdCallFile, thirdCallArgs, thirdCallOpts] = execFileSyncSpy.mock
.calls[2]! as unknown as [string, string[], { cwd?: string }]
expect(thirdCallFile).toBe("git")
expect(thirdCallArgs).toEqual(["ls-files", "--others", "--exclude-standard"])
expect(thirdCallOpts.cwd).toBe(directory)
expect(thirdCallArgs.join(" ")).not.toContain(directory)
- expect(readFileSyncMock).toHaveBeenCalled()
+ expect(readFileSyncSpy).toHaveBeenCalled()
expect(result).toEqual([
{