From 927309e8678e096c45b427e3e01369d42f149bf1 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Thu, 7 May 2026 19:53:24 +0800 Subject: [PATCH] test(server): real-fs coverage for Linux walker gitignore skip (#806) Adds a primitive test that initialises a real git repo, writes a .gitignore covering an ignored subtree, and asserts the Linux working tree walker registers watchers for kept directories but not for gitignored ones. Exercises the real `git ls-files -o -i --directory --exclude-standard` shell-out the new code path depends on. --- .../workspace-git-service.primitive.test.ts | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/packages/server/src/server/workspace-git-service.primitive.test.ts b/packages/server/src/server/workspace-git-service.primitive.test.ts index 35c721dd1..d57e1edad 100644 --- a/packages/server/src/server/workspace-git-service.primitive.test.ts +++ b/packages/server/src/server/workspace-git-service.primitive.test.ts @@ -1,5 +1,6 @@ import { execSync } from "node:child_process"; import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs"; +import { readdir } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; @@ -11,11 +12,13 @@ import { import { getCheckoutDiff as getCheckoutDiffUncached, getCheckoutStatus as getCheckoutStatusUncached, + resolveAbsoluteGitDir as resolveAbsoluteGitDirReal, type CheckoutDiffCompare, type CheckoutDiffResult, type CheckoutStatusGit, type PullRequestStatusResult, } from "../utils/checkout-git.js"; +import { runGitCommand as runGitCommandReal } from "../utils/run-git-command.js"; import { WorkspaceGitServiceImpl, type WorkspaceGitRuntimeSnapshot, @@ -207,6 +210,7 @@ interface CreateServiceOptions { runGitFetch?: ReturnType; runGitCommand?: ReturnType; watch?: ReturnType; + readdir?: ReturnType; now?: () => Date; } @@ -1454,4 +1458,48 @@ describe("WorkspaceGitServiceImpl D2 read methods", () => { service.dispose(); }); + + test("Linux working tree walker excludes gitignored directories", async () => { + const originalPlatform = process.platform; + Object.defineProperty(process, "platform", { configurable: true, value: "linux" }); + + const tempDir = realpathSync(mkdtempSync(join(tmpdir(), "workspace-git-service-ignored-"))); + const repoDir = join(tempDir, "repo"); + mkdirSync(join(repoDir, "ignored", "deep"), { recursive: true }); + mkdirSync(join(repoDir, "kept"), { recursive: true }); + execSync("git init -b main", { cwd: repoDir, stdio: "pipe" }); + writeFileSync(join(repoDir, ".gitignore"), "ignored/\n"); + writeFileSync(join(repoDir, "ignored", "log.txt"), "noise\n"); + writeFileSync(join(repoDir, "ignored", "deep", "log.txt"), "noise\n"); + writeFileSync(join(repoDir, "kept", "file.txt"), "keep\n"); + + const watchedPaths: string[] = []; + const watchSpy = (watchPath: string) => { + watchedPaths.push(watchPath); + return { close: vi.fn(), on: vi.fn().mockReturnThis() }; + }; + + const service = createService({ + watch: watchSpy as never, + readdir: readdir as never, + runGitCommand: runGitCommandReal as never, + getCheckoutStatus: getCheckoutStatusUncached as never, + resolveAbsoluteGitDir: resolveAbsoluteGitDirReal as never, + }); + + try { + const subscription = await service.requestWorkingTreeWatch(repoDir, vi.fn()); + + const ignoredRoot = join(repoDir, "ignored"); + expect(watchedPaths.filter((path) => path.startsWith(ignoredRoot))).toEqual([]); + expect(watchedPaths).toContain(repoDir); + expect(watchedPaths).toContain(join(repoDir, "kept")); + + subscription.unsubscribe(); + } finally { + service.dispose(); + rmSync(tempDir, { recursive: true, force: true }); + Object.defineProperty(process, "platform", { configurable: true, value: originalPlatform }); + } + }); });