test(daemon): stop skipping killStaleDaemon checks on Windows (#13154)

Three guards in daemon-health.test.ts skipped tests that assert PID-record and
start-time logic. The file's daemonTestSocketPath already returns a real named
pipe on win32, so none of them needed a Unix socket — the skips cost coverage
for nothing.

Verified on a Windows host: the file goes from 29 passed / 4 skipped to
32 passed / 1 skipped, restoring Windows coverage of killStaleDaemon's
ownership decisions, which #12882 changed.

The fourth guard stays: that test spawns a real child and binds a filesystem
socket path, which fails with listen EACCES on Windows.
This commit is contained in:
Neil 2026-08-08 01:01:30 -07:00 committed by GitHub
parent c991bb27d3
commit f858c5a13a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 44 additions and 50 deletions

View File

@ -170,29 +170,26 @@ describe('daemon health', () => {
}
})
it.skipIf(process.platform === 'win32')(
'does not unlink a live socket when the pid file does not match this daemon',
async () => {
const server = createServer((socket) => socket.end())
await new Promise<void>((resolve, reject) => {
server.once('error', reject)
server.listen(socketPath, () => {
server.off('error', reject)
resolve()
})
it('does not unlink a live socket when the pid file does not match this daemon', async () => {
const server = createServer((socket) => socket.end())
await new Promise<void>((resolve, reject) => {
server.once('error', reject)
server.listen(socketPath, () => {
server.off('error', reject)
resolve()
})
writeFileSync(getDaemonPidPath(dir), String(process.pid), { mode: 0o600 })
})
writeFileSync(getDaemonPidPath(dir), String(process.pid), { mode: 0o600 })
try {
await expect(killStaleDaemon(dir, socketPath, tokenPath)).resolves.toMatchObject({
killed: false
})
await expect(canConnect(socketPath)).resolves.toBe(true)
} finally {
await closeServer(server)
}
try {
await expect(killStaleDaemon(dir, socketPath, tokenPath)).resolves.toMatchObject({
killed: false
})
await expect(canConnect(socketPath)).resolves.toBe(true)
} finally {
await closeServer(server)
}
)
})
})
describe('parseDaemonPidFile', () => {
@ -354,7 +351,7 @@ describe('startTimeMatches', () => {
expect(startTimeMatches(process.pid, actual + 500)).toBe(true)
})
it.skipIf(process.platform === 'win32')('returns false for start times outside tolerance', () => {
it('returns false for start times outside tolerance', () => {
const actual = getProcessStartedAtMs(process.pid)
if (actual === null) {
return
@ -415,37 +412,34 @@ describe('killStaleDaemon pid identity guards', () => {
rmSync(dir, { recursive: true, force: true })
})
it.skipIf(process.platform === 'win32')(
'does not SIGTERM when the saved startedAtMs mismatches the current process',
async () => {
// Why: seed a pid file that claims the daemon is `process.pid` (us) but
// was started 1 hour ago. Our real start time is "now," so startTimeMatches
// returns false and isDaemonProcess rejects. killStaleDaemon must not call
// process.kill in that case.
const bogusStartedAtMs = Date.now() - 60 * 60 * 1000
writeFileSync(
getDaemonPidPath(dir),
serializeDaemonPidFile({ pid: process.pid, startedAtMs: bogusStartedAtMs }),
{ mode: 0o600 }
)
it('does not SIGTERM when the saved startedAtMs mismatches the current process', async () => {
// Why: seed a pid file that claims the daemon is `process.pid` (us) but
// was started 1 hour ago. Our real start time is "now," so startTimeMatches
// returns false and isDaemonProcess rejects. killStaleDaemon must not call
// process.kill in that case.
const bogusStartedAtMs = Date.now() - 60 * 60 * 1000
writeFileSync(
getDaemonPidPath(dir),
serializeDaemonPidFile({ pid: process.pid, startedAtMs: bogusStartedAtMs }),
{ mode: 0o600 }
)
// isDaemonProcess uses process.kill(pid, 0) as a liveness probe; that's
// expected and not a real kill. We only care that no actual termination
// signal is sent.
const killSpy = vi.spyOn(process, 'kill').mockImplementation(() => true)
try {
await expect(killStaleDaemon(dir, socketPath, tokenPath)).resolves.toMatchObject({
killed: false
})
const terminationSignals = killSpy.mock.calls.filter(
([, sig]) => sig === 'SIGTERM' || sig === 'SIGKILL'
)
expect(terminationSignals).toEqual([])
} finally {
killSpy.mockRestore()
}
// isDaemonProcess uses process.kill(pid, 0) as a liveness probe; that's
// expected and not a real kill. We only care that no actual termination
// signal is sent.
const killSpy = vi.spyOn(process, 'kill').mockImplementation(() => true)
try {
await expect(killStaleDaemon(dir, socketPath, tokenPath)).resolves.toMatchObject({
killed: false
})
const terminationSignals = killSpy.mock.calls.filter(
([, sig]) => sig === 'SIGTERM' || sig === 'SIGKILL'
)
expect(terminationSignals).toEqual([])
} finally {
killSpy.mockRestore()
}
)
})
})
describe('killStaleDaemon ownership decisions', () => {