mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 09:07:40 +00:00
Extract subprocess execution and process contracts into @orca/process-host with explicit public entry points, independent checks, and one Node 18 compatible CommonJS runtime. Migrate application consumers and integrate package builds, dependency selection, import enforcement, and runtime resource staging. Preserve daemon startup ownership and profile isolation, and qualify development rebuild recovery and concurrent output publication. Remove unnecessary aliases, test forwarding exports, and a duplicate output scan. Validation: full typechecks, changed-code quality, package and artifact checks, native Windows builds/types plus 306 runtime tests, and independent native reviews. Matched full E2E controls on original main and unchanged reviewed head both pass all 17 applicable tests (nine skips). Earlier intermittent E2E failures are documented in the PR; no assertions were weakened. The final combined-tree audit preserves both branches with no import-boundary violations.
123 lines
4.0 KiB
TypeScript
123 lines
4.0 KiB
TypeScript
import { ChildProcess } from 'node:child_process'
|
|
import { once } from 'node:events'
|
|
import { spawnProcess } from '@orca/process-host'
|
|
import type * as NodeChildProcess from 'node:child_process'
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
const { spawnMock, admitMock } = vi.hoisted(() => ({
|
|
spawnMock: vi.fn(),
|
|
admitMock: vi.fn(() => true)
|
|
}))
|
|
|
|
vi.mock('node:child_process', async (importOriginal) => ({
|
|
...(await importOriginal<typeof NodeChildProcess>()),
|
|
spawn: spawnMock
|
|
}))
|
|
vi.mock('../../own-chromium-tree-kill-guard', () => ({
|
|
admitSelfInitiatedTreeKill: admitMock
|
|
}))
|
|
|
|
import { killSpawnedCommandTree } from './spawned-command-tree-kill'
|
|
|
|
const originalPlatform = process.platform
|
|
|
|
function childWithPid(pid: number): ChildProcess {
|
|
const child = new ChildProcess()
|
|
Object.defineProperty(child, 'pid', { value: pid })
|
|
vi.spyOn(child, 'kill').mockReturnValue(true)
|
|
vi.spyOn(child, 'unref').mockImplementation(() => {})
|
|
return child
|
|
}
|
|
|
|
describe('Git command tree termination', () => {
|
|
beforeEach(() => {
|
|
Object.defineProperty(process, 'platform', { value: 'win32', configurable: true })
|
|
spawnMock.mockReset()
|
|
admitMock.mockReset().mockReturnValue(true)
|
|
})
|
|
|
|
afterEach(() => {
|
|
Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true })
|
|
vi.restoreAllMocks()
|
|
})
|
|
|
|
it.each([0, 128])(
|
|
'never taskkills a child that exited with code %i before close',
|
|
async (code) => {
|
|
const child = childWithPid(1234)
|
|
Object.defineProperty(child, 'exitCode', { value: code })
|
|
|
|
await killSpawnedCommandTree(child)
|
|
|
|
expect(spawnMock).not.toHaveBeenCalled()
|
|
expect(admitMock).not.toHaveBeenCalled()
|
|
expect(child.kill).toHaveBeenCalledOnce()
|
|
}
|
|
)
|
|
|
|
it('never taskkills a child that exited by signal before close', async () => {
|
|
const child = childWithPid(1234)
|
|
Object.defineProperty(child, 'signalCode', { value: 'SIGTERM' })
|
|
|
|
await killSpawnedCommandTree(child)
|
|
|
|
expect(spawnMock).not.toHaveBeenCalled()
|
|
expect(admitMock).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('still waits for tree termination when the Windows root has not exited', async () => {
|
|
const child = childWithPid(1234)
|
|
const killer = childWithPid(5678)
|
|
spawnMock.mockReturnValue(killer)
|
|
let settled = false
|
|
const pending = killSpawnedCommandTree(child).then(() => {
|
|
settled = true
|
|
})
|
|
|
|
await Promise.resolve()
|
|
expect(settled).toBe(false)
|
|
expect(spawnMock).toHaveBeenCalledWith('taskkill', ['/pid', '1234', '/t', '/f'], {
|
|
stdio: 'ignore',
|
|
windowsHide: true
|
|
})
|
|
killer.emit('close', 0)
|
|
await pending
|
|
expect(child.kill).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('preserves handle termination on POSIX', async () => {
|
|
Object.defineProperty(process, 'platform', { value: 'linux', configurable: true })
|
|
const child = childWithPid(1234)
|
|
|
|
await killSpawnedCommandTree(child)
|
|
|
|
expect(child.kill).toHaveBeenCalledOnce()
|
|
expect(spawnMock).not.toHaveBeenCalled()
|
|
})
|
|
it.skipIf(originalPlatform !== 'win32').each([0, 128])(
|
|
'does not taskkill an actual native Windows child after exit %i',
|
|
async (exitCode) => {
|
|
const original = await vi.importActual<typeof NodeChildProcess>('node:child_process')
|
|
spawnMock.mockImplementation((program, args, options) => {
|
|
if (program !== process.execPath) {
|
|
throw new Error('Unexpected external process in native exit probe')
|
|
}
|
|
return original.spawn(program, args, options)
|
|
})
|
|
const child = spawnProcess({
|
|
program: process.execPath,
|
|
args: ['-e', `process.exit(${exitCode})`]
|
|
})
|
|
const closed = once(child, 'close')
|
|
await once(child, 'exit')
|
|
expect(child.exitCode).toBe(exitCode)
|
|
expect(child.pid).toBeGreaterThan(0)
|
|
spawnMock.mockClear()
|
|
await killSpawnedCommandTree(child)
|
|
expect(spawnMock).not.toHaveBeenCalled()
|
|
expect(admitMock).not.toHaveBeenCalled()
|
|
await closed
|
|
}
|
|
)
|
|
})
|