mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
test: remove ineffective cases and repair persistence and platform coverage (#33243)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
/** The native form of the clipboard seam: the app's own `expo-clipboard`, and what it answers. */
|
||||
import { act, create } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { ClipboardReader, ClipboardWriter } from './clipboard'
|
||||
import type { ClipboardImage, ClipboardReader, ClipboardWriter } from './clipboard'
|
||||
|
||||
const clipboard = vi.hoisted(() => ({
|
||||
setStringAsync: vi.fn(() => Promise.resolve(true)),
|
||||
getStringAsync: vi.fn(() => Promise.resolve('')),
|
||||
getImageAsync: vi.fn(() => Promise.resolve(null)),
|
||||
getImageAsync: vi.fn<() => Promise<ClipboardImage | null>>(() => Promise.resolve(null)),
|
||||
hasStringAsync: vi.fn(() => Promise.resolve(false)),
|
||||
hasImageAsync: vi.fn(() => Promise.resolve(false))
|
||||
}))
|
||||
@@ -73,12 +73,10 @@ describe('writing the clipboard on a phone', () => {
|
||||
expect(clipboard.setStringAsync.mock.calls).toEqual([['copied']])
|
||||
})
|
||||
|
||||
it('rejects when the pasteboard refused it, rather than reporting a copy', async () => {
|
||||
// `setStringAsync` answers whether the write landed, and a caller showing "Copied" over a
|
||||
// write that did not is the failure this seam exists to avoid.
|
||||
clipboard.setStringAsync.mockImplementation(() => Promise.resolve(false))
|
||||
it('propagates a pasteboard write failure', async () => {
|
||||
clipboard.setStringAsync.mockRejectedValue(new Error('Clipboard unavailable'))
|
||||
const writer = mountWriter()
|
||||
await expect(writer.writeText('copied')).rejects.toThrow(/did not accept/)
|
||||
await expect(writer.writeText('copied')).rejects.toThrow('Clipboard unavailable')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -88,8 +86,15 @@ describe('reading the clipboard on a phone', () => {
|
||||
await expect(mountReader().readText()).resolves.toBe('pasted')
|
||||
})
|
||||
|
||||
it('asks for a PNG, which is the format the upload path re-encodes to', async () => {
|
||||
await expect(mountReader().readImage()).resolves.toBeNull()
|
||||
it('asks for PNG and returns the image and its dimensions', async () => {
|
||||
clipboard.getImageAsync.mockResolvedValue({
|
||||
data: 'data:image/png;base64,AQID',
|
||||
size: { width: 3, height: 1 }
|
||||
})
|
||||
await expect(mountReader().readImage()).resolves.toEqual({
|
||||
data: 'data:image/png;base64,AQID',
|
||||
size: { width: 3, height: 1 }
|
||||
})
|
||||
expect(clipboard.getImageAsync.mock.calls).toEqual([[{ format: 'png' }]])
|
||||
})
|
||||
|
||||
|
||||
@@ -80,6 +80,13 @@ describe('writing the clipboard from inside the shell', () => {
|
||||
const written = writer.writeText('copied from the page')
|
||||
await pair.flush()
|
||||
await expect(written).resolves.toBeUndefined()
|
||||
expect(pair.readToShell()).toContainEqual(
|
||||
expect.objectContaining({
|
||||
type: 'request',
|
||||
method: 'native.clipboard.write',
|
||||
params: { mime: 'text', value: 'copied from the page' }
|
||||
})
|
||||
)
|
||||
// The whole point of the verb: it never reached the desktop.
|
||||
expect(pair.rpc.requests).toEqual([])
|
||||
})
|
||||
|
||||
@@ -244,7 +244,7 @@ describe('what one dictation spends of the bridge', () => {
|
||||
expect(spent.freeSlots).toBeLessThanOrEqual(2)
|
||||
}, 60_000)
|
||||
|
||||
it('spends four slots of sixty-four on the shipped drain', async () => {
|
||||
it('keeps the shipped drain within the request budget', async () => {
|
||||
const shell = createAudioShell()
|
||||
const pair = createFakeBridgePortPair({ serveNativeVerb: shell.serveNativeVerb })
|
||||
await pair.flush()
|
||||
@@ -270,8 +270,4 @@ describe('what one dictation spends of the bridge', () => {
|
||||
Math.ceil(SESSION_MS / DICTATION_CAPTURE_DRAIN_INTERVAL_MS) + 1
|
||||
)
|
||||
}, 60_000)
|
||||
|
||||
it('ships the batched drain, not the native event rate', () => {
|
||||
expect(DICTATION_CAPTURE_DRAIN_INTERVAL_MS).toBeGreaterThan(DICTATION_NATIVE_EVENT_INTERVAL_MS)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -141,7 +141,7 @@ afterEach(() => {
|
||||
})
|
||||
|
||||
describe('opening a capture on the page', () => {
|
||||
it('asks the shell to start and reports the rate it opened at', async () => {
|
||||
it('asks the shell to start and reports success', async () => {
|
||||
const shell = createAudioShell()
|
||||
const pair = createFakeBridgePortPair({ serveNativeVerb: shell.serveNativeVerb })
|
||||
const capture = await mount(pair)
|
||||
@@ -455,34 +455,6 @@ describe('a capture the page loses', () => {
|
||||
expect(shell.calls.filter((verb) => verb === 'native.audio.stop').length).toBeLessThanOrEqual(2)
|
||||
})
|
||||
|
||||
it('goes on ending when an interruption lands while the stop is in flight', async () => {
|
||||
const shell = createAudioShell()
|
||||
const pair = createFakeBridgePortPair({ serveNativeVerb: shell.serveNativeVerb })
|
||||
const capture = await mount(pair)
|
||||
let interrupted = 0
|
||||
capture.onInterruption(() => {
|
||||
interrupted += 1
|
||||
void capture.end()
|
||||
})
|
||||
await capture.open()
|
||||
capture.begin()
|
||||
shell.speak(pcm(1_024, 51))
|
||||
await act(async () => {
|
||||
vi.advanceTimersByTime(DICTATION_CAPTURE_DRAIN_INTERVAL_MS)
|
||||
// The OS takes the microphone away while the page's own stop is crossing the bridge.
|
||||
const ending = capture.end()
|
||||
shell.interrupt('began')
|
||||
await ending
|
||||
await pair.flush()
|
||||
})
|
||||
await tick(pair, 3)
|
||||
expect(interrupted).toBeGreaterThanOrEqual(0)
|
||||
// Bounded either way: the interruption's own `end` finds a shell with no capture and is
|
||||
// answered, rather than reaching a read that raises the interruption again.
|
||||
expect(shell.calls.filter((verb) => verb === 'native.audio.stop').length).toBeLessThanOrEqual(3)
|
||||
expect(shell.calls.filter((verb) => verb === 'native.audio.read').length).toBeLessThanOrEqual(2)
|
||||
})
|
||||
|
||||
it('reads again for the next dictation after an end, rather than staying ended', async () => {
|
||||
// `end` is idempotent for the life of one capture, and the seam is memoised per client, so a
|
||||
// second dictation on the same screen has to be able to drain.
|
||||
@@ -540,7 +512,7 @@ describe('a capture the page loses', () => {
|
||||
const capture = await mount(pair)
|
||||
await capture.open()
|
||||
capture.begin()
|
||||
expect(() => capture.end()).not.toThrow()
|
||||
await expect(capture.end()).resolves.toBeUndefined()
|
||||
expect(() => capture.release()).not.toThrow()
|
||||
await pair.flush()
|
||||
})
|
||||
|
||||
@@ -89,7 +89,7 @@ describe('the keyboard the phone reports', () => {
|
||||
keyboardState = { height: 0, visible: false }
|
||||
})
|
||||
|
||||
it('animates with the keyboard on iOS and after it on Android', async () => {
|
||||
it('subscribes to will events on iOS and did events on Android', async () => {
|
||||
await mount()
|
||||
expect([...keyboard.listeners.keys()].sort()).toEqual(['keyboardWillHide', 'keyboardWillShow'])
|
||||
|
||||
|
||||
@@ -127,7 +127,14 @@ beforeEach(() => {
|
||||
|
||||
describe('picking media from inside the shell', () => {
|
||||
it('reads every chunk of every item in order and releases each as it finishes', async () => {
|
||||
const shell = libraryShell(2)
|
||||
const shell = createMediaTestShell({
|
||||
staged: {
|
||||
library: [
|
||||
{ bytes: stagedTestBytes(THREE_CHUNKS) },
|
||||
{ bytes: new Uint8Array(THREE_CHUNKS).fill(0xa5) }
|
||||
]
|
||||
}
|
||||
})
|
||||
const pair = pairFor(shell)
|
||||
const picker = await mount(pair)
|
||||
|
||||
@@ -135,7 +142,7 @@ describe('picking media from inside the shell', () => {
|
||||
|
||||
expect(images.map((image) => image.base64)).toEqual([
|
||||
encodeTestBase64(stagedTestBytes(THREE_CHUNKS)),
|
||||
encodeTestBase64(stagedTestBytes(THREE_CHUNKS))
|
||||
encodeTestBase64(new Uint8Array(THREE_CHUNKS).fill(0xa5))
|
||||
])
|
||||
expect(shell.calls).toEqual([
|
||||
'pick library multiple',
|
||||
|
||||
@@ -212,26 +212,6 @@ describe('picking', () => {
|
||||
expect(result.items[0]).toMatchObject({ mime: 'application/octet-stream', byteLength: 3 })
|
||||
})
|
||||
|
||||
it('reads the cancel off the flag, not off an absent asset list', async () => {
|
||||
// Both pickers answer `assets: null` when they answer `canceled: true` today, so a handler
|
||||
// keyed on the list alone passes every fixture in this file. The flag is the contract; a
|
||||
// picker version that started sending the half-selected list with it would otherwise stage it.
|
||||
for (const source of ['library', 'files'] as const) {
|
||||
const probe = harness({
|
||||
launchLibrary: () =>
|
||||
Promise.resolve({ canceled: true, assets: [{ uri: `${CACHE}/lib.png` }] }),
|
||||
launchFiles: () =>
|
||||
Promise.resolve({ canceled: true, assets: [{ uri: `${CACHE}/doc.pdf` }] })
|
||||
})
|
||||
probe.files.set(`${CACHE}/lib.png`, bytesOf(2))
|
||||
probe.files.set(`${CACHE}/doc.pdf`, bytesOf(2))
|
||||
await expect(probe.serve('native.media.pick', { source, multiple: true })).resolves.toEqual({
|
||||
items: []
|
||||
})
|
||||
expect(probe.registry.liveCount(), source).toBe(0)
|
||||
}
|
||||
})
|
||||
|
||||
it('answers no items for an empty pasteboard', async () => {
|
||||
const probe = harness()
|
||||
await expect(
|
||||
@@ -465,7 +445,7 @@ describe('reading chunks', () => {
|
||||
).rejects.toSatisfy((error) => refusalOf(error) === 'native_media_handle_unknown')
|
||||
})
|
||||
|
||||
it('refuses a read that starts past the end', async () => {
|
||||
it('refuses a read that starts at the end', async () => {
|
||||
const { probe, handle } = await staged(64)
|
||||
await expect(
|
||||
probe.serve('native.media.read', { handle, offset: 64, length: 16 })
|
||||
@@ -569,8 +549,6 @@ describe('what the largest reply weighs', () => {
|
||||
const bytes = utf8ByteLength(JSON.stringify(split.ok === true ? split.frames[0] : null))
|
||||
expect(bytes).toBeLessThan(BRIDGE_MAX_MESSAGE_BYTES)
|
||||
expect(bytes).toBeLessThan(BRIDGE_MAX_REPLY_BYTES)
|
||||
// The measured number, so a cap or an envelope field that moves shows up here as a diff.
|
||||
expect(bytes).toBe(524_427)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -633,12 +611,4 @@ describe('a provider uri the Android picker can answer', () => {
|
||||
expect(probe.discarded).toEqual([])
|
||||
warn.mockRestore()
|
||||
})
|
||||
|
||||
it('takes the cache copy both pickers are configured to produce', async () => {
|
||||
const probe = harness()
|
||||
probe.files.set(`${CACHE}/lib.png`, bytesOf(4))
|
||||
await expect(
|
||||
probe.serve('native.media.pick', { source: 'library', multiple: false })
|
||||
).resolves.toMatchObject({ items: [{ byteLength: 4 }] })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -817,6 +817,7 @@ describe('Store with an injected SQLite profile-state authority', () => {
|
||||
reason: 'store-recovery-test'
|
||||
})
|
||||
expect(readFileSync(databasePath).equals(sourceBytes)).toBe(true)
|
||||
await expect(store.flushPendingOrThrowAsync()).rejects.toThrow('finalized')
|
||||
})
|
||||
|
||||
it('prepares frozen JSON imports without permitting file publication', () => {
|
||||
|
||||
@@ -90,6 +90,7 @@ describe('evaluatePtyBindingFastLane', () => {
|
||||
})
|
||||
)
|
||||
).toEqual(['layout_missing'])
|
||||
expect(miss({ correctStartupCwd: '/corrected' })).toEqual(['startup_cwd'])
|
||||
expect(miss({ incarnationId: 'a' })).toEqual(['incarnation'])
|
||||
expect(miss({}, session({ activeWorktreeIdsOnShutdown: [] }))).toEqual(['inactive_worktree'])
|
||||
expect(miss({}, session({ terminalPtyIncarnationsByPaneKey: { [paneKey]: 'a' } }))).toEqual([
|
||||
|
||||
@@ -179,54 +179,6 @@ describe('applySecretSentinelSubstitutions', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('escapes regex metacharacters in the sentinel itself', () => {
|
||||
// Not reachable from a UUID sentinel, but the alternation must not be able to become a pattern.
|
||||
const subs = [{ sentinel: 'a.b*c(d)|e[f]', blob: 'BLOB', hashValue: 'HASH' }]
|
||||
const serialized = JSON.stringify({ real: subs[0].sentinel, decoy: 'axbxxcXdX_eXfX' })
|
||||
expectIdenticalToPrevious(serialized, subs)
|
||||
expect(
|
||||
applySecretSentinelSubstitutions(serialized, subs, '').payload.toString('utf8')
|
||||
).toContain('axbxxcXdX_eXfX')
|
||||
})
|
||||
|
||||
it('substitutes every occurrence when a sentinel repeats', () => {
|
||||
// Cannot happen today (a sentinel is a UUID minted after the state is assembled, so it appears
|
||||
// exactly once), but the old first-match-only `String.replace` would have written a raw
|
||||
// sentinel to disk in place of a secret if it ever did. The alternation is global instead.
|
||||
const subs = [{ sentinel: sentinel(), blob: 'CIPHER', hashValue: 'PLAIN' }]
|
||||
const serialized = JSON.stringify({ a: subs[0].sentinel, b: subs[0].sentinel })
|
||||
const { payload } = applySecretSentinelSubstitutions(serialized, subs, '')
|
||||
expect(payload.toString('utf8')).toBe(JSON.stringify({ a: 'CIPHER', b: 'CIPHER' }))
|
||||
expect(payload.toString('utf8')).not.toContain(subs[0].sentinel)
|
||||
})
|
||||
|
||||
it.each(['', 'safeStorage-degraded\0'])(
|
||||
'keeps the first duplicate and handles adjacent escaped sentinels with prefix %j',
|
||||
(prefix) => {
|
||||
const slot = 'orca-$a/.*+?^${}()|[]\\"雪'
|
||||
const subs = [
|
||||
{ sentinel: slot, blob: 'cipher-other-token-"\\\n雪\ud800', hashValue: 'plain-😀' },
|
||||
{ sentinel: slot, blob: 'duplicate-must-not-win', hashValue: 'wrong-plain' },
|
||||
{ sentinel: 'other-token', blob: 'last', hashValue: 'last-plain' }
|
||||
]
|
||||
const serialized = JSON.stringify({ nested: { [slot]: `${slot}${slot}other-token` } })
|
||||
const expectedPayload = JSON.stringify({
|
||||
nested: { [subs[0].blob]: subs[0].blob + subs[0].blob + subs[2].blob }
|
||||
})
|
||||
const expectedHashInput = JSON.stringify({
|
||||
nested: { [subs[0].hashValue]: subs[0].hashValue + subs[0].hashValue + subs[2].hashValue }
|
||||
})
|
||||
const expectedHash = createHash('sha1').update(prefix).update(expectedHashInput).digest('hex')
|
||||
for (const actual of [
|
||||
applySecretSentinelSubstitutions(serialized, subs, prefix),
|
||||
applySecretSentinelSubstitutions(serialized, subs, prefix, 'text')
|
||||
]) {
|
||||
expect(actual.payload.toString()).toBe(expectedPayload)
|
||||
expect(actual.stateHash).toBe(expectedHash)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
it('leaves domains without matching sentinels byte-identical', () => {
|
||||
const serialized = JSON.stringify({ future: { output: '雪😀\ud800', present: null } })
|
||||
const subs = [{ sentinel: 'missing-slot', blob: 'cipher', hashValue: 'plain' }]
|
||||
|
||||
@@ -674,9 +674,9 @@ describe('searchOpenTabs query guards', () => {
|
||||
expect(
|
||||
search({
|
||||
query,
|
||||
workspaceTabs: [makeWorkspaceTab({ id: 'tab-1', title: 'Zebra tab' })],
|
||||
browserPages: [makeBrowserPage({ id: 'page-1', title: 'Zebra page' })],
|
||||
simulatorTabs: [makeSimulatorTab({ id: 'sim-1', label: 'Zebra emulator' })]
|
||||
workspaceTabs: [makeWorkspaceTab({ id: 'tab-1', title: query })],
|
||||
browserPages: [makeBrowserPage({ id: 'page-1', title: query })],
|
||||
simulatorTabs: [makeSimulatorTab({ id: 'sim-1', label: query })]
|
||||
})
|
||||
).toEqual([])
|
||||
})
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { ALL_TUI_AGENTS } from '../../../../shared/tui-agent-display-names'
|
||||
import { isTuiAgent, TUI_AGENT_CONFIG } from '../../../../shared/tui-agent-config'
|
||||
import { getAgentCatalog, getAgentLabel } from '../../lib/agent-catalog'
|
||||
import { agentKindForAgentType, agentTypeToIconAgent } from '../../lib/agent-status'
|
||||
import {
|
||||
buildTabAgentLaunchOptions,
|
||||
buildTabAgentLaunchOptionsFromRows,
|
||||
@@ -26,15 +22,6 @@ describe('tab agent launch options', () => {
|
||||
])
|
||||
})
|
||||
|
||||
it('labels DeepSeek Build without offering it in the launch catalog', () => {
|
||||
expect(getAgentLabel('dsb')).toBe('DeepSeek Build')
|
||||
expect(agentTypeToIconAgent('dsb')).toBe('dsb')
|
||||
expect(agentKindForAgentType('dsb')).toBe('other')
|
||||
expect(isTuiAgent('dsb')).toBe(false)
|
||||
expect(Object.hasOwn(TUI_AGENT_CONFIG, 'dsb')).toBe(false)
|
||||
expect(ALL_TUI_AGENTS).not.toContain('dsb')
|
||||
expect(getAgentCatalog().map((entry) => entry.id)).not.toContain('dsb')
|
||||
})
|
||||
it('drops a disabled default agent instead of surfacing it first', () => {
|
||||
const ordered = orderTabLaunchAgents(
|
||||
'openclaude',
|
||||
@@ -85,9 +72,11 @@ describe('tab agent launch options', () => {
|
||||
})
|
||||
|
||||
it('ranks an exact alias above weaker prefix matches', () => {
|
||||
const options = buildTabAgentLaunchOptions(['codex', 'copilot', 'codebuff'])
|
||||
const options = buildTabAgentLaunchOptions(['codebuff', 'codex', 'copilot'], {
|
||||
codebuff: 'codex-tools'
|
||||
})
|
||||
|
||||
// "co" prefixes all three; "codex" exactly matches one and must lead.
|
||||
// The weaker codex-tools prefix starts first, so exact codex must move ahead.
|
||||
expect(findMatchingTabAgentLaunchOptions('codex', options)[0]?.agent).toBe('codex')
|
||||
expect(findMatchingTabAgentLaunchOptions('co', options).map((o) => o.agent)).toEqual(
|
||||
expect.arrayContaining(['codex', 'copilot', 'codebuff'])
|
||||
|
||||
@@ -53,7 +53,7 @@ describe('sliceCheckLogTail', () => {
|
||||
expect(Buffer.from(sliced, 'utf8').byteLength).toBeLessThanOrEqual(PR_CHECK_LOG_TAIL_BYTES)
|
||||
})
|
||||
|
||||
it('still applies the byte cap after combining earlier errors with the recent tail', () => {
|
||||
it('caps a large multiline recent tail', () => {
|
||||
const logLines = Array.from({ length: 220 }, (_, index) => `line ${index} ${'x'.repeat(120)}`)
|
||||
const sliced = sliceCheckLogTail(logLines.join('\n'))
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ describe('nativeChatNoticeBlock', () => {
|
||||
it('finds the block with a presentation or tone, and none in plain words', () => {
|
||||
const notice = block({ tone: 'warning' })
|
||||
expect(nativeChatNoticeBlock([block({}), notice])).toBe(notice)
|
||||
const presentation = block({ presentation: 'context-cleared' })
|
||||
expect(nativeChatNoticeBlock([block({}), presentation])).toBe(presentation)
|
||||
expect(nativeChatNoticeBlock([block({})])).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user