mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
test: remove redundant cases and strengthen persistence, terminal and mobile checks (#33318)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -61,6 +61,8 @@ describe('useMissingWorktreeBounce', () => {
|
||||
|
||||
// Why: navigation lands after the render, so the pre-unmount renders must not re-fire.
|
||||
render('repo::wt', 'missing')
|
||||
render('repo::wt', 'unknown')
|
||||
render('repo::wt', 'missing')
|
||||
expect(bounce).toHaveBeenCalledTimes(1)
|
||||
act(() => renderer?.unmount())
|
||||
})
|
||||
|
||||
@@ -587,40 +587,6 @@ describe('useMobileNativeChatAnswerSend', () => {
|
||||
expect(sendRequest).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('does not write a successor after the prior delivery becomes ambiguous', async () => {
|
||||
let rejectFirst: (error: Error) => void = () => undefined
|
||||
const sendRequest = vi
|
||||
.fn()
|
||||
.mockImplementationOnce(
|
||||
() =>
|
||||
new Promise((_resolve, reject) => {
|
||||
rejectFirst = reject
|
||||
})
|
||||
)
|
||||
.mockResolvedValue(acceptedResponse())
|
||||
await mount({ sendRequest } as unknown as RpcClient, vi.fn())
|
||||
|
||||
let first: Promise<boolean> | undefined
|
||||
let second: Promise<boolean> | undefined
|
||||
await act(async () => {
|
||||
first = answerSend?.answerAsk(TABS_OR_SPACES, [{ indices: [0] }])
|
||||
await Promise.resolve()
|
||||
})
|
||||
await act(async () => {
|
||||
second = answerSend?.answerAsk(TABS_OR_SPACES, [{ indices: [1] }])
|
||||
await Promise.resolve()
|
||||
})
|
||||
await act(async () => {
|
||||
rejectFirst(markRpcDeliveryUnknown(new Error('Connection closed')))
|
||||
await Promise.resolve()
|
||||
})
|
||||
|
||||
// The key may have landed; a blind successor could double-step the selector.
|
||||
await expect(first).resolves.toBe(false)
|
||||
await expect(second).resolves.toBe(false)
|
||||
expect(sendRequest).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('tells the user when a queued answer is fenced instead of dropping it silently', async () => {
|
||||
const onSendError = vi.fn()
|
||||
let rejectFirst: (error: Error) => void = () => undefined
|
||||
|
||||
@@ -149,17 +149,6 @@ describe('useMobileNativeChatDrafts glued pending sends', () => {
|
||||
expect(renderedPendingTexts(history, state)).toEqual(['run the tests', 'again'])
|
||||
})
|
||||
|
||||
// FP2: an older turn reads like the concatenation of two much later sends.
|
||||
it('keeps and renders both bubbles when an older turn spells them out', async () => {
|
||||
const history = [userTurn('m1', 'fix the bug', 1000), assistantTurn('m2', 'fixed', 1100)]
|
||||
await mount(history)
|
||||
rapidSend('fix the', 'bug')
|
||||
|
||||
await update([...history])
|
||||
expect(pendingTexts()).toEqual(['fix the', 'bug'])
|
||||
expect(renderedPendingTexts(history, state)).toEqual(['fix the', 'bug'])
|
||||
})
|
||||
|
||||
// A send of the same text as a message a Stop took back is resent as a new message: its bubble
|
||||
// waits beside the stopped row for that new row, never for the stopped one.
|
||||
it('keeps a resend bubble beside a stopped row of the same text until its own row lands', async () => {
|
||||
@@ -292,26 +281,6 @@ describe('useMobileNativeChatDrafts glued pending sends', () => {
|
||||
expect(state?.pending).toEqual([])
|
||||
})
|
||||
|
||||
// The same read seen one reconnect later: the send left, the agent echoed it,
|
||||
// and only then did the first authoritative read land.
|
||||
it('retires a hydration-time send after a reconnect delivered the read late', async () => {
|
||||
await mount([], true)
|
||||
act(() => send('run the tests'))
|
||||
// Still loading: the first read never arrived before the drop.
|
||||
await update([], true)
|
||||
|
||||
const afterReconnect = [
|
||||
userTurn('m1', 'run the tests', 5000),
|
||||
assistantTurn('m2', 'ok', 5100)
|
||||
]
|
||||
await update(afterReconnect)
|
||||
expect(state?.pending).toEqual([])
|
||||
|
||||
// Nothing later resurrects it, and the run stays glue-capable.
|
||||
await update([...afterReconnect, assistantTurn('m3', 'all green', 6000)])
|
||||
expect(state?.pending).toEqual([])
|
||||
})
|
||||
|
||||
// A reconnect leaves this session's own retained history on screen, so sends
|
||||
// made across it own a real boundary already. Moving it onto the read that
|
||||
// follows would push it past their own glued row and strand both bubbles.
|
||||
|
||||
@@ -105,8 +105,12 @@ describe('useMobileNativeChatFileSearch', () => {
|
||||
})
|
||||
|
||||
it('cancels an in-flight query on a cache hit so a stale result cannot clobber it', async () => {
|
||||
const sendRequest = vi.fn(async (_method: string, params: { query: string }) =>
|
||||
rpcSuccess(params.query === 'app' ? ['src/app.ts'] : ['src/beta.ts'])
|
||||
let resolveBeta: (reply: Awaited<ReturnType<RpcClient['sendRequest']>>) => void = () => {}
|
||||
const betaReply = new Promise<Awaited<ReturnType<RpcClient['sendRequest']>>>((resolve) => {
|
||||
resolveBeta = resolve
|
||||
})
|
||||
const sendRequest = vi.fn((_method: string, params: { query: string }) =>
|
||||
params.query === 'app' ? Promise.resolve(rpcSuccess(['src/app.ts'])) : betaReply
|
||||
)
|
||||
await mount(fakeClient({ sendRequest }))
|
||||
|
||||
@@ -132,6 +136,18 @@ describe('useMobileNativeChatFileSearch', () => {
|
||||
expect(
|
||||
sendRequest.mock.calls.filter(([, params]) => (params as { query: string }).query === 'beta')
|
||||
).toHaveLength(0)
|
||||
|
||||
act(() => state?.loadNativeChatFiles('beta'))
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(120)
|
||||
})
|
||||
expect(sendRequest).toHaveBeenCalledTimes(2)
|
||||
act(() => state?.loadNativeChatFiles('app'))
|
||||
await act(async () => {
|
||||
resolveBeta(rpcSuccess(['src/beta.ts']))
|
||||
await betaReply
|
||||
})
|
||||
expect(state?.nativeChatFilePaths).toEqual(['src/app.ts'])
|
||||
})
|
||||
|
||||
it('reloads the legacy inventory when the logical authority epoch advances', async () => {
|
||||
|
||||
@@ -198,12 +198,15 @@ describe('PrActionsEngine — auto-merge optimistic revert', () => {
|
||||
expect(engine.error).toBe('connection lost')
|
||||
})
|
||||
|
||||
it('keeps the optimistic value through success until refetch', async () => {
|
||||
const mutations = fakeMutations()
|
||||
it('shows the optimistic toggle while pending and clears it on success', async () => {
|
||||
const pending = deferred<{ ok: true }>()
|
||||
const mutations = fakeMutations({ setPRAutoMerge: vi.fn(() => pending.promise) })
|
||||
const { engine, refetch } = makeEngine(mutations)
|
||||
await engine.setAutoMerge(true)
|
||||
// After success the optimism clears to authoritative (refetch supplies truth).
|
||||
expect(engine.resolveAutoMerge(true)).toBe(true)
|
||||
const operation = engine.setAutoMerge(true)
|
||||
expect(engine.resolveAutoMerge(false)).toBe(true)
|
||||
pending.resolve({ ok: true })
|
||||
await operation
|
||||
expect(engine.resolveAutoMerge(false)).toBe(false)
|
||||
expect(refetch).toHaveBeenCalledOnce()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -65,11 +65,6 @@ describe('deriveMobilePrBranchContext', () => {
|
||||
expect(result.headSha).toBeNull()
|
||||
})
|
||||
|
||||
it('derives branch from status.branch', () => {
|
||||
const result = deriveMobilePrBranchContext(status({ branch: 'topic' }), null)
|
||||
expect(result.branch).toBe('topic')
|
||||
})
|
||||
|
||||
it('returns null branch when status.branch is absent', () => {
|
||||
const result = deriveMobilePrBranchContext(status({ branch: undefined }), null)
|
||||
expect(result.branch).toBeNull()
|
||||
|
||||
@@ -9,14 +9,15 @@ import {
|
||||
const STABLE_LEAF_ID = '00000000-0000-4000-8000-000000000001'
|
||||
|
||||
describe('remapManuallyUnreadTurnPaneKeys', () => {
|
||||
it('promotes legacy pane keys to the restored stable leaf like clear-completed cutoffs', () => {
|
||||
it('promotes legacy pane keys and keeps the newest value on collision', () => {
|
||||
const remap = new Map([['tab-1', new Map([['pane:1', STABLE_LEAF_ID]])]])
|
||||
const turns = { 'tab-1:pane:1': 42, 'tab-2:pane:9': 7 }
|
||||
const stableKey = makePaneKey('tab-1', STABLE_LEAF_ID)
|
||||
const turns = { 'tab-1:pane:1': 42, [stableKey]: 50, 'tab-2:pane:9': 7 }
|
||||
|
||||
const result = remapManuallyUnreadTurnPaneKeys(turns, remap)
|
||||
|
||||
expect(result.changed).toBe(true)
|
||||
expect(result.turns).toEqual({ [makePaneKey('tab-1', STABLE_LEAF_ID)]: 42, 'tab-2:pane:9': 7 })
|
||||
expect(result.turns).toEqual({ [stableKey]: 50, 'tab-2:pane:9': 7 })
|
||||
// Same remap contract as the cutoffs so the two never drift after a session restore.
|
||||
expect(remapActivityClearedAtPaneKeys(turns, remap).cutoffs).toEqual(result.turns)
|
||||
})
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { getDefaultWorkspaceSession } from '../../../shared/constants'
|
||||
import type { WorkspaceSessionState } from '../../../shared/workspace-session-state-types'
|
||||
import {
|
||||
deleteOwnerKeyedSessionFields,
|
||||
OWNER_KEYED_SESSION_FIELDS_DELETED_WITH_THEIR_OWNER
|
||||
} from './session-owner-fields'
|
||||
import { deleteOwnerKeyedSessionFields } from './session-owner-fields'
|
||||
|
||||
const REMOVED_OWNER_KEY = 'repo-1::/tmp/worktree-a'
|
||||
const RETAINED_OWNER_KEY = 'repo-1::/tmp/worktree-b'
|
||||
@@ -19,18 +16,27 @@ function sessionSeededWith(field: keyof WorkspaceSessionState): WorkspaceSession
|
||||
}
|
||||
|
||||
describe('deleting a workspace owner session fields', () => {
|
||||
it.each(OWNER_KEYED_SESSION_FIELDS_DELETED_WITH_THEIR_OWNER)(
|
||||
'deletes %s for the removed owner and keeps its sibling',
|
||||
(field) => {
|
||||
const session = sessionSeededWith(field)
|
||||
it.each<keyof WorkspaceSessionState>([
|
||||
'openFilesByWorktree',
|
||||
'activeFileIdByWorktree',
|
||||
'activeBrowserTabIdByWorktree',
|
||||
'clientHostedBrowserPagesByWorktree',
|
||||
'activeTabTypeByWorktree',
|
||||
'activeTabIdByWorktree',
|
||||
'unifiedTabs',
|
||||
'tabGroups',
|
||||
'tabGroupLayouts',
|
||||
'activeGroupIdByWorktree',
|
||||
'defaultTerminalTabsAppliedByWorktreeId'
|
||||
])('deletes %s for the removed owner and keeps its sibling', (field) => {
|
||||
const session = sessionSeededWith(field)
|
||||
|
||||
deleteOwnerKeyedSessionFields(session, REMOVED_OWNER_KEY, new Set())
|
||||
deleteOwnerKeyedSessionFields(session, REMOVED_OWNER_KEY, new Set())
|
||||
|
||||
expect((session as Record<string, unknown>)[field]).toEqual({
|
||||
[RETAINED_OWNER_KEY]: 'retained-value'
|
||||
})
|
||||
}
|
||||
)
|
||||
expect((session as Record<string, unknown>)[field]).toEqual({
|
||||
[RETAINED_OWNER_KEY]: 'retained-value'
|
||||
})
|
||||
})
|
||||
|
||||
it('advances the repo topology revision rather than deleting it', () => {
|
||||
const session: WorkspaceSessionState = {
|
||||
@@ -45,25 +51,7 @@ describe('deleting a workspace owner session fields', () => {
|
||||
expect(session.terminalTopologyRevisionByRepoId).toEqual({ 'repo-1': 5 })
|
||||
})
|
||||
|
||||
// Why spelled out rather than derived: the cases above iterate this list, so a field quietly
|
||||
// reclassified in the census loses its delete and its test case together.
|
||||
it('deletes exactly these fields with their owner', () => {
|
||||
expect(OWNER_KEYED_SESSION_FIELDS_DELETED_WITH_THEIR_OWNER).toEqual([
|
||||
'openFilesByWorktree',
|
||||
'activeFileIdByWorktree',
|
||||
'activeBrowserTabIdByWorktree',
|
||||
'clientHostedBrowserPagesByWorktree',
|
||||
'activeTabTypeByWorktree',
|
||||
'activeTabIdByWorktree',
|
||||
'unifiedTabs',
|
||||
'tabGroups',
|
||||
'tabGroupLayouts',
|
||||
'activeGroupIdByWorktree',
|
||||
'defaultTerminalTabsAppliedByWorktreeId'
|
||||
])
|
||||
})
|
||||
|
||||
// Why not in the list above: this map also holds host-qualified keys, so it is scanned rather
|
||||
// Why not in the table above: this map also holds host-qualified keys, so it is scanned rather
|
||||
// than indexed by the owner key, and the scan is the only thing that reaches a remote row.
|
||||
it('deletes the recency entry under a bare and a host-qualified key alike', () => {
|
||||
const session: WorkspaceSessionState = {
|
||||
|
||||
@@ -127,7 +127,7 @@ describe('workspace pane normalization tab index', () => {
|
||||
expect(lookup.get('first')).toBe(first)
|
||||
})
|
||||
|
||||
it('resumes scanning and makes later misses constant-time', () => {
|
||||
it('resumes scanning and reuses cached hits and misses', () => {
|
||||
const reads: string[] = []
|
||||
const tabs = ['first', 'second', 'third'].map((id) => {
|
||||
const tab = terminalTab(id, 'worktree', null)
|
||||
@@ -146,9 +146,12 @@ describe('workspace pane normalization tab index', () => {
|
||||
expect(reads).toEqual(['first'])
|
||||
expect(lookup.get('second')).toBe(tabs[1])
|
||||
expect(reads).toEqual(['first', 'second'])
|
||||
expect(lookup.get('first')).toBe(tabs[0])
|
||||
expect(reads).toEqual(['first', 'second'])
|
||||
expect(lookup.get('missing')).toBeUndefined()
|
||||
expect(reads).toEqual(['first', 'second', 'third'])
|
||||
expect(lookup.get('missing')).toBeUndefined()
|
||||
expect(lookup.get('first')).toBe(tabs[0])
|
||||
expect(reads).toEqual(['first', 'second', 'third'])
|
||||
})
|
||||
|
||||
|
||||
@@ -159,43 +159,6 @@ describe('Store', () => {
|
||||
}
|
||||
})
|
||||
|
||||
// Legacy sessions lack incarnation IDs; source-pane ownership must still permit splitting.
|
||||
it('admits a split whose source pane has no persisted incarnation', async () => {
|
||||
const store = await createStore()
|
||||
store.setWorkspaceSession(
|
||||
{
|
||||
...getDefaultWorkspaceSession(),
|
||||
tabsByWorktree: {
|
||||
wt1: [makeTerminalTab({ id: 'tab1', worktreeId: 'wt1', ptyId: 'pty-source' })]
|
||||
},
|
||||
terminalLayoutsByTabId: {
|
||||
tab1: {
|
||||
root: { type: 'leaf', leafId: TEST_LEAF_1 },
|
||||
activeLeafId: TEST_LEAF_1,
|
||||
expandedLeafId: null,
|
||||
ptyIdsByLeafId: { [TEST_LEAF_1]: 'pty-source' }
|
||||
}
|
||||
}
|
||||
},
|
||||
undefined
|
||||
)
|
||||
|
||||
expect(
|
||||
await store.persistPtyBinding({
|
||||
worktreeId: 'wt1',
|
||||
tabId: 'tab1',
|
||||
leafId: TEST_LEAF_2,
|
||||
ptyId: 'pty-split',
|
||||
expectedSourceBinding: {
|
||||
worktreeId: 'wt1',
|
||||
tabId: 'tab1',
|
||||
leafId: TEST_LEAF_1,
|
||||
ptyId: 'pty-source'
|
||||
}
|
||||
})
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('rejects competing PTY and incarnation changes during reconciliation', async () => {
|
||||
const store = await createStore()
|
||||
const paneKey = `tab1:${TEST_LEAF_1}`
|
||||
|
||||
@@ -553,24 +553,6 @@ describe('Store', () => {
|
||||
).toThrow('belongs to SSH connection "ssh-2"')
|
||||
})
|
||||
|
||||
it('updates SSH remote PTY leases when callers pass scoped app ids', async () => {
|
||||
const store = createStoreWithSshRemotePtyLeases([
|
||||
{
|
||||
targetId: 'ssh-1',
|
||||
ptyId: 'remote-pty',
|
||||
state: 'attached'
|
||||
}
|
||||
])
|
||||
|
||||
store.markSshRemotePtyLease(
|
||||
'ssh-1',
|
||||
'ssh:ssh-1@@remote-pty',
|
||||
PtyExitProof.from('exit-observed', undefined)
|
||||
)
|
||||
|
||||
expect(store.getSshRemotePtyLeases('ssh-1')).toEqual([])
|
||||
})
|
||||
|
||||
it('clears workspace bindings when marking an SSH remote PTY lease terminated', async () => {
|
||||
const store = await storeWithBoundSshPane()
|
||||
|
||||
|
||||
+15
-1
@@ -427,12 +427,26 @@ describe('Store', () => {
|
||||
ui: {},
|
||||
githubCache: { pr: {}, issue: {} },
|
||||
workspaceSession: {},
|
||||
migrationUnsupportedPtyEntries: {}
|
||||
migrationUnsupportedPtyEntries: {
|
||||
'stale-pty': {
|
||||
ptyId: 'stale-pty',
|
||||
tabId: 'tab-stale',
|
||||
paneKey: makePaneKey('tab-stale', TEST_LEAF_1),
|
||||
reason: 'legacy-numeric-pane-key',
|
||||
source: 'local',
|
||||
updatedAt: 123
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const store = await createStore()
|
||||
|
||||
expect(store.getRepos()).toHaveLength(1)
|
||||
store.flush()
|
||||
expect(readDataFile()).toMatchObject({
|
||||
migrationUnsupportedPtyEntries: [],
|
||||
legacyPaneKeyAliasEntries: []
|
||||
})
|
||||
})
|
||||
|
||||
it('remaps persisted agent acknowledgement pane keys when terminal leaves migrate to UUIDs', async () => {
|
||||
|
||||
-6
@@ -71,12 +71,6 @@ describe("main's Store load binds a tab's own terminal to its unbound pane", ()
|
||||
rmSync(testState.dir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
it('in a layout-owner build', () => {
|
||||
expect(loadStored().terminalLayoutsByTabId['tab-a']?.ptyIdsByLeafId).toEqual({
|
||||
[LEAF]: 'pty-a'
|
||||
})
|
||||
})
|
||||
|
||||
it("after the load purge, so a dropped workspace's pane cannot hold the terminal", () => {
|
||||
const session = tabLevelOnly()
|
||||
const gone = 'legacy-bare-key'
|
||||
|
||||
+6
-2
@@ -98,7 +98,9 @@ export function registerPtyConnectionColdRestoreRepaintTests(): void {
|
||||
`${RESET_GRAPHIC_RENDITION}snapshot-payload`,
|
||||
expect.any(Function)
|
||||
)
|
||||
expect(pane.terminal.write).not.toHaveBeenCalledWith('replay-payload', expect.any(Function))
|
||||
expect(
|
||||
pane.terminal.write.mock.calls.map(([data]) => terminalWriteText(data)).join('')
|
||||
).not.toContain('replay-payload')
|
||||
})
|
||||
|
||||
it('paints only relay replay when reattach result has replay and coldRestore but no snapshot', async () => {
|
||||
@@ -136,7 +138,9 @@ export function registerPtyConnectionColdRestoreRepaintTests(): void {
|
||||
`${RESET_GRAPHIC_RENDITION}replay-payload`,
|
||||
expect.any(Function)
|
||||
)
|
||||
expect(pane.terminal.write).not.toHaveBeenCalledWith('cold-payload', expect.any(Function))
|
||||
expect(
|
||||
pane.terminal.write.mock.calls.map(([data]) => terminalWriteText(data)).join('')
|
||||
).not.toContain('cold-payload')
|
||||
// Why: the replay branch supersedes cold-restore but must still ack, or the daemon redelivers the cold-restore payload next reattach.
|
||||
// A layout-owner window names the pane it painted, so main releases only its current terminal.
|
||||
expect(window.api.pty.ackColdRestore).toHaveBeenCalledWith(
|
||||
|
||||
+35
-23
@@ -214,31 +214,43 @@ export function registerPtyConnectionDeliberateSleepGuardTests(): void {
|
||||
})
|
||||
|
||||
it('drops the wake listener when a waiting pane is disposed', async () => {
|
||||
const { connectPanePty } = await import('./pty-connection')
|
||||
const { clearWorktreeSleepIntent, markWorktreeSleepIntent } =
|
||||
await import('@/lib/worktree-sleep-intent')
|
||||
const transport = createMockTransport()
|
||||
transportFactoryQueue.push(transport)
|
||||
markWorktreeSleepIntent('wt-1')
|
||||
const deps = createDeps({ tabId: 'tab-disposed', isVisibleRef: { current: false } })
|
||||
const sleepIntent = await import('@/lib/worktree-sleep-intent')
|
||||
const subscribe = sleepIntent.onWorktreeSleepIntentCleared
|
||||
const paneWakeListener = vi.fn()
|
||||
const subscribeSpy = vi
|
||||
.spyOn(sleepIntent, 'onWorktreeSleepIntentCleared')
|
||||
.mockImplementation((worktreeId, listener) =>
|
||||
subscribe(worktreeId, () => {
|
||||
paneWakeListener()
|
||||
listener()
|
||||
})
|
||||
)
|
||||
try {
|
||||
const { connectPanePty } = await import('./pty-connection')
|
||||
const transport = createMockTransport()
|
||||
transportFactoryQueue.push(transport)
|
||||
sleepIntent.markWorktreeSleepIntent('wt-1')
|
||||
const deps = createDeps({ tabId: 'tab-disposed', isVisibleRef: { current: false } })
|
||||
|
||||
const binding = connectPanePty(
|
||||
createPane(1) as never,
|
||||
createManager(1) as never,
|
||||
deps as never
|
||||
)
|
||||
await flushAsyncTicks()
|
||||
binding.dispose()
|
||||
// Why: the connect body already refuses a disposed session, so prove the
|
||||
// listener itself is gone: a wake after dispose reaches no subscriber.
|
||||
const wakeCalls: number[] = []
|
||||
const { onWorktreeSleepIntentCleared } = await import('@/lib/worktree-sleep-intent')
|
||||
onWorktreeSleepIntentCleared('wt-1', () => wakeCalls.push(1))
|
||||
clearWorktreeSleepIntent('wt-1')
|
||||
await flushAsyncTicks()
|
||||
const binding = connectPanePty(
|
||||
createPane(1) as never,
|
||||
createManager(1) as never,
|
||||
deps as never
|
||||
)
|
||||
await flushAsyncTicks()
|
||||
expect(subscribeSpy).toHaveBeenCalledTimes(1)
|
||||
binding.dispose()
|
||||
const wakeCalls: number[] = []
|
||||
subscribe('wt-1', () => wakeCalls.push(1))
|
||||
sleepIntent.clearWorktreeSleepIntent('wt-1')
|
||||
await flushAsyncTicks()
|
||||
|
||||
expect(transport.connect).not.toHaveBeenCalled()
|
||||
expect(wakeCalls).toHaveLength(1)
|
||||
expect(transport.connect).not.toHaveBeenCalled()
|
||||
expect(wakeCalls).toHaveLength(1)
|
||||
expect(paneWakeListener).not.toHaveBeenCalled()
|
||||
} finally {
|
||||
subscribeSpy.mockRestore()
|
||||
}
|
||||
})
|
||||
|
||||
it('still connects a slept pane that carries a queued startup', async () => {
|
||||
|
||||
+3
-8
@@ -516,14 +516,9 @@ export function registerPtyConnectionHiddenAtlasRecoveryTests(): void {
|
||||
|
||||
capturedDataCallback.current?.('\x1b]10;?\x1b\\\x1b]11;?\x1b\\startup frame\r\n')
|
||||
|
||||
expect(transport.sendInput).not.toHaveBeenCalledWith(
|
||||
'\x1b]10;rgb:1111/1111/1111\x1b\\',
|
||||
expect.anything()
|
||||
)
|
||||
expect(transport.sendInput).not.toHaveBeenCalledWith(
|
||||
'\x1b]11;rgb:1111/1111/1111\x1b\\',
|
||||
expect.anything()
|
||||
)
|
||||
const replies = transport.sendInput.mock.calls.map(([data]) => data).join('')
|
||||
expect(replies).not.toContain('\x1b]10;rgb:')
|
||||
expect(replies).not.toContain('\x1b]11;rgb:')
|
||||
|
||||
binding.dispose()
|
||||
})
|
||||
|
||||
@@ -118,7 +118,10 @@ describe('child-work admission of what a child is doing', () => {
|
||||
|
||||
it('carries owner and residency through, and clears the operation of a parked child', () => {
|
||||
const { store, admission } = setup()
|
||||
admission.announce(observation())
|
||||
admission.announce(
|
||||
observation({ operation: { toolName: 'Bash', basis: 'open', observedAt: 10 } })
|
||||
)
|
||||
expect(store.getChild('child-1')?.operation).toMatchObject({ toolName: 'Bash' })
|
||||
admission.announce(
|
||||
observation({
|
||||
aliases: [{ segmentId: 'segment-1', aliasKind: 'task_id', alias: 'shell-1' }],
|
||||
|
||||
@@ -118,6 +118,14 @@ describe('agent child-work admission', () => {
|
||||
).toMatchObject({ accepted: true, childWorkId: 'child-1', created: false })
|
||||
expect(store.getChildren(parent)).toMatchObject([{ childWorkId: 'child-1', kind: 'agent' }])
|
||||
expect(store.getSnapshot().aliases.every((alias) => alias.kind === 'agent')).toBe(true)
|
||||
expect(
|
||||
admission.announce(
|
||||
announce(parent, {
|
||||
aliases: [{ segmentId: 'segment-1', aliasKind: 'tool_use_id', alias: 'tool-1' }],
|
||||
observedAt: 13
|
||||
})
|
||||
)
|
||||
).toMatchObject({ accepted: true, childWorkId: 'child-1', created: false })
|
||||
})
|
||||
|
||||
it('preserves the logical id and prior outcome across an explicit resume fence', () => {
|
||||
|
||||
Reference in New Issue
Block a user