From 2eb0bb637dff41925d8ffa22ff9054ab656e4816 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 23 Oct 2026 21:53:27 -0700 Subject: [PATCH] test: strengthen hook, release, and mobile transport coverage (#33031) * test: exercise hook and decorative title limits * test: reject non-string launch preferences * test: preserve live builds on release rejection * test: verify restored child and expiry session state * test: strengthen mobile transport lifecycle coverage --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../release-javascript-artifact.test.mjs | 23 ++++--- ...lease-mac-build-workflow-dispatch.test.mjs | 17 ++--- mobile/src/transport/client-context.test.ts | 17 ++++- .../src/transport/client-context.web.test.tsx | 67 +++++++++++++++---- .../transport/execution-host-envelope.test.ts | 57 +++++++++++----- .../agent-decorative-title-signature.test.ts | 27 +++----- ...ent-hook-listener-claude-subagents.test.ts | 8 +-- ...-listener-claude-task-notification.test.ts | 9 ++- ...nt-hook-listener-claude-turn-state.test.ts | 3 +- src/shared/agent-kind.test.ts | 4 -- src/shared/agent-launch-preferences.test.ts | 4 +- 11 files changed, 156 insertions(+), 80 deletions(-) diff --git a/config/scripts/release-javascript-artifact.test.mjs b/config/scripts/release-javascript-artifact.test.mjs index ad81b23dca6..f33b179848d 100644 --- a/config/scripts/release-javascript-artifact.test.mjs +++ b/config/scripts/release-javascript-artifact.test.mjs @@ -1,3 +1,4 @@ +import { createHash } from 'node:crypto' import { mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { dirname, join } from 'node:path' @@ -97,7 +98,9 @@ describe('release JavaScript configuration', () => { const { root, env } = configurationFixture(version) const result = releaseJavascriptConfiguration(root, { ...env, ORCA_BUILD_IDENTITY: identity }) expect(result.identity).toBe(identity) - expect(result.telemetryKeySha256).toMatch(/^[a-f0-9]{64}$/) + expect(result.telemetryKeySha256).toBe( + createHash('sha256').update(env.ORCA_POSTHOG_WRITE_KEY).digest('hex') + ) expect(result.sourceSha).toMatch(/^[a-f0-9]{40}$/) }) @@ -200,15 +203,16 @@ describe('release JavaScript transfer', () => { ])('rejects a different %s before replacing the current build', (field) => { const options = fixture() packReleaseJavascript(options) + writeFileSync(join(options.root, 'out', 'main', 'index.js'), 'current build') + writeFileSync(join(options.root, 'out', 'current-only.js'), 'current only') + const current = javascriptInventory(join(options.root, 'out')) expect(() => restoreReleaseJavascript({ ...options, configuration: { ...configuration, [field]: 'different' } }) ).toThrow('JavaScript build configuration differs') - expect(readFileSync(join(options.root, 'out', 'main', 'index.js'), 'utf8')).toBe( - 'main/index.js' - ) + expect(javascriptInventory(join(options.root, 'out'))).toEqual(current) }) it('rejects a damaged archive before replacing the current build', () => { @@ -224,9 +228,13 @@ describe('release JavaScript transfer', () => { it('rejects a file inventory that no longer describes the archive', () => { const options = fixture() const manifest = packReleaseJavascript(options) + writeFileSync(join(options.root, 'out', 'main', 'index.js'), 'current build') + writeFileSync(join(options.root, 'out', 'current-only.js'), 'current only') + const current = javascriptInventory(join(options.root, 'out')) manifest.files[0].sha256 = 'c'.repeat(64) writeFileSync(join(options.artifactDir, 'manifest.json'), JSON.stringify(manifest)) expect(() => restoreReleaseJavascript(options)).toThrow('JavaScript file inventory differs') + expect(javascriptInventory(join(options.root, 'out'))).toEqual(current) }) it.each([ @@ -248,10 +256,9 @@ describe('release JavaScript transfer', () => { 'rejects symlinks instead of following files outside the build', () => { const options = fixture() - symlinkSync( - join(options.root, 'out', 'main', 'index.js'), - join(options.root, 'out', 'linked.js') - ) + const outside = join(options.root, 'outside.js') + writeFileSync(outside, 'outside the build') + symlinkSync(outside, join(options.root, 'out', 'linked.js')) expect(() => packReleaseJavascript(options)).toThrow('contains a symlink') } ) diff --git a/config/scripts/release-mac-build-workflow-dispatch.test.mjs b/config/scripts/release-mac-build-workflow-dispatch.test.mjs index bed598a03a5..f6410b846be 100644 --- a/config/scripts/release-mac-build-workflow-dispatch.test.mjs +++ b/config/scripts/release-mac-build-workflow-dispatch.test.mjs @@ -1,7 +1,6 @@ import { describe, expect, it, vi } from 'vitest' import { - dispatchReleaseMacBuildWorkflow, executeReleaseMacBuildMode, expectedReleaseMacBuildRunTitle, readReleaseMacBuildWorkflowOptions, @@ -22,13 +21,15 @@ const baseOptions = { describe('release mac build workflow dispatch', () => { it('passes the shared JavaScript commit only when the parent produced a bundle', async () => { - const request = vi.fn(async () => ({})) - const api = { owner: 'stablyai', repo: 'orca', request } - await dispatchReleaseMacBuildWorkflow(api, { - ...baseOptions, - javascriptSourceSha: 'a'.repeat(40) - }) - expect(request.mock.calls[0][2].inputs).toEqual({ + const { fetch, requests } = createGitHubFetch([ + jsonResponse(200, { workflow_run_id: 123 }), + jsonResponse(200, { id: 123, status: 'completed', conclusion: 'success' }) + ]) + await runReleaseMacBuildWorkflow( + { ...baseOptions, javascriptSourceSha: 'a'.repeat(40) }, + { fetch, now: () => Date.parse('2026-06-30T12:00:00Z'), sleep: vi.fn() } + ) + expect(requests[0].body.inputs).toEqual({ release_run_id: '777', tag: 'v1.2.3-rc.4', javascript_source_sha: 'a'.repeat(40) diff --git a/mobile/src/transport/client-context.test.ts b/mobile/src/transport/client-context.test.ts index 2fd56fa7768..1147be9a501 100644 --- a/mobile/src/transport/client-context.test.ts +++ b/mobile/src/transport/client-context.test.ts @@ -1,5 +1,7 @@ -import { createElement, type ReactElement } from 'react' -import { act, create, type ReactTestRenderer } from 'react-test-renderer' +import { createElement } from 'react' +import type { ReactElement } from 'react' +import { act, create } from 'react-test-renderer' +import type { ReactTestRenderer } from 'react-test-renderer' import { beforeEach, describe, expect, it, vi } from 'vitest' import type { ConnectionState } from './types' import type { RpcClient } from './rpc-client' @@ -180,6 +182,7 @@ describe('useHostClient', () => { connectMock.mockReturnValueOnce(client1).mockReturnValueOnce(client2) loadHostsMock.mockResolvedValue([HOST, host2]) + const selections: { hostId: string; value: ReturnType }[] = [] let selectedHostId = HOST.id let selectedClient: RpcClient | null = null let selectedClientId: string | null = null @@ -187,6 +190,7 @@ describe('useHostClient', () => { let renderer: ReactTestRenderer | null = null function Probe(): null { const selected = useHostClient(selectedHostId) + selections.push({ hostId: selectedHostId, value: selected }) selectedClient = selected.client selectedClientId = selected.clientId selectedState = selected.state @@ -213,6 +217,15 @@ describe('useHostClient', () => { expect(selectedClient).toBe(client2) expect(selectedClientId).toBe(host2.deviceToken) expect(selectedState).toBe('disconnected') + for (const { value } of selections.filter(({ hostId }) => hostId === host2.id)) { + expect(value.state).toBe('disconnected') + if (value.client === null) { + expect(value.clientId).toBeNull() + } else { + expect(value.client).toBe(client2) + expect(value.clientId).toBe(host2.deviceToken) + } + } expect(connectMock).toHaveBeenCalledTimes(2) } finally { act(() => renderer?.unmount()) diff --git a/mobile/src/transport/client-context.web.test.tsx b/mobile/src/transport/client-context.web.test.tsx index 1efc5771dd9..ee63f885f3e 100644 --- a/mobile/src/transport/client-context.web.test.tsx +++ b/mobile/src/transport/client-context.web.test.tsx @@ -1,5 +1,6 @@ import type { ReactElement } from 'react' import { act, create } from 'react-test-renderer' +import type { ReactTestRenderer } from 'react-test-renderer' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { BRIDGE_PROTOCOL_VERSION } from '../mobile-web-shell/bridge/bridge-envelope' import { BRIDGE_PAGE_CLIENT_ID } from '../mobile-web-shell/bridge/bridge-page-client-identity' @@ -47,6 +48,15 @@ function Screen(): null { return null } +const trees: ReactTestRenderer[] = [] +const clients: BridgeRpcClient[] = [] + +function mount(client: BridgeRpcClient): ReactTestRenderer { + const tree = create(render(client)) + trees.push(tree) + return tree +} + function render(client: BridgeRpcClient): ReactElement { return ( @@ -73,12 +83,16 @@ function installChannel(): { deliver: (frame: unknown) => void } { } /** What the entry hands the provider: one client, already holding a session. */ -function createReadyClient(deliver: (frame: unknown) => void): BridgeRpcClient { +function createReadyClient( + deliver: (frame: unknown) => void, + init: unknown = INIT +): BridgeRpcClient { const client = createShellPageClient() if (client === null) { throw new Error('no channel installed') } - deliver(INIT) + clients.push(client) + deliver(init) return client } @@ -97,6 +111,15 @@ beforeEach(() => { }) afterEach(() => { + act(() => { + for (const tree of trees.splice(0)) { + tree.unmount() + } + }) + for (const client of clients.splice(0)) { + client.close() + } + vi.restoreAllMocks() vi.useRealTimers() Reflect.deleteProperty(globalThis, 'orcaBridge') }) @@ -106,7 +129,7 @@ describe('the page provider', () => { const channel = installChannel() const client = createReadyClient(channel.deliver) act(() => { - create(render(client)) + mount(client) }) expect(screen.mounts).toBe(1) @@ -124,7 +147,7 @@ describe('the page provider', () => { const channel = installChannel() const client = createReadyClient(channel.deliver) act(() => { - create(render(client)) + mount(client) }) const context = readContext() @@ -138,7 +161,7 @@ describe('the page provider', () => { const channel = installChannel() const client = createReadyClient(channel.deliver) act(() => { - create(render(client)) + mount(client) }) // What `terminal.subscribe` carries as `client.id` and what the send gate reads. Null was not a @@ -152,7 +175,7 @@ describe('the page provider', () => { const channel = installChannel() const client = createReadyClient(channel.deliver) act(() => { - create(render(client)) + mount(client) }) const first = readContext().getClientId('host-a') @@ -160,8 +183,14 @@ describe('the page provider', () => { // The composer's send journal refuses a retained operation whose caller fingerprint moved, and // it has no expiry, so that would make "send it again" a permanent refusal for that message. act(() => { - channel.deliver({ ...INIT, sessionId: 'session-b' }) - create(render(client)) + for (const tree of trees.splice(0)) { + tree.unmount() + } + }) + client.close() + const nextChannel = installChannel() + act(() => { + mount(createReadyClient(nextChannel.deliver, { ...INIT, sessionId: 'session-b' })) }) expect(readContext().getClientId('host-a')).toBe(first) @@ -172,7 +201,7 @@ describe('the page provider', () => { const channel = installChannel() const client = createReadyClient(channel.deliver) act(() => { - create(render(client)) + mount(client) }) const listener = vi.fn() readContext().subscribeHostState('host-a', listener) @@ -193,10 +222,10 @@ describe('the page provider', () => { const channel = installChannel() const client = createReadyClient(channel.deliver) act(() => { - create(render(client)) + mount(client) }) const listener = vi.fn() - readContext().subscribeAllHosts(listener) + const unsubscribe = readContext().subscribeAllHosts(listener) act(() => { channel.deliver({ @@ -207,13 +236,22 @@ describe('the page provider', () => { }) expect(listener).toHaveBeenCalledTimes(1) + unsubscribe() + act(() => { + channel.deliver({ + v: BRIDGE_PROTOCOL_VERSION, + type: 'state', + connection: { ...INIT.connection, state: 'disconnected' } + }) + }) + expect(listener).toHaveBeenCalledTimes(1) }) it('stops listening when the screen that asked goes away', () => { const channel = installChannel() const client = createReadyClient(channel.deliver) act(() => { - create(render(client)) + mount(client) }) const listener = vi.fn() const unsubscribe = readContext().subscribeHostState('host-a', listener) @@ -235,10 +273,11 @@ describe('the page provider', () => { const client = createReadyClient(channel.deliver) const close = vi.spyOn(client, 'close') act(() => { - create(render(client)) + mount(client) }) const context = readContext() + expect(context.acquire('host-a', {})).toBe(client) context.release('host-a', {}) context.releaseAndCloseIfUnused('host-a', {}) context.closeIfUnused('host-a') @@ -255,7 +294,7 @@ describe('the page provider', () => { // renders, and an inert one painted controls whose only effect on the page was nothing. const channel = installChannel() act(() => { - create(render(createReadyClient(channel.deliver))) + mount(createReadyClient(channel.deliver)) }) expect(readContext().forceReconnect).toBeNull() }) diff --git a/mobile/src/transport/execution-host-envelope.test.ts b/mobile/src/transport/execution-host-envelope.test.ts index 961b8eb3941..d213eadf4e2 100644 --- a/mobile/src/transport/execution-host-envelope.test.ts +++ b/mobile/src/transport/execution-host-envelope.test.ts @@ -35,10 +35,13 @@ describe('executionHost on the envelope', () => { streams.subscribe('terminal.subscribe', params, () => {}) await vi.waitFor(() => expect(sent).toHaveLength(2)) disposeServer() - expect(sent.at(-1)).toMatchObject({ + expect(sent.at(-1)).toEqual({ + id: 'relay-3', method: 'terminal.unsubscribe', + params: { subscriptionId: 't1:phone', client: { id: 'phone' }, requestId: 'relay-1' }, envelope: { executionHost: SERVER } }) + streams.clear() }) }) @@ -59,13 +62,21 @@ describe('a relay stream slot', () => { envelope: { executionHost: SERVER, trace: 't' } }) await vi.waitFor(() => expect(sent).toHaveLength(1)) - streams.subscribe('terminal.subscribe', params, () => {}, { + const disposeNewer = streams.subscribe('terminal.subscribe', params, () => {}, { envelope: { trace: 't', executionHost: SERVER } }) await vi.waitFor(() => expect(sent).toHaveLength(2)) // The newer sibling already holds the slot on the server, so the older one sends no unsubscribe. disposeOlder() expect(sent.map((frame) => frame.method)).toEqual(['terminal.subscribe', 'terminal.subscribe']) + disposeNewer() + expect(sent.at(-1)).toEqual({ + id: 'relay-3', + method: 'terminal.unsubscribe', + params: { subscriptionId: 't1:phone', client: { id: 'phone' }, requestId: 'relay-2' }, + envelope: { trace: 't', executionHost: SERVER } + }) + streams.clear() }) }) @@ -81,18 +92,28 @@ describe('a server workspace call on the direct wire', () => { streams.subscribe(method, params, onData, options) } const server = scopeRpcClientToExecutionHost(direct, SERVER) - void server.sendRequest('worktree.ps', { limit: 1 }) - await vi.waitFor(() => expect(socket.sent).toHaveLength(1)) - server.subscribe('terminal.subscribe', { terminal: 't1' }, () => {}) - // What #26501 wrote with its own `executionHost` option; the desktop parses JSON, so key order is not read. - expect(socket.frames()).toStrictEqual([ - JSON.parse( - '{"id":"rpc-1","deviceToken":"device-token","method":"worktree.ps","params":{"limit":1},"executionHost":"runtime:env-1"}' - ), - JSON.parse( - '{"id":"rpc-2","deviceToken":"device-token","method":"terminal.subscribe","params":{"terminal":"t1"},"executionHost":"runtime:env-1"}' - ) - ]) + const request = server.sendRequest('worktree.ps', { limit: 1 }) + let dispose: (() => void) | undefined + try { + await vi.waitFor(() => expect(socket.sent).toHaveLength(1)) + dispose = server.subscribe('terminal.subscribe', { terminal: 't1' }, () => {}) + // What #26501 wrote with its own `executionHost` option; the desktop parses JSON, so key order is not read. + expect(socket.frames()).toStrictEqual([ + JSON.parse( + '{"id":"rpc-1","deviceToken":"device-token","method":"worktree.ps","params":{"limit":1},"executionHost":"runtime:env-1"}' + ), + JSON.parse( + '{"id":"rpc-2","deviceToken":"device-token","method":"terminal.subscribe","params":{"terminal":"t1"},"executionHost":"runtime:env-1"}' + ) + ]) + const reply: RpcResponse = { id: 'rpc-1', ok: true, result: { worktrees: [] } } + expect(requests.resolve(reply)).toBe(true) + await expect(request).resolves.toBe(reply) + } finally { + dispose?.() + requests.rejectAll('test cleanup') + await request.catch(() => {}) + } }) }) @@ -124,7 +145,11 @@ describe('scopeRpcClientToExecutionHost', () => { const client = fakeClient() const view = scopeRpcClientToExecutionHost(client, SERVER) void view.sendRequest('worktree.ps', { limit: 1 }, { timeoutMs: 5, envelope: { trace: 't' } }) - view.subscribe('terminal.subscribe', { terminal: 't1' }, () => {}) + const onBinaryFrame = vi.fn() + view.subscribe('terminal.subscribe', { terminal: 't1' }, () => {}, { + onBinaryFrame, + envelope: { trace: 'stream', executionHost: 'runtime:wrong' } + }) view.close() expect(client.sendRequest).toHaveBeenCalledWith( 'worktree.ps', @@ -135,7 +160,7 @@ describe('scopeRpcClientToExecutionHost', () => { 'terminal.subscribe', { terminal: 't1' }, expect.any(Function), - { envelope: { executionHost: SERVER } } + { onBinaryFrame, envelope: { trace: 'stream', executionHost: SERVER } } ) expect(client.close).not.toHaveBeenCalled() expect(scopeRpcClientToExecutionHost(client, SERVER)).toBe(view) diff --git a/src/shared/agent-decorative-title-signature.test.ts b/src/shared/agent-decorative-title-signature.test.ts index f5595897938..a9a1ea4b52f 100644 --- a/src/shared/agent-decorative-title-signature.test.ts +++ b/src/shared/agent-decorative-title-signature.test.ts @@ -1,14 +1,10 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' +import { describe, expect, it } from 'vitest' import { DECORATIVE_AGENT_TITLE_SIGNATURE_SOURCE_SCAN_LIMIT, getDecorativeAgentTitleSignature, isDecorativeAgentTitleFrameChange } from './agent-decorative-title-signature' -afterEach(() => { - vi.restoreAllMocks() -}) - const BRAILLE_SPINNER_FRAME_A = String.fromCharCode(0x280b) const BRAILLE_SPINNER_FRAME_B = String.fromCharCode(0x2819) @@ -26,27 +22,20 @@ describe('agent decorative title signatures', () => { expect(getDecorativeAgentTitleSignature('vim src/index.ts')).toBeNull() }) - it('folds title whitespace without full whitespace replacement', () => { - const replace = vi.spyOn(String.prototype, 'replace') + it('folds title whitespace in the decorative signature', () => { const title = ` \t${BRAILLE_SPINNER_FRAME_A}\t Codex is\nthinking ${' pasted text '.repeat(40)}` expect(getDecorativeAgentTitleSignature(title)).toBe( `working:Codex is thinking ${'pasted text '.repeat(40).trimEnd()}` ) - expect( - replace.mock.calls.filter( - ([pattern]) => pattern instanceof RegExp && pattern.source === '\\s+' - ) - ).toHaveLength(0) }) - it('does not normalize oversized title text on the hot path', () => { - const replace = vi.spyOn(String.prototype, 'replace') - const title = `${BRAILLE_SPINNER_FRAME_A} Codex is thinking ${'x'.repeat( - DECORATIVE_AGENT_TITLE_SIGNATURE_SOURCE_SCAN_LIMIT + 1 - )}` + it('accepts the scan limit and returns null for longer decorative titles', () => { + const prefix = `${BRAILLE_SPINNER_FRAME_A} Codex is thinking ` + const tail = 'x'.repeat(DECORATIVE_AGENT_TITLE_SIGNATURE_SOURCE_SCAN_LIMIT - prefix.length) + const title = `${prefix}${tail}` - expect(getDecorativeAgentTitleSignature(title)).toBeNull() - expect(replace).not.toHaveBeenCalled() + expect(getDecorativeAgentTitleSignature(title)).toBe(`working:Codex is thinking ${tail}`) + expect(getDecorativeAgentTitleSignature(`${title}x`)).toBeNull() }) }) diff --git a/src/shared/agent-hook-listener-claude-subagents.test.ts b/src/shared/agent-hook-listener-claude-subagents.test.ts index e8f19b0f55b..b3f12bbc444 100644 --- a/src/shared/agent-hook-listener-claude-subagents.test.ts +++ b/src/shared/agent-hook-listener-claude-subagents.test.ts @@ -598,10 +598,7 @@ describe('shared agent-hook-listener', () => { }) it('does not resurrect persisted idle child rows after a restart', () => { - // Why: the roster tracks only working children now. A persisted idle - // snapshot (from a build that kept idle rows) is a finished child, so - // hydration must drop it — otherwise restart would re-pile the exact - // squatting rows this fix removes. + // Idle snapshots have no live confirmation after restart. seedClaudeSubagentRosterFromSnapshots(state, PANE_KEY, [ { id: 'aprobe2-6d3cb5b52120b7bf', @@ -610,7 +607,8 @@ describe('shared agent-hook-listener', () => { agentType: 'security-reviewer' } ]) - claudeEvent({ hook_event_name: 'UserPromptSubmit', prompt: 'after restart' }) + const resumed = claudeEvent({ hook_event_name: 'UserPromptSubmit', prompt: 'after restart' }) + expect(resumed?.payload.subagents).toBeUndefined() const stop = claudeEvent({ hook_event_name: 'Stop', background_tasks: [ diff --git a/src/shared/agent-hook-listener-claude-task-notification.test.ts b/src/shared/agent-hook-listener-claude-task-notification.test.ts index f02ef4eca00..8457f1155f6 100644 --- a/src/shared/agent-hook-listener-claude-task-notification.test.ts +++ b/src/shared/agent-hook-listener-claude-task-notification.test.ts @@ -408,7 +408,7 @@ describe('Claude owed task notifications that never arrive', () => { wallClock.mockRestore() }) - it("restates under the stored row's launch token and owner, as a row with no hook event", () => { + it("restates under the stored row's launch token and session, as a row with no hook event", () => { const { post, restated } = host() const tokened = (payload: Record) => post(payload, PANE_KEY, { launchToken: 'launch-1' }) @@ -421,7 +421,12 @@ describe('Claude owed task notifications that never arrive', () => { vi.advanceTimersByTime(LEASE) - expect(restated[0]).toMatchObject({ launchToken: 'launch-1', payload: { state: 'done' } }) + expect(restated[0]).toMatchObject({ + launchToken: 'launch-1', + source: 'claude', + providerSession: { key: 'session_id', id: SESSION }, + payload: { state: 'done' } + }) expect(restated[0].hookEventName).toBeUndefined() }) diff --git a/src/shared/agent-hook-listener-claude-turn-state.test.ts b/src/shared/agent-hook-listener-claude-turn-state.test.ts index ad81b70a7b1..d4f47890d2f 100644 --- a/src/shared/agent-hook-listener-claude-turn-state.test.ts +++ b/src/shared/agent-hook-listener-claude-turn-state.test.ts @@ -12,6 +12,7 @@ import { normalizeAndAccept, PANE_KEY } from './agent-hook-listener-test-harness' +import { makePaneKey } from './stable-pane-id' describe('shared agent-hook-listener', () => { let state: HookListenerState @@ -154,7 +155,7 @@ describe('shared agent-hook-listener', () => { state, 'claude', { - paneKey: 'x'.repeat(300), + paneKey: makePaneKey('x'.repeat(300), '11111111-1111-4111-8111-111111111111'), payload: { hook_event_name: 'UserPromptSubmit', prompt: 'hi' } }, 'production' diff --git a/src/shared/agent-kind.test.ts b/src/shared/agent-kind.test.ts index edea4d20fc4..d99fbbc87c2 100644 --- a/src/shared/agent-kind.test.ts +++ b/src/shared/agent-kind.test.ts @@ -39,10 +39,6 @@ describe('agentKindToTuiAgent', () => { } }) - it("reverses Claude's product id back to the TuiAgent id", () => { - expect(agentKindToTuiAgent('claude-code')).toBe('claude') - }) - it('returns null for the catch-all and missing kinds', () => { expect(agentKindToTuiAgent('other')).toBeNull() expect(agentKindToTuiAgent(null)).toBeNull() diff --git a/src/shared/agent-launch-preferences.test.ts b/src/shared/agent-launch-preferences.test.ts index 36a3abc7ed1..90553a8bdd7 100644 --- a/src/shared/agent-launch-preferences.test.ts +++ b/src/shared/agent-launch-preferences.test.ts @@ -14,7 +14,9 @@ describe('toAgentLaunchPreferences', () => { }) it('answers nothing when no preference survives', () => { - expect(toAgentLaunchPreferences({ model: ' ', fastMode: true })).toBeUndefined() + expect( + toAgentLaunchPreferences({ model: ' ', effort: 123, mode: false, fastMode: true }) + ).toBeUndefined() expect(toAgentLaunchPreferences(undefined)).toBeUndefined() }) })