fix(subagent): close Codex provider CI gaps
This commit is contained in:
@@ -14,7 +14,7 @@
|
|||||||
config:
|
config:
|
||||||
env:
|
env:
|
||||||
OPENAI_API_KEY: !!js process.env.DSH_TEST_OPENAI_API_KEY
|
OPENAI_API_KEY: !!js process.env.DSH_TEST_OPENAI_API_KEY
|
||||||
CODEX_HOME: !!js process.cwd() + '/codex-home'
|
CODEX_HOME: !!js process.env.DSH_TEST_CODEX_HOME
|
||||||
HOME: !!js process.cwd()
|
HOME: !!js process.cwd()
|
||||||
XDG_CONFIG_HOME: !!js process.cwd() + '/xdg'
|
XDG_CONFIG_HOME: !!js process.cwd() + '/xdg'
|
||||||
PATH: !!js process.env.PATH
|
PATH: !!js process.env.PATH
|
||||||
|
|||||||
@@ -5,9 +5,10 @@
|
|||||||
* Code scenario and reruns both from its final stacked candidate.
|
* Code scenario and reruns both from its final stacked candidate.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { homedir } from 'node:os'
|
||||||
import { dirname, delimiter, join } from 'node:path'
|
import { dirname, delimiter, join } from 'node:path'
|
||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises'
|
import { mkdir, mkdtemp, readFile, readdir, rm, writeFile } from 'node:fs/promises'
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import {
|
import {
|
||||||
normalizeSessionLog,
|
normalizeSessionLog,
|
||||||
@@ -25,7 +26,7 @@ const testsDir = dirname(fileURLToPath(import.meta.url))
|
|||||||
const repoRoot = fileURLToPath(new URL('../../..', import.meta.url))
|
const repoRoot = fileURLToPath(new URL('../../..', import.meta.url))
|
||||||
const fixtureDir = join(testsDir, 'fixtures/subagent/subagent-codex')
|
const fixtureDir = join(testsDir, 'fixtures/subagent/subagent-codex')
|
||||||
const configPath = join(fixtureDir, 'cordis.yml')
|
const configPath = join(fixtureDir, 'cordis.yml')
|
||||||
const snapshotDir = join(testsDir, 'snapshots/subagent-codex')
|
const snapshotDir = join(testsDir, 'product-provider-snapshots/codex')
|
||||||
const sessionExpected = join(snapshotDir, 'session.expected.jsonl')
|
const sessionExpected = join(snapshotDir, 'session.expected.jsonl')
|
||||||
const evidenceExpected = join(snapshotDir, 'evidence.expected.json')
|
const evidenceExpected = join(snapshotDir, 'evidence.expected.json')
|
||||||
const cliBin = join(repoRoot, 'packages/examples/cli-demo/src/bin.ts')
|
const cliBin = join(repoRoot, 'packages/examples/cli-demo/src/bin.ts')
|
||||||
@@ -75,6 +76,7 @@ function responseInputTexts(body: Record<string, unknown>): string[] {
|
|||||||
|
|
||||||
describe('real product subagent providers through the Loader', () => {
|
describe('real product subagent providers through the Loader', () => {
|
||||||
it('pins the Codex tool, result, persisted Session, and process quiescence', async () => {
|
it('pins the Codex tool, result, persisted Session, and process quiescence', async () => {
|
||||||
|
const codexHome = await mkdtemp(join(homedir(), '.dsh-subagent-codex-loader-'))
|
||||||
const responses = await startResponsesFixture([
|
const responses = await startResponsesFixture([
|
||||||
{ kind: 'complete', text: CODEX_SENTINEL },
|
{ kind: 'complete', text: CODEX_SENTINEL },
|
||||||
])
|
])
|
||||||
@@ -96,12 +98,11 @@ describe('real product subagent providers through the Loader', () => {
|
|||||||
tsconfigPath: repoTsconfig,
|
tsconfigPath: repoTsconfig,
|
||||||
processTimeoutMs: 45_000,
|
processTimeoutMs: 45_000,
|
||||||
env: {
|
env: {
|
||||||
|
DSH_TEST_CODEX_HOME: codexHome,
|
||||||
DSH_TEST_OPENAI_API_KEY: FAKE_KEY,
|
DSH_TEST_OPENAI_API_KEY: FAKE_KEY,
|
||||||
PATH: `${codexBinDir}${delimiter}${process.env.PATH ?? ''}`,
|
PATH: `${codexBinDir}${delimiter}${process.env.PATH ?? ''}`,
|
||||||
},
|
},
|
||||||
async prepare(cwd): Promise<void> {
|
async prepare(): Promise<void> {
|
||||||
const codexHome = join(cwd, 'codex-home')
|
|
||||||
await mkdir(codexHome)
|
|
||||||
await writeFile(join(codexHome, 'config.toml'), [
|
await writeFile(join(codexHome, 'config.toml'), [
|
||||||
'model = "fixture-model"',
|
'model = "fixture-model"',
|
||||||
'model_provider = "fixture"',
|
'model_provider = "fixture"',
|
||||||
@@ -161,7 +162,10 @@ describe('real product subagent providers through the Loader', () => {
|
|||||||
expect(normalizedSession).toBe(await readFile(sessionExpected, 'utf8'))
|
expect(normalizedSession).toBe(await readFile(sessionExpected, 'utf8'))
|
||||||
expect(evidence).toBe(await readFile(evidenceExpected, 'utf8'))
|
expect(evidence).toBe(await readFile(evidenceExpected, 'utf8'))
|
||||||
} finally {
|
} finally {
|
||||||
await responses.close()
|
await Promise.all([
|
||||||
|
responses.close(),
|
||||||
|
rm(codexHome, { recursive: true, force: true }),
|
||||||
|
])
|
||||||
}
|
}
|
||||||
}, LOADER_SMOKE_TEST_TIMEOUT_MS + 30_000)
|
}, LOADER_SMOKE_TEST_TIMEOUT_MS + 30_000)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -65,16 +65,6 @@ export function textTask(prompt: readonly ContentBlock[]): string[] {
|
|||||||
return texts
|
return texts
|
||||||
}
|
}
|
||||||
|
|
||||||
async function treeExitsWithin(child: SubprocessHandle, ms: number): Promise<boolean> {
|
|
||||||
const controller = new AbortController()
|
|
||||||
const timer = setTimeout(() => { controller.abort() }, ms)
|
|
||||||
try {
|
|
||||||
return await child.waitForExit(controller.signal)
|
|
||||||
} finally {
|
|
||||||
clearTimeout(timer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the private wire, terminate the managed process tree, and wait for the
|
* Close the private wire, terminate the managed process tree, and wait for the
|
||||||
* subprocess owner to prove it is gone.
|
* subprocess owner to prove it is gone.
|
||||||
@@ -98,7 +88,7 @@ export async function disposeCodexChild(
|
|||||||
// A concurrently closed stdin does not change tree ownership below.
|
// A concurrently closed stdin does not change tree ownership below.
|
||||||
}
|
}
|
||||||
child.terminate()
|
child.terminate()
|
||||||
if (!(await treeExitsWithin(child, graceMs * 2))) {
|
if (!(await child.waitForExit(AbortSignal.timeout(graceMs * 2)))) {
|
||||||
throw new Error('subagent-codex: app-server process tree did not exit within its dispose window')
|
throw new Error('subagent-codex: app-server process tree did not exit within its dispose window')
|
||||||
}
|
}
|
||||||
await child.done
|
await child.done
|
||||||
|
|||||||
Reference in New Issue
Block a user