mirror of
https://github.com/stablyai/orca.git
synced 2026-09-23 08:02:31 +00:00
* fix(terminal): retire captured remote handles when pending panes close A restored pane can hold a scoped `remote:<environment>@@<handle>` layout binding while `remote.attach()` is still waiting for `terminal.resolvePane`. The transport's `getPtyId()` is null, so an explicit split close passed null to `closeWebRuntimeTerminal`, dropped the binding and destroyed only the viewer. The host terminal stayed connected. Only an exact scoped handle whose environment matches the owning workspace's runtime authorizes the close. The provider helper captures the pairing revision, runs its existing compatibility check, then rechecks pairing and ownership immediately before dispatch. Rebased onto main after #21001 was squash-merged. The previous head was a merge commit that carried its own conflict-resolution content -- the runtime branch in `terminal-pane-close-admission.ts` and the restored `it.each([false, true])` parameter -- which a plain rebase drops along with the merge. Rebuilt from the recorded net diff instead and verified byte-identical at 15 files, 906 insertions, 41 deletions. * test(memory): rebase the pending runtime-close proof onto the squashed base `fix.patch` recorded a baseline taken against #21001's pre-squash branch tip. Squash-merging #21001 replaced that tip with a single commit, so the recorded hunks no longer reverse-applied and `reproduce.mjs` aborted with `Source changed: use-terminal-pane-close-actions.ts` -- confirmed by running it before regenerating rather than assuming the rebase alone would fix it. Regenerated against `main` and re-run: 5 pass / 10 fail before, 15 pass / 0 fail after, exit 0, and every `results.json` hash recomputed from the run rather than hand-edited. --------- Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: Neil <neil@stably.ai>
71 lines
3.0 KiB
TypeScript
71 lines
3.0 KiB
TypeScript
import { expect, it, vi } from 'vitest'
|
|
import { OrcaRuntimeService } from '../../../src/main/runtime/orca-runtime'
|
|
import { preparePendingRuntimeClose } from '../../../src/renderer/src/components/terminal-pane/pending-runtime-pane-close-test-fixture'
|
|
|
|
it.each([false, true])(
|
|
'actual close RPC addresses only the captured host incarnation: replacement=%s',
|
|
async (replacement) => {
|
|
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: This fixture supplies the only store method used by the exercised register/resolve/close path.
|
|
const store = { getRepos: () => [] } as unknown as ConstructorParameters<
|
|
typeof OrcaRuntimeService
|
|
>[0]
|
|
const runtime = new OrcaRuntimeService(store)
|
|
const kill = vi.fn((id: string) => {
|
|
runtime.onPtyExit(id, 0)
|
|
return true
|
|
})
|
|
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: The real close method uses the supplied kill operation; this fixture launches no subprocess.
|
|
runtime.setPtyController({ kill } as Parameters<typeof runtime.setPtyController>[0])
|
|
const binding = {
|
|
tabId: 'tab-parent',
|
|
leafId: '11111111-1111-4111-8111-111111111111',
|
|
incarnationId: '11111111-1111-4111-8111-111111111111'
|
|
}
|
|
runtime.registerPty('host-pty', 'workspace', null, binding)
|
|
const paneKey = `${binding.tabId}:${binding.leafId}`
|
|
const original = runtime.resolveTerminalPane(paneKey, 'workspace')
|
|
const p = await preparePendingRuntimeClose(`remote:env-1@@${original.handle}`)
|
|
const beforeCall = p.runtimeCall.getMockImplementation()!
|
|
p.runtimeCall.mockImplementation(async (request) => {
|
|
if (request.method !== 'terminal.close') {
|
|
return beforeCall(request)
|
|
}
|
|
const params = request.params
|
|
if (
|
|
!params ||
|
|
typeof params !== 'object' ||
|
|
!('terminal' in params) ||
|
|
typeof params.terminal !== 'string'
|
|
) {
|
|
throw new Error('expected captured terminal handle')
|
|
}
|
|
return { ok: true, result: { close: await runtime.closeTerminal(params.terminal) } }
|
|
})
|
|
vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
try {
|
|
p.actions.executeClosePane(1)
|
|
if (replacement) {
|
|
runtime.registerPty('host-pty', 'workspace', null, {
|
|
...binding,
|
|
incarnationId: '22222222-2222-4222-8222-222222222222'
|
|
})
|
|
expect(runtime.resolveTerminalPane(paneKey, 'workspace').handle).not.toBe(original.handle)
|
|
}
|
|
p.acceptCompatibility()
|
|
await p.settle(original.handle)
|
|
expect(p.runtimeCall).toHaveBeenCalledWith(
|
|
expect.objectContaining({ method: 'terminal.close', params: { terminal: original.handle } })
|
|
)
|
|
if (replacement) {
|
|
expect(kill).not.toHaveBeenCalled()
|
|
expect(runtime.resolveTerminalPane(paneKey, 'workspace').connected).toBe(true)
|
|
} else {
|
|
expect(kill).toHaveBeenCalledExactlyOnceWith('host-pty')
|
|
}
|
|
expect(window.api.pty.kill).not.toHaveBeenCalled()
|
|
} finally {
|
|
runtime.onPtyExit('host-pty', 0)
|
|
}
|
|
}
|
|
)
|