From 8fecf6a3ef766f962244c19458fce35d869f313c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=82CHES?= Date: Sat, 21 Mar 2026 11:45:36 -0600 Subject: [PATCH] fix: normalize paths in tests to handle Windows 8.3 short-path forms (#1804) --- .../gsd/tests/repo-identity-worktree.test.ts | 20 +++++++++++++++---- .../extensions/gsd/tests/worktree.test.ts | 18 ++++++++++++++--- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts b/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts index 8133d1306..da8d7dda6 100644 --- a/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +++ b/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts @@ -8,6 +8,17 @@ import { createTestContext } from "./test-helpers.ts"; const { assertEq, assertTrue, report } = createTestContext(); +/** + * Normalize a path for reliable comparison on Windows CI runners. + * `os.tmpdir()` may return the 8.3 short-path form (e.g. `C:\Users\RUNNER~1`) + * while `realpathSync` and git resolve to the long form (`C:\Users\runneradmin`). + * Apply `realpathSync` and lowercase on Windows to eliminate both discrepancies. + */ +function normalizePath(p: string): string { + const resolved = realpathSync(p); + return process.platform === "win32" ? resolved.toLowerCase() : resolved; +} + function run(command: string, cwd: string): string { return execSync(command, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim(); } @@ -90,16 +101,17 @@ async function main(): Promise { const fixedExternal = ensureGsdSymlink(moveRepo); const before = readRepoMeta(fixedExternal); assertTrue(before !== null, "repo metadata exists before repo move"); - assertEq(before!.gitRoot, realpathSync(moveRepo), "repo metadata tracks current git root before move"); + assertEq(normalizePath(before!.gitRoot), normalizePath(moveRepo), "repo metadata tracks current git root before move"); - const movedBase = join(tmpdir(), `gsd-repo-identity-moved-${Date.now()}-${Math.random().toString(36).slice(2)}`); - renameSync(moveRepo, movedBase); + const movedBaseRaw = join(tmpdir(), `gsd-repo-identity-moved-${Date.now()}-${Math.random().toString(36).slice(2)}`); + renameSync(moveRepo, movedBaseRaw); + const movedBase = realpathSync(movedBaseRaw); const movedExternal = ensureGsdSymlink(movedBase); assertEq(realpathSync(movedExternal), realpathSync(fixedExternal), "fixed project id keeps the same external state dir"); const after = readRepoMeta(movedExternal); assertTrue(after !== null, "repo metadata exists after repo move"); - assertEq(after!.gitRoot, realpathSync(movedBase), "repo metadata gitRoot is refreshed to moved repo path"); + assertEq(normalizePath(after!.gitRoot), normalizePath(movedBase), "repo metadata gitRoot is refreshed to moved repo path"); assertEq(after!.createdAt, before!.createdAt, "repo metadata preserves createdAt on refresh"); rmSync(movedBase, { recursive: true, force: true }); diff --git a/src/resources/extensions/gsd/tests/worktree.test.ts b/src/resources/extensions/gsd/tests/worktree.test.ts index d95a00c94..e0b5fb1cf 100644 --- a/src/resources/extensions/gsd/tests/worktree.test.ts +++ b/src/resources/extensions/gsd/tests/worktree.test.ts @@ -20,6 +20,18 @@ import { _resetHasChangesCache } from "../native-git-bridge.ts"; import { createTestContext } from './test-helpers.ts'; const { assertEq, assertTrue, report } = createTestContext(); + +/** + * Normalize a path for reliable comparison on Windows CI runners. + * `os.tmpdir()` may return the 8.3 short-path form (e.g. `C:\Users\RUNNER~1`) + * while `realpathSync` and git resolve to the long form (`C:\Users\runneradmin`). + * Apply `realpathSync` and lowercase on Windows to eliminate both discrepancies. + */ +function normalizePath(p: string): string { + const resolved = realpathSync(p); + return process.platform === "win32" ? resolved.toLowerCase() : resolved; +} + function run(command: string, cwd: string): string { return execSync(command, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim(); } @@ -236,7 +248,7 @@ async function main(): Promise { // Real symlink + git worktree scenario, with deep nested path from cwd { const fakeHome = mkdtempSync(join(tmpdir(), "gsd-home-")); - const project = mkdtempSync(join(tmpdir(), "gsd-proj-")); + const project = realpathSync(mkdtempSync(join(tmpdir(), "gsd-proj-"))); const storage = join(fakeHome, ".gsd", "projects", "abc123def456"); mkdirSync(storage, { recursive: true }); symlinkSync(storage, join(project, ".gsd")); @@ -253,8 +265,8 @@ async function main(): Promise { process.env.GSD_HOME = join(fakeHome, ".gsd"); assertEq( - resolveProjectRoot(realpathSync(deep)), - realpathSync(project), + normalizePath(resolveProjectRoot(realpathSync(deep))), + normalizePath(project), "resolves to real project root from deep symlink-resolved worktree path", ); delete process.env.GSD_HOME;