test: make five suites Windows-runner clean

First ci-windows run exposed four platform assumptions in the fixtures:

- smoke: the scratch repo inherited the runner's system-wide
  core.autocrlf=true (Git for Windows default), so every index restore
  smudged LF to CRLF and broke the byte-exact assertions — pin
  core.autocrlf=false in the fixture (eol policy is not what these
  destructive-op tests are about).
- git / git-worktree: Windows TEMP is the 8.3 short form
  (C:\Users\RUNNER~1\...) while git reports long paths, and
  realpathSync does not expand 8.3 aliases — switch the canonicalizer
  to realpathSync.native (libuv resolves via GetFinalPathNameByHandle,
  which does); same trick as the existing macOS /var workaround.
- bundle-route: the ETag memo revalidates on stat (mtime+size) and
  Windows updates file timestamps lazily, so the equal-size rewrite
  still looked unchanged — change the length too.
- market-manifest: GNU tar (Git Bash) reads the drive-letter colon of
  absolute Windows paths as a remote-host spec — run it from the pack
  dir with the bare tarball name instead.
This commit is contained in:
Menghuan1918
2026-09-03 10:42:56 +08:00
parent 79d7165f91
commit 24db20a796
5 changed files with 28 additions and 7 deletions
+6 -1
View File
@@ -84,7 +84,12 @@ describe('/sidebar/bundle route', () => {
try {
const first = fakeRes()
await handler(req('GET', '/sidebar/bundle/editor.js'), first as unknown as ServerResponse)
writeFileSync(join(dir, 'client-editor.js'), 'window.__ModuleLoader__ && 1;')
// The rewrite must change the LENGTH too: the ETag memo revalidates on
// stat (mtime+size), and Windows updates file timestamps lazily — an
// equal-size rewrite right after the first read can still look
// unchanged (stale 304). A size delta busts the memo deterministically
// on every platform.
writeFileSync(join(dir, 'client-editor.js'), 'window.__ModuleLoader__ && 1; // rotated')
const second = fakeRes()
await handler(req('GET', '/sidebar/bundle/editor.js', { 'if-none-match': first.headers.etag! }), second as unknown as ServerResponse)
expect(second.status).toBe(200)
+6 -3
View File
@@ -68,8 +68,11 @@ describe('linked Git worktrees', () => {
git(main, ['worktree', 'add', '-q', '-b', 'agent', agent])
writeFileSync(join(agent, 'tracked.txt'), 'changed by agent\n')
// macOS tmpdir() keeps the /var symlink while git reports the resolved
// /private/var prefix — canonicalize every path handed to git APIs.
const agentPath = realpathSync(agent)
// /private/var prefix; on Windows TEMP is often the 8.3 short form
// (C:\Users\RUNNER~1\...) while git reports the long path. The NATIVE
// realpath resolves both aliasings (libuv's GetFinalPathNameByHandle
// expands 8.3 names) — canonicalize every path handed to git APIs.
const agentPath = realpathSync.native(agent)
const listed = await worktrees(main)
expect(listed).toHaveLength(2)
@@ -87,7 +90,7 @@ describe('linked Git worktrees', () => {
rmSync(agent, { recursive: true, force: true })
const remaining = await worktrees(main)
expect(remaining).toHaveLength(1)
expect(resolve(remaining[0]!.path)).toBe(resolve(realpathSync(main)))
expect(resolve(remaining[0]!.path)).toBe(resolve(realpathSync.native(main)))
expect(remaining[0]!.current).toBe(true)
await expect(resolveWorktree(main, agentPath)).rejects.toThrow('unknown linked worktree')
} finally {
+6 -2
View File
@@ -11,8 +11,12 @@ import { parseLogLines, parsePorcelainZ, repoRoots, status } from '../src/git.ts
const execFileAsync = promisify(execFile)
const normalizePath = (path: string): string => path.replaceAll('\\', '/')
// macOS tmpdir() is the /var symlink while git reports the resolved
// /private/var prefix — canonicalize both sides before comparing.
const canonical = (path: string): string => normalizePath(realpathSync(path))
// /private/var prefix; on Windows TEMP is often the 8.3 short form
// (C:\Users\RUNNER~1\...) while git reports the long path. The NATIVE
// realpath resolves both aliasings (libuv canonicalizes on Windows via
// GetFinalPathNameByHandle, which expands 8.3 names) — canonicalize both
// sides before comparing.
const canonical = (path: string): string => normalizePath(realpathSync.native(path))
describe('git parsing', () => {
it('discovers and selects direct child repositories under a workspace directory', async () => {
+4 -1
View File
@@ -57,7 +57,10 @@ function packedManifest(): Record<string, unknown> {
// spawn directly (ENOENT). Run it through the shell so the same call
// works on every platform; `tar` is available on all supported OSes.
execFileSync('pnpm', ['pack', '--pack-destination', dir], { cwd: ROOT, stdio: 'pipe', shell: process.platform === 'win32' })
execFileSync('tar', ['-xzf', join(dir, `${pkg.name}-${pkg.version}.tgz`), '-C', dir, 'package/package.json'], { stdio: 'pipe' })
// Never hand tar an absolute Windows path: Git Bash's GNU tar reads the
// drive-letter colon as a remote-host spec ("Cannot connect to C:").
// Run it from the pack dir with the bare tarball name instead.
execFileSync('tar', ['-xzf', `${pkg.name}-${pkg.version}.tgz`, 'package/package.json'], { cwd: dir, stdio: 'pipe' })
return JSON.parse(readFileSync(join(dir, 'package/package.json'), 'utf8')) as Record<string, unknown>
} finally {
rmSync(dir, { recursive: true, force: true })
+6
View File
@@ -409,6 +409,12 @@ describe('git destructive operations (scratch repository)', () => {
const makeScratchRepo = (): string => {
const dir = mkdtempSync(join(tmpdir(), 'dsh-sidebar-git-'))
gitRun(dir, ['init', '-q'])
// Pin the eol policy: Git for Windows defaults to core.autocrlf=true
// (system gitconfig on the CI runner, and many dev machines), which
// smudges LF→CRLF on every index restore and breaks the byte-exact
// assertions below. The destructive-op behavior under test is orthogonal
// to the machine's eol policy.
gitRun(dir, ['config', 'core.autocrlf', 'false'])
gitRun(dir, ['checkout', '-q', '-b', 'main'])
writeFileSync(join(dir, 'a.txt'), 'one\ntwo\nthree\n')
gitRun(dir, ['add', '-A'])