mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
test: remove redundant cases and strengthen state-transition assertions (#33309)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -38,10 +38,8 @@ describe('mobile terminal records', () => {
|
||||
})
|
||||
|
||||
try {
|
||||
for (let comparison = 0; comparison < 1_000; comparison += 1) {
|
||||
expect(mobileTerminalThemesEqual(darkTheme, equivalentTheme)).toBe(true)
|
||||
expect(mobileTerminalThemesEqual(darkTheme, lightTheme)).toBe(false)
|
||||
}
|
||||
expect(mobileTerminalThemesEqual(darkTheme, equivalentTheme)).toBe(true)
|
||||
expect(mobileTerminalThemesEqual(darkTheme, lightTheme)).toBe(false)
|
||||
} finally {
|
||||
stringify.mockRestore()
|
||||
}
|
||||
|
||||
@@ -166,10 +166,6 @@ describe('getMobileSessionTabTitle', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('keeps generic status titles unstripped when no agent identity is known', () => {
|
||||
expect(getMobileSessionTabTitle(terminalTab('✳ investigating'))).toBe('✳ investigating')
|
||||
})
|
||||
|
||||
it('preserves browser title fallbacks after moving the helper out of the route', () => {
|
||||
const blankBrowserTab: Extract<MobileSessionTab, { type: 'browser' }> = {
|
||||
type: 'browser',
|
||||
|
||||
@@ -67,20 +67,6 @@ describe('resolveTerminalViewportResubscribe', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('keeps holding on absent dims across repeated frames without spending budget', () => {
|
||||
for (let frame = 0; frame < 50; frame += 1) {
|
||||
expect(
|
||||
resolveTerminalViewportResubscribe({
|
||||
hostCols: null,
|
||||
hostRows: null,
|
||||
viewportMeasured: true,
|
||||
viewport: PHONE,
|
||||
attempts: 0
|
||||
}).kind
|
||||
).toBe('hold')
|
||||
}
|
||||
})
|
||||
|
||||
it('converges when host dims match the measured viewport', () => {
|
||||
expect(
|
||||
resolveTerminalViewportResubscribe({
|
||||
@@ -307,7 +293,10 @@ describe('runTerminalViewportFitPass', () => {
|
||||
}
|
||||
}
|
||||
|
||||
const settle = () => new Promise((resolve) => setTimeout(resolve, 0))
|
||||
const settle = async () => {
|
||||
await Promise.resolve()
|
||||
await Promise.resolve()
|
||||
}
|
||||
|
||||
it('holds without any teardown when host dims are absent (STA-3337 regression)', async () => {
|
||||
const h = makeHarness({ viewportMeasured: true, viewport: PHONE })
|
||||
|
||||
@@ -31,25 +31,26 @@ function makeEngine(mutations: Partial<PrActionMutations>) {
|
||||
describe('PrActionsEngine — scoped busy clear (overlapping actions)', () => {
|
||||
it('a late-resolving action does not clear a newer action busy state', async () => {
|
||||
const slow = deferred<GitHubPrMutationOutcome>()
|
||||
const newer = deferred<GitHubPrMutationOutcome>()
|
||||
const engine = makeEngine({
|
||||
// merge resolves slowly; state resolves immediately.
|
||||
mergePR: () => slow.promise,
|
||||
updatePRState: async () => ({ ok: true })
|
||||
updatePRState: () => newer.promise
|
||||
})
|
||||
|
||||
// Start merge (sets busy = merge) but don't let it resolve yet.
|
||||
const mergePromise = engine.merge()
|
||||
expect(engine.isBusy({ kind: 'merge' })).toBe(true)
|
||||
|
||||
// While merge is in-flight, run state to completion (sets then clears busy=state).
|
||||
await engine.updateState('closed')
|
||||
// State began after merge, so it overwrote busy to 'state', then cleared its own.
|
||||
expect(engine.isBusy({ kind: 'state' })).toBe(false)
|
||||
const statePromise = engine.updateState('closed')
|
||||
expect(engine.isBusy({ kind: 'state' })).toBe(true)
|
||||
|
||||
// Now let the slow merge finish. Its finally must NOT clear a busy it no longer owns.
|
||||
slow.resolve({ ok: true })
|
||||
await mergePromise
|
||||
// busy stays null (state already cleared); the key point is merge didn't clobber.
|
||||
expect(engine.isBusy({ kind: 'state' })).toBe(true)
|
||||
|
||||
newer.resolve({ ok: true })
|
||||
await statePromise
|
||||
expect(engine.busy).toBeNull()
|
||||
})
|
||||
|
||||
|
||||
@@ -116,6 +116,8 @@ describe('isMutablePRConversationComment', () => {
|
||||
it('requires a positive integer id', () => {
|
||||
expect(isMutablePRConversationComment(comment({ id: 0 }))).toBe(false)
|
||||
expect(isMutablePRConversationComment(comment({ id: -1 }))).toBe(false)
|
||||
expect(isMutablePRConversationComment(comment({ id: 1.5 }))).toBe(false)
|
||||
expect(isMutablePRConversationComment(comment({ id: Number.MAX_SAFE_INTEGER + 1 }))).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ describe('shouldShowSessionHeaderChecksAction', () => {
|
||||
shouldShowSessionHeaderChecksAction({
|
||||
isFolderWorkspaceRoute: false,
|
||||
repoContextLoaded: false,
|
||||
hostedChecksSupported: false
|
||||
hostedChecksSupported: true
|
||||
})
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
@@ -64,9 +64,9 @@ describe('resolveTabStripScrollOffset', () => {
|
||||
tabWidth: 128,
|
||||
viewportWidth: 0,
|
||||
contentWidth: 0,
|
||||
currentOffset: 0
|
||||
currentOffset: 150
|
||||
})
|
||||
).toBe(0)
|
||||
).toBe(150)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -2,11 +2,7 @@ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync
|
||||
import { tmpdir } from 'node:os'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
isProfileStateSqliteAvailable,
|
||||
openProfileStateDatabase,
|
||||
profileStateDatabaseFile
|
||||
} from './profile-state-database'
|
||||
import { openProfileStateDatabase, profileStateDatabaseFile } from './profile-state-database'
|
||||
import {
|
||||
createProfileStateStore as createProfileStateStoreImpl,
|
||||
type ProfileStateStoreFactoryOptions
|
||||
@@ -138,18 +134,6 @@ describe('profile state Store authority factory', () => {
|
||||
expect(() => initial.value.takeParsedState?.()).toThrow('already been consumed')
|
||||
})
|
||||
|
||||
it('uses a capability probe that remains false on a Node 18-style host', () => {
|
||||
const original = process.getBuiltinModule
|
||||
vi.spyOn(process, 'getBuiltinModule').mockImplementation((id) => {
|
||||
if (id === 'node:sqlite') {
|
||||
return undefined
|
||||
}
|
||||
return original(id)
|
||||
})
|
||||
|
||||
expect(isProfileStateSqliteAvailable()).toBe(false)
|
||||
})
|
||||
|
||||
it('imports legacy state by default and preserves its original bytes', () => {
|
||||
const options = createOptions()
|
||||
const source = JSON.stringify({ settings: { theme: 'dark' }, unknownDomain: { keep: true } })
|
||||
@@ -195,21 +179,6 @@ describe('profile state Store authority factory', () => {
|
||||
}
|
||||
)
|
||||
|
||||
it('migrates JSON and constructs a SQLite Store', () => {
|
||||
const options = createOptions()
|
||||
const source = JSON.stringify({ settings: { theme: 'dark' }, unknownDomain: { keep: true } })
|
||||
writeFileSync(options.dataFile, source)
|
||||
|
||||
const result = createProfileStateStore({ ...options })
|
||||
|
||||
expect(result.backend).toBe('sqlite')
|
||||
expect(result.classification).toBe('json-only')
|
||||
expect(result.migrated).toBe(true)
|
||||
expect(result.store.getSettings().theme).toBe('dark')
|
||||
expect(existsSync(options.databaseFile)).toBe(true)
|
||||
expect(readFileSync(options.dataFile, 'utf8')).toBe(source)
|
||||
})
|
||||
|
||||
it('reuses the candidate authority after the legacy export is removed', () => {
|
||||
const options = createOptions()
|
||||
writeFileSync(options.dataFile, JSON.stringify({ settings: { theme: 'dark' } }))
|
||||
@@ -224,28 +193,6 @@ describe('profile state Store authority factory', () => {
|
||||
expect(result.store.getSettings().theme).toBe('dark')
|
||||
})
|
||||
|
||||
it('fails closed instead of falling back to a stale JSON mirror when SQLite is missing', () => {
|
||||
const options = createOptions()
|
||||
writeFileSync(options.dataFile, JSON.stringify({ settings: { theme: 'dark' } }))
|
||||
const migrated = createProfileStateStore({ ...options })
|
||||
const exportPath = profileStateJsonExportPath(options.dataFile, 1)
|
||||
expect(existsSync(exportPath)).toBe(true)
|
||||
migrated.store.freezeWrites()
|
||||
rmSync(options.databaseFile)
|
||||
|
||||
expect(() => createProfileStateStore({ ...options })).toThrow(ProfileStateRecoveryRequiredError)
|
||||
})
|
||||
|
||||
it('does not let candidate mode re-import JSON after SQLite was established', () => {
|
||||
const options = createOptions()
|
||||
writeFileSync(options.dataFile, JSON.stringify({ settings: { theme: 'dark' } }))
|
||||
const migrated = createProfileStateStore({ ...options })
|
||||
migrated.store.freezeWrites()
|
||||
rmSync(options.databaseFile)
|
||||
|
||||
expect(() => createProfileStateStore({ ...options })).toThrow(ProfileStateRecoveryRequiredError)
|
||||
})
|
||||
|
||||
it('reopens JSON after an explicit rollback removes the SQLite export marker', () => {
|
||||
const options = createOptions()
|
||||
writeFileSync(options.dataFile, JSON.stringify({ settings: { theme: 'dark' } }))
|
||||
|
||||
@@ -107,19 +107,6 @@ it('keeps an unacknowledged close pending, then holds abort until the terminated
|
||||
expect(vi.getTimerCount()).toBe(0)
|
||||
})
|
||||
|
||||
it('completes a clean close only after the acknowledged worker exits', async () => {
|
||||
const { client, worker, onFailure } = await createClient()
|
||||
let settled = false
|
||||
const closing = client.close().then(() => (settled = true))
|
||||
await waitForPoll()
|
||||
expect(settled).toBe(false)
|
||||
worker.emit('exit', 0)
|
||||
await closing
|
||||
expect(worker.terminate).not.toHaveBeenCalled()
|
||||
expect(onFailure).not.toHaveBeenCalled()
|
||||
expect(vi.getTimerCount()).toBe(0)
|
||||
})
|
||||
|
||||
it('joins an aborted writer without reporting a close request that was never sent', async () => {
|
||||
const { client, worker } = await createClient()
|
||||
const aborting = client.abort()
|
||||
|
||||
@@ -88,28 +88,6 @@ describe('writer protocol refuses uncertain acknowledgements', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('keeps an unanswered request pending past the slow warning without faulting', async () => {
|
||||
const notify = vi.fn()
|
||||
const client = clientFor('', undefined, notify)
|
||||
await client.ready
|
||||
let settled = false
|
||||
const write = client
|
||||
.writeSerializedDomains([{ domain: 'settings', payload: '{}' }])
|
||||
.finally(() => (settled = true))
|
||||
await new Promise((resolve) => setTimeout(resolve, 200))
|
||||
expect(settled).toBe(false)
|
||||
expect(notify).not.toHaveBeenCalled()
|
||||
expect(() => client.assertWritable()).toThrow(
|
||||
expect.objectContaining({ code: 'profile-state-writer-busy' })
|
||||
)
|
||||
const rejected = expect(write).rejects.toMatchObject({
|
||||
code: 'profile-state-writer-aborted',
|
||||
outcome: 'indeterminate'
|
||||
})
|
||||
await client.abort()
|
||||
await rejected
|
||||
})
|
||||
|
||||
it('reports an idle writer exit even without a subsequent save', async () => {
|
||||
const notify = vi.fn()
|
||||
const client = clientFor('', undefined, notify, 'setTimeout(() => process.exit(1), 50)')
|
||||
|
||||
@@ -515,7 +515,15 @@ describe.each(([APPLY, MOVE, SAVE] as const).map((name) => [name, WRITERS[name]]
|
||||
it.each(WORKSPACE_LAYOUT_PROFILES.map((profile) => [profile.name, profile] as const))(
|
||||
'%s: the same session, key order aside, as its load normalizes nothing',
|
||||
(_name, profile) => {
|
||||
for (const call of write(profile).flat()) {
|
||||
const calls = write(profile).flat()
|
||||
const skipsPatch =
|
||||
profile.name === 'empty profile'
|
||||
? writer !== SAVE
|
||||
: writer === MOVE
|
||||
? profile.hostId !== LOCAL_EXECUTION_HOST_ID
|
||||
: writer === SAVE && profile.hostId.startsWith('runtime:')
|
||||
expect(calls.length === 0).toBe(skipsPatch)
|
||||
for (const call of calls) {
|
||||
expect(recordsDiffering(call.stored, call.before)).toEqual([])
|
||||
expect(stableJson(call.patched)).toBe(stableJson(call.full))
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ describe('folder workspace sort stamps', () => {
|
||||
|
||||
operations.updateFolderWorkspace(id, { smartTier: undefined, smartAt: undefined })
|
||||
expect(operations.getFolderWorkspace(id)?.smartTier).toBeUndefined()
|
||||
expect(operations.getFolderWorkspace(id)?.smartAt).toBeUndefined()
|
||||
// An update that does not name the tier leaves it alone.
|
||||
operations.updateFolderWorkspace(id, { smartTier: 'working', smartAt: 10 })
|
||||
operations.updateFolderWorkspace(id, { comment: 'x' })
|
||||
|
||||
@@ -71,6 +71,8 @@ describe('installMouseHideWhileTyping', () => {
|
||||
const disposable = installMouseHideWhileTyping(terminal, container as unknown as HTMLElement)
|
||||
disposable.dispose()
|
||||
|
||||
expect(terminal.onData.mock.results[0]?.value.dispose).toHaveBeenCalledTimes(1)
|
||||
|
||||
expect(container.style.cursor).toBe('')
|
||||
// After dispose, mousemove should not crash and cursor stays restored.
|
||||
container.dispatchEvent(new Event('mousemove'))
|
||||
|
||||
@@ -111,7 +111,7 @@ describe('useOsc52ClipboardDefaultOnNotice', () => {
|
||||
expect(storeState.clearOsc52ClipboardDefaultOnNotice).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('dedupes the StrictMode double-invoke with a stable toast id', async () => {
|
||||
it('assigns a stable toast id for notice deduplication', async () => {
|
||||
// Why pin the id: the effect re-runs against the same closure, so the early return
|
||||
// cannot catch the second pass — sonner collapses it only because the id matches.
|
||||
await mountProbe(true)
|
||||
|
||||
@@ -760,9 +760,12 @@ describe('createPaneForegroundAgentTracker', () => {
|
||||
onCommandFinished: () => tracker.onCommandFinished()
|
||||
})
|
||||
|
||||
lifecycle.handlePtyData("printf '\\033]133;A\\007\\033]777;agent=codex\\007\\033]133;D;0\\007'")
|
||||
lifecycle.handlePtyData('\x1b]133;C\x07\x1b]777;agent=codex\x07')
|
||||
await flushSettleRead(COMMAND_SETTLE_MS)
|
||||
|
||||
expect(remoteRead).toHaveBeenCalledExactlyOnceWith('ssh:conn-1@@pty-9', {
|
||||
expectedIncarnationId: 'inc-remote'
|
||||
})
|
||||
expect(publish).not.toHaveBeenCalledWith(
|
||||
expect.objectContaining({ agent: 'codex', shellForeground: false })
|
||||
)
|
||||
|
||||
@@ -268,7 +268,7 @@ describe('startParkedTerminalByteWatcher', () => {
|
||||
dispose()
|
||||
})
|
||||
|
||||
it('suppresses the completion OS notification when only terminal attention is on', async () => {
|
||||
it('forwards completion to notification policy when only terminal attention is on', async () => {
|
||||
mockStoreState.settings = {
|
||||
...mockStoreState.settings,
|
||||
experimentalTerminalAttention: true,
|
||||
|
||||
@@ -10,7 +10,7 @@ describe('parseOsc7', () => {
|
||||
expect(parseOsc7('file:///home/jin')).toBe('/home/jin')
|
||||
})
|
||||
|
||||
it('percent-decodes spaces and unicode', () => {
|
||||
it('percent-decodes spaces', () => {
|
||||
expect(parseOsc7('file:///home/jin/my%20code')).toBe('/home/jin/my code')
|
||||
})
|
||||
|
||||
|
||||
@@ -24,12 +24,13 @@ describe('resolveAgentStartupPlanInputs with extra agent args', () => {
|
||||
extraAgentArgs: '--model opus --add-dir "my docs"'
|
||||
})
|
||||
const plan = buildAgentStartupPlan({ ...inputs, prompt: 'go' })
|
||||
expect(plan?.launchCommand).toContain("'--add-dir' 'my docs'")
|
||||
expect(plan?.launchCommand).toContain('opus')
|
||||
expect(plan?.launchCommand).not.toContain('sonnet')
|
||||
expect(plan?.launchCommand).toContain('--dangerously-skip-permissions')
|
||||
})
|
||||
|
||||
it('builds the same command the desktop path builds', () => {
|
||||
it('matches explicitly composed launch defaults and extras', () => {
|
||||
const headless = resolveAgentStartupPlanInputs({
|
||||
agent: 'claude',
|
||||
settings: SETTINGS,
|
||||
|
||||
Reference in New Issue
Block a user