From 65f60f2bafe43e4d8c222afd6c893fb88114940f Mon Sep 17 00:00:00 2001 From: JUN <243035832+lidge-jun@users.noreply.github.com> Date: Sat, 31 Oct 2026 07:37:50 +0900 Subject: [PATCH] test: select injected stat targets by canonical path Regression tests from #8019/#8023 matched the spied path by raw string; code under test stats canonical paths (/private/var on macOS, long names for an 8.3 short TEMP on hosted Windows), so the injection never applied (lane=all run 41136081084). Add tests/helpers/same-path.ts and use it for every target selection. --- tests/clients/remote-workspace.test.ts | 5 +-- .../codex-catalog-write-audit.test.ts | 7 ++-- .../codex-coordinator-doctor.test.ts | 3 +- .../codex-history-provider.test.ts | 7 ++-- .../codex-main-account-refresh.test.ts | 3 +- .../codex-native-residue.test.ts | 3 +- .../codex-shim-windows-revision.test.ts | 7 ++-- .../codex-transition-state.test.ts | 3 +- .../native-main-owner-lifetime.test.ts | 3 +- tests/config/config-mutation-lock.test.ts | 3 +- tests/helpers/same-path.ts | 34 +++++++++++++++++++ tests/lib/file-identity.test.ts | 5 +-- tests/providers/cc-switch-import.test.ts | 3 +- .../command-code-project-context.test.ts | 25 +++++++------- tests/service/service-ownership-state.test.ts | 3 +- 15 files changed, 81 insertions(+), 33 deletions(-) create mode 100644 tests/helpers/same-path.ts diff --git a/tests/clients/remote-workspace.test.ts b/tests/clients/remote-workspace.test.ts index bd68c779e1d..42742ef7501 100644 --- a/tests/clients/remote-workspace.test.ts +++ b/tests/clients/remote-workspace.test.ts @@ -1,3 +1,4 @@ +import { samePath } from "../helpers/same-path"; import { afterEach, describe, expect, spyOn, test } from "bun:test"; import * as fs from "node:fs"; import { createHash, randomUUID } from "node:crypto"; @@ -153,7 +154,7 @@ describe("remote workspace file identity", () => { const realLstat = fs.lstatSync, realFstat = fs.fstatSync; const lstat = spyOn(fs, "lstatSync").mockImplementation(((path, options) => { const stat = options?.bigint ? realLstat(path, { bigint: true }) : realLstat(path); - return path === target ? Object.assign(Object.create(Object.getPrototypeOf(stat)), stat, + return samePath(path, target) ? Object.assign(Object.create(Object.getPrototypeOf(stat)), stat, { ino: options?.bigint ? id : Number(id) }) : stat; }) as typeof fs.lstatSync); const fstat = spyOn(fs, "fstatSync").mockImplementation(((fd, options) => { @@ -176,7 +177,7 @@ describe("remote workspace file identity", () => { const lstat = spyOn(fs, "lstatSync").mockImplementation(((path, options) => { const stat = options?.bigint ? realLstat(path, { bigint: true }) : realLstat(path); const ino = id + (replacement ? 1n : 0n); - return path === state.executorRoot ? Object.assign(Object.create(Object.getPrototypeOf(stat)), stat, + return samePath(path, state.executorRoot) ? Object.assign(Object.create(Object.getPrototypeOf(stat)), stat, { ino: options?.bigint ? ino : Number(ino) }) : stat; }) as typeof fs.lstatSync); try { diff --git a/tests/codex-integration/codex-catalog-write-audit.test.ts b/tests/codex-integration/codex-catalog-write-audit.test.ts index a75d2b1c9a2..4b4dd3c660c 100644 --- a/tests/codex-integration/codex-catalog-write-audit.test.ts +++ b/tests/codex-integration/codex-catalog-write-audit.test.ts @@ -1,3 +1,4 @@ +import { samePath } from "../helpers/same-path"; import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test"; import { existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, statSync, writeFileSync, symlinkSync } from "node:fs"; import * as fs from "node:fs"; @@ -599,7 +600,7 @@ describe("Windows audit privacy (r4176128642)", () => { { ctimeNs: stat.ctimeNs + (hardened ? 1_000_000n : 0n) }) as fs.BigIntStats; const fstat = spyOn(fs, "fstatSync").mockImplementation(((fd, options) => options?.bigint ? postAcl(realFstat(fd, { bigint: true })) : realFstat(fd)) as typeof fs.fstatSync); const lstat = spyOn(fs, "lstatSync").mockImplementation(((path, options) => - path === auditPath() && options?.bigint ? postAcl(realLstat(path, { bigint: true })) : realLstat(path)) as typeof fs.lstatSync); + samePath(path, auditPath()) && options?.bigint ? postAcl(realLstat(path, { bigint: true })) : realLstat(path)) as typeof fs.lstatSync); const write = spyOn(fs, "writeSync"); const open = spyOn(fs, "openSync"); harden.mockImplementation((path, options) => { @@ -630,7 +631,7 @@ describe("Windows audit privacy (r4176128642)", () => { }) as typeof fs.fstatSync); const lstat = spyOn(fs, "lstatSync").mockImplementation(((path, options) => { const stat = options?.bigint ? realLstat(path, { bigint: true }) : realLstat(path); - return path === auditPath() ? Object.assign(Object.create(Object.getPrototypeOf(stat)), stat, + return samePath(path, auditPath()) ? Object.assign(Object.create(Object.getPrototypeOf(stat)), stat, { ino: options?.bigint ? identity + 1n : Number(identity + 1n) }) : stat; }) as typeof fs.lstatSync); try { @@ -718,7 +719,7 @@ describe("Windows audit privacy (r4176128642)", () => { const realOpen = fs.openSync; const open = spyOn(fs, "openSync").mockImplementation((path, flags, mode) => { const fd = realOpen(path, flags, mode); - if (phase === "before" && path === auditPath()) substitute(); + if (phase === "before" && samePath(path, auditPath())) substitute(); return fd; }); harden.mockImplementation(() => { substitute(); return { ok: true }; }); diff --git a/tests/codex-integration/codex-coordinator-doctor.test.ts b/tests/codex-integration/codex-coordinator-doctor.test.ts index d7d85da1f14..e7edaa29742 100644 --- a/tests/codex-integration/codex-coordinator-doctor.test.ts +++ b/tests/codex-integration/codex-coordinator-doctor.test.ts @@ -1,3 +1,4 @@ +import { samePath } from "../helpers/same-path"; import { afterEach, beforeEach, expect, spyOn, test } from "bun:test"; import * as fs from "node:fs"; import { chmodSync, existsSync, mkdtempSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs"; @@ -88,7 +89,7 @@ test("doctor detects adjacent large file identities", () => { let observations = 0; const spy = spyOn(fs, "lstatSync").mockImplementation(((target, options) => { const value = options?.bigint ? realLstat(target, { bigint: true }) : realLstat(target); - if (target !== coordinatorPath) return value; + if (!samePath(target, coordinatorPath)) return value; const ino = id + BigInt(observations++ > 0); return Object.assign(Object.create(Object.getPrototypeOf(value)), value, { ino: options?.bigint ? ino : Number(ino) }); }) as typeof fs.lstatSync); diff --git a/tests/codex-integration/codex-history-provider.test.ts b/tests/codex-integration/codex-history-provider.test.ts index c4ac9817963..28f1d94a377 100644 --- a/tests/codex-integration/codex-history-provider.test.ts +++ b/tests/codex-integration/codex-history-provider.test.ts @@ -1,3 +1,4 @@ +import { samePath } from "../helpers/same-path"; import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, utimesSync, writeFileSync } from "node:fs"; import * as fs from "node:fs"; import { tmpdir } from "node:os"; @@ -116,13 +117,13 @@ describe("Codex history provider sync", () => { const rolloutFds = new Set(); const open = spyOn(fs, "openSync").mockImplementation(((path, flags, mode) => { const fd = realOpen(path, flags, mode); - if (path === fixture.rollout) rolloutFds.add(fd); + if (samePath(path, fixture.rollout)) rolloutFds.add(fd); return fd; }) as typeof fs.openSync); const close = spyOn(fs, "closeSync").mockImplementation((fd => { rolloutFds.delete(fd); realClose(fd); }) as typeof fs.closeSync); const lstat = spyOn(fs, "lstatSync").mockImplementation(((path, options) => { const stat = options?.bigint ? realLstat(path, { bigint: true }) : realLstat(path); - return path === fixture.rollout && armed ? Object.assign(Object.create(Object.getPrototypeOf(stat)), stat, + return samePath(path, fixture.rollout) && armed ? Object.assign(Object.create(Object.getPrototypeOf(stat)), stat, { ino: options?.bigint ? id : Number(id) }) : stat; }) as typeof fs.lstatSync); const fstat = spyOn(fs, "fstatSync").mockImplementation(((fd, options) => { @@ -1327,7 +1328,7 @@ describe("Design B migration helpers", () => { const stat = spyOn(fs, "statSync").mockImplementation(((path, options) => { const result = options?.bigint ? real(path, { bigint: true }) : real(path); const ino = id + (replaced ? 1n : 0n); - return path === fixture.dbPath ? Object.assign(Object.create(Object.getPrototypeOf(result)), result, + return samePath(path, fixture.dbPath) ? Object.assign(Object.create(Object.getPrototypeOf(result)), result, { ino: options?.bigint ? ino : Number(ino) }) : result; }) as typeof fs.statSync); setAfterNoopPendingCountForTests(() => { replaced = true; }); diff --git a/tests/codex-integration/codex-main-account-refresh.test.ts b/tests/codex-integration/codex-main-account-refresh.test.ts index 2535bc88242..81610c1d0db 100644 --- a/tests/codex-integration/codex-main-account-refresh.test.ts +++ b/tests/codex-integration/codex-main-account-refresh.test.ts @@ -1,3 +1,4 @@ +import { samePath } from "../helpers/same-path"; import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test"; import * as fs from "node:fs"; import { existsSync, mkdtempSync, readFileSync, readdirSync, renameSync, writeFileSync } from "node:fs"; @@ -43,7 +44,7 @@ describe("native main token refresh", () => { const spy = spyOn(fs, "statSync").mockImplementation(((target, options) => { const value = options?.bigint ? realStat(target, { bigint: true }) : realStat(target); const ino = replaced ? id + 1n : id; - return target === path ? Object.assign(Object.create(Object.getPrototypeOf(value)), value, { ino: options?.bigint ? ino : Number(ino) }) : value; + return samePath(target, path) ? Object.assign(Object.create(Object.getPrototypeOf(value)), value, { ino: options?.bigint ? ino : Number(ino) }) : value; }) as typeof fs.statSync); setMainAuthJsonBeforeRenameHookForTests(() => { replaced = true; }); try { diff --git a/tests/codex-integration/codex-native-residue.test.ts b/tests/codex-integration/codex-native-residue.test.ts index 3291cd5119d..d2d81c02ba2 100644 --- a/tests/codex-integration/codex-native-residue.test.ts +++ b/tests/codex-integration/codex-native-residue.test.ts @@ -1,3 +1,4 @@ +import { samePath } from "../helpers/same-path"; import { afterEach, beforeEach, expect, spyOn, test } from "bun:test"; import * as fs from "node:fs"; import { createHash, randomUUID } from "node:crypto"; @@ -77,7 +78,7 @@ test("residue inspection detects adjacent large file identities", () => { let observations = 0; const spy = spyOn(fs, "statSync").mockImplementation(((target, options) => { const value = options?.bigint ? realStat(target, { bigint: true }) : realStat(target); - if (target !== path) return value; + if (!samePath(target, path)) return value; const ino = id + BigInt(observations++ > 0); return Object.assign(Object.create(Object.getPrototypeOf(value)), value, { ino: options?.bigint ? ino : Number(ino) }); }) as typeof fs.statSync); diff --git a/tests/codex-integration/codex-shim-windows-revision.test.ts b/tests/codex-integration/codex-shim-windows-revision.test.ts index dbd9c281c82..423e1696fa7 100644 --- a/tests/codex-integration/codex-shim-windows-revision.test.ts +++ b/tests/codex-integration/codex-shim-windows-revision.test.ts @@ -1,3 +1,4 @@ +import { samePath } from "../helpers/same-path"; /** * An installed Windows CMD or PowerShell wrapper from before the bypass-token fix loads the * service token before honoring OCX_SHIM_BYPASS. Those wrappers carry the shim marker but no @@ -61,7 +62,7 @@ test("shim probe rejects adjacent NTFS-sized file identities", () => { let calls = 0; const stat = spyOn(fs, "lstatSync").mockImplementation(((target, options) => { const result = options?.bigint ? real(target, { bigint: true }) : real(target); - if (target !== path) return result; + if (!samePath(target, path)) return result; const ino = id + BigInt(calls++ > 0); return Object.assign(Object.create(Object.getPrototypeOf(result)), result, { ino: options?.bigint ? ino : Number(ino) }); @@ -80,7 +81,7 @@ for (const [name, owns] of [ const real = fs.lstatSync; const stat = spyOn(fs, "lstatSync").mockImplementation(((target, options) => { const value = options?.bigint ? real(target, { bigint: true }) : real(target); - return target === path ? Object.assign(Object.create(Object.getPrototypeOf(value)), value, + return samePath(target, path) ? Object.assign(Object.create(Object.getPrototypeOf(value)), value, { dev: options?.bigint ? 1n : 1, ino: options?.bigint ? id : Number(id) }) : value; }) as typeof fs.lstatSync); try { @@ -110,7 +111,7 @@ test("shim state read rejects adjacent NTFS-sized file identities", () => { const realLstat = fs.lstatSync, realFstat = fs.fstatSync; const lstat = spyOn(fs, "lstatSync").mockImplementation(((target, options) => { const stat = options?.bigint ? realLstat(target, { bigint: true }) : realLstat(target); - return target === path ? Object.assign(Object.create(Object.getPrototypeOf(stat)), stat, + return samePath(target, path) ? Object.assign(Object.create(Object.getPrototypeOf(stat)), stat, { ino: options?.bigint ? id : Number(id) }) : stat; }) as typeof fs.lstatSync); const fstat = spyOn(fs, "fstatSync").mockImplementation(((fd, options) => { diff --git a/tests/codex-integration/codex-transition-state.test.ts b/tests/codex-integration/codex-transition-state.test.ts index 5fc97aaea9e..4314b675ff1 100644 --- a/tests/codex-integration/codex-transition-state.test.ts +++ b/tests/codex-integration/codex-transition-state.test.ts @@ -1,3 +1,4 @@ +import { samePath } from "../helpers/same-path"; import { afterEach, beforeEach, expect, spyOn, test } from "bun:test"; import * as fs from "node:fs"; import { chmodSync, mkdirSync, mkdtempSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs"; @@ -67,7 +68,7 @@ test("coordinator detects adjacent large file identities", () => { const spy = spyOn(fs, "lstatSync").mockImplementation(((target, options) => { const value = options?.bigint ? realLstat(target, { bigint: true }) : realLstat(target); const ino = replaced ? id + 1n : id; - return target === coordinatorPath ? Object.assign(Object.create(Object.getPrototypeOf(value)), value, { ino: options?.bigint ? ino : Number(ino) }) : value; + return samePath(target, coordinatorPath) ? Object.assign(Object.create(Object.getPrototypeOf(value)), value, { ino: options?.bigint ? ino : Number(ino) }) : value; }) as typeof fs.lstatSync); try { const controller = openCodexCoordinatorTransaction(coordinatorPath); diff --git a/tests/codex-integration/native-main-owner-lifetime.test.ts b/tests/codex-integration/native-main-owner-lifetime.test.ts index c47d572850d..425bf74a26a 100644 --- a/tests/codex-integration/native-main-owner-lifetime.test.ts +++ b/tests/codex-integration/native-main-owner-lifetime.test.ts @@ -1,3 +1,4 @@ +import { samePath } from "../helpers/same-path"; import { afterEach, describe, expect, spyOn, test } from "bun:test"; import * as fs from "node:fs"; import { Database } from "bun:sqlite"; @@ -45,7 +46,7 @@ test("stable lock rejects adjacent large file identities", () => { const lstat = spyOn(fs, "lstatSync").mockImplementation(((target, options) => { const value = options?.bigint ? realLstat(target, { bigint: true }) : realLstat(target); const ino = replaced ? id + 1n : id; - return target === path ? Object.assign(Object.create(Object.getPrototypeOf(value)), value, { ino: options?.bigint ? ino : Number(ino) }) : value; + return samePath(target, path) ? Object.assign(Object.create(Object.getPrototypeOf(value)), value, { ino: options?.bigint ? ino : Number(ino) }) : value; }) as typeof fs.lstatSync); try { const handle = openStableLockFile(path); diff --git a/tests/config/config-mutation-lock.test.ts b/tests/config/config-mutation-lock.test.ts index 4b3103cc0bb..a64d3e93841 100644 --- a/tests/config/config-mutation-lock.test.ts +++ b/tests/config/config-mutation-lock.test.ts @@ -1,3 +1,4 @@ +import { samePath } from "../helpers/same-path"; import { afterEach, beforeEach, expect, spyOn, test } from "bun:test"; import * as fs from "node:fs"; import { closeSync, existsSync, linkSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs"; @@ -297,7 +298,7 @@ test("initial config file identity preserves adjacent NTFS-sized values", () => }) as typeof fs.fstatSync); const lstat = spyOn(fs, "lstatSync").mockImplementation(((path, options) => { const stat = options?.bigint ? realLstat(path, { bigint: true }) : realLstat(path); - return path === tempPath ? Object.assign(Object.create(Object.getPrototypeOf(stat)), stat, + return samePath(path, tempPath) ? Object.assign(Object.create(Object.getPrototypeOf(stat)), stat, { ino: options?.bigint ? identity + 1n : Number(identity + 1n) }) : stat; }) as typeof fs.lstatSync); try { diff --git a/tests/helpers/same-path.ts b/tests/helpers/same-path.ts new file mode 100644 index 00000000000..c10c5dc1c37 --- /dev/null +++ b/tests/helpers/same-path.ts @@ -0,0 +1,34 @@ +import { realpathSync } from "node:fs"; +import { basename, dirname, join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +function canonicalPath(path: string): string { + let current = resolve(path); + const remainder: string[] = []; + for (;;) { + try { + return join(realpathSync.native(current), ...remainder.reverse()); + } catch { + // Missing components are resolved against their nearest existing ancestor. + const parent = dirname(current); + if (parent === current) return resolve(path); + remainder.push(basename(current)); + current = parent; + } + } +} + +export function samePath(target: unknown, expected: string): boolean { + if (typeof target === "number" || typeof target === "bigint") return false; + try { + const path = target instanceof URL ? fileURLToPath(target) : String(target); + const actual = canonicalPath(path); + const reference = canonicalPath(expected); + return process.platform === "win32" + ? actual.toLowerCase() === reference.toLowerCase() + : actual === reference; + } catch { + // Invalid paths and unsupported URL schemes never match. + return false; + } +} diff --git a/tests/lib/file-identity.test.ts b/tests/lib/file-identity.test.ts index f91b709d100..7716c208203 100644 --- a/tests/lib/file-identity.test.ts +++ b/tests/lib/file-identity.test.ts @@ -1,3 +1,4 @@ +import { samePath } from "../helpers/same-path"; import { expect, spyOn, test } from "bun:test"; import * as fs from "node:fs"; import { tmpdir } from "node:os"; @@ -56,11 +57,11 @@ for (const [name, read] of [ const realStat = fs.statSync, realLstat = fs.lstatSync, realFstat = fs.fstatSync; const stat = spyOn(fs, "statSync").mockImplementation(((target, options) => { const value = options?.bigint ? realStat(target, { bigint: true }) : realStat(target); - return target === path ? Object.assign(Object.create(Object.getPrototypeOf(value)), value, { ino: options?.bigint ? id : Number(id) }) : value; + return samePath(target, path) ? Object.assign(Object.create(Object.getPrototypeOf(value)), value, { ino: options?.bigint ? id : Number(id) }) : value; }) as typeof fs.statSync); const lstat = spyOn(fs, "lstatSync").mockImplementation(((target, options) => { const value = options?.bigint ? realLstat(target, { bigint: true }) : realLstat(target); - return target === path ? Object.assign(Object.create(Object.getPrototypeOf(value)), value, { ino: options?.bigint ? id : Number(id) }) : value; + return samePath(target, path) ? Object.assign(Object.create(Object.getPrototypeOf(value)), value, { ino: options?.bigint ? id : Number(id) }) : value; }) as typeof fs.lstatSync); const fstat = spyOn(fs, "fstatSync").mockImplementation(((fd, options) => { const value = options?.bigint ? realFstat(fd, { bigint: true }) : realFstat(fd); diff --git a/tests/providers/cc-switch-import.test.ts b/tests/providers/cc-switch-import.test.ts index e308da771d2..d152fcee1ef 100644 --- a/tests/providers/cc-switch-import.test.ts +++ b/tests/providers/cc-switch-import.test.ts @@ -1,3 +1,4 @@ +import { samePath } from "../helpers/same-path"; import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test"; import { Database } from "bun:sqlite"; import * as fs from "node:fs"; @@ -115,7 +116,7 @@ describe("CC-Switch keyless provider import", () => { const realLstat = fs.lstatSync, realFstat = fs.fstatSync; const lstat = spyOn(fs, "lstatSync").mockImplementation(((target, options) => { const stat = options?.bigint ? realLstat(target, { bigint: true }) : realLstat(target); - return target === path ? Object.assign(Object.create(Object.getPrototypeOf(stat)), stat, + return samePath(target, path) ? Object.assign(Object.create(Object.getPrototypeOf(stat)), stat, { ino: options?.bigint ? identity : Number(identity) }) : stat; }) as typeof fs.lstatSync); const fstat = spyOn(fs, "fstatSync").mockImplementation(((fd, options) => { diff --git a/tests/providers/command-code-project-context.test.ts b/tests/providers/command-code-project-context.test.ts index cd1deee5f69..250cf6dd04f 100644 --- a/tests/providers/command-code-project-context.test.ts +++ b/tests/providers/command-code-project-context.test.ts @@ -1,3 +1,4 @@ +import { samePath } from "../helpers/same-path"; import { describe, test, expect, beforeEach, afterEach, mock } from "bun:test"; import { execFileSync } from "node:child_process"; import { @@ -88,12 +89,12 @@ describe("loadCommandCodeProjectContext", () => { writeFileSync(agentsPath, "project memory", "utf8"); lstatMock.mockImplementation((async (path: Parameters[0], options?: { bigint?: boolean }) => { const stat = options?.bigint ? await realLstat(path, { bigint: true }) : await realLstat(path); - return String(path) === agentsPath ? Object.assign(Object.create(Object.getPrototypeOf(stat)), stat, + return samePath(path, agentsPath) ? Object.assign(Object.create(Object.getPrototypeOf(stat)), stat, { ino: options?.bigint ? identity + 1n : Number(identity + 1n) }) : stat; }) as typeof realLstat); openMock.mockImplementation(async path => { const handle = await realOpen(path); - if (String(path) !== agentsPath) return handle; + if (!samePath(path, agentsPath)) return handle; return { ...handle, read: handle.read.bind(handle), close: handle.close.bind(handle), stat: async (options?: { bigint?: boolean }) => { const stat = options?.bigint ? await handle.stat({ bigint: true }) : await handle.stat(); @@ -148,7 +149,7 @@ describe("loadCommandCodeProjectContext", () => { let releaseStalled: () => Promise = async () => {}; if (stalled === "lstat") { lstatMock.mockImplementation(path => { - if (String(path) !== agentsPath) return realLstat(path); + if (!samePath(path, agentsPath)) return realLstat(path); stalledCalls++; return new Promise(resolve => { releaseStalled = async () => { resolve(await realLstat(path)); }; @@ -156,7 +157,7 @@ describe("loadCommandCodeProjectContext", () => { }); } else { realpathMock.mockImplementation(path => { - if (String(path) !== agentsPath) return realRealpath(path); + if (!samePath(path, agentsPath)) return realRealpath(path); stalledCalls++; return new Promise(resolve => { releaseStalled = async () => { resolve(await realRealpath(path)); }; @@ -249,7 +250,7 @@ describe("loadCommandCodeProjectContext", () => { try { execFileSync("mkfifo", [fifo]); openMock.mockImplementation(async (path, flags) => { - if (String(path) === fifo) { + if (samePath(path, fifo)) { openedFifo = true; throw new Error("FIFO must be rejected before open"); } @@ -303,7 +304,7 @@ describe("loadCommandCodeProjectContext", () => { try { writeFileSync(agentsPath, "recovered memory", "utf8"); lstatMock.mockImplementation(path => { - if (String(path) !== agentsPath) return realLstat(path); + if (!samePath(path, agentsPath)) return realLstat(path); return new Promise(resolve => { release = async () => { resolve(await realLstat(path)); }; }); @@ -413,7 +414,7 @@ describe("loadCommandCodeProjectContext", () => { }; opendirMock.mockImplementation(async path => { - if (String(path) === skillRoot) { + if (samePath(path, skillRoot)) { return hangingDir as Awaited>; } return realOpendir(path); @@ -464,7 +465,7 @@ describe("loadCommandCodeProjectContext", () => { let entriesIterated = 0; opendirMock.mockImplementation(async path => { const dir = await realOpendir(path); - if (String(path) !== skillRoot) return dir; + if (!samePath(path, skillRoot)) return dir; const realIter = dir[Symbol.asyncIterator](); return { close: () => dir.close(), @@ -560,7 +561,7 @@ describe("loadCommandCodeProjectContext", () => { writeFileSync(agentsPath, "x".repeat(4 * 1024 * 1024), "utf8"); openMock.mockImplementation(async path => { const handle = await realOpen(path); - if (String(path) !== agentsPath) return handle; + if (!samePath(path, agentsPath)) return handle; const originalRead = handle.read.bind(handle); return { ...handle, @@ -636,7 +637,7 @@ describe("loadCommandCodeProjectContext", () => { }, }; openMock.mockImplementation(async path => { - if (String(path) === agentsPath) { + if (samePath(path, agentsPath)) { return hangingFile as Awaited>; } return realOpen(path); @@ -700,7 +701,7 @@ describe("loadCommandCodeProjectContext", () => { writeFileSync(outsidePath, "outside secret", "utf8"); symlinkSync(insidePath, agentsPath); openMock.mockImplementation(async path => { - if (String(path) === agentsPath) { + if (samePath(path, agentsPath)) { unlinkSync(agentsPath); symlinkSync(outsidePath, agentsPath); } @@ -761,7 +762,7 @@ describe("loadCommandCodeProjectContext", () => { if (path !== skillFile) return; realpathMock.mockImplementation(async (p, opts) => { const resolved = await realRealpath(p, opts); - if (typeof resolved === "string" && resolved === skillFile) { + if (typeof resolved === "string" && samePath(resolved, skillFile)) { changedCanonicalPaths += 1; return resolved.replace(/SKILL\.md$/, "skill.md"); } diff --git a/tests/service/service-ownership-state.test.ts b/tests/service/service-ownership-state.test.ts index ee4565599c6..6aa451de3b4 100644 --- a/tests/service/service-ownership-state.test.ts +++ b/tests/service/service-ownership-state.test.ts @@ -1,3 +1,4 @@ +import { samePath } from "../helpers/same-path"; /** * Durable runtime ownership in the shared service install state. * @@ -598,7 +599,7 @@ describe("the anchor lock", () => { const lstat = spyOn(fs, "lstatSync").mockImplementation(((path, options) => { const stat = options?.bigint ? realLstat(path, { bigint: true }) : realLstat(path); const ino = identity + (changed ? 1n : 0n); - return path === lockPath ? Object.assign(Object.create(Object.getPrototypeOf(stat)), stat, + return samePath(path, lockPath) ? Object.assign(Object.create(Object.getPrototypeOf(stat)), stat, { ino: options?.bigint ? ino : Number(ino) }) : stat; }) as typeof fs.lstatSync); try {