mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
test: remove redundant cases and strengthen session boundary coverage (#33129)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { runProcessSync } from '@orca/process-host'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { packagedNodePtyFloorDockerArgs } from './run-linux-packaged-node-pty-floor-smoke.mjs'
|
||||
|
||||
@@ -24,13 +28,37 @@ describe('packaged node-pty Linux floor smoke', () => {
|
||||
).toThrow('linux-packaged-node-pty-floor-app-directory-invalid')
|
||||
})
|
||||
|
||||
it('loads node-pty from packaged runtime resources', () => {
|
||||
const childSource = readFileSync(
|
||||
new URL('./linux-packaged-node-pty-floor-child.cjs', import.meta.url),
|
||||
'utf8'
|
||||
it.each([
|
||||
['valid output', 0, 'orca-node-pty-floor-ok', 0],
|
||||
['nonzero exit', 3, 'orca-node-pty-floor-ok', 1],
|
||||
['wrong output', 0, 'unexpected', 1]
|
||||
])('validates packaged node-pty %s', (_name, exitCode, output, expectedCode) => {
|
||||
const resources = mkdtempSync(join(tmpdir(), 'packaged-node-pty-floor-'))
|
||||
const moduleDirectory = join(resources, 'node_modules', 'node-pty')
|
||||
mkdirSync(moduleDirectory, { recursive: true })
|
||||
writeFileSync(
|
||||
join(moduleDirectory, 'index.js'),
|
||||
`module.exports = { spawn() { return {
|
||||
onData(callback) { callback(${JSON.stringify(output)}) },
|
||||
onExit(callback) { queueMicrotask(() => callback({ exitCode: ${exitCode} })) },
|
||||
kill() {}
|
||||
} } }`
|
||||
)
|
||||
|
||||
expect(childSource).toContain("join(resourcesDirectory, 'node_modules', 'node-pty')")
|
||||
expect(childSource).not.toContain("join(resourcesDirectory, 'app.asar'")
|
||||
try {
|
||||
const result = runProcessSync({
|
||||
program: process.execPath,
|
||||
args: [
|
||||
fileURLToPath(new URL('./linux-packaged-node-pty-floor-child.cjs', import.meta.url)),
|
||||
resources
|
||||
]
|
||||
})
|
||||
expect(result.code, result.stderr).toBe(expectedCode)
|
||||
expect(result.stdout).toBe(expectedCode === 0 ? 'orca-node-pty-floor-ok\n' : '')
|
||||
if (expectedCode !== 0) {
|
||||
expect(result.stderr).toContain('packaged node-pty floor smoke failed:')
|
||||
}
|
||||
} finally {
|
||||
rmSync(resources, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -26,18 +26,6 @@ describe('resolveActiveSessionTab', () => {
|
||||
expect(result.retainSelectedSessionTabId).toBe(false)
|
||||
})
|
||||
|
||||
it('keeps the device on a terminal tab when an agent turn flags another terminal active', () => {
|
||||
const result = resolveActiveSessionTab(
|
||||
[terminalTab('agent', true), terminalTab('shell', false)],
|
||||
{
|
||||
pendingActiveSessionTabId: null,
|
||||
selectedSessionTabId: 'shell'
|
||||
}
|
||||
)
|
||||
|
||||
expect(result.activeTab?.id).toBe('shell')
|
||||
})
|
||||
|
||||
it('follows the host when it explicitly navigates this device', () => {
|
||||
const result = resolveActiveSessionTab(
|
||||
[terminalTab('agent', true), browserTab('browser', false)],
|
||||
|
||||
@@ -20,81 +20,13 @@ function baseInput(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replays the route state the session screen actually holds
|
||||
* (`app/h/[hostId]/session/[worktreeId].tsx`): `applySessionTabs` publishes the
|
||||
* tab list and flips `terminalsLoaded`, `handleCloseSessionTab` prunes the
|
||||
* closed tab and nulls the active handle.
|
||||
*/
|
||||
class SessionRouteState {
|
||||
newlyCreatedWorkspace = true
|
||||
tabsLoaded = false
|
||||
tabIds: string[] = []
|
||||
activeHandle: string | null = null
|
||||
sawSessionTabs = false
|
||||
autoCreatedForWorktree = false
|
||||
|
||||
applySessionTabs(tabIds: string[], activeHandle: string | null = tabIds[0] ? 'pty-1' : null) {
|
||||
this.tabIds = tabIds
|
||||
this.activeHandle = activeHandle
|
||||
if (tabIds.length > 0) {
|
||||
this.sawSessionTabs = true
|
||||
}
|
||||
this.tabsLoaded = true
|
||||
}
|
||||
|
||||
closeSessionTab(tabId: string) {
|
||||
this.tabIds = this.tabIds.filter((candidate) => candidate !== tabId)
|
||||
this.activeHandle = null
|
||||
}
|
||||
|
||||
gate(): boolean {
|
||||
return shouldAutoCreateInitialSessionTerminal({
|
||||
newlyCreatedWorkspace: this.newlyCreatedWorkspace,
|
||||
connected: true,
|
||||
tabsLoaded: this.tabsLoaded,
|
||||
visibleTabCount: this.tabIds.length,
|
||||
hasActiveTerminalHandle: this.activeHandle !== null,
|
||||
createInFlight: false,
|
||||
sawSessionTabs: this.sawSessionTabs,
|
||||
autoCreatedForWorktree: this.autoCreatedForWorktree
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
describe('shouldAutoCreateInitialSessionTerminal', () => {
|
||||
it('creates the first terminal for a workspace that hydrates with nothing', () => {
|
||||
const route = new SessionRouteState()
|
||||
route.applySessionTabs([])
|
||||
expect(route.gate()).toBe(true)
|
||||
expect(shouldAutoCreateInitialSessionTerminal(baseInput())).toBe(true)
|
||||
})
|
||||
|
||||
it('does not re-create after the user closes the last tab (#9717, #7345)', () => {
|
||||
const route = new SessionRouteState()
|
||||
route.applySessionTabs(['tab-1'])
|
||||
expect(route.gate()).toBe(false)
|
||||
|
||||
route.closeSessionTab('tab-1')
|
||||
expect(route.tabIds).toHaveLength(0)
|
||||
expect(route.activeHandle).toBeNull()
|
||||
// Emptiness that follows a populated list is a close, not a cold hydrate.
|
||||
expect(route.gate()).toBe(false)
|
||||
})
|
||||
|
||||
it('does not re-create after the consumed creation route remounts empty', () => {
|
||||
const creationRoute = new SessionRouteState()
|
||||
creationRoute.applySessionTabs([])
|
||||
expect(creationRoute.gate()).toBe(true)
|
||||
creationRoute.newlyCreatedWorkspace = false
|
||||
creationRoute.autoCreatedForWorktree = true
|
||||
creationRoute.applySessionTabs(['tab-1'])
|
||||
creationRoute.closeSessionTab('tab-1')
|
||||
expect(creationRoute.gate()).toBe(false)
|
||||
|
||||
const reopenedRoute = new SessionRouteState()
|
||||
reopenedRoute.newlyCreatedWorkspace = creationRoute.newlyCreatedWorkspace
|
||||
reopenedRoute.applySessionTabs([])
|
||||
expect(reopenedRoute.gate()).toBe(false)
|
||||
it('does not re-create once this route has shown session tabs', () => {
|
||||
expect(shouldAutoCreateInitialSessionTerminal(baseInput({ sawSessionTabs: true }))).toBe(false)
|
||||
})
|
||||
|
||||
it('does not create from stale empty state while switching ordinary workspace routes', () => {
|
||||
@@ -109,13 +41,6 @@ describe('shouldAutoCreateInitialSessionTerminal', () => {
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('does not re-create when the host drops every tab mid-session', () => {
|
||||
const route = new SessionRouteState()
|
||||
route.applySessionTabs(['tab-1', 'tab-2'])
|
||||
route.applySessionTabs([], null)
|
||||
expect(route.gate()).toBe(false)
|
||||
})
|
||||
|
||||
it('stays armed while an empty workspace is still loading', () => {
|
||||
expect(shouldAutoCreateInitialSessionTerminal(baseInput({ tabsLoaded: false }))).toBe(false)
|
||||
})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { randomBytes } from 'node:crypto'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { hookMount, performHookAction } from '../test-support/rpc-recording/hook-mount'
|
||||
import { mountFixture } from '../test-support/rpc-recording/recorder-fixture-shape'
|
||||
@@ -82,7 +81,7 @@ function mountDocuments(readReply: () => Promise<RpcResponse>) {
|
||||
unmount: hook.unmount,
|
||||
read(id: string) {
|
||||
const target = tab(id)
|
||||
tabs.current = [target]
|
||||
tabs.current = [...tabs.current.filter((current) => current.id !== id), target]
|
||||
return performHookAction(() => readers?.readMarkdownTab(target))
|
||||
},
|
||||
close(id: string) {
|
||||
@@ -107,15 +106,30 @@ function delayedReply() {
|
||||
}
|
||||
|
||||
describe('mobile Markdown document lifetime', () => {
|
||||
it('releases 40 MiB of clean content after twenty reads and successful closes', async () => {
|
||||
const h = mountDocuments(async () => success(randomBytes(1536 * 1024).toString('base64')))
|
||||
it('releases only the closed clean document and preserves other cached documents', async () => {
|
||||
const readReply = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(success('# a'))
|
||||
.mockResolvedValueOnce(success('# b'))
|
||||
.mockResolvedValueOnce(success('# c'))
|
||||
const h = mountDocuments(readReply)
|
||||
try {
|
||||
for (let index = 0; index < 20; index += 1) {
|
||||
await h.read('tab-' + index)
|
||||
const doc = h.docs.get('tab-' + index)
|
||||
expect(doc?.status === 'ready' && Buffer.byteLength(doc.content)).toBe(2 * 1024 * 1024)
|
||||
await h.close('tab-' + index)
|
||||
}
|
||||
await h.read('a')
|
||||
await h.read('b')
|
||||
await h.read('c')
|
||||
expect(h.docs.get('a')).toMatchObject({ status: 'ready', content: '# a' })
|
||||
expect(h.docs.get('b')).toMatchObject({ status: 'ready', content: '# b' })
|
||||
expect(h.docs.get('c')).toMatchObject({ status: 'ready', content: '# c' })
|
||||
const a = h.docs.get('a')
|
||||
const c = h.docs.get('c')
|
||||
await h.close('b')
|
||||
expect([...h.docs.keys()]).toEqual(['a', 'c'])
|
||||
expect(h.docs.get('a')).toBe(a)
|
||||
expect(h.docs.get('c')).toBe(c)
|
||||
await h.close('a')
|
||||
expect([...h.docs.keys()]).toEqual(['c'])
|
||||
expect(h.docs.get('c')).toBe(c)
|
||||
await h.close('c')
|
||||
expect(h.docs.size).toBe(0)
|
||||
} finally {
|
||||
h.unmount()
|
||||
|
||||
@@ -12,19 +12,28 @@ const readOnlyDoc = {
|
||||
}
|
||||
|
||||
describe('markdownReaderStatusText', () => {
|
||||
it('names a truncated preview and the full file size the way file tabs do', () => {
|
||||
it('names a truncated preview with or without the full file size', () => {
|
||||
expect(
|
||||
markdownReaderStatusText({ ...readOnlyDoc, truncated: true, byteLength: 3_000_000 })
|
||||
).toBe('Preview truncated. File size: 2.9 MB.')
|
||||
expect(markdownReaderStatusText({ ...readOnlyDoc, truncated: true })).toBe('Preview truncated.')
|
||||
})
|
||||
|
||||
it('keeps read-only, stale, and save-error states as before', () => {
|
||||
it('shows read-only, stale and save-error states, and no status for a clean editable document', () => {
|
||||
expect(
|
||||
markdownReaderStatusText({ ...readOnlyDoc, editable: true, readOnlyReason: undefined })
|
||||
).toBeNull()
|
||||
expect(markdownReaderStatusText(readOnlyDoc)).toBe('Read only')
|
||||
expect(
|
||||
markdownReaderStatusText({ ...readOnlyDoc, readOnlyReason: undefined, stale: true })
|
||||
).toBe('Changed on desktop')
|
||||
expect(markdownReaderStatusText({ ...readOnlyDoc, saveError: 'Save failed' })).toBe(
|
||||
'Save failed'
|
||||
)
|
||||
expect(
|
||||
markdownReaderStatusText({
|
||||
...readOnlyDoc,
|
||||
editable: true,
|
||||
readOnlyReason: undefined,
|
||||
saveError: 'Save failed'
|
||||
})
|
||||
).toBe('Save failed')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -13,7 +13,7 @@ function clientWith(sendRequest: RpcClient['sendRequest']): Pick<RpcClient, 'sen
|
||||
}
|
||||
|
||||
describe('mobile session tab activation', () => {
|
||||
it('retries terminal focus once on the authenticated replacement after cutover', async () => {
|
||||
it('retries terminal focus once after cutover', async () => {
|
||||
const sendRequest = vi
|
||||
.fn<RpcClient['sendRequest']>()
|
||||
.mockRejectedValueOnce(new LogicalClientCutoverError())
|
||||
|
||||
@@ -17,6 +17,12 @@ describe('runAcceptedMobileSessionTabsEffects', () => {
|
||||
const activateBrowserTab = vi.fn()
|
||||
const options = {
|
||||
effectiveTabs: [
|
||||
{
|
||||
id: 'browser-other',
|
||||
type: 'browser' as const,
|
||||
isActive: false,
|
||||
browserPageId: 'page-other'
|
||||
},
|
||||
{
|
||||
id: 'browser-1',
|
||||
type: 'browser' as const,
|
||||
@@ -39,7 +45,12 @@ describe('runAcceptedMobileSessionTabsEffects', () => {
|
||||
runAcceptedMobileSessionTabsEffects(options)
|
||||
|
||||
expect(pendingPageId).toBeNull()
|
||||
expect(activateBrowserTab).toHaveBeenCalledTimes(1)
|
||||
expect(activateBrowserTab).toHaveBeenCalledExactlyOnceWith({
|
||||
id: 'browser-1',
|
||||
type: 'browser',
|
||||
isActive: true,
|
||||
browserPageId: 'page-1'
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
@@ -86,7 +97,12 @@ describe('runAcceptedMobileSessionTabsEffects', () => {
|
||||
|
||||
runAcceptedMobileSessionTabsEffects({
|
||||
...base,
|
||||
effectiveTabs: [markdown],
|
||||
effectiveTabs: [
|
||||
{ ...markdown, id: 'browser-dirty', type: 'browser' },
|
||||
{ ...markdown, id: 'markdown-inactive', isActive: false },
|
||||
{ ...markdown, id: 'markdown-clean', isDirty: false },
|
||||
markdown
|
||||
],
|
||||
source: 'stream'
|
||||
})
|
||||
expect(markActiveMarkdownStale).toHaveBeenCalledExactlyOnceWith('markdown-1')
|
||||
|
||||
@@ -240,6 +240,13 @@ describe('shouldActivateOpenedMobileSessionTab', () => {
|
||||
activeTerminalHandle: null
|
||||
})
|
||||
).toBe(false)
|
||||
expect(
|
||||
shouldActivateOpenedMobileSessionTab({
|
||||
...currentTerminalState,
|
||||
activated: false,
|
||||
activeTerminalHandle: 'pty-2'
|
||||
})
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('allows a structured agent-session tab to anchor chat file activation', () => {
|
||||
@@ -255,5 +262,16 @@ describe('shouldActivateOpenedMobileSessionTab', () => {
|
||||
activeTabType: 'agent-session'
|
||||
})
|
||||
).toBe(true)
|
||||
expect(
|
||||
shouldActivateOpenedMobileSessionTab({
|
||||
...currentTerminalState,
|
||||
activated: false,
|
||||
sourceTerminalHandle: null,
|
||||
activeTerminalHandle: null,
|
||||
sourceSessionTabId: 'agent-tab-1',
|
||||
activeSessionTabId: 'agent-tab-2',
|
||||
activeTabType: 'agent-session'
|
||||
})
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { MobileSessionTab } from './mobile-session-route-types'
|
||||
import {
|
||||
LAUNCHED_SELECTION_SNAPSHOT_BUDGET,
|
||||
isLaunchedSurfaceListed,
|
||||
launchedSelection,
|
||||
pendingSelectionHandle,
|
||||
@@ -101,13 +100,13 @@ describe('resolveLaunchedSelection', () => {
|
||||
|
||||
it('waits without a countdown until the reply, then gives up after the budget', () => {
|
||||
let selection: PendingSessionSelection | null = launchedSelection('l1', { pane: PANE }, null)
|
||||
for (let snapshot = 0; snapshot < LAUNCHED_SELECTION_SNAPSHOT_BUDGET * 2; snapshot += 1) {
|
||||
for (let snapshot = 0; snapshot < 10; snapshot += 1) {
|
||||
selection = resolveLaunchedSelection(selection, []).selection
|
||||
}
|
||||
expect(selection).toEqual(launchedSelection('l1', { pane: PANE }, null))
|
||||
|
||||
selection = withLaunchReply(selection, 'l1', { handle: 'term_9' })
|
||||
for (let snapshot = 1; snapshot < LAUNCHED_SELECTION_SNAPSHOT_BUDGET; snapshot += 1) {
|
||||
for (let snapshot = 1; snapshot < 5; snapshot += 1) {
|
||||
const next = resolveLaunchedSelection(selection, [chatTab('other', 'claude_other')])
|
||||
expect(next.landedTabId).toBeNull()
|
||||
expect(next.selection?.kind).toBe('launched')
|
||||
|
||||
@@ -10,6 +10,7 @@ const native = vi.hoisted(() => {
|
||||
platform,
|
||||
remove,
|
||||
dismiss: vi.fn(),
|
||||
setLeaveDrafts: vi.fn(),
|
||||
addEventListener: vi.fn((_event: string, _handler: () => boolean) => ({ remove }))
|
||||
}
|
||||
})
|
||||
@@ -67,7 +68,7 @@ function scopeWith(markdownDocs: Map<string, MarkdownDocState>): MobileSessionMa
|
||||
setMarkdownDocs: () => {},
|
||||
discardMarkdownTarget: null,
|
||||
setDiscardMarkdownTarget: () => {},
|
||||
setLeaveDrafts: () => {},
|
||||
setLeaveDrafts: native.setLeaveDrafts,
|
||||
markdownSaveSeqRef: { current: new Map() },
|
||||
markdownSaveInFlightRef: { current: new Set() },
|
||||
showToast: () => {},
|
||||
@@ -107,6 +108,7 @@ beforeEach(() => {
|
||||
native.addEventListener.mockClear()
|
||||
native.remove.mockClear()
|
||||
native.dismiss.mockClear()
|
||||
native.setLeaveDrafts.mockClear()
|
||||
leaves.back = null
|
||||
leaves.replaced = []
|
||||
})
|
||||
@@ -166,6 +168,9 @@ describe("the session's hardware back gate", () => {
|
||||
})
|
||||
expect(left).not.toHaveBeenCalled()
|
||||
expect(native.dismiss).toHaveBeenCalledTimes(1)
|
||||
expect(native.setLeaveDrafts).toHaveBeenCalledExactlyOnceWith([
|
||||
{ tabId: 'tab-1', title: 'Markdown', content: 'edited' }
|
||||
])
|
||||
})
|
||||
|
||||
it('registers once as the drafts change, and the newest drafts are what a press reads', () => {
|
||||
|
||||
@@ -88,6 +88,10 @@ describe('applyClosedTabTombstones', () => {
|
||||
|
||||
it('stops suppressing and clears an expired tombstone even if still present', () => {
|
||||
const tombstones = new Map([['a', 5000]])
|
||||
expect(
|
||||
applyClosedTabTombstones([tab('a'), tab('b')], tombstones, 4999).map((t) => t.id)
|
||||
).toEqual(['b'])
|
||||
expect(tombstones.has('a')).toBe(true)
|
||||
const result = applyClosedTabTombstones([tab('a'), tab('b')], tombstones, 5000)
|
||||
expect(result.map((t) => t.id)).toEqual(['a', 'b'])
|
||||
expect(tombstones.has('a')).toBe(false)
|
||||
|
||||
@@ -126,6 +126,9 @@ describe("a launch pane's verdict in the window", () => {
|
||||
it('never touches a pane no launch laid out', () => {
|
||||
apply({ kind: 'withdrawn' }, '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d')
|
||||
expect(launchTab()).toBeDefined()
|
||||
expect(launchTab()?.agentLaunchPane).toEqual({ leafId: LEAF, operationId: 'op-1' })
|
||||
expect(agentLaunchPanePrompt(tabId)).toBe('fix the build')
|
||||
expect(applyClosedTerminalLeafNotice).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -227,12 +227,13 @@ describe("a new tab during startup, before this computer's runtime answered", ()
|
||||
it.each(['claude', 'opencode'] as const)(
|
||||
'opens the %s chat when the capabilities arrive within the wait, after a failed probe',
|
||||
async (agent) => {
|
||||
vi.useFakeTimers()
|
||||
const route = awaitingRoute(agent)
|
||||
const relaunch = fakeRelaunch()
|
||||
|
||||
const result = launchOnceHostAnswered(route, { ...args, agent }, startupPlan, relaunch)
|
||||
// The failed probe has answered; the launch is still waiting for one that lands.
|
||||
await new Promise((resolve) => setTimeout(resolve, 20))
|
||||
await vi.advanceTimersByTimeAsync(20)
|
||||
expect(relaunch).not.toHaveBeenCalled()
|
||||
await capabilitiesLand()
|
||||
|
||||
@@ -240,6 +241,7 @@ describe("a new tab during startup, before this computer's runtime answered", ()
|
||||
kind: 'structured',
|
||||
sessionId: 'session-1'
|
||||
})
|
||||
expect(relaunch).toHaveBeenCalledOnce()
|
||||
expect(relaunch.mock.calls[0]?.[0]).toMatchObject({
|
||||
agentSessionLaunchPlan: { route: 'structured-native-chat' }
|
||||
})
|
||||
|
||||
@@ -94,19 +94,37 @@ describe('agentSessionFingerprintConflict', () => {
|
||||
describe('admitAgentSessionMutation', () => {
|
||||
const base = { envelope: envelope(), hostFingerprint: 'f'.repeat(64), lease: LEASE }
|
||||
|
||||
it('admits a first-time operation under a live lease at the expected fence', () => {
|
||||
it('admits a matching first-time operation and refuses a mismatched payload', () => {
|
||||
expect(admitAgentSessionMutation({ ...base, ledger: ADMIT('f'.repeat(64)) }).decision).toBe(
|
||||
'admit'
|
||||
)
|
||||
expect(
|
||||
admitAgentSessionMutation({
|
||||
...base,
|
||||
hostFingerprint: 'a'.repeat(64),
|
||||
ledger: ADMIT('a'.repeat(64))
|
||||
})
|
||||
).toMatchObject({
|
||||
decision: 'refused',
|
||||
refusal: {
|
||||
code: 'agent_session_operation_conflict',
|
||||
details: { reason: 'fingerprintMismatch' }
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('replays a recorded operation without re-checking the fence', () => {
|
||||
it('replays a recorded outcome even while the lease is unreconciled', () => {
|
||||
const recorded = {
|
||||
...row('f'.repeat(64)),
|
||||
outcome: { status: 'succeeded' as const, sessionId: 'created-session' }
|
||||
}
|
||||
const admission = admitAgentSessionMutation({
|
||||
...base,
|
||||
envelope: envelope({ expectedRuntimeFence: 1 }),
|
||||
ledger: { decision: 'replay', row: row('f'.repeat(64)) }
|
||||
lease: { ...LEASE, unreconciled: true },
|
||||
ledger: { decision: 'replay', row: recorded }
|
||||
})
|
||||
expect(admission.decision).toBe('replay')
|
||||
expect(admission).toEqual({ decision: 'replay', row: recorded })
|
||||
})
|
||||
|
||||
it('admits a writer whatever fence the client last saw', () => {
|
||||
|
||||
@@ -156,6 +156,7 @@ describe('retention', () => {
|
||||
const late = agentSessionOperationExpiry(NOW + 10_000, NOW)
|
||||
expect(late).toBe(agentSessionOperationExpiry(NOW + 10_000, NOW + 10_000))
|
||||
expect(late).toBeGreaterThan(agentSessionOperationExpiry(NOW, NOW))
|
||||
expect(agentSessionOperationExpiry(NOW, NOW + 10_000)).toBe(late)
|
||||
})
|
||||
|
||||
it('drops only rows past their own expiry', () => {
|
||||
|
||||
@@ -71,11 +71,15 @@ describe('current thread goal', () => {
|
||||
|
||||
it('takes the latest transition by sequence for items held unordered', () => {
|
||||
const paused = goal({ status: 'paused' })
|
||||
const latest = goalRow(5, 'set', paused)
|
||||
const unordered = new Map([
|
||||
['b', goalRow(5, 'set', paused)],
|
||||
['b', latest],
|
||||
['a', goalRow(2, 'set')]
|
||||
])
|
||||
expect(currentAgentSessionThreadGoalSourceBySequence(unordered.values())?.goal).toEqual(paused)
|
||||
expect(currentAgentSessionThreadGoalSourceBySequence(unordered.values())).toEqual({
|
||||
item: latest,
|
||||
goal: paused
|
||||
})
|
||||
expect(currentAgentSessionThreadGoalSourceBySequence([])?.goal).toBeUndefined()
|
||||
})
|
||||
|
||||
|
||||
@@ -165,20 +165,6 @@ describe('a refusal a failed request carried in its error', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it("keeps a journal refusal's reason", () => {
|
||||
const corrupt = {
|
||||
code: 'agent_session_journal_unreadable',
|
||||
details: { reason: 'journalCorrupt' }
|
||||
}
|
||||
// Not "Orca couldn't confirm what happened": the host refused it before running it.
|
||||
expect(
|
||||
agentSessionThrownFailure(saved({ ...payload, data: { refusal: corrupt } }), 'runtime_error')
|
||||
).toEqual({
|
||||
kind: 'refused',
|
||||
...corrupt
|
||||
})
|
||||
})
|
||||
|
||||
it("degrades a reason another build added to the code's own words", () => {
|
||||
const newer = {
|
||||
code: 'agent_session_journal_unreadable',
|
||||
|
||||
Reference in New Issue
Block a user