test: strengthen routing, scheduling, and session assertions (#33189)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-10-24 09:03:09 -07:00
committed by GitHub
co-authored by OrcaWin
parent 6f0834212e
commit 41d25c074e
9 changed files with 74 additions and 22 deletions
@@ -32,7 +32,9 @@ describe('mobile E2EE v2 key schedule', () => {
it('derives unique direction keys and session IDs across fresh client nonces', () => {
const { hello, ready, sharedSecret } = createMobileE2EEV2Fixture()
const fingerprints = new Set<string>()
const mobileToDesktopKeys = new Set<string>()
const desktopToMobileKeys = new Set<string>()
const sessionIds = new Set<string>()
for (let index = 0; index < 128; index++) {
const nonce = new Uint8Array(32)
new DataView(nonce.buffer).setUint32(28, index, false)
@@ -47,12 +49,12 @@ describe('mobile E2EE v2 key schedule', () => {
clientNonce: handshake.clientNonce,
desktopNonce: handshake.desktopNonce
})
fingerprints.add(
[schedule.mobileToDesktopKey, schedule.desktopToMobileKey, schedule.sessionId]
.map(hex)
.join(':')
)
mobileToDesktopKeys.add(hex(schedule.mobileToDesktopKey))
desktopToMobileKeys.add(hex(schedule.desktopToMobileKey))
sessionIds.add(hex(schedule.sessionId))
}
expect(fingerprints.size).toBe(128)
expect(mobileToDesktopKeys.size).toBe(128)
expect(desktopToMobileKeys.size).toBe(128)
expect(sessionIds.size).toBe(128)
})
})
@@ -56,7 +56,7 @@ function logicalClient(initial: ConnectionState) {
}
describe('direct pairing upgrade controller', () => {
it('upgrades immediately after an authenticated direct connection', async () => {
it('forwards a completed upgrade for an already connected client', async () => {
const logical = logicalClient('connected')
const upgrade = vi.fn(async () => upgraded)
const onUpgraded = vi.fn(async () => {})
@@ -83,7 +83,9 @@ describe('direct pairing upgrade controller', () => {
await controller.start()
controller.setForeground(false)
controller.setForeground(true)
await vi.waitFor(() => expect(onUpgraded).toHaveBeenCalledOnce())
await Promise.resolve()
expect(onUpgraded).toHaveBeenCalledOnce()
expect(upgrade).toHaveBeenCalledTimes(2)
})
@@ -2,7 +2,7 @@ import { describe, expect, it, vi } from 'vitest'
import { scheduleOrphanedMobileRelayCleanup } from './mobile-relay-orphan-cleanup'
describe('mobile relay orphan cleanup', () => {
it('durably schedules credential deletion before removing an orphan overlay pointer', async () => {
it('schedules each orphan credential cleanup before removing its overlay pointer', async () => {
const order: string[] = []
const deleteCredential = vi.fn(async () => {})
const scheduleCleanup = vi.fn(async (hostId: string) => {
@@ -23,7 +23,7 @@ describe('mobile relay orphan cleanup', () => {
expect(scheduleCleanup).toHaveBeenCalledWith('host-1', deleteCredential)
})
it('retains the overlay pointer when durable cleanup scheduling fails', async () => {
it('retains the overlay pointer when the cleanup scheduler rejects', async () => {
const removeOverlay = vi.fn(async () => {})
await scheduleOrphanedMobileRelayCleanup({
hostIds: ['host-1'],
@@ -1,14 +1,18 @@
import { describe, expect, it, vi } from 'vitest'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { createRelayRecoveryLog } from './mobile-relay-recovery-log'
import type { ConnectionLogEntry } from './types'
afterEach(() => vi.restoreAllMocks())
describe('createRelayRecoveryLog', () => {
it('uses distinct ids across logger instances', () => {
vi.spyOn(Date, 'now').mockReturnValue(1)
const entries: ConnectionLogEntry[] = []
const sink = vi.fn((entry: ConnectionLogEntry) => entries.push(entry))
createRelayRecoveryLog(() => 1, sink)('first')
createRelayRecoveryLog(() => 1, sink)('second')
expect(entries).toHaveLength(2)
expect(entries[0]?.id).not.toBe(entries[1]?.id)
})
})
@@ -1,4 +1,4 @@
import { randomBytes } from 'node:crypto'
import { createHash } from 'node:crypto'
import { gunzipSync } from 'fflate'
import { describe, expect, it } from 'vitest'
import { encodeMobileWebBundleRange } from '../../../src/main/runtime/rpc/methods/mobile-web-bundle-range-encoding'
@@ -29,7 +29,11 @@ describe('a host-encoded range decoded by the phone', () => {
})
it('leaves incompressible bytes as identity, which needs no decoder', async () => {
const raw = randomBytes(4096)
const raw = Buffer.concat(
Array.from({ length: 128 }, (_, index) =>
createHash('sha256').update(`range-${index}`).digest()
)
)
const encoded = await encodeMobileWebBundleRange(raw)
expect(encoded.encoding).toBe('identity')
@@ -1,4 +1,5 @@
import { describe, expect, it } from 'vitest'
import { describe, expect, it, vi } from 'vitest'
import { runRpcOperation } from './rpc-operation'
import {
MOBILE_WEB_BUNDLE_CHUNK_BYTES,
MOBILE_WEB_BUNDLE_RANGE_BYTES,
@@ -69,10 +70,18 @@ describe('mobile web bundle range reply reader', () => {
}
})
it('is a require-result read of the range method', () => {
expect(mobileWebBundleRangeRead.method).toBe(MOBILE_WEB_BUNDLE_RANGE_METHOD)
expect(mobileWebBundleRangeRead.acceptance).toBe('require-result-or-throw')
expect(mobileWebBundleRangeRead.barrier).toBe('on-settle')
it('requests a range and surfaces the host refusal', async () => {
const params = { buildId: 'a'.repeat(64), path: 'index.html', offset: 0 }
const sendRequest = vi.fn(async () => ({
id: 'range-1',
ok: false as const,
error: { code: 'forbidden', message: 'no' }
}))
await expect(
runRpcOperation({ sendRequest }, mobileWebBundleRangeRead, params)
).rejects.toThrow('forbidden: no')
expect(sendRequest).toHaveBeenCalledWith(MOBILE_WEB_BUNDLE_RANGE_METHOD, params, undefined)
})
})
@@ -8,12 +8,20 @@ import {
describe('app menu selection actions', () => {
it.each(['copy', 'select-all'] as const)('reports whether %s has an owned target', (action) => {
const handler = (event: Event): void => event.preventDefault()
let receivedAction: unknown
const handler = (event: Event): void => {
if (event instanceof CustomEvent) {
receivedAction = event.detail
}
event.preventDefault()
}
window.addEventListener(APP_MENU_SELECTION_ACTION_EVENT, handler)
expect(dispatchAppMenuSelectionAction(action)).toBe(true)
const claimed = dispatchAppMenuSelectionAction(action)
window.removeEventListener(APP_MENU_SELECTION_ACTION_EVENT, handler)
expect(claimed).toBe(true)
expect(receivedAction).toBe(action)
})
it('leaves unowned actions available for native fallback', () => {
+7 -1
View File
@@ -37,13 +37,19 @@ function find(
} = {}
): string | null {
const livePtyIds = new Set(options.livePtyIds ?? runs.map((entry) => entry.terminalPtyId))
const statusesByPaneKey = new Map(
runs.map((entry) => [
entry.terminalPaneKey,
options.status ?? { state: 'done', agentType: 'claude' }
])
)
return findReusableAutomationRun({
automationId: 'auto-1',
agentId: 'claude',
worktreeId: 'wt-1',
currentRunId: 'run-current',
runs,
agentStatusForPane: () => options.status ?? { state: 'done', agentType: 'claude' },
agentStatusForPane: (paneKey) => statusesByPaneKey.get(paneKey),
liveSession: (candidate) => (livePtyIds.has(candidate.terminalPtyId) ? candidate.id : null)
})
}
+17
View File
@@ -33,6 +33,23 @@ describe('stepCalmStatusReveal', () => {
it('says an outage that outlasts the delay, and holds it long enough to read', () => {
const shownAt = CALM_STATUS_REVEAL_DELAY_MS
const pending = stepCalmStatusReveal(CALM_STATUS_IDLE, {
down: true,
immediate: false,
now: 0
})
expect(pending.recheckInMs).toBe(CALM_STATUS_REVEAL_DELAY_MS)
const shown = stepCalmStatusReveal(pending.next, {
down: true,
immediate: false,
now: shownAt
})
const recovering = stepCalmStatusReveal(shown.next, {
down: false,
immediate: false,
now: shownAt + 100
})
expect(recovering.recheckInMs).toBe(CALM_STATUS_MIN_VISIBLE_MS - 100)
expect(
run([
{ at: 0, down: true },