fix(acp-snapshot): await delayed Windows exit markers
A successful Windows termination request can end the process before Node publishes exitCode or signalCode. If a child error wins the shutdown race, give that accepted exit a bounded observation window before escalating or reporting fallback refusal. Cover a delayed real exit edge, preserve prompt refusal behavior for a genuinely live child, and document the launcher grace without weakening the complete stdio and parser drain boundary.
This commit is contained in:
@@ -202,6 +202,28 @@ describe('runScenario', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('preserves the child error when the requested signal publishes its exit marker later', async () => {
|
||||
const { dir } = await scenario({})
|
||||
const launched = launchAcpTestAgent({ agent: AGENT, cwd: dir })
|
||||
await launched.spawned
|
||||
|
||||
const childFailure = Object.assign(new Error('signal failed before the delayed exit marker'), { code: 'EPERM' })
|
||||
const originalKill = launched.child.kill.bind(launched.child)
|
||||
const kill = vi.spyOn(launched.child, 'kill').mockImplementation((signal) => {
|
||||
expect(signal).toBe('SIGTERM')
|
||||
setTimeout(() => { originalKill('SIGKILL') }, 10)
|
||||
return true
|
||||
})
|
||||
try {
|
||||
launched.child.emit('error', childFailure)
|
||||
await expect(launched.close('SIGTERM')).rejects.toBe(childFailure)
|
||||
expect(kill).toHaveBeenCalledOnce()
|
||||
} finally {
|
||||
kill.mockRestore()
|
||||
if (launched.child.exitCode === null && launched.child.signalCode === null) originalKill('SIGKILL')
|
||||
}
|
||||
})
|
||||
|
||||
it('preserves the child error when fallback refusal races with an exit marker', async () => {
|
||||
const { dir } = await scenario({})
|
||||
const launched = launchAcpTestAgent({ agent: AGENT, cwd: dir })
|
||||
|
||||
Reference in New Issue
Block a user