test(codex): type the dispatch fixtures instead of asserting over them

main's new casting gate (#20367 base) flags type assertions on changed
lines. Replace them with checked types: the recording sink already
satisfies its interface, both CodexSession fixtures are now annotated and
carry real collaborators, the settlement assertion compares whole
identities, and the integration helper reads submissions through the
host's public journalSnapshot instead of its private session map.
This commit is contained in:
Merge Sim
2026-09-13 13:39:20 -07:00
parent 69f14aed95
commit e980959aef
5 changed files with 49 additions and 24 deletions
@@ -4,6 +4,7 @@ import type { AgentJournalItemBody } from '../../shared/agent-session-journal-ty
import type { StructuredAgentSessionEventSink } from '../native-chat/agent-session-wire/structured-agent-session-event-sink'
import { CodexBackgroundTaskTracker } from './codex-background-task-tracker'
import { createCodexJournalTranslator } from './codex-structured-journal-translation'
import { CodexPromptRegistry } from './codex-structured-prompt-replies'
import { closeCodexPublishedSession } from './codex-structured-session-close'
import type { CodexSession } from './codex-structured-session-state'
@@ -49,18 +50,29 @@ describe('requested-close durable turn timing', () => {
observedAt: 1_000
})
).toEqual({ accepted: true })
const session = {
connection: { close: vi.fn(async () => true) },
const session: CodexSession = {
connection: {
pid: 4321,
closed: false,
request: async () => ({}),
notify: () => {},
respond: () => {},
respondWithError: () => {},
close: async () => true
},
backgroundTasks: new CodexBackgroundTaskTracker('thread-1'),
ended: false,
requestedClose: false,
fence: 7,
acquisitionGeneration: 'generation-1',
threadId: 'thread-1',
prompts: { clear: vi.fn() },
historyPath: null,
prompts: new CodexPromptRegistry(),
options: new Map(),
reportedOptions: {},
dispatchEchoes: createCodexDispatchEchoes(),
translator
} as unknown as CodexSession
}
const sessions = new Map([['session-1', session]])
const onEvent = vi.fn()
@@ -74,14 +74,29 @@ describe('codex dispatch admission', () => {
echoUserMessage(connection, { turnId: 'turn-1', itemId: 'item-u2', clientId: 'client-2' })
echoUserMessage(connection, { turnId: 'turn-1', itemId: 'item-u1', clientId: 'client-1' })
expect(
settlements.map((settlement) => [
settlement.clientMessageId,
(settlement.providerIdentity as { ordinal: number }).ordinal
])
).toEqual([
['client-2', 0],
['client-1', 1]
// Ordinals follow the ECHO order, and each one lands on the send whose
// `clientId` it carried -- not on the send that was queued in that slot.
expect(settlements).toEqual([
{
sessionId: 'session-1',
clientMessageId: 'client-2',
providerIdentity: {
provider: 'codex',
threadId: CODEX_TEST_THREAD_ID,
turnId: 'turn-1',
ordinal: 0
}
},
{
sessionId: 'session-1',
clientMessageId: 'client-1',
providerIdentity: {
provider: 'codex',
threadId: CODEX_TEST_THREAD_ID,
turnId: 'turn-1',
ordinal: 1
}
}
])
})
@@ -77,7 +77,7 @@ export function recordingSink(): StructuredAgentSessionEventSink {
appendItem: () => {},
appendTombstone: () => {},
publish: () => {}
} as unknown as StructuredAgentSessionEventSink
}
}
export async function acquiredCodexAdapter(input: {
@@ -12,6 +12,7 @@ import {
} from './codex-structured-session-adapter'
import { handleCodexSessionExit } from './codex-structured-session-close'
import { CodexBackgroundTaskTracker } from './codex-background-task-tracker'
import { CodexPromptRegistry } from './codex-structured-prompt-replies'
import type { CodexSession } from './codex-structured-session-state'
import type { StructuredAgentSessionAdapter } from '../native-chat/agent-session-wire/structured-agent-session-adapter'
import { StructuredAgentSessionAdapterRouter } from '../native-chat/agent-session-wire/structured-agent-session-adapter-router'
@@ -85,12 +86,13 @@ describe('Codex structured session close lifecycle', () => {
respondWithError: () => {},
close: async () => true
}
const prompts = { clear: vi.fn() } as unknown as CodexSession['prompts']
const prompts = new CodexPromptRegistry()
const clearPrompts = vi.spyOn(prompts, 'clear')
const translator = {
handle: vi.fn().mockReturnValueOnce({ accepted: false, reason: 'backpressure' as const }),
dispose: vi.fn()
} as unknown as NonNullable<CodexSession['translator']>
const session = {
const session: CodexSession = {
connection,
backgroundTasks: new CodexBackgroundTaskTracker('thread-1'),
ended: false,
@@ -104,7 +106,7 @@ describe('Codex structured session close lifecycle', () => {
reportedOptions: {},
dispatchEchoes: createCodexDispatchEchoes(),
translator
} as CodexSession
}
const sessions = new Map([['session-1', session]])
const onEvent = vi.fn()
@@ -119,7 +121,7 @@ describe('Codex structured session close lifecycle', () => {
})
).toBe(true)
expect(session.ended).toBe(true)
expect(prompts.clear).toHaveBeenCalledOnce()
expect(clearPrompts).toHaveBeenCalledOnce()
expect(onEvent).toHaveBeenCalledOnce()
expect(translator.dispose).toHaveBeenCalledOnce()
expect(onEvent.mock.calls[0]?.[0]).toMatchObject({
@@ -283,13 +283,9 @@ function textOf(item: AgentJournalRenderItem): string {
/** The durable submission row, which settlement rewrites after the send returns. */
function submissionOf(clientMessageId: string): AgentJournalSubmission | undefined {
const host = getStructuredAgentSessionHost() as unknown as {
sessions: Map<string, { journal: AgentSessionJournal }>
} | null
return host?.sessions
.get(SESSION)
?.journal.submissions()
.find((entry) => entry.clientMessageId === clientMessageId)
return getStructuredAgentSessionHost()
?.journalSnapshot(SESSION)
.submissions.find((entry) => entry.clientMessageId === clientMessageId)
}
async function historyPage(