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:
OrcaWin
2026-10-24 17:35:35 -07:00
committed by GitHub
co-authored by OrcaWin
parent 7ffcb7c7b4
commit e8a18a03d5
18 changed files with 128 additions and 210 deletions
@@ -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()
@@ -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 () => {
@@ -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'
@@ -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(
@@ -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 () => {
@@ -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', () => {