diff --git a/config/scripts/vitest-node-runtime-files.mjs b/config/scripts/vitest-node-runtime-files.mjs index 18d93fefe10..69e473c91d2 100644 --- a/config/scripts/vitest-node-runtime-files.mjs +++ b/config/scripts/vitest-node-runtime-files.mjs @@ -74,6 +74,8 @@ export const NODE_RUNTIME_INCLUDE = [ 'src/shared/remote-runtime-connect-bound.test.ts', 'src/shared/remote-runtime-subscription-connect-bound.test.ts', 'src/main/codex/codex-shared-server-probe.test.ts', + 'src/main/opencode/opencode-launch-model-context-supervision.test.ts', + 'src/main/hermes/hermes-config-remote-adapter.test.ts', 'src/relay/relay-reconnect-listener-peer-write-error.test.ts', 'src/relay/relay-primary-channel.test.ts', 'src/renderer/src/lib/monaco-languages/monarch-upstream-mdx-recursion.test.ts', @@ -81,6 +83,7 @@ export const NODE_RUNTIME_INCLUDE = [ 'src/main/runtime/structured-agent-session-startup-tab-restore.test.ts', 'src/shared/remote-runtime-shared-control-connection.test.ts', 'src/main/runtime/relay/relay-control-client.test.ts', + 'src/main/ipc/runtime-environment-preference.test.ts', 'src/main/ipc/runtime-environment-transport-routing-tailscale-hint.test.ts', 'src/renderer/src/components/emulator-pane/emulator-device-frame-visibility.test.tsx', 'src/main/claude/claude-agent-sdk-contract-pins.test.ts', diff --git a/src/main/codex/codex-app-server-connection.test.ts b/src/main/codex/codex-app-server-connection.test.ts index 8e0920a2ba7..68c2726a4f5 100644 --- a/src/main/codex/codex-app-server-connection.test.ts +++ b/src/main/codex/codex-app-server-connection.test.ts @@ -14,6 +14,7 @@ import { } from './codex-app-server-connection' import { PROVIDER_SUPERVISOR_MAX_STOP_MS } from '../provider-process/provider-process-supervisor' import { isCodexAppServerUnsupportedError } from './codex-app-server-session' +import { isCodexAppServerHandshakeExitUnprovenError } from './codex-app-server-handshake-exit-proof' // close() waits out the supervisor's own stop before forcing the tree. const GRACEFUL_EXIT_MS = process.platform === 'win32' ? 1_500 : PROVIDER_SUPERVISOR_MAX_STOP_MS @@ -126,18 +127,11 @@ async function flushStreams(): Promise { await new Promise((resolve) => setImmediate(resolve)) } -async function closeWithoutObservedExit( - connection: CodexAppServerConnection, - child: StubChild -): Promise { +async function closeWithoutObservedExit(start: () => Promise, child: StubChild): Promise { const forcedKill = new Promise((resolve) => { - child.kill.mockImplementation((signal) => { - if (signal === 'SIGKILL') { - resolve() - } - }) + child.kill.mockImplementation((signal) => (signal === 'SIGKILL' ? resolve() : undefined)) }) - const closing = connection.close() + const closing = start() await flushStreams() await vi.advanceTimersByTimeAsync(GRACEFUL_EXIT_MS) await forcedKill @@ -461,25 +455,28 @@ describe('openCodexAppServerConnection', () => { }) it('exposes an unproven handshake child for later cleanup', async () => { - vi.useFakeTimers() + vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout'] }) const { child, spawnImpl } = stubChild({ exitOnStdinEnd: false }) child.stdin.once('data', () => { child.stdout.write( `${JSON.stringify({ id: 1, error: { code: -32602, message: 'initialize failed' } })}\n` ) }) - const opening = rejection( - openCodexAppServerConnection({ command: 'codex', args: ['app-server'] }, {}, spawnImpl) + const error = await closeWithoutObservedExit( + () => + rejection( + openCodexAppServerConnection({ command: 'codex', args: ['app-server'] }, {}, spawnImpl) + ), + child ) - - await vi.advanceTimersByTimeAsync(GRACEFUL_EXIT_MS + 3_500) - const error = (await opening) as Error & { connection?: CodexAppServerConnection } - expect(error.name).toBe('CodexAppServerHandshakeExitUnprovenError') + if (!isCodexAppServerHandshakeExitUnprovenError(error)) { + throw error + } expect(error.connection).toBeDefined() child.emit('exit', 1, null) child.emit('close', 1, null) - await expect(error.connection?.close()).resolves.toBe(true) + await expect(error.connection.close()).resolves.toBe(true) }) it('times out one request without ending the connection', async () => { @@ -531,7 +528,7 @@ describe('openCodexAppServerConnection', () => { spawnImpl ) - await expect(closeWithoutObservedExit(connection, child)).resolves.toBe(false) + await expect(closeWithoutObservedExit(() => connection.close(), child)).resolves.toBe(false) expect(vi.getTimerCount()).toBe(0) }, 10_000) @@ -567,7 +564,7 @@ describe('openCodexAppServerConnection', () => { spawnImpl ) - await expect(closeWithoutObservedExit(connection, child)).resolves.toBe(false) + await expect(closeWithoutObservedExit(() => connection.close(), child)).resolves.toBe(false) child.emit('exit', 0, null) await expect(connection.close()).resolves.toBe(true) diff --git a/src/main/ipc/folder-repo-git-upgrade.test.ts b/src/main/ipc/folder-repo-git-upgrade.test.ts index 836ff0e7a65..5fe485ee3a2 100644 --- a/src/main/ipc/folder-repo-git-upgrade.test.ts +++ b/src/main/ipc/folder-repo-git-upgrade.test.ts @@ -206,12 +206,16 @@ describe('folder repo git upgrade watch', () => { pollIntervalMs: POLL_MS, idlePollIntervalMs: IDLE_POLL_MS }) - await tick() - - expect(store.updateRepo).toHaveBeenCalledWith('folder-repo', { - kind: 'git', - folderUpgradeGitRootPath: join(root, 'symlinked-project').replaceAll('\\', '/') - }) + // A fixed tick wait can finish before the marker stat resolves under load. + await vi.waitFor( + () => { + expect(store.updateRepo).toHaveBeenCalledWith('folder-repo', { + kind: 'git', + folderUpgradeGitRootPath: join(root, 'symlinked-project').replaceAll('\\', '/') + }) + }, + { timeout: 5_000, interval: POLL_MS } + ) }) it('retries after the last extra folder workspace is removed', async () => {