mirror of
https://github.com/stablyai/orca.git
synced 2026-10-24 16:03:45 +00:00
test(agent-status): cover the closed-tab lift IPC and its renderer trigger
The lift's only production trigger — the renderer re-mirror call — and the IPC handler that carries it had no test: reverting either left every suite green. The server-side method was the sole covered hunk. Also adds liftClosedAgentStatusTabs to the agentHookServer double; the handler swallows its own errors, so the omitted field would have passed vacuously.
This commit is contained in:
@@ -9,6 +9,7 @@ import { makePaneKey } from '../../shared/stable-pane-id'
|
||||
|
||||
const dropStatusEntry = vi.fn()
|
||||
const dropStatusEntriesByTabPrefix = vi.fn()
|
||||
const liftClosedAgentStatusTabs = vi.fn()
|
||||
const retirePaneAuthority = vi.fn()
|
||||
const transferPaneAuthority = vi.fn()
|
||||
const canTransferPaneAuthority = vi.fn(() => true)
|
||||
@@ -45,6 +46,7 @@ vi.mock('../agent-hooks/server', async () => {
|
||||
agentHookServer: {
|
||||
dropStatusEntry,
|
||||
dropStatusEntriesByTabPrefix,
|
||||
liftClosedAgentStatusTabs,
|
||||
retirePaneAuthority,
|
||||
transferPaneAuthority,
|
||||
canTransferPaneAuthority,
|
||||
@@ -106,6 +108,7 @@ vi.mock('../kimi/hook-service', () => ({
|
||||
beforeEach(() => {
|
||||
dropStatusEntry.mockReset()
|
||||
dropStatusEntriesByTabPrefix.mockReset()
|
||||
liftClosedAgentStatusTabs.mockReset()
|
||||
retirePaneAuthority.mockReset()
|
||||
transferPaneAuthority.mockReset()
|
||||
canTransferPaneAuthority.mockReset()
|
||||
@@ -389,6 +392,65 @@ describe('agentStatus:dropByTabPrefix IPC', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('agentStatus:liftClosedTabs IPC', () => {
|
||||
it('forwards validated tab ids so a returning tab stops being suppressed', async () => {
|
||||
const { registerAgentHookHandlers } = await import('./agent-hooks')
|
||||
registerAgentHookHandlers()
|
||||
|
||||
const handler = onHandlers.get('agentStatus:liftClosedTabs')
|
||||
expect(handler).toBeDefined()
|
||||
handler!({}, ['tab-1', 'tab-2'])
|
||||
|
||||
expect(liftClosedAgentStatusTabs).toHaveBeenCalledWith(['tab-1', 'tab-2'])
|
||||
})
|
||||
|
||||
it('ignores a payload that is not an array', async () => {
|
||||
const { registerAgentHookHandlers } = await import('./agent-hooks')
|
||||
registerAgentHookHandlers()
|
||||
|
||||
const handler = onHandlers.get('agentStatus:liftClosedTabs')!
|
||||
for (const value of ['tab-1', 123, undefined, null, {}] as unknown[]) {
|
||||
expect(() => handler({}, value)).not.toThrow()
|
||||
}
|
||||
|
||||
expect(liftClosedAgentStatusTabs).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('filters malformed ids out of an otherwise valid array', async () => {
|
||||
const { registerAgentHookHandlers } = await import('./agent-hooks')
|
||||
registerAgentHookHandlers()
|
||||
|
||||
onHandlers.get('agentStatus:liftClosedTabs')!({}, [
|
||||
'tab-1',
|
||||
123,
|
||||
null,
|
||||
'tab-1:leaf',
|
||||
' leading-space',
|
||||
'x'.repeat(161)
|
||||
])
|
||||
|
||||
// Why assert the exact argument: a filter that let a malformed id through would still
|
||||
// "have been called", so only the forwarded value proves the boundary held.
|
||||
expect(liftClosedAgentStatusTabs).toHaveBeenCalledWith(['tab-1'])
|
||||
})
|
||||
|
||||
it('skips the server entirely when every id is rejected', async () => {
|
||||
const { registerAgentHookHandlers } = await import('./agent-hooks')
|
||||
registerAgentHookHandlers()
|
||||
|
||||
onHandlers.get('agentStatus:liftClosedTabs')!({}, ['', 'tab-1:leaf', 'x'.repeat(161)])
|
||||
|
||||
expect(liftClosedAgentStatusTabs).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('removes any existing listener before registering the lift channel', async () => {
|
||||
const { registerAgentHookHandlers } = await import('./agent-hooks')
|
||||
registerAgentHookHandlers()
|
||||
|
||||
expect(removeAllListeners).toHaveBeenCalledWith('agentStatus:liftClosedTabs')
|
||||
})
|
||||
})
|
||||
|
||||
describe('agent pane authority IPC', () => {
|
||||
it('retires one validated pane authority', async () => {
|
||||
const { registerAgentHookHandlers } = await import('./agent-hooks')
|
||||
|
||||
@@ -273,16 +273,23 @@ function observeSidebar(store: TestStore, now: number): SidebarObservation {
|
||||
}
|
||||
}
|
||||
|
||||
const liftClosedTabs = vi.fn()
|
||||
|
||||
describe('a host-retracted paired tab leaves no ghost agent row behind', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
vi.setSystemTime(T0)
|
||||
resetWebSessionTabsSnapshotFreshnessForTests()
|
||||
resetRendererOwnedAgentStatusPanesForTests()
|
||||
liftClosedTabs.mockClear()
|
||||
// Why stub the bridge: lifting only the renderer marker leaves the tab suppressed in the
|
||||
// main-process closed-tab set, which gates every hook event upstream of the renderer.
|
||||
vi.stubGlobal('window', { ...globalThis.window, api: { agentStatus: { liftClosedTabs } } })
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers()
|
||||
vi.unstubAllGlobals()
|
||||
resetRendererOwnedAgentStatusPanesForTests()
|
||||
})
|
||||
|
||||
@@ -591,6 +598,10 @@ describe('a host-retracted paired tab leaves no ghost agent row behind', () => {
|
||||
store.getState().recentlyClosedAgentStatusTabIds[mirrorTabId(RETRACTED_TAB)],
|
||||
'a re-mirrored tab id kept its closed-tab marker'
|
||||
).toBeUndefined()
|
||||
expect(
|
||||
liftClosedTabs.mock.calls.flatMap(([tabIds]) => tabIds as string[]),
|
||||
'the renderer lifted its own marker but left the tab suppressed in the main process'
|
||||
).toContain(mirrorTabId(RETRACTED_TAB))
|
||||
|
||||
// The returning pane's byte-derived status must land again.
|
||||
const release2 = replayClientByteStatus(
|
||||
|
||||
Reference in New Issue
Block a user