test(mobile): drop the collapsed host/shell roundtrip matrices

Each `it.each([[true, true]])` ran a single case while advertising a matrix, and
the branches guarded by `host && shell` were dead. Naming the case says what the
test proves, and deleting the constant branches leaves only the lane that runs.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
This commit is contained in:
Jinwoo-H
2026-09-07 04:40:20 -04:00
parent 70298a5cfa
commit d7400d250b
3 changed files with 78 additions and 103 deletions
@@ -3,8 +3,8 @@ import { nativeChatBridgeFixture } from './mobile-web-host-native-chat-test-fixt
afterEach(() => vi.restoreAllMocks())
async function fixture(host = true, shell = true) {
const f = nativeChatBridgeFixture(host, shell)
async function fixture() {
const f = nativeChatBridgeFixture()
const workspaceId = (await f.client.workspaceSnapshot({ limit: 10 })).workspaces[0]!.id
const snapshot = await f.client.sessionSnapshot({ workspaceId })
const tab = snapshot.tabs.find((tab) => tab.type === 'terminal')!
@@ -18,8 +18,8 @@ async function fixture(host = true, shell = true) {
}
}
describe('generic native-chat actions', () => {
it.each([[true, true]])('host=%s shell=%s uses a compatible action lane', async (host, shell) => {
const f = await fixture(host, shell)
it('uses the generic mutate lane instead of terminal.send', async () => {
const f = await fixture()
const result = await f.client.nativeChat.sendMessage(
{ ...f.payload, text: 'hello' },
undefined,
@@ -28,23 +28,19 @@ describe('generic native-chat actions', () => {
expect(result.outcome).toBe('accepted')
expect(
f.sendRequest.mock.calls.filter(([name]) => name === 'mobileWeb.nativeChat.mutate')
).toHaveLength(host && shell ? 1 : 0)
expect(f.sendRequest.mock.calls.filter(([name]) => name === 'terminal.send')).toHaveLength(
host && shell ? 0 : 1
)
if (host && shell) {
expect(result).toMatchObject({ futureReceipt: { revision: 2 } })
const [, params] = f.sendRequest.mock.calls.find(
([name]) => name === 'mobileWeb.nativeChat.mutate'
)!
expect(params).toMatchObject({
action: 'sendMessage',
text: 'hello',
resourceId: 'opaque-resource'
})
expect(params).not.toHaveProperty('sessionId')
expect(params).not.toHaveProperty('deadline')
}
).toHaveLength(1)
expect(f.sendRequest.mock.calls.filter(([name]) => name === 'terminal.send')).toHaveLength(0)
expect(result).toMatchObject({ futureReceipt: { revision: 2 } })
const [, params] = f.sendRequest.mock.calls.find(
([name]) => name === 'mobileWeb.nativeChat.mutate'
)!
expect(params).toMatchObject({
action: 'sendMessage',
text: 'hello',
resourceId: 'opaque-resource'
})
expect(params).not.toHaveProperty('sessionId')
expect(params).not.toHaveProperty('deadline')
f.client.dispose()
})
it.each(['respond', 'stop', 'prepareCommit'] as const)(
@@ -4,8 +4,8 @@ import { MOBILE_WEB_BRIDGE_ROUNDTRIP_CONTEXT } from './mobile-web-bridge-roundtr
import { nativeChatBridgeFixture as fixture } from './mobile-web-host-native-chat-test-fixture'
describe('native-chat generic read migration', () => {
it.each([[true, true]])('host=%s shell=%s', async (host, shell) => {
const f = fixture(host, shell)
it('reads native chat through the generic host lane', async () => {
const f = fixture()
const workspaceId = (await f.client.workspaceSnapshot({ limit: 10 })).workspaces[0]!.id
const session = await f.client.sessionSnapshot({ workspaceId })
const tab = session.tabs.find((tab) => tab.type === 'terminal')!
@@ -17,43 +17,34 @@ describe('native-chat generic read migration', () => {
tab.id
)
expect(result.messages[0].blocks[0]).toMatchObject({ type: 'text', text: 'hello' })
if (host && shell) {
const pageSession = boundDocument(f)
expect(result).toEqual(f.transcript)
expect(f.sendRequest).toHaveBeenCalledWith(
'mobileWeb.nativeChat.bind',
{
worktree: 'id:host-workspace',
pageSession,
tabId: 'tab'
},
expect.objectContaining({ beforeSend: expect.any(Function) })
)
expect(f.sendRequest).toHaveBeenCalledWith(
'mobileWeb.nativeChat.read',
{
worktree: 'id:host-workspace',
pageSession,
resourceId: 'opaque-resource',
read: { limit: 20 }
},
expect.objectContaining({ beforeSend: expect.any(Function) })
)
expect(f.sendRequest.mock.calls.some(([method]) => method === 'nativeChat.readSession')).toBe(
false
)
} else {
expect(
f.sendRequest.mock.calls.some(([method]) => method === 'mobileWeb.nativeChat.bind')
).toBe(false)
expect(f.sendRequest.mock.calls.some(([method]) => method === 'nativeChat.readSession')).toBe(
true
)
}
const pageSession = boundDocument(f)
expect(result).toEqual(f.transcript)
expect(f.sendRequest).toHaveBeenCalledWith(
'mobileWeb.nativeChat.bind',
{
worktree: 'id:host-workspace',
pageSession,
tabId: 'tab'
},
expect.objectContaining({ beforeSend: expect.any(Function) })
)
expect(f.sendRequest).toHaveBeenCalledWith(
'mobileWeb.nativeChat.read',
{
worktree: 'id:host-workspace',
pageSession,
resourceId: 'opaque-resource',
read: { limit: 20 }
},
expect.objectContaining({ beforeSend: expect.any(Function) })
)
expect(f.sendRequest.mock.calls.some(([method]) => method === 'nativeChat.readSession')).toBe(
false
)
expect(JSON.stringify(f.shellMessages)).not.toContain('private-session')
})
it.each([[true, true]])('stream host=%s shell=%s', async (host, shell) => {
const f = fixture(host, shell)
it('streams native chat through the generic host lane', async () => {
const f = fixture()
const workspaceId = (await f.client.workspaceSnapshot({ limit: 10 })).workspaces[0]!.id
const session = await f.client.sessionSnapshot({ workspaceId })
const tab = session.tabs[0]
@@ -68,21 +59,15 @@ describe('native-chat generic read migration', () => {
vi.fn()
)
await subscription.ready
const generic = host && shell
expect(f.subscribe.mock.calls[0][0]).toBe(
generic ? 'mobileWeb.nativeChat.subscribe' : 'nativeChat.subscribe'
)
expect(f.subscribe.mock.calls[0][0]).toBe('mobileWeb.nativeChat.subscribe')
const event = { type: 'snapshot', ...f.transcript }
f.emit(event)
await vi.waitFor(() => expect(onEvent).toHaveBeenCalledOnce())
if (generic) {
const pageSession = boundDocument(f)
expect(onEvent).toHaveBeenCalledWith(event)
expect(f.subscribe.mock.calls[0][1]).toMatchObject({
pageSession,
resourceId: 'opaque-resource'
})
}
expect(onEvent).toHaveBeenCalledWith(event)
expect(f.subscribe.mock.calls[0][1]).toMatchObject({
pageSession: boundDocument(f),
resourceId: 'opaque-resource'
})
subscription.unsubscribe()
expect(f.unsubscribe).toHaveBeenCalledOnce()
})
@@ -3,7 +3,7 @@ import type { RpcClient } from '../transport/rpc-client'
import { createMobileWebBridgeRoundtripFixture } from './mobile-web-bridge-roundtrip-fixture'
import { MOBILE_WEB_PRODUCTION_GRANTS } from './mobile-web-production-grants'
function fixture(catalogAvailable = true, genericShell = true) {
function fixture() {
let emit: (event: unknown) => void = () => {}
const unsubscribe = vi.fn()
const subscribe = vi.fn<RpcClient['subscribe']>((_method, _params, listener) => {
@@ -21,44 +21,40 @@ function fixture(catalogAvailable = true, genericShell = true) {
}
}
}
return catalogAvailable
? {
ok: true,
result: {
grants: [
{
method: 'mobileWeb.files.watch',
mode: 'subscription',
workspaceParam: 'worktree',
unsubscribeMethod: 'files.unwatch',
maxRequestBytes: 1024,
maxResponseBytes: 512 * 1024
},
{
method: 'future.events',
mode: 'subscription',
workspaceParam: 'scope',
unsubscribeMethod: 'future.release',
maxRequestBytes: 1024,
maxResponseBytes: 512 * 1024
}
]
return {
ok: true,
result: {
grants: [
{
method: 'mobileWeb.files.watch',
mode: 'subscription',
workspaceParam: 'worktree',
unsubscribeMethod: 'files.unwatch',
maxRequestBytes: 1024,
maxResponseBytes: 512 * 1024
},
{
method: 'future.events',
mode: 'subscription',
workspaceParam: 'scope',
unsubscribeMethod: 'future.release',
maxRequestBytes: 1024,
maxResponseBytes: 512 * 1024
}
}
: { ok: false, error: { code: 'method_not_found', message: 'Old host' } }
]
}
}
})
const bridge = createMobileWebBridgeRoundtripFixture({
grants: MOBILE_WEB_PRODUCTION_GRANTS.filter(
(grant) => genericShell || grant.operation !== 'hostSubscribe'
),
grants: MOBILE_WEB_PRODUCTION_GRANTS,
rpcClient: { sendRequest, subscribe } as unknown as RpcClient
})
return { ...bridge, subscribe, unsubscribe, emit: (event: unknown) => emit(event) }
}
describe('generic subscription bridge compatibility', () => {
it.each([[true, true]])('source-control catalog=%s shell=%s', async (catalog, shell) => {
const f = fixture(catalog, shell)
it('subscribes to source control through the generic shell lane', async () => {
const f = fixture()
const workspace = (await f.client.workspaceSnapshot({ limit: 10 })).workspaces[0]!.id
const onEvent = vi.fn()
const onError = vi.fn()
@@ -68,11 +64,9 @@ describe('generic subscription bridge compatibility', () => {
onError
)
await subscription.ready
const generic = catalog && shell
expect(f.subscribe.mock.calls[0]?.[0]).toBe(generic ? 'mobileWeb.files.watch' : 'files.watch')
expect(f.subscribe.mock.calls[0]?.[0]).toBe('mobileWeb.files.watch')
f.emit({
type: 'changed',
...(generic ? {} : { worktree: 'id:host-workspace' }),
events: [],
futureField: 'new'
})