test(ci): read the Lefthook fixture lock path without trimming

git() trims its output, which also drops a worktree directory's own
trailing whitespace; the installer removes only the line terminator. Derive
the expected lock path through gitPath()/stripGitLineTerminator(), which
applies the installer's exact rule, so the same-string claim holds for
every path.

Adds the trailing-space injection case (skipped on Windows, which cannot
create such a directory); it fails with the same probe ENOENT under the
trimming derivation.
This commit is contained in:
Tianyi Cui committed 2026-09-26 01:10:43 +08:00
1 parent acb66b4c23
commit 1829cdfec9
1 file changed
+32 -5
+32 -5
View File
@@ -55,6 +55,24 @@ function git(fixture: Fixture, cwd: string, args: string[]): string {
return result.stdout.trim()
}
/** Remove the line terminator Git appends to a printed path, as the installer does. */
function stripGitLineTerminator(output: string): string {
const withoutLineFeed = output.endsWith('\n') ? output.slice(0, -1) : output
return process.platform === 'win32' && withoutLineFeed.endsWith('\r')
? withoutLineFeed.slice(0, -1)
: withoutLineFeed
}
// `git()` trims, which also drops a worktree directory's own trailing whitespace. Paths that must
// equal the installer's own reading keep only the line terminator, so a trailing space survives.
function gitPath(fixture: Fixture, cwd: string, args: string[]): string {
const result = gitResult(fixture, cwd, args)
if (result.status !== 0) {
throw new Error(`git ${args.join(' ')} failed: ${result.stderr}`)
}
return stripGitLineTerminator(result.stdout)
}
function write(path: string, content: string, mode?: number): void {
mkdirSync(dirname(path), { recursive: true })
writeFileSync(path, content, mode === undefined ? undefined : { mode })
@@ -161,12 +179,12 @@ function gitDirectory(fixture: Fixture, root: string): string {
// The installer resolves the common directory against Git's own top-level path, and the host
// spells that path differently from the fixture directory this file created: Git canonicalizes
// Windows 8.3 short names and the macOS `/var` symlink, while `mkdtempSync` returns the temp
// directory's own spelling. Resolving the relative common directory against Git's top-level
// instead of the fixture directory gives the same string the installer computes, so an injected
// failure on this path reaches the installer's own lock instead of missing it.
// directory's own spelling. Reading both values the way the installer reads them (`gitPath`, not
// the trimming `git()`) keeps the relative common directory resolved to the same string the
// installer computes, so an injected failure on this path reaches the installer's own lock.
function commonDirectory(fixture: Fixture): string {
const root = git(fixture, fixture.main, ['rev-parse', '--show-toplevel'])
const output = git(fixture, fixture.main, ['rev-parse', '--git-common-dir'])
const root = gitPath(fixture, fixture.main, ['rev-parse', '--show-toplevel'])
const output = gitPath(fixture, fixture.main, ['rev-parse', '--git-common-dir'])
return isAbsolute(output) ? output : resolve(root, output)
}
@@ -397,6 +415,15 @@ syncBuiltinESMExports()
await expectInjectedLockAccessFailure(fixture, { operation: 'openSync', code: 'EPERM', expires: false })
})
// A worktree directory can end in a space on POSIX hosts; Git prints that path verbatim while a
// trimming read would drop the space and name a different directory, so the injection only fires
// when the expected path is read the installer's way. Windows cannot create such a directory.
it.skipIf(process.platform === 'win32')('handles an injected lock failure in a worktree whose path ends with a space', async () => {
await expectInjectedLockAccessFailure(createFixture({ main: 'main ' }), {
operation: 'openSync', code: 'EPERM', expires: false,
})
})
it('waits for a concurrent installer to finish publishing its lock record', async () => {
const fixture = createFixture()
const lockPath = installLockPath(fixture)