Skip to content

Commit 2983478

Browse files
mason: de-flake project identity tests
Co-authored-by: Alfonso [Magic Context] <288211368+alfonso-magic-context@users.noreply.github.com>
1 parent 9f171ee commit 2983478

1 file changed

Lines changed: 43 additions & 64 deletions

File tree

packages/plugin/src/features/magic-context/project-identity.test.ts

Lines changed: 43 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { afterEach, describe, expect, it, mock } from "bun:test";
2-
import { execFileSync } from "node:child_process";
2+
import type { execFileSync } from "node:child_process";
33
import { createHash } from "node:crypto";
4-
import { chmodSync, mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
4+
import { chmodSync, mkdirSync, mkdtempSync, rmSync, symlinkSync } from "node:fs";
55
import { tmpdir } from "node:os";
66
import path, { join } from "node:path";
77
import {
@@ -18,6 +18,8 @@ import {
1818
} from "./project-identity";
1919

2020
const tempDirs: string[] = [];
21+
const FIRST_ROOT_COMMIT = "abcdef1234567890abcdef1234567890abcdef12";
22+
const SECOND_ROOT_COMMIT = "1234567890abcdef1234567890abcdef12345678";
2123

2224
afterEach(() => {
2325
__resetProjectIdentityForTests();
@@ -38,36 +40,17 @@ function makeTempDir(prefix: string): string {
3840
return dir;
3941
}
4042

41-
function runGit(directory: string, args: string[]): string {
42-
return execFileSync("git", args, {
43-
cwd: directory,
44-
encoding: "utf8",
45-
env: { ...process.env, LC_ALL: "C", LANG: "C" },
46-
stdio: ["ignore", "pipe", "pipe"],
47-
});
48-
}
49-
50-
function makeGitRepo(): string {
51-
const dir = makeTempDir("project-identity-git-");
52-
runGit(dir, ["init"]);
53-
writeFileSync(join(dir, "README.md"), "# test\n", "utf8");
54-
runGit(dir, ["add", "README.md"]);
55-
runGit(dir, [
56-
"-c",
57-
"user.email=test@example.com",
58-
"-c",
59-
"user.name=Test User",
60-
"-c",
61-
"commit.gpgsign=false",
62-
"commit",
63-
"-m",
64-
"initial commit",
65-
]);
43+
function makeRepoWithGitMetadata(prefix: string): string {
44+
const dir = makeTempDir(prefix);
45+
mkdirSync(join(dir, ".git"));
6646
return dir;
6747
}
6848

69-
function rootCommit(directory: string): string {
70-
return runGit(directory, ["rev-list", "--max-parents=0", "HEAD"]).split("\n")[0]!.trim();
49+
function returningRootCommit(rootCommit: string): typeof execFileSync {
50+
// Keep the real `.git` walk on disk, but route git output through the test
51+
// seam. Under heavy machine load, launching git can stall while the
52+
// operating system assesses the binary, which makes direct calls flaky.
53+
return (() => `${rootCommit}\n`) as typeof execFileSync;
7154
}
7255

7356
function expectedDirIdentity(directory: string): string {
@@ -113,12 +96,12 @@ function makeGitFailure(fields: {
11396

11497
describe("project identity", () => {
11598
it("resolveProjectIdentityStrict returns the git root commit identity", () => {
116-
const repo = makeGitRepo();
117-
const commit = rootCommit(repo);
99+
const repo = makeRepoWithGitMetadata("project-identity-git-");
100+
__setProjectIdentityTestHooks({ execFileSync: returningRootCommit(FIRST_ROOT_COMMIT) });
118101

119102
const identity = resolveProjectIdentityStrict(repo);
120103

121-
expect(identity).toBe(`git:${commit}`);
104+
expect(identity).toBe(`git:${FIRST_ROOT_COMMIT}`);
122105
expect(identity.slice("git:".length).length).toBeGreaterThanOrEqual(7);
123106
});
124107

@@ -142,7 +125,8 @@ describe("project identity", () => {
142125
});
143126

144127
it("resolveProjectIdentityStrict caches git identities across calls", () => {
145-
const repo = makeGitRepo();
128+
const repo = makeRepoWithGitMetadata("project-identity-cache-");
129+
__setProjectIdentityTestHooks({ execFileSync: returningRootCommit(FIRST_ROOT_COMMIT) });
146130
const first = resolveProjectIdentityStrict(repo);
147131

148132
chmodSync(repo, 0o000);
@@ -159,10 +143,10 @@ describe("project identity", () => {
159143
expect(resolveProjectIdentity(directory)).toBe(expectedDirIdentity(directory));
160144
});
161145

162-
it("uses the no-git fast path without spawning git", () => {
146+
it("uses the no-git fast path without invoking git", () => {
163147
const directory = makeTempDir("project-identity-no-git-fast-");
164148
const execMock = mock(() => {
165-
throw new Error("git should not be spawned for a directory with no .git ancestor");
149+
throw new Error("git should not be launched for a directory with no .git ancestor");
166150
});
167151
__setProjectIdentityTestHooks({ execFileSync: execMock as unknown as typeof execFileSync });
168152

@@ -171,16 +155,16 @@ describe("project identity", () => {
171155
});
172156

173157
it("detects git metadata in ancestors for subdirectory sessions", () => {
174-
const repo = makeGitRepo();
158+
const repo = makeRepoWithGitMetadata("project-identity-ancestor-");
175159
const subdir = join(repo, "nested", "session");
176160
mkdirSync(subdir, { recursive: true });
161+
__setProjectIdentityTestHooks({ execFileSync: returningRootCommit(FIRST_ROOT_COMMIT) });
177162

178-
expect(resolveProjectIdentity(subdir)).toBe(`git:${rootCommit(repo)}`);
163+
expect(resolveProjectIdentity(subdir)).toBe(`git:${FIRST_ROOT_COMMIT}`);
179164
});
180165

181166
it("detects git metadata through symlinked checkout paths", () => {
182-
const repo = makeTempDir("project-identity-symlink-repo-");
183-
writeFileSync(join(repo, ".git"), "gitdir: .git/worktrees/main\n", "utf8");
167+
const repo = makeRepoWithGitMetadata("project-identity-symlink-repo-");
184168
const subdir = join(repo, "nested", "session");
185169
mkdirSync(subdir, { recursive: true });
186170
const linkParent = makeTempDir("project-identity-symlink-parent-");
@@ -191,49 +175,47 @@ describe("project identity", () => {
191175
if ((error as { code?: unknown }).code === "EPERM") return;
192176
throw error;
193177
}
194-
const execMock = mock(() => "abcdef1234567890\n");
178+
const execMock = mock(() => `${FIRST_ROOT_COMMIT}\n`);
195179
__setProjectIdentityTestHooks({ execFileSync: execMock as unknown as typeof execFileSync });
196180

197-
expect(resolveProjectIdentity(link)).toBe("git:abcdef1234567890");
181+
expect(resolveProjectIdentity(link)).toBe(`git:${FIRST_ROOT_COMMIT}`);
198182
expect(execMock).toHaveBeenCalledTimes(1);
199183
});
200184

201185
it("reuses the last successful git identity during transient failures and cooldown", () => {
202-
const directory = makeTempDir("project-identity-last-known-");
203-
mkdirSync(join(directory, ".git"));
186+
const directory = makeRepoWithGitMetadata("project-identity-last-known-");
204187
let now = 1_000;
205188
let mode: "first" | "timeout" | "second" = "first";
206189
const execMock = mock(() => {
207-
if (mode === "first") return "abcdef1234567890\n";
208-
if (mode === "second") return "1234567890abcdef\n";
190+
if (mode === "first") return `${FIRST_ROOT_COMMIT}\n`;
191+
if (mode === "second") return `${SECOND_ROOT_COMMIT}\n`;
209192
throw makeGitFailure({ code: "ETIMEDOUT" });
210193
});
211194
__setProjectIdentityTestHooks({
212195
execFileSync: execMock as unknown as typeof execFileSync,
213196
nowMs: () => now,
214197
});
215198

216-
expect(resolveProjectIdentity(directory)).toBe("git:abcdef1234567890");
199+
expect(resolveProjectIdentity(directory)).toBe(`git:${FIRST_ROOT_COMMIT}`);
217200
__clearProjectIdentityResolutionCacheForTests(directory);
218201
mode = "timeout";
219202

220-
expect(resolveProjectIdentity(directory)).toBe("git:abcdef1234567890");
221-
expect(resolveProjectIdentity(directory)).toBe("git:abcdef1234567890");
203+
expect(resolveProjectIdentity(directory)).toBe(`git:${FIRST_ROOT_COMMIT}`);
204+
expect(resolveProjectIdentity(directory)).toBe(`git:${FIRST_ROOT_COMMIT}`);
222205
expect(execMock).toHaveBeenCalledTimes(2);
223206

224207
mode = "second";
225208
now += 5 * 60 * 1000 + 1;
226-
expect(resolveProjectIdentity(directory)).toBe("git:1234567890abcdef");
209+
expect(resolveProjectIdentity(directory)).toBe(`git:${SECOND_ROOT_COMMIT}`);
227210
expect(execMock).toHaveBeenCalledTimes(3);
228211
});
229212

230213
it("classifies dubious ownership and falls back until the cooldown expires", () => {
231-
const directory = makeTempDir("project-identity-dubious-");
232-
mkdirSync(join(directory, ".git"));
214+
const directory = makeRepoWithGitMetadata("project-identity-dubious-");
233215
let now = 1_000;
234216
let recovered = false;
235217
const execMock = mock(() => {
236-
if (recovered) return "abcdef1234567890\n";
218+
if (recovered) return `${FIRST_ROOT_COMMIT}\n`;
237219
throw makeGitFailure({
238220
stderr:
239221
"fatal: detected dubious ownership in repository at '/repo'\n" +
@@ -261,13 +243,12 @@ describe("project identity", () => {
261243
expect(execMock).toHaveBeenCalledTimes(2);
262244

263245
now += 5 * 60 * 1000 + 1;
264-
expect(resolveProjectIdentity(directory)).toBe("git:abcdef1234567890");
246+
expect(resolveProjectIdentity(directory)).toBe(`git:${FIRST_ROOT_COMMIT}`);
265247
expect(execMock).toHaveBeenCalledTimes(3);
266248
});
267249

268-
it("cools down git timeouts so immediate retries do not respawn git", () => {
269-
const directory = makeTempDir("project-identity-timeout-");
270-
mkdirSync(join(directory, ".git"));
250+
it("cools down git timeouts so immediate retries do not rerun git", () => {
251+
const directory = makeRepoWithGitMetadata("project-identity-timeout-");
271252
const execMock = mock(() => {
272253
throw makeGitFailure({ code: "ETIMEDOUT" });
273254
});
@@ -279,11 +260,10 @@ describe("project identity", () => {
279260
});
280261

281262
it("re-probes after a transient cooldown is cleared", () => {
282-
const directory = makeTempDir("project-identity-clear-cooldown-");
283-
mkdirSync(join(directory, ".git"));
263+
const directory = makeRepoWithGitMetadata("project-identity-clear-cooldown-");
284264
let recovered = false;
285265
const execMock = mock(() => {
286-
if (recovered) return "abcdef1234567890\n";
266+
if (recovered) return `${FIRST_ROOT_COMMIT}\n`;
287267
throw makeGitFailure({ code: "ETIMEDOUT" });
288268
});
289269
__setProjectIdentityTestHooks({ execFileSync: execMock as unknown as typeof execFileSync });
@@ -292,13 +272,12 @@ describe("project identity", () => {
292272
recovered = true;
293273
__clearProjectIdentityTransientCooldownForTests(directory);
294274

295-
expect(resolveProjectIdentity(directory)).toBe("git:abcdef1234567890");
275+
expect(resolveProjectIdentity(directory)).toBe(`git:${FIRST_ROOT_COMMIT}`);
296276
expect(execMock).toHaveBeenCalledTimes(2);
297277
});
298278

299279
it("still propagates permission_denied git failures", () => {
300-
const directory = makeTempDir("project-identity-permission-");
301-
mkdirSync(join(directory, ".git"));
280+
const directory = makeRepoWithGitMetadata("project-identity-permission-");
302281
__setProjectIdentityTestHooks({
303282
execFileSync: mock(() => {
304283
throw makeGitFailure({ code: "EACCES" });
@@ -329,8 +308,8 @@ describe("project identity", () => {
329308
expect(storedPathBelongsToIdentity("dir:deadbeef", "dir:deadbeef")).toBe(true);
330309
// Mismatched identity.
331310
expect(storedPathBelongsToIdentity("git:abc123", "git:other")).toBe(false);
332-
// A raw filesystem path stored before normalization must still match the
333-
// identity it normalizes to (the #11 case Pi previously rejected).
311+
// Raw filesystem paths stored before normalization must still match the
312+
// identity they normalize to.
334313
const directory = makeTempDir("project-identity-belongs-");
335314
const identity = expectedDirIdentity(directory);
336315
expect(storedPathBelongsToIdentity(directory, identity)).toBe(true);

0 commit comments

Comments
 (0)