Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions config/scripts/vitest-node-runtime-files.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,16 @@ 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',
'src/main/ipc/orca-profiles-switch-persistence.test.ts',
'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',
Expand Down
37 changes: 17 additions & 20 deletions src/main/codex/codex-app-server-connection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -126,18 +127,11 @@ async function flushStreams(): Promise<void> {
await new Promise((resolve) => setImmediate(resolve))
}

async function closeWithoutObservedExit(
connection: CodexAppServerConnection,
child: StubChild
): Promise<boolean> {
async function closeWithoutObservedExit<T>(start: () => Promise<T>, child: StubChild): Promise<T> {
const forcedKill = new Promise<void>((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
Expand Down Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand Down
16 changes: 10 additions & 6 deletions src/main/ipc/folder-repo-git-upgrade.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down
Loading