test: remove 170 low-value renderer tests (#37539)

This commit is contained in:
OrcaWin
2026-11-02 12:41:03 -08:00
committed by GitHub
parent aab098fb30
commit f77a2a5e25
31 changed files with 15 additions and 3509 deletions
+3 -7
View File
@@ -2321,10 +2321,7 @@
},
{
"file": "src/renderer/src/lib/workspace-session-host-contention.test.ts",
"assertions": [
"keeps an SSH claimant out of the rotating runtime partition",
"does not strand the runtime co-claimant when the SSH row is written"
]
"assertions": ["does not strand the runtime co-claimant when the SSH row is written"]
},
{
"file": "src/shared/workspace-session-partition-owner.test.ts",
@@ -5781,7 +5778,7 @@
"providers": ["remote-runtime", "ssh"],
"coveredPlatforms": ["macos"],
"coveredProviders": [],
"coverageNotes": "A headed macOS desktop host and separate paired Electron client exercise a real mirrored terminal link, owner-asserted host browser creation, exact host/client page identity, rendered DOM, explicit browser-tab activation, client external-browser suppression, exact host/client close convergence, and continued original-PTY liveness. Unit contracts cover OSC, WebLinksAddon, click fallback, explicit System Browser routing, failed creation, incapable runtimes, and unchanged direct SSH behavior.",
"coverageNotes": "A headed macOS desktop host and separate paired Electron client exercise a real mirrored terminal link, owner-asserted host browser creation, exact host/client page identity, rendered DOM, explicit browser-tab activation, client external-browser suppression, exact host/client close convergence, and continued original-PTY liveness. Unit contracts cover OSC, WebLinksAddon, click fallback, explicit System Browser routing, failed creation, and unchanged direct SSH behavior. The test-value audit removed separate supplied owner/capability wrapper examples; their coverage loss is accepted.",
"motivatingLinks": [
"https://stablygroup.slack.com/archives/C0AK2T3JEF4/p1786567689897109",
"https://github.com/stablyai/orca/issues/12418"
@@ -5811,8 +5808,7 @@
{
"file": "src/renderer/src/lib/workspace-browser-tab-open.test.ts",
"assertions": [
"an asserted runtime cannot fall back to a client browser",
"mismatched and incapable runtime owners fail closed"
"false or rejected runtime creation does not fall back to a client browser"
]
},
{
-18
View File
@@ -10903,36 +10903,18 @@
"src/renderer/src/lib/windows-terminal-capabilities-race.test.ts": 450,
"src/renderer/src/lib/windows-terminal-capabilities.test.ts": 585,
"src/renderer/src/lib/windows-terminal-capability-reprobe.test.ts": 198,
"src/renderer/src/lib/work-item-lookup-text.test.ts": 65,
"src/renderer/src/lib/work-item-repo-row.test.ts": 43,
"src/renderer/src/lib/worker-auto-close.test.ts": 387,
"src/renderer/src/lib/workspace-activation-path-gate.test.ts": 2107,
"src/renderer/src/lib/workspace-activation-terminal-focus.test.ts": 532,
"src/renderer/src/lib/workspace-attachment-observed-origin.test.ts": 112,
"src/renderer/src/lib/workspace-browser-tab-close.test.ts": 290,
"src/renderer/src/lib/workspace-browser-tab-open-placement.test.ts": 1996,
"src/renderer/src/lib/workspace-browser-tab-open.test.ts": 605,
"src/renderer/src/lib/workspace-directory.test.ts": 303,
"src/renderer/src/lib/workspace-doc-address-input.test.ts": 470,
"src/renderer/src/lib/workspace-editor-ssh-target.test.ts": 337,
"src/renderer/src/lib/workspace-emoji-picker-defaults.test.ts": 125,
"src/renderer/src/lib/workspace-emoji-shortcodes.lazy.test.ts": 85,
"src/renderer/src/lib/workspace-emoji-shortcodes.test.ts": 90,
"src/renderer/src/lib/workspace-file-browser-open-target.test.ts": 340,
"src/renderer/src/lib/workspace-file-drag-source-runtime.test.ts": 67,
"src/renderer/src/lib/workspace-file-drag.test.ts": 70,
"src/renderer/src/lib/workspace-ports/workspace-port-groups.test.ts": 46,
"src/renderer/src/lib/workspace-ports/workspace-port-host-scoped-scan.test.ts": 529,
"src/renderer/src/lib/workspace-ports/workspace-port-scan-debounce.test.ts": 57,
"src/renderer/src/lib/workspace-ports/workspace-port-scan-publish.test.ts": 595,
"src/renderer/src/lib/workspace-session-browser-history.test.ts": 419,
"src/renderer/src/lib/workspace-session-editor-drafts.test.ts": 324,
"src/renderer/src/lib/workspace-session-folder-host-twin.test.ts": 95,
"src/renderer/src/lib/workspace-session-host-contention.test.ts": 265,
"src/renderer/src/lib/workspace-session-host-emptied-fields.test.ts": 279,
"src/renderer/src/lib/workspace-session-host-persistence.test.ts": 313,
"src/renderer/src/lib/workspace-session-host-split.test.ts": 261,
"src/renderer/src/lib/workspace-session-hydration-keys.test.ts": 269,
"src/renderer/src/lib/workspace-session-liveness.test.ts": 298,
"src/renderer/src/lib/workspace-session-patch.test.ts": 336,
"src/renderer/src/lib/workspace-session-persistence-gate.test.ts": 314,
@@ -81,7 +81,6 @@ describe('a folder whose project group mixes local and SSH repos (layout-owner b
})
describe('every folder kind routes where the window saves it (layout-owner build)', () => {
// The window's side of each case runs in workspace-session-folder-host-twin.test.ts.
it.each(FOLDER_HOST_CASES)('$name', async ({ folder, groups, expected }) => {
const { runtime, applyLayoutCommand } = runtimeWithMixedFolder(folder, groups)
await runtime.applyLayoutCommand(RATIOS)
@@ -1,42 +0,0 @@
import { describe, expect, it } from 'vitest'
import { isWorkItemLookupText } from './work-item-lookup-text'
describe('isWorkItemLookupText', () => {
it('detects GitHub PR and issue URLs', () => {
expect(isWorkItemLookupText('https://github.com/stablyai/orca/pull/4900')).toBe(true)
expect(isWorkItemLookupText('https://github.com/stablyai/orca/issues/123')).toBe(true)
expect(isWorkItemLookupText(' https://www.github.com/stablyai/orca/pull/1 ')).toBe(true)
})
it('detects hash-number shorthand', () => {
expect(isWorkItemLookupText('#4900')).toBe(true)
})
it('detects GitLab issue and MR URLs on any host', () => {
expect(isWorkItemLookupText('https://gitlab.com/group/project/-/merge_requests/7')).toBe(true)
expect(isWorkItemLookupText('https://gitlab.example.com/group/sub/project/-/issues/42')).toBe(
true
)
})
it('detects modern /-/work_items/<iid> GitLab URLs', () => {
expect(isWorkItemLookupText('https://gitlab.com/group/project/-/work_items/42')).toBe(true)
expect(
isWorkItemLookupText('https://gitlab.example.com:8443/group/sub/project/-/work_items/9')
).toBe(true)
})
it('detects Linear issue URLs', () => {
expect(isWorkItemLookupText('https://linear.app/acme/issue/STA-123/fix-the-bug')).toBe(true)
})
it('rejects deliberate workspace names', () => {
expect(isWorkItemLookupText('')).toBe(false)
expect(isWorkItemLookupText(' ')).toBe(false)
expect(isWorkItemLookupText('my-feature')).toBe(false)
expect(isWorkItemLookupText('fix-2')).toBe(false)
expect(isWorkItemLookupText('terminal scrollbar polish')).toBe(false)
expect(isWorkItemLookupText('https://example.com/some/page')).toBe(false)
expect(isWorkItemLookupText('https://linear.app/acme/project/mobile')).toBe(false)
})
})
@@ -1,35 +0,0 @@
import { describe, expect, it } from 'vitest'
import type { Repo } from '../../../shared/repo-types'
import { getWorkItemRepo } from './work-item-repo-row'
const localRow: Repo = {
id: 'twin',
path: '/laptop/twin',
displayName: 'Twin',
badgeColor: '#000',
addedAt: 1
}
const sshRow: Repo = { ...localRow, path: '/remote/twin', connectionId: 'builder' }
const runtimeRow: Repo = { ...localRow, executionHostId: 'runtime:env-1' }
describe('getWorkItemRepo (GitHub and GitLab items)', () => {
it('picks the twin on the stamped host', () => {
expect(getWorkItemRepo([sshRow, localRow], { repoId: 'twin', repoHostId: 'ssh:builder' })).toBe(
sshRow
)
})
it('refuses a twin whose stamped host has no row', () => {
expect(
getWorkItemRepo([sshRow, localRow], { repoId: 'twin', repoHostId: 'runtime:gone' })
).toBeUndefined()
})
it('keeps a lone row even when the fetch stamped the source host', () => {
expect(getWorkItemRepo([runtimeRow], { repoId: 'twin', repoHostId: 'local' })).toBe(runtimeRow)
})
it('keeps the by-id row for an unstamped item', () => {
expect(getWorkItemRepo([sshRow, localRow], { repoId: 'twin' })).toBe(localRow)
})
})
@@ -3,7 +3,7 @@ import type { FolderWorkspace } from '../../../shared/folder-workspace-types'
import type { ProjectGroup } from '../../../shared/project-group-types'
import type { FolderWorkspacePathStatusReason } from '../../../shared/folder-workspace-path-status'
import { folderWorkspaceKey } from '../../../shared/workspace-scope'
import { getDefaultSettings } from '../../../shared/constants'
import '../../../shared/constants'
import { getFolderWorkspaceStatusRequestSnapshot } from '@/store/folder-workspaces/folder-path-status'
import {
makeGroup,
@@ -99,15 +99,6 @@ describe('Cmd/Ctrl+1-9 folder-workspace path gate (#10716)', () => {
expect(mocks.toastError).toHaveBeenCalledTimes(1)
})
it('blocks the number shortcut when the SSH connection owning the folder is ambiguous', async () => {
await seedFolderWorkspace({ exists: false, reason: 'ambiguous-connection' })
activateAndRevealWorkspace(folderWorkspaceKey(folderWorkspace.id))
expect(store.getState().activeWorkspaceKey).toBeNull()
expect(mocks.toastError).toHaveBeenCalledTimes(1)
})
it('still activates the number shortcut when the folder path is present', async () => {
await seedFolderWorkspace({ exists: true })
@@ -117,58 +108,6 @@ describe('Cmd/Ctrl+1-9 folder-workspace path gate (#10716)', () => {
expect(mocks.toastError).not.toHaveBeenCalled()
})
it.each(['local', 'runtime:builder'] as const)(
'keeps the selected %s host on a folder sidebar reveal',
async (executionHostId) => {
await seedFolderWorkspace({ exists: true })
store.setState({
folderWorkspaces: [
{
...folderWorkspace,
executionHostId: executionHostId === 'local' ? 'runtime:builder' : 'local'
},
{ ...folderWorkspace, executionHostId }
]
})
activateAndRevealWorkspace(folderWorkspaceKey(folderWorkspace.id), {
executionHostId,
providesInitialSurface: true
})
expect(store.getState().pendingRevealWorktree).toMatchObject({
worktreeId: folderWorkspaceKey(folderWorkspace.id),
executionHostId
})
}
)
it.each([
['local', null],
['ssh:builder', 'builder']
] as const)(
'reveals the %s catalog owner when an unrelated server is focused',
async (executionHostId, connectionId) => {
await seedFolderWorkspace({ exists: true })
store.setState({
projectGroups: [{ ...projectGroup, connectionId }],
settings: {
...getDefaultSettings('/tmp'),
activeRuntimeEnvironmentId: 'focused-server'
}
})
activateAndRevealWorkspace(folderWorkspaceKey(folderWorkspace.id), {
providesInitialSurface: true
})
expect(store.getState().pendingRevealWorktree).toMatchObject({
worktreeId: folderWorkspaceKey(folderWorkspace.id),
executionHostId
})
}
)
it.each(['local', 'runtime:builder'] as const)(
'checks the selected %s folder when the same id is active on another host',
async (executionHostId) => {
@@ -229,9 +229,4 @@ describe('workspace activation focus', () => {
flushFrame()
expect(mocks.focus).not.toHaveBeenCalled()
})
it('declines a failed activation', () => {
expect(queueWorkspaceActivationTerminalFocus('wt-1', false)).toBe(false)
expect(mocks.subscribe).not.toHaveBeenCalled()
})
})
@@ -1,154 +0,0 @@
import { describe, expect, it } from 'vitest'
import type { AppState } from '@/store/types'
import { makeTerminalTab } from '../store/slices/worktrees-slice-test-fixtures'
import { singlePaneLayoutSnapshot } from '../store/slices/terminal-helpers'
import { makePaneKey } from '../../../shared/stable-pane-id'
import {
getWorkspaceAttachmentObservedOrigin,
getWorkspaceAttachmentTerminalOrigin
} from './workspace-attachment-observed-origin'
const workspaceId = 'repo::/feature'
const leaf = '11111111-1111-4111-8111-111111111111'
const otherLeaf = '22222222-2222-4222-8222-222222222222'
const context = { tabId: 'tab', paneKey: makePaneKey('tab', leaf), ptyId: 'pty' }
function state(): Pick<
AppState,
'tabsByWorktree' | 'terminalLayoutsByTabId' | 'agentStatusByPaneKey'
> {
return {
tabsByWorktree: {
[workspaceId]: [makeTerminalTab({ id: 'tab', worktreeId: workspaceId, ptyId: 'pty' })]
},
terminalLayoutsByTabId: { tab: singlePaneLayoutSnapshot(leaf, 'pty') },
agentStatusByPaneKey: {
[context.paneKey]: {
state: 'working',
prompt: '',
updatedAt: 1,
stateStartedAt: 1,
paneKey: context.paneKey,
stateHistory: [],
worktreeId: workspaceId,
terminalHandle: 'pty',
agentType: 'codex',
providerSession: { id: 'provider-session', key: 'session_id' }
}
}
}
}
describe('reference evidence terminal identity', () => {
it('records the currently bound provider session', () => {
expect(getWorkspaceAttachmentTerminalOrigin(state(), workspaceId, context, 'local')).toEqual(
expect.objectContaining({
tabId: 'tab',
paneKey: context.paneKey,
hostId: 'local',
sessionId: 'provider-session',
agent: 'codex',
kind: 'observed'
})
)
})
it('rejects removed leaves even if a stale PTY mapping is retained', () => {
const current = state()
current.terminalLayoutsByTabId.tab = {
...singlePaneLayoutSnapshot(otherLeaf, 'another-pty'),
ptyIdsByLeafId: { [leaf]: 'pty', [otherLeaf]: 'another-pty' }
}
expect(
getWorkspaceAttachmentTerminalOrigin(current, workspaceId, context, 'local')
).toBeUndefined()
})
it('rejects a PTY rebound in the same pane', () => {
const current = state()
current.terminalLayoutsByTabId.tab = singlePaneLayoutSnapshot(leaf, 'replacement-pty')
expect(
getWorkspaceAttachmentTerminalOrigin(current, workspaceId, context, 'local')
).toBeUndefined()
})
it('retains terminal evidence without attributing a session from a different PTY', () => {
const current = state()
const entry = current.agentStatusByPaneKey[context.paneKey]
if (!entry) {
throw new Error('Expected source session')
}
entry.terminalHandle = 'previous-pty'
const origin = getWorkspaceAttachmentTerminalOrigin(current, workspaceId, context, 'local')
expect(origin).toMatchObject({ tabId: 'tab', kind: 'observed' })
expect(origin?.sessionId).toBeUndefined()
expect(origin?.agent).toBeUndefined()
})
it('does not record an observation before a PTY is running', () => {
const current = state()
current.terminalLayoutsByTabId.tab = singlePaneLayoutSnapshot(leaf)
current.tabsByWorktree[workspaceId][0].ptyId = null
current.agentStatusByPaneKey = {}
expect(
getWorkspaceAttachmentTerminalOrigin(
current,
workspaceId,
{ tabId: 'tab', paneKey: context.paneKey },
'local'
)
).toBeUndefined()
})
})
describe('reference evidence native chat identity', () => {
it('records the chat tab and its session without a terminal pane', () => {
const chat = {
...state(),
unifiedTabsByWorktree: {
[workspaceId]: [
{
id: 'chat-tab',
entityId: 'chat-session',
groupId: 'group',
worktreeId: workspaceId,
contentType: 'agent-session' as const,
agentSessionAgent: 'claude' as const,
label: 'Plan refactor',
customLabel: null,
color: null,
sortOrder: 0,
createdAt: 0
}
]
}
}
expect(
getWorkspaceAttachmentObservedOrigin(
chat,
workspaceId,
{ kind: 'agent-session', tabId: 'chat-tab' },
'local'
)
).toEqual({
kind: 'observed',
tabId: 'chat-tab',
hostId: 'local',
label: 'Plan refactor',
agent: 'claude',
sessionId: 'chat-session'
})
const renamed = {
...chat,
unifiedTabsByWorktree: {
[workspaceId]: chat.unifiedTabsByWorktree[workspaceId].map((tab) => ({
...tab,
customLabel: 'Pinned chat'
}))
}
}
expect(
getWorkspaceAttachmentObservedOrigin(
renamed,
workspaceId,
{ kind: 'agent-session', tabId: 'chat-tab' },
'local'
)
).toMatchObject({ label: 'Pinned chat' })
})
})
@@ -1,79 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
const mocks = vi.hoisted(() => {
const state: Record<string, unknown> = {}
return {
state,
closeOnHosts: vi.fn(),
destroyWorkspaceWebviews: vi.fn(),
closeBrowserTab: vi.fn(),
closeUnifiedTab: vi.fn()
}
})
vi.mock('@/store', () => ({ useAppStore: { getState: () => mocks.state } }))
vi.mock('@/store/slices/browser-webview-cleanup', () => ({
destroyWorkspaceWebviews: mocks.destroyWorkspaceWebviews
}))
vi.mock('@/runtime/browser-workspace-tab-close', () => ({
closeBrowserWorkspaceTabOnHosts: mocks.closeOnHosts
}))
import { closeWorkspaceBrowserTab } from './workspace-browser-tab-close'
const WORKTREE_ID = 'repo::/w'
type OwnerHost = 'local' | 'runtime:env-1'
function stateOwnedBy(hostIds: OwnerHost[], pages: { id: string }[] = []) {
return {
repos: hostIds.map((hostId) => ({ id: 'repo', executionHostId: hostId })),
worktreesByRepo: {
repo: hostIds.map((hostId) => ({
id: WORKTREE_ID,
repoId: 'repo',
hostId,
...(hostId === 'local' ? {} : { runtimeOwnerEnvironmentId: 'env-1' })
}))
},
browserPagesByWorkspace: { 'browser-ws': pages },
closeBrowserTab: mocks.closeBrowserTab,
closeUnifiedTab: mocks.closeUnifiedTab
}
}
describe('closeWorkspaceBrowserTab owner', () => {
beforeEach(() => {
vi.clearAllMocks()
mocks.closeOnHosts.mockReturnValue({
hostEnvironmentIds: ['env-1'],
closesLocally: false,
removesVisibleTab: true
})
})
it("closes a pageless mirror on its runtime owner's host", () => {
mocks.state = stateOwnedBy(['runtime:env-1'])
closeWorkspaceBrowserTab(WORKTREE_ID, 'browser-ws', 'visible-tab')
expect(mocks.closeOnHosts).toHaveBeenCalledWith(
expect.objectContaining({ focusedEnvironmentId: 'env-1', visibleTabId: 'visible-tab' })
)
expect(mocks.closeUnifiedTab).toHaveBeenCalledWith('visible-tab', undefined)
})
it('refuses a pageless mirror of a local twin: no host close, no local teardown', () => {
mocks.state = stateOwnedBy(['local', 'runtime:env-1'])
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
const plan = closeWorkspaceBrowserTab(WORKTREE_ID, 'browser-ws', 'visible-tab')
expect(plan).toEqual({ hostEnvironmentIds: [], closesLocally: false, removesVisibleTab: false })
expect(mocks.closeOnHosts).not.toHaveBeenCalled()
expect(mocks.closeBrowserTab).not.toHaveBeenCalled()
expect(mocks.closeUnifiedTab).not.toHaveBeenCalled()
expect(mocks.destroyWorkspaceWebviews).not.toHaveBeenCalled()
expect(warn.mock.calls[0]).toContain(WORKTREE_ID)
warn.mockRestore()
})
})
@@ -1,18 +1,10 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import {
LOCAL_EXECUTION_HOST_ID,
toRuntimeExecutionHostId,
toSshExecutionHostId
} from '../../../shared/execution-host'
import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../shared/constants'
import { toRuntimeExecutionHostId } from '../../../shared/execution-host'
import '../../../shared/constants'
import { BROWSER_SCREENCAST_RUNTIME_CAPABILITY } from '../../../shared/protocol-version'
import { BROWSER_CLIENT_HOSTING_RUNTIME_CAPABILITIES } from '../../../shared/browser-client-hosting-eligibility'
import * as clientCreationActionPolicy from './client-creation-action-policy'
import {
canOpenWorkspaceBrowserTabOnRuntime,
canOpenWorkspaceBrowserTabOnSsh,
openWorkspaceBrowserTab
} from './workspace-browser-tab-open'
import '../../../shared/browser-client-hosting-eligibility'
import './client-creation-action-policy'
import { openWorkspaceBrowserTab } from './workspace-browser-tab-open'
function createBrowserTabMock() {
return vi.fn((_worktreeId: string, _url: string, _options: Record<string, unknown>) => ({
@@ -75,345 +67,6 @@ beforeEach(() => {
})
describe('openWorkspaceBrowserTab', () => {
it('opens client-owned searches with a safe title and the desktop default profile', async () => {
const createBrowserTab = createBrowserTabMock()
const sshHost = toSshExecutionHostId('ssh-target')
mocks.state = {
...ownerState(sshHost),
createBrowserTab,
settings: { browserDefaultProfileIdByHostId: { local: 'local-profile' } }
}
expect(canOpenWorkspaceBrowserTabOnSsh(mocks.state as never, WORKSPACE_ID, 'ssh-target')).toBe(
true
)
await openWorkspaceBrowserTab({
workspaceId: WORKSPACE_ID,
targetGroupId: 'group-1',
url: 'https://www.google.com/search?q=private%20query',
intent: { kind: 'search', engine: 'google' },
expectedSshConnectionId: 'ssh-target'
})
expect(createBrowserTab).toHaveBeenCalledWith(
WORKSPACE_ID,
'https://www.google.com/search?q=private%20query',
{
activate: true,
browserRuntimeEnvironmentId: null,
executionHostId: 'ssh:ssh-target',
focusAddressBar: false,
sessionProfileId: 'local-profile',
targetGroupId: 'group-1',
title: 'Search Google'
}
)
expect(mocks.createRemote).not.toHaveBeenCalled()
})
it('creates a client tab without activating it when requested', async () => {
const createBrowserTab = createBrowserTabMock()
const sshHost = toSshExecutionHostId('ssh-target')
mocks.state = {
...ownerState(sshHost),
createBrowserTab
}
await openWorkspaceBrowserTab({
workspaceId: WORKSPACE_ID,
url: 'https://github.com/acme/orca/pull/456',
afterTabId: 'source-unified-tab',
intent: { kind: 'url' },
focusOnCreate: false,
selectWorktree: false
})
expect(createBrowserTab).toHaveBeenCalledWith(
WORKSPACE_ID,
'https://github.com/acme/orca/pull/456',
expect.objectContaining({ activate: false, afterTabId: 'source-unified-tab' })
)
})
it('fails closed when the asserted SSH browser route is opted out or belongs to another host', async () => {
const sshHost = toSshExecutionHostId('ssh-target')
mocks.state = {
...ownerState(sshHost),
settings: { browserSshWorkspaceRoutingDisabledTargetIds: ['ssh-target'] },
createBrowserTab: createBrowserTabMock()
}
expect(canOpenWorkspaceBrowserTabOnSsh(mocks.state as never, WORKSPACE_ID, 'ssh-target')).toBe(
false
)
expect(canOpenWorkspaceBrowserTabOnSsh(mocks.state as never, WORKSPACE_ID, 'ssh-other')).toBe(
false
)
await expect(
openWorkspaceBrowserTab({
workspaceId: WORKSPACE_ID,
url: 'http://0.0.0.0:8000/',
intent: { kind: 'url' },
expectedSshConnectionId: 'ssh-target'
})
).rejects.toThrow('Unable to open URL.')
expect(mocks.state.createBrowserTab).not.toHaveBeenCalled()
})
it('surfaces the opening workspace and titles runtime-owned URL tabs by target', async () => {
const createBrowserTab = createBrowserTabMock()
mocks.state = {
...ownerState(toRuntimeExecutionHostId('hub-a')),
...browserCapableRuntime('hub-a'),
createBrowserTab
}
expect(canOpenWorkspaceBrowserTabOnRuntime(mocks.state as never, WORKSPACE_ID, 'hub-a')).toBe(
true
)
await openWorkspaceBrowserTab({
workspaceId: WORKSPACE_ID,
url: 'https://example.com/docs?token=secret',
targetGroupId: 'group-1',
intent: { kind: 'url' }
})
expect(mocks.createRemote).toHaveBeenCalledWith({
worktreeId: WORKSPACE_ID,
environmentId: 'hub-a',
url: 'https://example.com/docs?token=secret',
targetGroupId: 'group-1',
clientTargetGroupId: 'group-1',
selectWorktree: true,
stagedTitle: 'example.com/docs',
stagedFocusAddressBar: false,
failureLogMode: 'operation-only'
})
expect(createBrowserTab).not.toHaveBeenCalled()
})
it('stages a runtime tab without selecting the worktree or focusing the browser', async () => {
mocks.state = {
...ownerState(toRuntimeExecutionHostId('hub-a')),
...browserCapableRuntime('hub-a'),
createBrowserTab: createBrowserTabMock()
}
await openWorkspaceBrowserTab({
workspaceId: WORKSPACE_ID,
url: 'https://gitlab.com/acme/orca/-/merge_requests/77',
afterTabId: 'source-unified-tab',
intent: { kind: 'url' },
focusOnCreate: false,
selectWorktree: false
})
expect(mocks.createRemote).toHaveBeenCalledWith(
expect.objectContaining({
clientAfterTabId: 'source-unified-tab',
focusOnCreate: false,
selectWorktree: false,
url: 'https://gitlab.com/acme/orca/-/merge_requests/77'
})
)
})
it('waits for host registration before reconciling an asserted runtime link', async () => {
const createBrowserTab = createBrowserTabMock()
mocks.state = {
...ownerState(toRuntimeExecutionHostId('hub-a')),
...browserCapableRuntime('hub-a'),
createBrowserTab
}
await openWorkspaceBrowserTab({
workspaceId: WORKSPACE_ID,
url: 'https://example.com/pinned',
intent: { kind: 'url' },
expectedRuntimeEnvironmentId: 'hub-a'
})
expect(mocks.createRemote).toHaveBeenCalledWith(
expect.objectContaining({
environmentId: 'hub-a',
waitForRegistration: true,
worktreeId: WORKSPACE_ID
})
)
expect(createBrowserTab).not.toHaveBeenCalled()
})
it('keeps an asserted runtime link on its owner when policy selects the local client', async () => {
const createBrowserTab = createBrowserTabMock()
mocks.state = {
...ownerState(toRuntimeExecutionHostId('hub-a')),
...browserCapableRuntime('hub-a'),
createBrowserTab
}
const policySpy = vi
.spyOn(clientCreationActionPolicy, 'getClientCreationActionPolicy')
.mockReturnValue({
'managed-browser': { state: 'enabled', provider: 'local-client' },
'mobile-emulator': { state: 'enabled', provider: 'local-client' }
})
try {
await openWorkspaceBrowserTab({
workspaceId: WORKSPACE_ID,
url: 'https://example.com/pinned',
intent: { kind: 'url' },
expectedRuntimeEnvironmentId: 'hub-a'
})
expect(mocks.createRemote).toHaveBeenCalledWith(
expect.objectContaining({
environmentId: 'hub-a',
waitForRegistration: true,
worktreeId: WORKSPACE_ID
})
)
expect(createBrowserTab).not.toHaveBeenCalled()
} finally {
policySpy.mockRestore()
}
})
it('forwards an explicit server placement for owner-pinned remote panes', async () => {
mocks.state = {
...ownerState(toRuntimeExecutionHostId('hub-a')),
...browserCapableRuntime('hub-a'),
createBrowserTab: createBrowserTabMock()
}
await openWorkspaceBrowserTab({
workspaceId: WORKSPACE_ID,
url: 'https://example.com/pinned',
intent: { kind: 'url' },
expectedRuntimeEnvironmentId: 'hub-a',
placementPreference: 'server'
})
expect(mocks.createRemote).toHaveBeenCalledWith(
expect.objectContaining({ placementPreference: 'server' })
)
})
it('fails closed when the workspace route swaps away from the pane runtime before opening', async () => {
mocks.state = {
...ownerState(toRuntimeExecutionHostId('hub-a')),
...browserCapableRuntime('hub-a'),
createBrowserTab: createBrowserTabMock()
}
const paneRuntimeEnvironmentId = 'hub-a'
mocks.state = {
...ownerState(toRuntimeExecutionHostId('hub-b')),
...browserCapableRuntime('hub-b'),
createBrowserTab: createBrowserTabMock()
}
await expect(
openWorkspaceBrowserTab({
workspaceId: WORKSPACE_ID,
url: 'https://example.com/',
intent: { kind: 'url' },
expectedRuntimeEnvironmentId: paneRuntimeEnvironmentId
})
).rejects.toThrow('Unable to open URL.')
expect(mocks.createRemote).not.toHaveBeenCalled()
expect(mocks.state.createBrowserTab).not.toHaveBeenCalled()
})
it('fails closed for a blank asserted runtime owner', async () => {
mocks.state = {
...ownerState(toRuntimeExecutionHostId('hub-a')),
...browserCapableRuntime('hub-a'),
createBrowserTab: createBrowserTabMock()
}
await expect(
openWorkspaceBrowserTab({
workspaceId: WORKSPACE_ID,
url: 'https://example.com/',
intent: { kind: 'url' },
expectedRuntimeEnvironmentId: ' '
})
).rejects.toThrow('Unable to open URL.')
expect(mocks.createRemote).not.toHaveBeenCalled()
expect(mocks.state.createBrowserTab).not.toHaveBeenCalled()
})
it('routes an asserted paired-HUB SSH owner through that runtime', async () => {
const sshHost = toSshExecutionHostId('ssh-target')
mocks.state = {
...ownerState(sshHost, 'hub-a'),
...browserCapableRuntime('hub-a'),
createBrowserTab: createBrowserTabMock()
}
await openWorkspaceBrowserTab({
workspaceId: WORKSPACE_ID,
url: 'http://localhost:3000/',
intent: { kind: 'url' },
expectedRuntimeEnvironmentId: 'hub-a'
})
expect(mocks.createRemote).toHaveBeenCalledWith(
expect.objectContaining({ environmentId: 'hub-a', worktreeId: WORKSPACE_ID })
)
expect(mocks.state.createBrowserTab).not.toHaveBeenCalled()
})
it('does not fall back to a client browser for an incapable asserted runtime', async () => {
mocks.state = {
...ownerState(toRuntimeExecutionHostId('hub-a')),
createBrowserTab: createBrowserTabMock()
}
expect(canOpenWorkspaceBrowserTabOnRuntime(mocks.state as never, WORKSPACE_ID, 'hub-a')).toBe(
false
)
await expect(
openWorkspaceBrowserTab({
workspaceId: WORKSPACE_ID,
url: 'https://example.com/',
intent: { kind: 'url' },
expectedRuntimeEnvironmentId: 'hub-a'
})
).rejects.toThrow('Unable to open URL.')
expect(mocks.createRemote).not.toHaveBeenCalled()
expect(mocks.state.createBrowserTab).not.toHaveBeenCalled()
})
it.each(['runtime', 'ssh'] as const)(
'rejects another owner for an opted-in %s preview client provider',
async (kind) => {
const hostId =
kind === 'runtime' ? toRuntimeExecutionHostId('source') : toSshExecutionHostId('source')
const createBrowserTab = createBrowserTabMock()
mocks.state = {
...ownerState(hostId),
settings: { browserSshWorkspaceRoutingEnabled: false },
createBrowserTab
}
const request = {
workspaceId: WORKSPACE_ID,
url: 'https://example.com/',
intent: { kind: 'url' as const },
allowClientBrowserFallback: true
}
const expectedOwner = (id: string) =>
kind === 'runtime' ? { expectedRuntimeEnvironmentId: id } : { expectedSshConnectionId: id }
await expect(
openWorkspaceBrowserTab({ ...request, ...expectedOwner('other') })
).rejects.toThrow('Unable to open URL.')
expect(createBrowserTab).not.toHaveBeenCalled()
expect(mocks.createRemote).not.toHaveBeenCalled()
}
)
it.each([false, true])(
'does not fall back to a client browser when asserted runtime creation fails (client provider opt-in %s)',
async (allowClientBrowserFallback) => {
@@ -440,178 +93,6 @@ describe('openWorkspaceBrowserTab', () => {
}
)
it('does not route an asserted floating-terminal source to either runtime or local browser', async () => {
const createBrowserTab = createBrowserTabMock()
mocks.state = {
activeWorktreeId: FLOATING_TERMINAL_WORKTREE_ID,
activeWorkspaceExecutionHostId: toRuntimeExecutionHostId('hub-a'),
...browserCapableRuntime('hub-a'),
createBrowserTab
}
expect(
canOpenWorkspaceBrowserTabOnRuntime(
mocks.state as never,
FLOATING_TERMINAL_WORKTREE_ID,
'hub-a'
)
).toBe(false)
await expect(
openWorkspaceBrowserTab({
workspaceId: FLOATING_TERMINAL_WORKTREE_ID,
url: 'http://localhost:3000/',
intent: { kind: 'url' },
expectedRuntimeEnvironmentId: 'hub-a'
})
).rejects.toThrow('Unable to open URL.')
expect(mocks.createRemote).not.toHaveBeenCalled()
expect(createBrowserTab).not.toHaveBeenCalled()
})
it('keeps runtime browser actions unavailable in a paired-web floating workspace', async () => {
mocks.pairedWeb = true
mocks.state = {
activeWorktreeId: FLOATING_TERMINAL_WORKTREE_ID,
activeWorkspaceExecutionHostId: toRuntimeExecutionHostId('hub-a'),
...browserCapableRuntime('hub-a'),
createBrowserTab: createBrowserTabMock()
}
expect(
canOpenWorkspaceBrowserTabOnRuntime(
mocks.state as never,
FLOATING_TERMINAL_WORKTREE_ID,
'hub-a'
)
).toBe(false)
await expect(
openWorkspaceBrowserTab({
workspaceId: FLOATING_TERMINAL_WORKTREE_ID,
url: 'http://localhost:3000/',
intent: { kind: 'url' },
expectedRuntimeEnvironmentId: 'hub-a'
})
).rejects.toThrow('Unable to open URL.')
expect(mocks.createRemote).not.toHaveBeenCalled()
expect(mocks.state.createBrowserTab).not.toHaveBeenCalled()
})
// Two dev servers on one host must not share a tab title.
it('keeps the port in local-dev tab titles', async () => {
const createBrowserTab = createBrowserTabMock()
mocks.state = {
...ownerState(LOCAL_EXECUTION_HOST_ID),
createBrowserTab
}
for (const url of ['http://localhost:3000/', 'http://localhost:5173/app']) {
await openWorkspaceBrowserTab({ workspaceId: WORKSPACE_ID, url, intent: { kind: 'url' } })
}
expect(createBrowserTab.mock.calls.map((call) => call[2].title)).toEqual([
'localhost:3000',
'localhost:5173/app'
])
})
it('uses the desktop provider when the runtime cannot stream browsers', async () => {
const createBrowserTab = createBrowserTabMock()
const sshHost = toSshExecutionHostId('ssh-target')
mocks.state = {
...ownerState(sshHost, 'hub-a'),
createBrowserTab,
settings: { browserDefaultProfileIdByHostId: { local: 'local-profile' } }
}
mocks.createRemote.mockResolvedValue(false)
await openWorkspaceBrowserTab({
workspaceId: WORKSPACE_ID,
url: 'https://www.google.com/search?q=hooks',
intent: { kind: 'search', engine: 'google' }
})
expect(mocks.createRemote).not.toHaveBeenCalled()
expect(createBrowserTab).toHaveBeenCalledWith(
WORKSPACE_ID,
'https://www.google.com/search?q=hooks',
expect.objectContaining({ sessionProfileId: 'local-profile', title: 'Search Google' })
)
})
it.each([
['desktop hosting is on', true],
['the setting asks for server rendering', false]
])(
'opens an asserted runtime link through desktop hosting without screencast support when %s',
async (_label, enabled) => {
mocks.state = {
...ownerState(toRuntimeExecutionHostId('hub-a')),
settings: { browserClientHostedRemoteEnabled: enabled },
runtimeStatusByEnvironmentId: new Map([
['hub-a', { status: { capabilities: [...BROWSER_CLIENT_HOSTING_RUNTIME_CAPABILITIES] } }]
]),
createBrowserTab: vi.fn()
}
expect(canOpenWorkspaceBrowserTabOnRuntime(mocks.getState(), WORKSPACE_ID, 'hub-a')).toBe(
true
)
await openWorkspaceBrowserTab({
workspaceId: WORKSPACE_ID,
url: 'http://localhost:3000/',
intent: { kind: 'url' },
expectedRuntimeEnvironmentId: 'hub-a'
})
expect(mocks.createRemote).toHaveBeenCalledWith(
expect.objectContaining({ environmentId: 'hub-a', waitForRegistration: true })
)
expect(mocks.state.createBrowserTab).not.toHaveBeenCalled()
}
)
it.each([
['an older peer lacks capabilities', undefined, true, false],
[
'a peer lacks a hosting capability',
BROWSER_CLIENT_HOSTING_RUNTIME_CAPABILITIES.slice(1),
true,
false
],
[
'the viewing client is paired web',
[...BROWSER_CLIENT_HOSTING_RUNTIME_CAPABILITIES],
true,
true
]
])(
'refuses an asserted runtime link when %s',
async (_label, capabilities, enabled, pairedWeb) => {
mocks.pairedWeb = pairedWeb
mocks.state = {
...ownerState(toRuntimeExecutionHostId('hub-a')),
settings: { browserClientHostedRemoteEnabled: enabled },
runtimeStatusByEnvironmentId: new Map([['hub-a', { status: { capabilities } }]]),
createBrowserTab: vi.fn()
}
expect(canOpenWorkspaceBrowserTabOnRuntime(mocks.getState(), WORKSPACE_ID, 'hub-a')).toBe(
false
)
await expect(
openWorkspaceBrowserTab({
workspaceId: WORKSPACE_ID,
url: 'http://localhost:3000/',
intent: { kind: 'url' },
expectedRuntimeEnvironmentId: 'hub-a'
})
).rejects.toThrow('Unable to open URL.')
expect(mocks.createRemote).not.toHaveBeenCalled()
expect(mocks.state.createBrowserTab).not.toHaveBeenCalled()
}
)
it('does not fall back locally when the runtime create outcome is unknown', async () => {
const createBrowserTab = createBrowserTabMock()
mocks.state = {
@@ -675,114 +156,3 @@ describe('openWorkspaceBrowserTab', () => {
expect(mocks.state.createBrowserTab).not.toHaveBeenCalled()
})
})
describe('links from a managed server reached over SSH', () => {
function sshDeployedRuntimeState(
orcadDeployment: Record<string, unknown>,
settings: Record<string, unknown> = {}
): Record<string, unknown> {
return {
...ownerState(toRuntimeExecutionHostId('orcad-env')),
runtimeEnvironments: [{ id: 'orcad-env', orcadDeployment }],
// Headless orcad advertises no screencast backend.
runtimeStatusByEnvironmentId: new Map([
['orcad-env', { status: { capabilities: [] }, checkedAt: 1 }]
]),
settings,
createBrowserTab: createBrowserTabMock()
}
}
const sshDeployment = {
kind: 'ssh',
sshTargetId: 'ssh-target',
sshTargetGeneration: 2,
localPort: 41000,
remotePort: 41001
}
it('opens the link in an SSH-routed desktop tab the runtime workspace still owns', async () => {
mocks.state = sshDeployedRuntimeState(sshDeployment)
expect(canOpenWorkspaceBrowserTabOnRuntime(mocks.getState(), WORKSPACE_ID, 'orcad-env')).toBe(
true
)
await openWorkspaceBrowserTab({
workspaceId: WORKSPACE_ID,
url: 'http://localhost:3000/',
intent: { kind: 'url' },
expectedRuntimeEnvironmentId: 'orcad-env'
})
expect(mocks.state.createBrowserTab).toHaveBeenCalledWith(
WORKSPACE_ID,
'http://localhost:3000/',
expect.objectContaining({
browserRuntimeEnvironmentId: null,
executionHostId: toRuntimeExecutionHostId('orcad-env')
})
)
expect(mocks.createRemote).not.toHaveBeenCalled()
})
it('keeps a screencast-capable deployment on its runtime browser', async () => {
mocks.state = {
...sshDeployedRuntimeState(sshDeployment),
...browserCapableRuntime('orcad-env')
}
await openWorkspaceBrowserTab({
workspaceId: WORKSPACE_ID,
url: 'http://localhost:3000/',
intent: { kind: 'url' },
expectedRuntimeEnvironmentId: 'orcad-env'
})
expect(mocks.createRemote).toHaveBeenCalledWith(
expect.objectContaining({ environmentId: 'orcad-env', waitForRegistration: true })
)
expect(mocks.state.createBrowserTab).not.toHaveBeenCalled()
})
it.each([
['SSH browser routing is off', sshDeployment, { browserSshWorkspaceRoutingEnabled: false }],
[
'the user browses this host from this device',
sshDeployment,
{ browserSshWorkspaceRoutingDisabledTargetIds: ['ssh-target'] }
],
['the server is a WSL deployment', { kind: 'wsl', distro: 'Ubuntu' }, {}]
])('offers only the system browser when %s', async (_label, deployment, settings) => {
mocks.state = sshDeployedRuntimeState(deployment, settings)
expect(canOpenWorkspaceBrowserTabOnRuntime(mocks.getState(), WORKSPACE_ID, 'orcad-env')).toBe(
false
)
await expect(
openWorkspaceBrowserTab({
workspaceId: WORKSPACE_ID,
url: 'http://localhost:3000/',
intent: { kind: 'url' },
expectedRuntimeEnvironmentId: 'orcad-env'
})
).rejects.toThrow('Unable to open URL.')
expect(mocks.state.createBrowserTab).not.toHaveBeenCalled()
expect(mocks.createRemote).not.toHaveBeenCalled()
})
it('does not lend the SSH route to a link asserting another runtime', () => {
mocks.state = sshDeployedRuntimeState(sshDeployment)
expect(canOpenWorkspaceBrowserTabOnRuntime(mocks.getState(), WORKSPACE_ID, 'other-env')).toBe(
false
)
})
it('offers no in-app browser from the paired web client', () => {
mocks.pairedWeb = true
mocks.state = sshDeployedRuntimeState(sshDeployment)
expect(canOpenWorkspaceBrowserTabOnRuntime(mocks.getState(), WORKSPACE_ID, 'orcad-env')).toBe(
false
)
})
})
@@ -1,51 +0,0 @@
import { describe, expect, it } from 'vitest'
import { folderWorkspaceKey } from '../../../shared/workspace-scope'
import {
detectedListingFixture,
worktreeFixture
} from '../components/native-chat/native-chat-workspace-test-fixtures'
import { makeFolderWorkspace } from '@/store/slices/worktrees-slice-test-fixtures'
import { resolveWorkspaceDirectory, type WorkspaceDirectoryState } from './workspace-directory'
function catalog(overrides: Partial<WorkspaceDirectoryState> = {}): WorkspaceDirectoryState {
return {
detectedWorktreesByRepo: {},
floatingWorkspacePath: null,
folderWorkspaces: [],
worktreesByRepo: { repo: [worktreeFixture('wt-1', '/visible', { hostId: 'local' })] },
...overrides
}
}
describe('resolveWorkspaceDirectory', () => {
it('prefers a visible catalog row and resolves a detected-only row from the same snapshot', () => {
const state = catalog({
detectedWorktreesByRepo: {
repo: detectedListingFixture([
worktreeFixture('wt-1', '/detected-shadow', { hostId: 'local' }),
worktreeFixture('wt-detected', '/detected', { hostId: 'ssh:box-1' })
])
}
})
expect(resolveWorkspaceDirectory(state, 'wt-1')).toBe('/visible')
expect(resolveWorkspaceDirectory(state, 'wt-detected', 'ssh:box-1')).toBe('/detected')
expect(resolveWorkspaceDirectory(state, 'wt-detected', 'local')).toBeNull()
})
it('applies the same host rules when the caller passes no detected rows', () => {
const folderKey = folderWorkspaceKey('folder-1')
const state: WorkspaceDirectoryState = {
floatingWorkspacePath: null,
folderWorkspaces: [
makeFolderWorkspace({ folderPath: '/workspace/remote', connectionId: 'box-1' })
],
worktreesByRepo: { repo: [worktreeFixture('wt-1', '/visible')] }
}
// Why: an unhosted row is a local row, and a folder on another host is not this one.
expect(resolveWorkspaceDirectory(state, 'wt-1', 'local')).toBe('/visible')
expect(resolveWorkspaceDirectory(state, folderKey, 'local')).toBeNull()
expect(resolveWorkspaceDirectory(state, folderKey, 'ssh:box-1')).toBe('/workspace/remote')
})
})
@@ -59,167 +59,6 @@ beforeEach(() => {
})
describe('resolveWorkspaceDocAddressTarget', () => {
it('resolves a file URL inside a sibling folder workspace on the same host', () => {
expect(
resolveWorkspaceDocAddressTarget(makeState(), CURRENT, 'file:///srv/site/report.html')
).toMatchObject({
status: 'workspace-doc',
docLocation: {
worktreeId: 'folder:folder-1',
filePath: '/srv/site/report.html'
}
})
})
it('does not cross SSH targets behind the same paired server', () => {
places.set(CURRENT, 'ssh:first')
places.set(OTHER, 'ssh:second')
resolveWorkspaceDocAddressTarget(makeState(), CURRENT, 'file:///home/alice/wt2/report.html')
expect(plan.calls).toEqual([{ worktreeId: CURRENT, filePath: '/home/alice/wt2/report.html' }])
})
it('routes a pasted remote file URL through its workspace preview', () => {
expect(
resolveWorkspaceDocAddressTarget(
makeState(),
CURRENT,
' file:///home/alice/wt1/docs/report%20one.html '
)
).toMatchObject({
status: 'workspace-doc',
docLocation: {
worktreeId: CURRENT,
filePath: '/home/alice/wt1/docs/report one.html'
}
})
})
it('does not choose a matching root on another host', () => {
hosts.set(OTHER, 'server-2')
plan.result = { status: 'unsupported', message: 'outside workspace', reason: 'no-channel' }
expect(
resolveWorkspaceDocAddressTarget(makeState(), CURRENT, 'file:///home/alice/wt2/report.html')
).toEqual({ status: 'unsupported', message: 'outside workspace' })
expect(plan.calls).toEqual([{ worktreeId: CURRENT, filePath: '/home/alice/wt2/report.html' }])
})
it('skips ambiguous sibling hosts and uses the current host boundary', () => {
hosts.set(OTHER, null)
resolveWorkspaceDocAddressTarget(makeState(), CURRENT, 'file:///home/alice/wt2/report.html')
expect(plan.calls).toEqual([{ worktreeId: CURRENT, filePath: '/home/alice/wt2/report.html' }])
})
it('refuses a file URL when its current workspace host is ambiguous', () => {
hosts.set(CURRENT, null)
expect(
resolveWorkspaceDocAddressTarget(makeState(), CURRENT, 'file:///home/alice/wt1/report.html')
).toMatchObject({ status: 'unsupported' })
expect(plan.calls).toEqual([])
})
it('resolves an absolute path inside the current worktree to its document', () => {
const target = resolveWorkspaceDocAddressTarget(
makeState(),
CURRENT,
'/home/alice/wt1/docs/report.html'
)
expect(target).toEqual({
status: 'workspace-doc',
docLocation: {
kind: 'workspace-doc',
worktreeId: CURRENT,
filePath: '/home/alice/wt1/docs/report.html'
}
})
})
it('falls back to another known worktree only when the current one does not contain the path', () => {
const target = resolveWorkspaceDocAddressTarget(
makeState(),
CURRENT,
'/home/alice/wt2/index.html'
)
expect(target).toMatchObject({
status: 'workspace-doc',
docLocation: { worktreeId: OTHER, filePath: '/home/alice/wt2/index.html' }
})
})
// Prefer nested roots over outer containment so grants select the correct workspace/host owner.
it('attributes a file in a nested workspace to the nested root, not the outer one', () => {
const state = makeState()
const stateWithNested = {
...state,
allWorktrees: () => [...state.allWorktrees(), { id: 'repo2::/srv', path: '/srv' }]
} as typeof state
const target = resolveWorkspaceDocAddressTarget(
stateWithNested,
CURRENT,
'/srv/site/index.html'
)
expect(target).toMatchObject({
status: 'workspace-doc',
docLocation: { worktreeId: 'folder:folder-1' }
})
})
it('resolves a folder workspace root under its folder key', () => {
const target = resolveWorkspaceDocAddressTarget(makeState(), CURRENT, '/srv/site/index.html')
expect(target).toMatchObject({
status: 'workspace-doc',
docLocation: { worktreeId: 'folder:folder-1', filePath: '/srv/site/index.html' }
})
})
it('keeps a path outside every known workspace on the current host', () => {
expect(resolveWorkspaceDocAddressTarget(makeState(), CURRENT, '/etc/motd.html')).toEqual({
status: 'workspace-doc',
docLocation: { kind: 'workspace-doc', worktreeId: CURRENT, filePath: '/etc/motd.html' }
})
})
it('resolves a ./ relative path against the current worktree', () => {
const target = resolveWorkspaceDocAddressTarget(makeState(), CURRENT, './docs/report.html')
expect(target).toMatchObject({
status: 'workspace-doc',
docLocation: { worktreeId: CURRENT, filePath: '/home/alice/wt1/docs/report.html' }
})
})
// Containment is lexical, so dot segments are refused rather than resolved.
it('refuses dot segments in both the relative and the absolute form', () => {
expect(resolveWorkspaceDocAddressTarget(makeState(), CURRENT, './a/../../etc/x.html')).toEqual({
status: 'not-a-workspace-doc'
})
expect(
resolveWorkspaceDocAddressTarget(makeState(), CURRENT, '/home/alice/wt1/../wt9/x.html')
).toEqual({ status: 'not-a-workspace-doc' })
})
// "docs/report.html" is indistinguishable from a hostname with a path.
it('leaves bare relative text and non-previewable extensions to the URL pipeline', () => {
expect(resolveWorkspaceDocAddressTarget(makeState(), CURRENT, 'docs/report.html')).toEqual({
status: 'not-a-workspace-doc'
})
expect(
resolveWorkspaceDocAddressTarget(makeState(), CURRENT, '/home/alice/wt1/notes.md')
).toEqual({ status: 'not-a-workspace-doc' })
expect(plan.calls).toEqual([])
})
it('keeps a local file on the file:// pipeline and surfaces an unsupported plan as its message', () => {
plan.result = { status: 'browser-tab', url: 'file:///x', title: 'x' }
expect(
resolveWorkspaceDocAddressTarget(makeState(), CURRENT, '/home/alice/wt1/x.html')
).toEqual({ status: 'not-a-workspace-doc' })
plan.result = { status: 'unsupported', message: 'no channel', reason: 'no-channel' }
expect(
resolveWorkspaceDocAddressTarget(makeState(), CURRENT, '/home/alice/wt1/x.html')
).toEqual({ status: 'unsupported', message: 'no channel' })
})
it('resolves a current-workspace address without enumerating unrelated workspace roots', () => {
const state = makeState()
state.allWorktrees = vi.fn(() => {
@@ -231,26 +70,6 @@ describe('resolveWorkspaceDocAddressTarget', () => {
expect(state.allWorktrees).not.toHaveBeenCalled()
})
// The current worktree outranks every other root, including a more specific one nested inside it.
it('keeps the current worktree ahead of a workspace nested inside it', () => {
const state = makeState()
const nestedInsideCurrent = {
...state,
allWorktrees: () => [
...state.allWorktrees(),
{ id: 'repo3::/home/alice/wt1/vendor', path: '/home/alice/wt1/vendor' }
]
} as typeof state
expect(
resolveWorkspaceDocAddressTarget(
nestedInsideCurrent,
CURRENT,
'/home/alice/wt1/vendor/index.html'
)
).toMatchObject({ status: 'workspace-doc', docLocation: { worktreeId: CURRENT } })
})
it('short-circuits for a folder workspace that is the current workspace', () => {
const state = makeState()
const folderCurrent = {
@@ -1,86 +0,0 @@
import { describe, expect, it } from 'vitest'
import { getWorkspaceEditorSshHost } from './workspace-editor-ssh-target'
const link = { sshTargetId: 'ssh-1', sshTargetGeneration: 1, localPort: 41000, remotePort: 6768 }
const WORKTREE_ID = 'repo-1::/home/ada/project'
function stateWith(
environment: Record<string, unknown>,
worktreeHostId: 'runtime:env-1' | 'ssh:box' = 'runtime:env-1'
): Parameters<typeof getWorkspaceEditorSshHost>[0] {
return {
runtimeEnvironments: [{ id: 'env-1', ...environment }],
runtimeEnvironmentCatalogHydrated: true,
repos: [{ id: 'repo-1', connectionId: null, executionHostId: 'runtime:env-1' }],
worktreesByRepo: {
'repo-1': [
{
id: WORKTREE_ID,
repoId: 'repo-1',
hostId: worktreeHostId,
runtimeOwnerEnvironmentId: 'env-1'
}
]
}
}
}
describe('getWorkspaceEditorSshHost', () => {
it('names the SSH host a managed server runs on for a workspace on that machine', () => {
expect(
getWorkspaceEditorSshHost(stateWith({ orcadDeployment: link }), {
workspaceId: WORKTREE_ID,
hostId: 'runtime:env-1'
})
).toEqual({ environmentId: 'env-1', sshTargetId: 'ssh-1' })
})
it('names the SSH host of a paired server reached over SSH access', () => {
const sshAccess = { ...link, endpointId: 'tunnel', previousPreferredEndpointId: 'lan' }
expect(
getWorkspaceEditorSshHost(stateWith({ sshAccess }), { workspaceId: WORKTREE_ID })
).toEqual({ environmentId: 'env-1', sshTargetId: 'ssh-1' })
})
it('refuses a workspace the server reaches through its own SSH host', () => {
expect(
getWorkspaceEditorSshHost(stateWith({ orcadDeployment: link }, 'ssh:box'), {
workspaceId: WORKTREE_ID,
hostId: 'ssh:box'
})
).toBeNull()
})
it('places a folder workspace by its own SSH connection, not only its server stamp', () => {
const folderState = (
connectionId: string | null
): Parameters<typeof getWorkspaceEditorSshHost>[0] => ({
runtimeEnvironments: [{ id: 'env-1', orcadDeployment: link }],
runtimeEnvironmentCatalogHydrated: true,
folderWorkspaces: [
{ id: 'fw-1', projectGroupId: 'g-1', connectionId, executionHostId: 'runtime:env-1' }
],
projectGroups: [{ id: 'g-1', executionHostId: 'runtime:env-1' }]
})
const ref = { workspaceId: 'folder:fw-1', hostId: 'runtime:env-1' as const }
expect(getWorkspaceEditorSshHost(folderState(null), ref)).toEqual({
environmentId: 'env-1',
sshTargetId: 'ssh-1'
})
expect(getWorkspaceEditorSshHost(folderState('nested'), ref)).toBeNull()
})
it.each([
['a network-paired server', {}],
['an ephemeral VM', { orcadDeployment: { ...link, sshTargetId: 'runtime-ssh-vm-1' } }]
])('refuses %s', (_label, environment) => {
expect(
getWorkspaceEditorSshHost(stateWith(environment), { workspaceId: WORKTREE_ID })
).toBeNull()
})
it('refuses a local or missing workspace', () => {
expect(getWorkspaceEditorSshHost({}, { workspaceId: 'local-repo::/tmp/x' })).toBeNull()
expect(getWorkspaceEditorSshHost(stateWith({ orcadDeployment: link }), null)).toBeNull()
})
})
@@ -1,14 +0,0 @@
import { describe, expect, it } from 'vitest'
import { getWorkspaceEmojiPickerDefaults } from './workspace-emoji-picker-defaults'
import { searchWorkspaceEmojiShortcodes } from './workspace-emoji-shortcodes'
describe('getWorkspaceEmojiPickerDefaults', () => {
it('matches the shortcode catalog exactly and lists distinct emoji', () => {
const defaults = getWorkspaceEmojiPickerDefaults()
expect(defaults).toHaveLength(48)
expect(new Set(defaults.map((entry) => entry.emoji)).size).toBe(48)
for (const entry of defaults) {
expect(searchWorkspaceEmojiShortcodes(entry.shortcode, 1)[0]).toEqual(entry)
}
})
})
@@ -3,7 +3,7 @@ import type { WorkspaceEmojiSuggestion } from './workspace-emoji-shortcodes'
// Lives apart from the shortcode index so only the lazily loaded picker panel ships it.
// Shown before the user searches: emoji that read well as workspace markers.
// Only emoji-presentation code points: text-default ones (gear, warning, sun) render as monochrome glyphs.
// Literal pairs so opening the picker never builds the full shortcode catalog; a test keeps them in sync.
// Literal pairs so opening the picker never builds the full shortcode catalog.
const WORKSPACE_EMOJI_PICKER_DEFAULTS: readonly WorkspaceEmojiSuggestion[] = [
{ emoji: '🚀', shortcode: 'rocket' },
{ emoji: '🐛', shortcode: 'bug' },
@@ -1,21 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
describe('workspace emoji shortcode index laziness', () => {
beforeEach(() => {
vi.resetModules()
})
it('does not build the shared catalog when the renderer index is imported', async () => {
const shortcodeIndex = await import('./workspace-emoji-shortcodes')
const catalog = await import('../../../shared/emoji-shortcode-catalog')
expect(catalog.isEmojiShortcodeCatalogBuiltForTest()).toBe(false)
// Cursor/regex-only paths must stay off the catalog too.
expect(shortcodeIndex.getActiveWorkspaceEmojiShortcode('hi :tad', 7)).not.toBeNull()
expect(catalog.isEmojiShortcodeCatalogBuiltForTest()).toBe(false)
expect(shortcodeIndex.searchWorkspaceEmojiShortcodes('tada')[0]?.emoji).toBe('🎉')
expect(catalog.isEmojiShortcodeCatalogBuiltForTest()).toBe(true)
})
})
@@ -1,71 +0,0 @@
import { describe, expect, it } from 'vitest'
import {
applyWorkspaceEmojiSuggestion,
getActiveWorkspaceEmojiShortcode,
insertWorkspaceEmojiAtSelection,
replaceCompletedWorkspaceEmojiShortcode
} from './workspace-emoji-shortcodes'
describe('workspace emoji shortcodes', () => {
it('detects a shortcode at the caret without matching URLs or mid-word colons', () => {
expect(getActiveWorkspaceEmojiShortcode('Ship :win', 9)).toEqual({
start: 5,
end: 9,
query: 'win'
})
expect(getActiveWorkspaceEmojiShortcode('https://example.com', 19)).toBeNull()
expect(getActiveWorkspaceEmojiShortcode('fix:win', 7)).toBeNull()
})
it('replaces a completed shortcode and preserves text after the caret', () => {
expect(replaceCompletedWorkspaceEmojiShortcode('Ship :wink: today', 11)).toEqual({
value: 'Ship 😉 today',
cursor: 7
})
})
it('replaces a completed flag shortcode', () => {
expect(replaceCompletedWorkspaceEmojiShortcode(':flag_kr:', 9)).toEqual({
value: '🇰🇷',
cursor: 4
})
})
it('applies a selected suggestion to the active shortcode range', () => {
expect(
applyWorkspaceEmojiSuggestion(
'Ship :win today',
{ start: 5, end: 9, query: 'win' },
{ emoji: '😉', shortcode: 'wink' }
)
).toEqual({
value: 'Ship 😉 today',
cursor: 8
})
})
})
describe('insertWorkspaceEmojiAtSelection', () => {
it('pads the emoji so it never fuses with neighbouring text', () => {
expect(insertWorkspaceEmojiAtSelection('user-support', 0, 0, '🚀')).toEqual({
value: '🚀 user-support',
cursor: 3
})
expect(insertWorkspaceEmojiAtSelection('user-support', 12, 12, '🚀')).toEqual({
value: 'user-support 🚀 ',
cursor: 16
})
expect(insertWorkspaceEmojiAtSelection('ship it', 4, 4, '🚀')).toEqual({
value: 'ship 🚀 it',
cursor: 7
})
})
it('replaces the selected range and clamps out-of-range selections', () => {
expect(insertWorkspaceEmojiAtSelection('ship bug fix', 5, 8, '🐛')).toEqual({
value: 'ship 🐛 fix',
cursor: 7
})
expect(insertWorkspaceEmojiAtSelection('', 4, 9, '✨')).toEqual({ value: '✨ ', cursor: 2 })
})
})
@@ -1,90 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import {
getWorkspaceFileBrowserOpenTarget,
REMOTE_FILE_BROWSER_UNSUPPORTED_MESSAGE,
TWIN_FILE_BROWSER_UNSUPPORTED_MESSAGE
} from './workspace-file-browser-open-target'
import { localAndSshTwinRows } from './workspace-twin-rows.test-fixture'
const mocks = vi.hoisted(() => ({
environmentId: null as string | null,
connectionId: null as string | null,
twin: false
}))
vi.mock('@/lib/worktree-runtime-owner', () => ({
runtimeEnvironmentIdOrLocalUnlessTwin: () => mocks.environmentId
}))
vi.mock('@/store', () => ({
useAppStore: {
getState: () =>
mocks.twin
? localAndSshTwinRows('repo-1', 'wt-1')
: {
repos: [{ id: 'repo-1', connectionId: mocks.connectionId }],
worktreesByRepo: { 'repo-1': [{ id: 'wt-1', repoId: 'repo-1' }] }
}
}
}))
beforeEach(() => {
mocks.environmentId = null
mocks.connectionId = null
mocks.twin = false
})
describe('getWorkspaceFileBrowserOpenTarget', () => {
it('returns a reusable browser navigation target for local files', () => {
expect(
getWorkspaceFileBrowserOpenTarget({
filePath: 'C:\\repo\\demo page.html',
worktreeId: 'wt-1'
})
).toEqual({
status: 'ready',
url: 'file:///C:/repo/demo%20page.html',
title: 'demo page.html'
})
})
it('still refuses remote files, which have no local file URL', () => {
mocks.connectionId = 'ssh-1'
expect(
getWorkspaceFileBrowserOpenTarget({
filePath: '/home/alice/report.html',
worktreeId: 'wt-1'
})
).toEqual({
status: 'unsupported',
reason: 'remote-worktree',
message: REMOTE_FILE_BROWSER_UNSUPPORTED_MESSAGE
})
})
it('refuses a file:// URL for a project two hosts publish, naming why', () => {
mocks.twin = true
expect(
getWorkspaceFileBrowserOpenTarget({ filePath: '/srv/repo/report.html', worktreeId: 'wt-1' })
).toEqual({
status: 'unsupported',
reason: 'remote-worktree',
message: TWIN_FILE_BROWSER_UNSUPPORTED_MESSAGE
})
})
it('refuses paired files even when their connection ID is null', () => {
mocks.environmentId = 'managed-host'
expect(
getWorkspaceFileBrowserOpenTarget({
filePath: '/srv/repo/report.html',
worktreeId: 'wt-1'
})
).toEqual({
status: 'unsupported',
reason: 'remote-worktree',
message: REMOTE_FILE_BROWSER_UNSUPPORTED_MESSAGE
})
})
})
@@ -1,49 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { ExecutionHostId } from '../../../shared/execution-host'
import { readWorkspaceFileDragSource } from './workspace-file-drag'
import { writeWorkspaceFileDragSourceForWorkspace } from './workspace-file-drag-source'
const mocks = vi.hoisted((): { host: ExecutionHostId | null; environmentId: string | null } => ({
host: 'ssh:target-a',
environmentId: null
}))
vi.mock('@/store', () => ({
useAppStore: { getState: () => ({ settings: { activeRuntimeEnvironmentId: 'foreign-env' } }) }
}))
vi.mock('./worktree-runtime-owner', () => ({
getKnownExecutionHostIdForWorktree: () => mocks.host
}))
vi.mock('./resolve-owner', () => ({
runtimeEnvironmentIdForWorkspaceOwner: () => ({
kind: 'resolved',
environmentId: mocks.environmentId
})
}))
describe('workspace drag source server namespace', () => {
beforeEach(() => {
mocks.host = 'ssh:target-a'
mocks.environmentId = null
})
it.each([null, 'env-a'])('stamps SSH ownership from its workspace: %s', (environmentId) => {
mocks.environmentId = environmentId
const payload = new Map<string, string>()
writeWorkspaceFileDragSourceForWorkspace(
{ setData: (type, value) => payload.set(type, value) },
'folder:source'
)
expect(readWorkspaceFileDragSource({ getData: (type) => payload.get(type) ?? '' })).toEqual({
executionHostId: 'ssh:target-a',
workspaceId: 'folder:source',
...(environmentId ? { runtimeEnvironmentId: environmentId } : {})
})
})
it('leaves an unknown workspace unstamped despite the focused server', () => {
mocks.host = null
const setData = vi.fn()
writeWorkspaceFileDragSourceForWorkspace({ setData }, 'folder:missing')
expect(setData).not.toHaveBeenCalled()
})
})
@@ -5,9 +5,7 @@ import {
WORKSPACE_FILE_PATHS_MIME,
WORKSPACE_FILE_PATH_MIME,
encodeWorkspaceFilePaths,
readWorkspaceFileDragPaths,
readWorkspaceFileDragSource,
writeWorkspaceFileDragSource
readWorkspaceFileDragPaths
} from './workspace-file-drag'
vi.mock('../../../shared/cross-platform-path', async (importOriginal) => {
@@ -31,74 +29,6 @@ class FakeDataTransfer {
}
describe('workspace file drag payloads', () => {
it('rejects missing and malformed workspace ownership', () => {
const transfer = new FakeDataTransfer()
expect(readWorkspaceFileDragSource(transfer)).toBeNull()
transfer.setData('application/x-orca-workspace-file-source', '{"workspaceId":"worktree-1"}')
expect(readWorkspaceFileDragSource(transfer)).toBeNull()
})
it('round-trips the server namespace of a nested SSH target', () => {
const transfer = new FakeDataTransfer()
const source = {
executionHostId: 'ssh:remote-1',
workspaceId: 'folder:docs',
runtimeEnvironmentId: 'env-1'
} as const
writeWorkspaceFileDragSource(transfer, source)
expect(readWorkspaceFileDragSource(transfer)).toEqual(source)
})
it.each([null, 123, '', ' '])('rejects a malformed server namespace %s', (value) => {
const transfer = new FakeDataTransfer()
transfer.setData(
'application/x-orca-workspace-file-source',
JSON.stringify({
version: 1,
executionHostId: 'ssh:remote-1',
workspaceId: 'folder:docs',
runtimeEnvironmentId: value
})
)
expect(readWorkspaceFileDragSource(transfer)).toBeNull()
})
it('round-trips bounded multi-path payloads and removes nested duplicates', () => {
const transfer = new FakeDataTransfer()
transfer.setData(
WORKSPACE_FILE_PATHS_MIME,
encodeWorkspaceFilePaths(['/repo/src', '/repo/src/index.ts', '/repo/README.md'])
)
expect(readWorkspaceFileDragPaths(transfer)).toEqual({
byteLength: 42,
pathCount: 2,
paths: ['/repo/src', '/repo/README.md'],
status: 'accepted'
})
const selected = readWorkspaceFileDragPaths(transfer, { collapseDescendantsForMove: false })
if (selected.status !== 'accepted') {
throw new Error('Expected selected resources to be accepted')
}
expect(selected.paths).toEqual(['/repo/src', '/repo/src/index.ts', '/repo/README.md'])
})
it('keeps sibling paths whose names only share a prefix', () => {
const transfer = new FakeDataTransfer()
transfer.setData(
WORKSPACE_FILE_PATHS_MIME,
encodeWorkspaceFilePaths(['/repo/src', '/repo/src-other', '/repo/src-other/index.ts'])
)
const result = readWorkspaceFileDragPaths(transfer)
expect(result).toMatchObject({
pathCount: 2,
paths: ['/repo/src', '/repo/src-other'],
status: 'accepted'
})
})
it('normalizes each accepted path once while pruning Windows-style nested duplicates', () => {
const normalizePathForComparison = vi.mocked(
crossPlatformPath.normalizeRuntimePathForComparison
@@ -124,19 +54,6 @@ describe('workspace file drag payloads', () => {
expect(normalizePathForComparison).toHaveBeenCalledTimes(paths.length)
})
it('rejects oversized raw multi-path data before JSON parsing', () => {
const transfer = new FakeDataTransfer()
const oversizedPrefix = '😀'.repeat(3)
transfer.setData(WORKSPACE_FILE_PATHS_MIME, `${oversizedPrefix}not-json`)
expect(readWorkspaceFileDragPaths(transfer, { maxPathBytes: 5 })).toEqual({
byteLength: 8,
pathCount: 0,
reason: 'paths-too-large',
status: 'rejected'
})
})
it('rejects decoded path lists by path count without falling back to a single path', () => {
const transfer = new FakeDataTransfer()
transfer.setData(WORKSPACE_FILE_PATHS_MIME, JSON.stringify(['/repo/a', '/repo/b']))
@@ -1,18 +0,0 @@
import { describe, expect, it } from 'vitest'
import {
getExternalWorkspacePorts,
getWorkspacePortGroups,
getWorkspacePortsByWorktreeId
} from './workspace-port-groups'
describe('workspace port group caches', () => {
it('returns stable empty references when no scan result exists', () => {
expect(getWorkspacePortsByWorktreeId(null).size).toBe(0)
expect(getWorkspacePortGroups(null)).toEqual([])
expect(getExternalWorkspacePorts(null)).toEqual([])
expect(getWorkspacePortsByWorktreeId(null)).toBe(getWorkspacePortsByWorktreeId(undefined))
expect(getWorkspacePortGroups(null)).toBe(getWorkspacePortGroups(undefined))
expect(getExternalWorkspacePorts(null)).toBe(getExternalWorkspacePorts(undefined))
})
})
@@ -1,173 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
const { callRuntimeRpc, supportsCapability } = vi.hoisted(() => ({
callRuntimeRpc: vi.fn(),
supportsCapability: vi.fn()
}))
vi.mock('@/runtime/runtime-rpc-client', async (importOriginal) => ({
...(await importOriginal<Record<string, unknown>>()),
callRuntimeRpc,
runtimeEnvironmentSupportsCapability: supportsCapability
}))
import { RuntimeRpcCallError } from '@/runtime/runtime-rpc-result'
import {
killWorkspacePortOnExecutionHost,
scanWorkspacePortsOnExecutionHost,
WORKSPACE_PORTS_SERVER_SSH_UPDATE_REASON,
type HostScopedPortHost
} from './workspace-port-scan-client'
import { WORKSPACE_PORTS_HOST_SCOPED_RUNTIME_CAPABILITY } from '../../../../shared/workspace-ports-runtime-capabilities'
const host: HostScopedPortHost = {
route: { target: { kind: 'environment', environmentId: 'env-e' }, at: 'ssh:t' },
executionHostId: 'ssh:t',
worktreeId: 'repo-1::/srv/w'
}
const rowOnT = {
id: '0.0.0.0:3020:4242',
kind: 'external',
bindHost: '0.0.0.0',
connectHost: '127.0.0.1',
port: 3020,
pid: 4242,
protocol: 'unknown'
}
beforeEach(() => {
callRuntimeRpc.mockReset()
supportsCapability.mockReset()
supportsCapability.mockResolvedValue(true)
})
describe('scanWorkspacePortsOnExecutionHost', () => {
it('asks the server to scan the workspace host and keeps the host it names', async () => {
callRuntimeRpc.mockResolvedValue({
executionHostId: 'ssh:t',
platform: 'linux',
scannedAt: 1,
ports: [rowOnT]
})
const scan = await scanWorkspacePortsOnExecutionHost(host)
expect(supportsCapability).toHaveBeenCalledWith(
'env-e',
WORKSPACE_PORTS_HOST_SCOPED_RUNTIME_CAPABILITY
)
expect(callRuntimeRpc).toHaveBeenCalledWith(
host.route.target,
'workspacePorts.scanHost',
{ worktree: 'id:repo-1::/srv/w' },
expect.objectContaining({ timeoutMs: 15_000 })
)
expect(scan).toMatchObject({ executionHostId: 'ssh:t', ports: [rowOnT] })
expect(scan.unavailableReason).toBeUndefined()
})
it('never asks an older server, whose scan would show the server instead of its SSH host', async () => {
supportsCapability.mockResolvedValue(false)
const scan = await scanWorkspacePortsOnExecutionHost(host)
expect(callRuntimeRpc).not.toHaveBeenCalled()
expect(scan).toMatchObject({
executionHostId: 'ssh:t',
ports: [],
unavailableReason: WORKSPACE_PORTS_SERVER_SSH_UPDATE_REASON
})
})
it('says "update server" when the server lacks the method despite advertising it', async () => {
callRuntimeRpc.mockRejectedValue(
new RuntimeRpcCallError({
id: 'r',
ok: false,
error: { code: 'method_not_found', message: 'Unknown method' },
_meta: { runtimeId: 'runtime-e' }
})
)
await expect(scanWorkspacePortsOnExecutionHost(host)).resolves.toMatchObject({
ports: [],
unavailableReason: WORKSPACE_PORTS_SERVER_SSH_UPDATE_REASON
})
})
it("scans a recipe VM through this app's own runtime without a capability probe", async () => {
const recipeVm: HostScopedPortHost = {
route: { target: { kind: 'local' }, at: 'ssh:runtime-ssh-vm1' },
executionHostId: 'ssh:runtime-ssh-vm1',
worktreeId: 'repo-1::/srv/w'
}
callRuntimeRpc.mockResolvedValue({
executionHostId: 'ssh:runtime-ssh-vm1',
platform: 'unknown',
scannedAt: 1,
ports: [],
unavailableReason: 'The SSH host is not connected.'
})
const scan = await scanWorkspacePortsOnExecutionHost(recipeVm)
expect(supportsCapability).not.toHaveBeenCalled()
expect(callRuntimeRpc).toHaveBeenCalledWith(
{ kind: 'local' },
'workspacePorts.scanHost',
{ worktree: 'id:repo-1::/srv/w' },
expect.anything()
)
// An unreachable VM stays "could not look", never an empty "no ports".
expect(scan.unavailableReason).toBe('The SSH host is not connected.')
})
it("drops rows the server scanned on a host other than the workspace's", async () => {
callRuntimeRpc.mockResolvedValue({
executionHostId: 'local',
platform: 'linux',
scannedAt: 1,
ports: [rowOnT]
})
const scan = await scanWorkspacePortsOnExecutionHost(host)
expect(scan.ports).toEqual([])
expect(scan.unavailableReason).toMatch(/different host/)
})
})
describe('killWorkspacePortOnExecutionHost', () => {
it('stops a row only through the server, naming the host the row was scanned on', async () => {
callRuntimeRpc.mockResolvedValue({ ok: true })
await expect(
killWorkspacePortOnExecutionHost(host, { scannedHostId: 'ssh:t', pid: 4242, port: 3020 })
).resolves.toEqual({ ok: true })
// Equal server/SSH pid+port values are unrelated processes; never fall back to repo-scoped server kills.
expect(callRuntimeRpc).toHaveBeenCalledOnce()
expect(callRuntimeRpc).toHaveBeenCalledWith(
host.route.target,
'workspacePorts.killHost',
{ worktree: 'id:repo-1::/srv/w', executionHostId: 'ssh:t', pid: 4242, port: 3020 },
expect.objectContaining({ timeoutMs: 15_000 })
)
})
it('reports "update server" instead of falling back to the server-local kill', async () => {
callRuntimeRpc.mockRejectedValue(
new RuntimeRpcCallError({
id: 'r',
ok: false,
error: { code: 'method_not_found', message: 'Unknown method' },
_meta: { runtimeId: 'runtime-e' }
})
)
await expect(
killWorkspacePortOnExecutionHost(host, { scannedHostId: 'ssh:t', pid: 4242, port: 3020 })
).resolves.toEqual({ ok: false, reason: WORKSPACE_PORTS_SERVER_SSH_UPDATE_REASON })
expect(callRuntimeRpc).toHaveBeenCalledOnce()
})
})
@@ -21,15 +21,6 @@ function good(portIds: string[]): WorkspacePortScanResult {
}
}
function unavailable(): WorkspacePortScanResult {
return { platform: 'unknown', scannedAt: 1, ports: [], unavailableReason: 'scan failed' }
}
/** What the ports popover publishes when its own scan fails: reason + last-good ports. */
function unavailableWithRetainedPorts(portIds: string[]): WorkspacePortScanResult {
return { ...good(portIds), unavailableReason: 'scan failed' }
}
const FAILURE_THRESHOLD = 2
function createHarness(): {
@@ -58,46 +49,6 @@ function createHarness(): {
}
describe('reconcileTransientPortScanFailures', () => {
it('keeps the live indicator solid through a single transient failure', () => {
const { apply } = createHarness()
const first = apply([{ key: 'host:all', result: good(['tcp:3000']) }])
expect(first[0].result.ports).toHaveLength(1)
const second = apply([{ key: 'host:all', result: unavailable() }])
expect(second[0].result.ports).toHaveLength(1)
expect(second[0].result.unavailableReason).toBeUndefined()
})
it('drops ports only after failures reach the tolerance', () => {
const { apply } = createHarness()
apply([{ key: 'h:all', result: good(['tcp:3000']) }])
apply([{ key: 'h:all', result: unavailable() }])
const third = apply([{ key: 'h:all', result: unavailable() }])
expect(third[0].result.ports).toHaveLength(0)
expect(third[0].result.unavailableReason).toBe('scan failed')
})
it('clears immediately when a reachable host reports zero ports', () => {
const { apply } = createHarness()
apply([{ key: 'h:all', result: good(['tcp:3000']) }])
const next = apply([{ key: 'h:all', result: good([]) }])
expect(next[0].result.ports).toHaveLength(0)
expect(next[0].result.unavailableReason).toBeUndefined()
})
it('resets the failure streak after a successful scan', () => {
const { apply } = createHarness()
apply([{ key: 'h:all', result: good(['tcp:3000']) }])
apply([{ key: 'h:all', result: unavailable() }])
apply([{ key: 'h:all', result: good(['tcp:3000']) }])
const afterRecovery = apply([{ key: 'h:all', result: unavailable() }])
expect(afterRecovery[0].result.ports).toHaveLength(1)
})
it('prunes state for hosts that disappear', () => {
const { apply, state } = createHarness()
apply([{ key: 'gone:all', result: good(['tcp:3000']) }])
@@ -105,52 +56,4 @@ describe('reconcileTransientPortScanFailures', () => {
expect(state.has('gone:all')).toBe(false)
expect(state.has('other:all')).toBe(true)
})
// Popover failures already publish last-good ports; do not count that as spent grace or the next poll drops them immediately.
it('still grants the grace period after a failure that retained its ports', () => {
const { apply, publish } = createHarness()
apply([{ key: 'h:all', result: good(['tcp:3000']) }])
const popoverResult = unavailableWithRetainedPorts(['tcp:3000'])
publish('h:all', popoverResult)
const next = apply([{ key: 'h:all', result: unavailable() }])
expect(next[0].result).toBe(popoverResult)
expect(next[0].result.ports).toHaveLength(1)
})
it('still drops retained ports once failures reach the tolerance', () => {
const { apply, publish } = createHarness()
apply([{ key: 'h:all', result: good(['tcp:3000']) }])
publish('h:all', unavailableWithRetainedPorts(['tcp:3000']))
apply([{ key: 'h:all', result: unavailable() }])
const next = apply([{ key: 'h:all', result: unavailable() }])
expect(next[0].result.ports).toHaveLength(0)
expect(next[0].result.unavailableReason).toBe('scan failed')
})
it('uses a newer manual result instead of resurrecting stale ports', () => {
const { apply, publish } = createHarness()
apply([{ key: 'h:all', result: good(['tcp:3000']) }])
publish('h:all', good([]))
const next = apply([{ key: 'h:all', result: unavailable() }])
expect(next[0].result.ports).toHaveLength(0)
expect(next[0].result.unavailableReason).toBeUndefined()
})
it('starts a fresh failure streak after a newer manual result', () => {
const { apply, publish } = createHarness()
apply([{ key: 'h:all', result: good(['tcp:3000']) }])
apply([{ key: 'h:all', result: unavailable() }])
const manualResult = good([])
publish('h:all', manualResult)
const next = apply([{ key: 'h:all', result: unavailable() }])
expect(next[0].result).toBe(manualResult)
})
})
@@ -1,139 +0,0 @@
// @vitest-environment happy-dom
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { WorkspacePortScanResult } from '../../../../shared/workspace-ports'
vi.mock('@/lib/worktree-activation', () => ({
activateAndRevealWorktree: vi.fn()
}))
vi.mock('@/runtime/runtime-rpc-client', () => ({
callRuntimeRpc: vi.fn(),
assertRuntimeEnvironmentCapability: vi.fn()
}))
vi.mock('@/runtime/runtime-rpc-result', () => ({
RuntimeRpcCallError: class RuntimeRpcCallError extends Error {
code?: string
}
}))
vi.mock('./workspace-port-scan-client', () => ({
runWorkspacePortScanForTarget: vi.fn()
}))
const { publishWorkspacePortScanForHost, WORKSPACE_PORT_ALL_HOSTS_SCAN_KEY } =
await import('./workspace-port-actions')
type WorkspacePortScanPublisher = Parameters<typeof publishWorkspacePortScanForHost>[0]
function scanWithPort(port: number, scannedAt: number): WorkspacePortScanResult {
return {
platform: 'linux',
scannedAt,
ports: [
{
id: `tcp:${port}`,
bindHost: '0.0.0.0',
connectHost: '127.0.0.1',
port,
pid: 100 + port,
processName: 'node',
protocol: 'http',
kind: 'external'
}
]
}
}
/** Mirrors the store's replaceWorkspacePortScans semantics: one atomic update. */
function makeStoreHarness(initial: Record<string, WorkspacePortScanResult> = {}): {
scansByKey: Record<string, WorkspacePortScanResult>
projections: { key: string; result: WorkspacePortScanResult }[]
publisher: Omit<WorkspacePortScanPublisher, 'scanKey' | 'scan'>
} {
let scansByKey: Record<string, WorkspacePortScanResult> = { ...initial }
const projections: { key: string; result: WorkspacePortScanResult }[] = []
return {
get scansByKey() {
return scansByKey
},
projections,
publisher: {
replaceWorkspacePortScans: (
nextScansByKey: Record<string, WorkspacePortScanResult>,
projection: { key: string; result: WorkspacePortScanResult } | null
) => {
scansByKey = nextScansByKey
if (projection) {
projections.push(projection)
}
},
getWorkspacePortScansByKey: () => scansByKey
}
}
}
describe('publishWorkspacePortScanForHost', () => {
let localScan: WorkspacePortScanResult
let remoteScan: WorkspacePortScanResult
beforeEach(() => {
localScan = scanWithPort(5173, 10)
remoteScan = scanWithPort(3000, 20)
})
it('keeps the other host in the projection when one host refreshes', () => {
const harness = makeStoreHarness({ 'local:all': localScan })
publishWorkspacePortScanForHost({
...harness.publisher,
scanKey: 'environment:env-1:all',
scan: remoteScan
})
const projection = harness.projections.at(-1)
expect(projection?.key).toBe(WORKSPACE_PORT_ALL_HOSTS_SCAN_KEY)
expect(projection?.result.ports.map((port) => port.port).sort()).toEqual([3000, 5173])
expect(Object.keys(harness.scansByKey).sort()).toEqual(['environment:env-1:all', 'local:all'])
})
it('publishes map and projection in a single store update', () => {
const harness = makeStoreHarness({ 'local:all': localScan })
const replaceSpy = vi.spyOn(harness.publisher, 'replaceWorkspacePortScans')
publishWorkspacePortScanForHost({
...harness.publisher,
scanKey: 'environment:env-1:all',
scan: remoteScan
})
// Atomic replacement must notify once and keep map/projection coherent.
expect(replaceSpy).toHaveBeenCalledTimes(1)
const [nextScans, projection] = replaceSpy.mock.calls[0]
expect(Object.keys(nextScans).sort()).toEqual(['environment:env-1:all', 'local:all'])
expect(projection?.key).toBe(WORKSPACE_PORT_ALL_HOSTS_SCAN_KEY)
})
it('does not accumulate duplicate rows across repeated publishes', () => {
const harness = makeStoreHarness({ 'local:all': localScan })
publishWorkspacePortScanForHost({
...harness.publisher,
scanKey: 'environment:env-1:all',
scan: remoteScan
})
publishWorkspacePortScanForHost({
...harness.publisher,
scanKey: 'environment:env-1:all',
scan: { ...remoteScan, scannedAt: 30 }
})
// Never store aggregate in per-host state or the next merge multiplies its own rows.
expect(harness.scansByKey[WORKSPACE_PORT_ALL_HOSTS_SCAN_KEY]).toBeUndefined()
expect(
harness.projections
.at(-1)
?.result.ports.map((port) => port.port)
.sort()
).toEqual([3000, 5173])
})
})
@@ -1,210 +0,0 @@
import { describe, expect, it } from 'vitest'
import type { WorkspaceSessionSnapshot } from './workspace-session'
import { buildWorkspaceSessionPayload } from './workspace-session'
function createSnapshot(
overrides: Partial<WorkspaceSessionSnapshot> = {}
): WorkspaceSessionSnapshot {
return {
activeRepoId: 'repo-1',
activeWorkspaceKey: 'worktree:wt-1',
activeWorktreeId: 'wt-1',
activeTabId: 'tab-1',
tabsByWorktree: {},
ptyIdsByTabId: {},
terminalLayoutsByTabId: {},
localOnlyScrollbackByTabId: {},
activeTabIdByWorktree: {},
openFiles: [],
editorDrafts: {},
markdownFrontmatterVisible: {},
activeFileIdByWorktree: {},
activeTabTypeByWorktree: {},
browserTabsByWorktree: {},
browserPagesByWorkspace: {},
remoteBrowserPageHandlesByPageId: {},
activeBrowserTabIdByWorktree: {},
browserUrlHistory: [],
workspaceDocHistory: [],
unifiedTabsByWorktree: {},
groupsByWorktree: {},
layoutByWorktree: {},
activeGroupIdByWorktree: {},
sshConnectionStates: new Map(),
repos: [],
worktreesByRepo: {},
lastKnownRelayPtyIdByTabId: {},
lastVisitedAtByWorktreeId: {},
defaultTerminalTabsAppliedByWorktreeId: {},
...overrides
}
}
describe('workspace session editor drafts', () => {
it('persists dirty editor drafts without saving clean file content', () => {
const payload = buildWorkspaceSessionPayload(
createSnapshot({
openFiles: [
{
id: '/tmp/dirty.md',
filePath: '/tmp/dirty.md',
relativePath: 'dirty.md',
worktreeId: 'wt-1',
language: 'markdown',
mode: 'edit',
isDirty: true
} as never,
{
id: '/tmp/clean.md',
filePath: '/tmp/clean.md',
relativePath: 'clean.md',
worktreeId: 'wt-1',
language: 'markdown',
mode: 'edit',
isDirty: false
} as never
],
editorDrafts: {
'/tmp/dirty.md': '',
'/tmp/clean.md': 'clean draft should not persist'
}
})
)
expect(payload.openFilesByWorktree?.['wt-1']).toEqual([
expect.objectContaining({
filePath: '/tmp/dirty.md',
dirtyDraftContent: ''
}),
expect.not.objectContaining({
dirtyDraftContent: expect.any(String)
})
])
})
it('persists the SSH target that owns an external host file', () => {
const payload = buildWorkspaceSessionPayload(
createSnapshot({
openFiles: [
{
id: '/tmp/ssh-preview.png',
filePath: '/tmp/ssh-preview.png',
relativePath: '/tmp/ssh-preview.png',
worktreeId: 'wt-1',
language: 'png',
mode: 'edit',
isDirty: false,
externalSshTargetId: 'ssh-1'
} as never
]
})
)
expect(payload.openFilesByWorktree?.['wt-1']?.[0]).toEqual(
expect.objectContaining({ externalSshTargetId: 'ssh-1' })
)
})
it('persists the disk baseline signature only alongside a dirty draft', () => {
const payload = buildWorkspaceSessionPayload(
createSnapshot({
openFiles: [
{
id: '/tmp/dirty.md',
filePath: '/tmp/dirty.md',
relativePath: 'dirty.md',
worktreeId: 'wt-1',
language: 'markdown',
mode: 'edit',
isDirty: true,
lastKnownDiskSignature: 'abc123'
} as never,
{
id: '/tmp/clean.md',
filePath: '/tmp/clean.md',
relativePath: 'clean.md',
worktreeId: 'wt-1',
language: 'markdown',
mode: 'edit',
isDirty: false,
lastKnownDiskSignature: 'def456'
} as never
],
editorDrafts: {
'/tmp/dirty.md': 'unsaved edits'
}
})
)
expect(payload.openFilesByWorktree?.['wt-1']).toEqual([
expect.objectContaining({
filePath: '/tmp/dirty.md',
dirtyDraftContent: 'unsaved edits',
lastKnownDiskSignature: 'abc123'
}),
// Clean tabs need no restore-conflict signature; persisting it bloats every write.
expect.not.objectContaining({
lastKnownDiskSignature: expect.any(String)
})
])
})
it('persists readOnly:true and never emits a dirty draft for read-only tabs', () => {
const payload = buildWorkspaceSessionPayload(
createSnapshot({
openFiles: [
{
id: '/home/user/.claude/log.jsonl',
filePath: '/home/user/.claude/log.jsonl',
relativePath: '/home/user/.claude/log.jsonl',
worktreeId: 'wt-1',
language: 'jsonl',
mode: 'edit',
// Why: even if isDirty is somehow set, a read-only tab must not
// persist a draft that a restore could write back to disk.
isDirty: true,
readOnly: true,
liveTail: true
} as never
],
editorDrafts: {
'/home/user/.claude/log.jsonl': 'stray draft that must not persist'
}
})
)
const persisted = payload.openFilesByWorktree?.['wt-1']?.[0]
expect(persisted).toEqual(
expect.objectContaining({
filePath: '/home/user/.claude/log.jsonl',
readOnly: true,
liveTail: true
})
)
expect(persisted).toEqual(
expect.not.objectContaining({ dirtyDraftContent: expect.any(String) })
)
})
it('omits readOnly for ordinary writable tabs (writable is the default)', () => {
const payload = buildWorkspaceSessionPayload(
createSnapshot({
openFiles: [
{
id: '/tmp/clean.md',
filePath: '/tmp/clean.md',
relativePath: 'clean.md',
worktreeId: 'wt-1',
language: 'markdown',
mode: 'edit',
isDirty: false
} as never
]
})
)
const persisted = payload.openFilesByWorktree?.['wt-1']?.[0]
expect(persisted).toEqual(expect.objectContaining({ filePath: '/tmp/clean.md' }))
expect(persisted).not.toHaveProperty('readOnly')
})
})
@@ -1,29 +0,0 @@
import { describe, expect, it } from 'vitest'
import { getFolderWorkspaceHostId } from '../../../shared/folder-workspace-execution-host'
import { FOLDER_HOST_CASES } from '../../../shared/folder-workspace-host-routing.test-fixture'
import { folderWorkspaceKey } from '../../../shared/workspace-scope'
import { adoptFromEndpoint } from '../store/adopt-from-endpoint'
import { buildHostIdByWorktreeId } from './workspace-session-host-persistence'
// Main's router is checked against the same table in folder-workspace-mixed-host-routing.test.ts.
describe("the window's save partition for each folder kind local main lists", () => {
it.each(FOLDER_HOST_CASES)('$name', ({ folder, groups, expected }) => {
const local = { kind: 'local' } as const
const stampedGroups = groups.map((row) =>
adoptFromEndpoint(local, { kind: 'projectGroup', row })
)
const stampedFolder = adoptFromEndpoint(local, {
kind: 'folderWorkspace',
row: folder,
// As the window's catalog resolver does, over the groups it already stamped.
resolveOwnHostId: (row) => getFolderWorkspaceHostId(row, stampedGroups)
})
const hostIdOf = buildHostIdByWorktreeId({
repos: [],
projectGroups: stampedGroups,
folderWorkspaces: [stampedFolder],
worktreesByRepo: {}
})
expect(hostIdOf(folderWorkspaceKey(folder.id))).toBe(expected)
})
})
@@ -5,14 +5,10 @@ import type { TerminalTab } from '../../../shared/terminal-tab-types'
import type { WorkspaceSessionState } from '../../../shared/workspace-session-state-types'
import type { ExecutionHostId } from '../../../shared/execution-host'
import { folderWorkspaceKey } from '../../../shared/workspace-scope'
import {
indexWorktreeHostClaims,
mergeWorkspaceSessionsWithHostShadow
} from './workspace-session-host-contention'
import { pickPrimaryHostForClaims } from '../../../shared/workspace-session-contested-primary-host'
import './workspace-session-host-contention'
import '../../../shared/workspace-session-contested-primary-host'
import { fetchWorkspaceSessionWithRuntimeHostOwners } from './workspace-session-host-hydration'
import {
buildHostIdByWorktreeId,
patchWorkspaceSessionByHost,
persistWorkspaceSessionByHost,
type HostPersistenceState
@@ -58,86 +54,6 @@ function contestedState(overrides: Partial<HostPersistenceState> = {}): HostPers
}
}
describe('indexWorktreeHostClaims', () => {
it('records every host publishing the same workspace id', () => {
const claims = indexWorktreeHostClaims(contestedState().worktreesByRepo, new Map())
expect([...(claims.get(SHARED_ID) ?? [])].sort()).toEqual(['local', SSH_HOST])
})
it('attributes an unqualified row through its repo when the repo names one host', () => {
const claims = indexWorktreeHostClaims(
{ 'repo-a': [{ id: 'repo-a::/work/a', repoId: 'repo-a' }] },
new Map([['repo-a', SSH_HOST]])
)
expect([...(claims.get('repo-a::/work/a') ?? [])]).toEqual([SSH_HOST])
})
it('leaves an unqualified row unattributed when its repo id is itself ambiguous', () => {
const claims = indexWorktreeHostClaims(
{ 'repo-a': [{ id: 'repo-a::/work/a', repoId: 'repo-a' }] },
new Map([['repo-a', null]])
)
expect(claims.has('repo-a::/work/a')).toBe(false)
})
it('prefers local as primary, else the lowest host id', () => {
expect(pickPrimaryHostForClaims([SSH_HOST, 'local'])).toBe('local')
expect(pickPrimaryHostForClaims(['runtime:b', 'runtime:a'])).toBe('runtime:a')
})
})
describe('buildHostIdByWorktreeId for a contested workspace id', () => {
it('routes a local/SSH collision to one deterministic primary', () => {
expect(buildHostIdByWorktreeId(contestedState())(SHARED_ID)).toBe('local')
})
it('gives two runtime claimants a runtime primary instead of folding them into local', () => {
const owner = buildHostIdByWorktreeId({
repos: [],
worktreesByRepo: {
'repo-shared': [
{ id: SHARED_ID, repoId: 'repo-shared', hostId: 'runtime:env-b' },
{ id: SHARED_ID, repoId: 'repo-shared', hostId: 'runtime:env-a' }
]
}
})
expect(owner(SHARED_ID)).toBe('runtime:env-a')
})
})
describe('mergeWorkspaceSessionsWithHostShadow', () => {
it('parks a co-claimant partition entry instead of letting it win the shared key', () => {
const merged = mergeWorkspaceSessionsWithHostShadow({
local: sessionWithTabs({ [SHARED_ID]: [tab('local-tab')] }),
[SSH_HOST]: sessionWithTabs({ [SHARED_ID]: [tab('ssh-tab')] })
})
expect(merged.session.tabsByWorktree[SHARED_ID]?.map((entry) => entry.id)).toEqual([
'local-tab'
])
expect(
(merged.shadow[SSH_HOST]?.tabsByWorktree?.[SHARED_ID] ?? []).map((entry) => entry.id)
).toEqual(['ssh-tab'])
expect(merged.shadow.local).toBeUndefined()
})
it('leaves uncontested partitions untouched', () => {
const merged = mergeWorkspaceSessionsWithHostShadow({
local: sessionWithTabs({ 'repo-a::/a': [tab('a', 'repo-a::/a')] }),
'runtime:env-1': sessionWithTabs({
'repo-b::/b': [tab('b', 'repo-b::/b')]
})
})
expect(Object.keys(merged.session.tabsByWorktree).sort()).toEqual(['repo-a::/a', 'repo-b::/b'])
expect(merged.shadow).toEqual({})
})
})
type SessionWriteMock = Mock<
(session: WorkspaceSessionState, hostId?: ExecutionHostId) => Promise<void>
>
@@ -318,11 +234,6 @@ describe('read-time primary is the one the write path honours', () => {
}
}
it('keeps an SSH claimant out of the rotating runtime partition', () => {
// Claim sorting favors runtime over ssh, which must not move SSH rows out of their own partition.
expect(buildHostIdByWorktreeId(sshVersusRuntimeState())(SHARED_ID)).toBe(SSH_HOST)
})
it('does not strand the runtime co-claimant when the SSH row is written', async () => {
const set: SessionWriteMock = vi.fn(async () => {})
await persistWorkspaceSessionByHost(
@@ -371,33 +282,6 @@ describe('read-time primary is the one the write path honours', () => {
expect(localWrite?.tabsByWorktree[SHARED_ID]).toBeUndefined()
})
it('still migrates a workspace the catalog has re-attributed to another partition', () => {
const owner = buildHostIdByWorktreeId({
repos: [],
worktreesByRepo: {
'repo-shared': [{ id: SHARED_ID, repoId: 'repo-shared', hostId: RUNTIME_HOST }]
},
contestedPrimaryHostBySessionKey: { [SHARED_ID]: 'local' }
})
expect(owner(SHARED_ID)).toBe(RUNTIME_HOST)
})
it('records the partition every restored key came from, contested or not', () => {
const merged = mergeWorkspaceSessionsWithHostShadow({
local: sessionWithTabs({ [SHARED_ID]: [tab('local-tab')] }),
[RUNTIME_HOST]: sessionWithTabs({
[SHARED_ID]: [tab('runtime-tab')],
[RUNTIME_ONLY_ID]: [tab('runtime-only-tab', RUNTIME_ONLY_ID)]
})
})
expect(merged.primaryHostBySessionKey).toEqual({
[SHARED_ID]: 'local',
[RUNTIME_ONLY_ID]: RUNTIME_HOST
})
})
it('does not name a runtime owner for a key the local partition kept', async () => {
const read = await fetchWorkspaceSessionWithRuntimeHostOwners(
{
@@ -2,18 +2,12 @@ import { describe, expect, it, vi } from 'vitest'
import { getDefaultWorkspaceSession } from '../../../shared/constants'
import type { TerminalLayoutSnapshot } from '../../../shared/terminal-tab-types'
import type { WorkspaceSessionState } from '../../../shared/workspace-session-state-types'
import { folderWorkspaceKey, worktreeWorkspaceKey } from '../../../shared/workspace-scope'
import { folderWorkspaceKey } from '../../../shared/workspace-scope'
import {
buildHostIdByWorktreeId,
buildWorkspaceSessionHostSnapshots,
patchWorkspaceSessionByHost,
persistWorkspaceSessionByHost,
type HostPersistenceState
} from './workspace-session-host-persistence'
import {
fetchWorkspaceSessionFromHosts,
fetchWorkspaceSessionWithRuntimeHostOwners
} from './workspace-session-host-hydration'
import { fetchWorkspaceSessionFromHosts } from './workspace-session-host-hydration'
describe('fetchWorkspaceSessionFromHosts', () => {
it('reads saved runtime host partitions before runtime repos are loaded', async () => {
@@ -51,140 +45,6 @@ describe('fetchWorkspaceSessionFromHosts', () => {
expect(session.tabsByWorktree[worktreeId]).toEqual(remoteSession.tabsByWorktree[worktreeId])
})
it('returns runtime owners for worktrees loaded from runtime host partitions', async () => {
const worktreeId = 'remote-repo::/srv/remote-wt'
const get = vi.fn(async (hostId?: string): Promise<WorkspaceSessionState> => {
if (hostId === 'runtime:env-1') {
return {
...getDefaultWorkspaceSession(),
tabsByWorktree: {
[worktreeId]: [
{
id: 'remote-tab',
ptyId: null,
worktreeId,
title: 'Remote',
customTitle: null,
color: null,
sortOrder: 0,
createdAt: 1
}
]
}
}
}
return getDefaultWorkspaceSession()
})
const read = await fetchWorkspaceSessionWithRuntimeHostOwners({ get }, [], ['runtime:env-1'])
expect(read.session.tabsByWorktree[worktreeId]).toHaveLength(1)
expect(read.runtimeHostIdByWorkspaceSessionKey).toEqual({
[worktreeId]: 'runtime:env-1'
})
})
it('normalizes canonical worktree session keys in runtime owner maps', async () => {
const worktreeId = 'remote-repo::/srv/remote-wt'
const workspaceKey = worktreeWorkspaceKey(worktreeId)
const get = vi.fn(async (hostId?: string): Promise<WorkspaceSessionState> => {
if (hostId === 'runtime:env-1') {
return {
...getDefaultWorkspaceSession(),
tabsByWorktree: {
[workspaceKey]: [
{
id: 'remote-tab',
ptyId: null,
worktreeId,
title: 'Remote',
customTitle: null,
color: null,
sortOrder: 0,
createdAt: 1
}
]
}
}
}
return getDefaultWorkspaceSession()
})
const read = await fetchWorkspaceSessionWithRuntimeHostOwners({ get }, [], ['runtime:env-1'])
expect(read.runtimeHostIdByWorkspaceSessionKey).toEqual({
[worktreeId]: 'runtime:env-1'
})
})
it('returns runtime owners for folder workspace session keys', async () => {
const folderKey = folderWorkspaceKey('folder-1')
const get = vi.fn(async (hostId?: string): Promise<WorkspaceSessionState> => {
if (hostId === 'runtime:env-1') {
return {
...getDefaultWorkspaceSession(),
activeWorkspaceKey: folderKey,
tabsByWorktree: {
[folderKey]: [
{
id: 'remote-folder-tab',
ptyId: null,
worktreeId: folderKey,
title: 'Remote folder',
customTitle: null,
color: null,
sortOrder: 0,
createdAt: 1
}
]
}
}
}
return getDefaultWorkspaceSession()
})
const read = await fetchWorkspaceSessionWithRuntimeHostOwners({ get }, [], ['runtime:env-1'])
expect(read.session.tabsByWorktree[folderKey]).toHaveLength(1)
expect(read.runtimeHostIdByWorkspaceSessionKey).toEqual({
[folderKey]: 'runtime:env-1'
})
})
it('returns runtime owners for sleeping-agent-only runtime worktrees', async () => {
const worktreeId = 'remote-repo::/srv/sleeping-wt'
const get = vi.fn(async (hostId?: string): Promise<WorkspaceSessionState> => {
if (hostId === 'runtime:env-1') {
return {
...getDefaultWorkspaceSession(),
sleepingAgentSessionsByPaneKey: {
'remote-tab:leaf-1': {
paneKey: 'remote-tab:leaf-1',
tabId: 'remote-tab',
worktreeId,
agent: 'codex',
providerSession: { key: 'session_id', id: 'codex-session-1' },
prompt: 'finish the task',
state: 'working',
capturedAt: 1,
updatedAt: 1
}
}
}
}
return getDefaultWorkspaceSession()
})
const read = await fetchWorkspaceSessionWithRuntimeHostOwners({ get }, [], ['runtime:env-1'])
expect(read.session.sleepingAgentSessionsByPaneKey?.['remote-tab:leaf-1']?.worktreeId).toBe(
worktreeId
)
expect(read.runtimeHostIdByWorkspaceSessionKey).toEqual({
[worktreeId]: 'runtime:env-1'
})
})
it('routes restored runtime folder workspace patches back to the runtime host', async () => {
const folderKey = folderWorkspaceKey('folder-1')
const patch = vi.fn().mockResolvedValue(undefined)
@@ -391,88 +251,6 @@ describe('fetchWorkspaceSessionFromHosts', () => {
'runtime:env-1'
)
})
it('defaults duplicate repo ids to local when the worktree has no host metadata', () => {
const owner = buildHostIdByWorktreeId({
repos: [
{ id: 'same-repo', connectionId: null, executionHostId: 'local' },
{
id: 'same-repo',
connectionId: null,
executionHostId: 'runtime:env-1'
}
],
worktreesByRepo: {
'same-repo': [{ id: 'same-repo::/local-only', repoId: 'same-repo' }]
}
})
expect(owner('same-repo::/local-only')).toBe('local')
})
it('routes canonical worktree keys using their raw worktree owner', () => {
const worktreeId = 'remote-repo::/srv/remote-wt'
const owner = buildHostIdByWorktreeId({
repos: [],
worktreesByRepo: {
'remote-repo': [{ id: worktreeId, repoId: 'remote-repo', hostId: 'runtime:env-1' }]
}
})
expect(owner(worktreeWorkspaceKey(worktreeId))).toBe('runtime:env-1')
})
it('builds local-first host snapshots', () => {
const localWorktreeId = 'local-repo::C:\\src\\local'
const remoteWorktreeId = 'remote-repo::/srv/remote'
const makeTab = (id: string, worktreeId: string) => ({
id,
ptyId: null,
worktreeId,
title: id,
customTitle: null,
color: null,
sortOrder: 0,
createdAt: 1
})
const payload: WorkspaceSessionState = {
...getDefaultWorkspaceSession(),
tabsByWorktree: {
[localWorktreeId]: [makeTab('local-tab', localWorktreeId)],
[remoteWorktreeId]: [makeTab('remote-tab', remoteWorktreeId)]
}
}
const state = {
repos: [
{ id: 'local-repo', connectionId: null, executionHostId: 'local' },
{
id: 'remote-repo',
connectionId: null,
executionHostId: 'runtime:env-1'
}
],
worktreesByRepo: {
'local-repo': [{ id: localWorktreeId, repoId: 'local-repo' }],
'remote-repo': [
{
id: remoteWorktreeId,
repoId: 'remote-repo',
hostId: 'runtime:env-1'
}
]
}
} satisfies HostPersistenceState
const snapshots = buildWorkspaceSessionHostSnapshots(payload, state)
expect(snapshots.map((snapshot) => snapshot.hostId)).toEqual([undefined, 'runtime:env-1'])
expect(snapshots[0].state.tabsByWorktree).toEqual({
[localWorktreeId]: [expect.objectContaining({ id: 'local-tab' })]
})
expect(snapshots[1].state.tabsByWorktree).toEqual({
[remoteWorktreeId]: [expect.objectContaining({ id: 'remote-tab' })]
})
})
})
describe('patchWorkspaceSessionByHost write outcome', () => {
@@ -600,105 +378,3 @@ describe('patchWorkspaceSessionByHost tab-keyed routing', () => {
)
})
})
describe('buildHostIdByWorktreeId nested ownership', () => {
it('persists an SSH worktree in its paired HUB session partition', () => {
const worktreeId = 'nested-repo::/srv/remote-wt'
const owner = buildHostIdByWorktreeId({
repos: [
{
id: 'nested-repo',
connectionId: 'hub-private-ssh',
executionHostId: 'runtime:owner-hub'
}
],
worktreesByRepo: {
'nested-repo': [
{
id: worktreeId,
repoId: 'nested-repo',
hostId: 'ssh:hub-private-ssh',
runtimeOwnerEnvironmentId: 'owner-hub'
}
]
}
})
expect(owner(worktreeId)).toBe('runtime:owner-hub')
})
})
describe('persistWorkspaceSessionByHost', () => {
it('awaits every host write before crossing the durable flush boundary', async () => {
const localWorktreeId = 'local-repo::/src/local'
const remoteWorktreeId = 'remote-repo::/srv/remote'
const payload: WorkspaceSessionState = {
...getDefaultWorkspaceSession(),
tabsByWorktree: {
[localWorktreeId]: [
{
id: 'local-tab',
ptyId: null,
worktreeId: localWorktreeId,
title: 'Local',
customTitle: null,
color: null,
sortOrder: 0,
createdAt: 1
}
],
[remoteWorktreeId]: [
{
id: 'remote-tab',
ptyId: null,
worktreeId: remoteWorktreeId,
title: 'Remote',
customTitle: null,
color: null,
sortOrder: 0,
createdAt: 1
}
]
}
}
const set = vi.fn().mockResolvedValue(undefined)
const flush = vi.fn().mockResolvedValue(undefined)
await persistWorkspaceSessionByHost({ get: vi.fn(), set, patch: vi.fn(), flush }, payload, {
repos: [
{ id: 'local-repo', connectionId: null, executionHostId: 'local' },
{
id: 'remote-repo',
connectionId: null,
executionHostId: 'runtime:env-1'
}
],
worktreesByRepo: {
'local-repo': [{ id: localWorktreeId, repoId: 'local-repo' }],
'remote-repo': [
{
id: remoteWorktreeId,
repoId: 'remote-repo',
hostId: 'runtime:env-1'
}
]
}
})
expect(set).toHaveBeenCalledTimes(2)
expect(set).toHaveBeenCalledWith(
expect.objectContaining({
tabsByWorktree: { [localWorktreeId]: expect.any(Array) }
}),
undefined
)
expect(set).toHaveBeenCalledWith(
expect.objectContaining({
tabsByWorktree: { [remoteWorktreeId]: expect.any(Array) }
}),
'runtime:env-1'
)
expect(flush).toHaveBeenCalledTimes(1)
expect(set.mock.invocationCallOrder.at(-1)).toBeLessThan(flush.mock.invocationCallOrder[0]!)
})
})
@@ -1,500 +0,0 @@
import { describe, it, expect } from 'vitest'
import {
splitWorkspaceSessionByHost,
mergeWorkspaceSessionsFromHosts,
type HostIdByWorktreeId
} from './workspace-session-host-split'
import { getDefaultWorkspaceSession } from '../../../shared/constants'
import { LOCAL_EXECUTION_HOST_ID, type ExecutionHostId } from '../../../shared/execution-host'
import { HOST_PARTITION_REDUNDANT_GLOBAL_FIELDS } from '../../../shared/workspace-session-host-field-ownership'
import type { BrowserPage } from '../../../shared/browser-workspace-types'
import type { Tab } from '../../../shared/tab-types'
import type { TerminalLayoutSnapshot, TerminalTab } from '../../../shared/terminal-tab-types'
import type { WorkspaceSessionState } from '../../../shared/workspace-session-state-types'
const RUNTIME_A: ExecutionHostId = 'runtime:env-a'
const RUNTIME_B: ExecutionHostId = 'runtime:env-b'
function makeTab(id: string, worktreeId: string): TerminalTab {
return {
id,
ptyId: null,
worktreeId,
title: id,
customTitle: null,
color: null,
sortOrder: 0,
createdAt: 1
}
}
function makeUnifiedTab(id: string, worktreeId: string): Tab {
return {
id,
entityId: id,
groupId: `group-${worktreeId}`,
worktreeId,
contentType: 'terminal',
label: id,
customLabel: null,
color: null,
sortOrder: 0,
createdAt: 1
}
}
function makeLayout(): TerminalLayoutSnapshot {
return { root: { type: 'leaf', leafId: 'leaf-1' }, activeLeafId: 'leaf-1', expandedLeafId: null }
}
function makeBrowserPage(id: string, workspaceId: string, worktreeId: string): BrowserPage {
return {
id,
workspaceId,
worktreeId,
url: 'https://example.com',
title: 'Example',
loading: false,
faviconUrl: null,
canGoBack: false,
canGoForward: false,
loadError: null,
createdAt: 1
}
}
/** worktree id convention in these tests: `<host>-wt-...`, except local ones. */
function ownerByPrefix(): HostIdByWorktreeId {
return (worktreeId: string) => {
if (worktreeId.startsWith('a-')) {
return RUNTIME_A
}
if (worktreeId.startsWith('b-')) {
return RUNTIME_B
}
return LOCAL_EXECUTION_HOST_ID
}
}
describe('splitWorkspaceSessionByHost', () => {
it('keeps global fields only on the local slice', () => {
const state: WorkspaceSessionState = {
...getDefaultWorkspaceSession(),
activeRepoId: 'repo-1',
activeWorktreeId: 'local-wt',
activeTabId: 'tab-1',
browserUrlHistory: [
{ url: 'u', normalizedUrl: 'u', title: 't', lastVisitedAt: 1, visitCount: 1 }
],
activeConnectionIdsAtShutdown: ['ssh-target']
}
const slices = splitWorkspaceSessionByHost(state, ownerByPrefix())
expect(slices[LOCAL_EXECUTION_HOST_ID]?.activeRepoId).toBe('repo-1')
expect(slices[LOCAL_EXECUTION_HOST_ID]?.activeConnectionIdsAtShutdown).toEqual(['ssh-target'])
// No runtime slice is created when nothing is worktree-owned by it.
expect(slices[RUNTIME_A]).toBeUndefined()
})
it('never replicates a local-owned global onto a non-local slice', () => {
// One global template copied local browserUrlHistory into every runtime, undoing #18161 on the next save (~66KB/host at 200 entries).
const state: WorkspaceSessionState = {
...getDefaultWorkspaceSession(),
browserUrlHistory: [
{ url: 'u', normalizedUrl: 'u', title: 't', lastVisitedAt: 1, visitCount: 1 }
],
workspaceDocHistory: [
{
docLocation: { kind: 'workspace-doc', worktreeId: 'local-wt', filePath: 'a.md' },
title: 'a.md',
lastVisitedAt: 2,
visitCount: 1
}
],
tabsByWorktree: {
'local-wt': [makeTab('t-local', 'local-wt')],
'a-wt': [makeTab('t-a', 'a-wt')],
'b-wt': [makeTab('t-b', 'b-wt')]
}
}
const slices = splitWorkspaceSessionByHost(state, ownerByPrefix())
expect(Object.keys(slices).sort()).toEqual([LOCAL_EXECUTION_HOST_ID, RUNTIME_A, RUNTIME_B])
for (const field of HOST_PARTITION_REDUNDANT_GLOBAL_FIELDS) {
expect(slices[LOCAL_EXECUTION_HOST_ID]?.[field]).toEqual(state[field])
expect(Object.hasOwn(slices[RUNTIME_A] ?? {}, field)).toBe(false)
expect(Object.hasOwn(slices[RUNTIME_B] ?? {}, field)).toBe(false)
}
// Local already supplies globals, so merge never falls back to remote copies.
const merged = mergeWorkspaceSessionsFromHosts(slices)
for (const field of HOST_PARTITION_REDUNDANT_GLOBAL_FIELDS) {
expect(merged[field]).toEqual(state[field])
}
})
it('routes worktree-keyed maps to their owner host', () => {
const state: WorkspaceSessionState = {
...getDefaultWorkspaceSession(),
tabsByWorktree: {
'local-wt': [makeTab('t-local', 'local-wt')],
'a-wt': [makeTab('t-a', 'a-wt')],
'b-wt': [makeTab('t-b', 'b-wt')]
}
}
const slices = splitWorkspaceSessionByHost(state, ownerByPrefix())
expect(Object.keys(slices[LOCAL_EXECUTION_HOST_ID]?.tabsByWorktree ?? {})).toEqual(['local-wt'])
expect(Object.keys(slices[RUNTIME_A]?.tabsByWorktree ?? {})).toEqual(['a-wt'])
expect(Object.keys(slices[RUNTIME_B]?.tabsByWorktree ?? {})).toEqual(['b-wt'])
})
it('routes host-qualified visit recency to the partition the key names', () => {
const state: WorkspaceSessionState = {
...getDefaultWorkspaceSession(),
lastVisitedAtByWorktreeId: {
'local-wt': 1,
'a-wt': 2,
'ssh:builder|ssh-wt': 3,
'runtime:env-a|a-wt': 4
}
}
const slices = splitWorkspaceSessionByHost(state, ownerByPrefix())
// Host-qualified recency must share its workspace’s partition or adoption records visits without corresponding tabs (#12721).
expect(slices[LOCAL_EXECUTION_HOST_ID]?.lastVisitedAtByWorktreeId).toEqual({ 'local-wt': 1 })
expect(slices[RUNTIME_A]?.lastVisitedAtByWorktreeId).toEqual({
'a-wt': 2,
'runtime:env-a|a-wt': 4
})
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the literal is a well-formed ssh: host id; ExecutionHostId is a template-literal type a plain string cannot satisfy.
expect(slices['ssh:builder' as ExecutionHostId]?.lastVisitedAtByWorktreeId).toEqual({
'ssh:builder|ssh-wt': 3
})
})
it('routes tab-keyed maps via the owning tab worktree (legacy + unified)', () => {
const state: WorkspaceSessionState = {
...getDefaultWorkspaceSession(),
tabsByWorktree: { 'a-wt': [makeTab('t-a', 'a-wt')] },
unifiedTabs: { 'b-wt': [makeUnifiedTab('t-b', 'b-wt')] },
terminalLayoutsByTabId: { 't-a': makeLayout(), 't-b': makeLayout() },
remoteSessionIdsByTabId: { 't-a': 'sess-a', 't-b': 'sess-b' }
}
const slices = splitWorkspaceSessionByHost(state, ownerByPrefix())
expect(slices[RUNTIME_A]?.terminalLayoutsByTabId).toHaveProperty('t-a')
expect(slices[RUNTIME_B]?.terminalLayoutsByTabId).toHaveProperty('t-b')
expect(slices[RUNTIME_A]?.remoteSessionIdsByTabId).toEqual({ 't-a': 'sess-a' })
expect(slices[RUNTIME_B]?.remoteSessionIdsByTabId).toEqual({ 't-b': 'sess-b' })
})
it('keeps orphan tab layouts (unknown worktree) in the local slice', () => {
const state: WorkspaceSessionState = {
...getDefaultWorkspaceSession(),
terminalLayoutsByTabId: { orphan: makeLayout() }
}
const slices = splitWorkspaceSessionByHost(state, ownerByPrefix())
expect(slices[LOCAL_EXECUTION_HOST_ID]?.terminalLayoutsByTabId).toHaveProperty('orphan')
expect(slices[RUNTIME_A]).toBeUndefined()
})
it('routes tab-keyed rows through a caller-supplied tab index when the payload has no tab rows', () => {
// Layouts/PTY-only patches omit tabsByWorktree; use the live tab index for ownership.
const state: WorkspaceSessionState = {
...getDefaultWorkspaceSession(),
terminalLayoutsByTabId: { 't-a': makeLayout() },
remoteSessionIdsByTabId: { 't-a': 'sess-a' },
terminalPtyIncarnationsByPaneKey: { 't-a:leaf-1': 'inc-3' }
}
const slices = splitWorkspaceSessionByHost(state, ownerByPrefix(), {
worktreeIdByTabId: new Map([['t-a', 'a-wt-1']])
})
expect(slices[RUNTIME_A]?.terminalLayoutsByTabId).toHaveProperty('t-a')
expect(slices[RUNTIME_A]?.remoteSessionIdsByTabId).toEqual({ 't-a': 'sess-a' })
expect(slices[RUNTIME_A]?.terminalPtyIncarnationsByPaneKey).toEqual({ 't-a:leaf-1': 'inc-3' })
expect(slices[LOCAL_EXECUTION_HOST_ID]?.terminalLayoutsByTabId).toEqual({})
expect(slices[LOCAL_EXECUTION_HOST_ID]?.remoteSessionIdsByTabId).toEqual({})
})
it('keeps a tab the supplied index does not name in the local slice', () => {
const state: WorkspaceSessionState = {
...getDefaultWorkspaceSession(),
terminalLayoutsByTabId: { orphan: makeLayout() }
}
const slices = splitWorkspaceSessionByHost(state, ownerByPrefix(), {
worktreeIdByTabId: new Map([['t-a', 'a-wt-1']])
})
expect(slices[LOCAL_EXECUTION_HOST_ID]?.terminalLayoutsByTabId).toHaveProperty('orphan')
expect(slices[RUNTIME_A]).toBeUndefined()
})
it('routes browser pages via their record worktreeId', () => {
const state: WorkspaceSessionState = {
...getDefaultWorkspaceSession(),
browserPagesByWorkspace: {
'ws-a': [makeBrowserPage('p-a', 'ws-a', 'a-wt')],
'ws-local': [makeBrowserPage('p-local', 'ws-local', 'local-wt')]
}
}
const slices = splitWorkspaceSessionByHost(state, ownerByPrefix())
expect(slices[RUNTIME_A]?.browserPagesByWorkspace).toHaveProperty('ws-a')
expect(slices[LOCAL_EXECUTION_HOST_ID]?.browserPagesByWorkspace).toHaveProperty('ws-local')
})
it('routes markdown frontmatter visibility via the open file worktree', () => {
const state: WorkspaceSessionState = {
...getDefaultWorkspaceSession(),
openFilesByWorktree: {
'a-wt': [
{
filePath: '/a/file.md',
relativePath: 'file.md',
worktreeId: 'a-wt',
language: 'markdown'
}
]
},
markdownFrontmatterVisible: { '/a/file.md': true, '/unknown.md': true }
}
const slices = splitWorkspaceSessionByHost(state, ownerByPrefix())
expect(slices[RUNTIME_A]?.markdownFrontmatterVisible).toEqual({ '/a/file.md': true })
// Unknown file id has no owner → stays local.
expect(slices[LOCAL_EXECUTION_HOST_ID]?.markdownFrontmatterVisible).toEqual({
'/unknown.md': true
})
})
it('partitions activeWorktreeIdsOnShutdown by owner', () => {
const state: WorkspaceSessionState = {
...getDefaultWorkspaceSession(),
activeWorktreeIdsOnShutdown: ['a-wt', 'b-wt', 'local-wt']
}
const slices = splitWorkspaceSessionByHost(state, ownerByPrefix())
expect(slices[RUNTIME_A]?.activeWorktreeIdsOnShutdown).toEqual(['a-wt'])
expect(slices[RUNTIME_B]?.activeWorktreeIdsOnShutdown).toEqual(['b-wt'])
expect(slices[LOCAL_EXECUTION_HOST_ID]?.activeWorktreeIdsOnShutdown).toEqual(['local-wt'])
})
it('routes sleeping agent records via their worktreeId', () => {
const state: WorkspaceSessionState = {
...getDefaultWorkspaceSession(),
sleepingAgentSessionsByPaneKey: {
'pane-a': {
paneKey: 'pane-a',
worktreeId: 'a-wt',
agent: 'claude',
providerSession: { key: 'session_id', id: 'x' },
prompt: 'p',
state: 'done',
capturedAt: 1,
updatedAt: 2
}
}
}
const slices = splitWorkspaceSessionByHost(state, ownerByPrefix())
expect(slices[RUNTIME_A]?.sleepingAgentSessionsByPaneKey).toHaveProperty('pane-a')
})
it('routes terminal incarnation authority with its owning surface', () => {
const state: WorkspaceSessionState = {
...getDefaultWorkspaceSession(),
tabsByWorktree: { 'a-wt': [makeTab('tab-a', 'a-wt')] },
terminalPtyIncarnationsByPaneKey: { 'tab-a:leaf-a': 'inc-a' },
terminalSurfaceTombstonesByPaneKey: {
'tab-b:leaf-b': {
worktreeId: 'b-wt',
parentTabId: 'tab-b',
leafId: 'leaf-b',
ptyId: 'pty-b',
incarnationId: 'inc-b',
retiredAt: 1
}
}
}
const slices = splitWorkspaceSessionByHost(state, ownerByPrefix())
expect(slices[RUNTIME_A]?.terminalPtyIncarnationsByPaneKey).toEqual({
'tab-a:leaf-a': 'inc-a'
})
expect(slices[RUNTIME_B]?.terminalSurfaceTombstonesByPaneKey).toHaveProperty('tab-b:leaf-b')
})
it('does not send host-private topology authority through renderer partitions', () => {
const state: WorkspaceSessionState = {
...getDefaultWorkspaceSession(),
tabsByWorktree: { 'a-wt': [makeTab('tab-a', 'a-wt')] },
terminalTopologyRevisionByRepoId: { 'a-repo': 7 }
}
const slices = splitWorkspaceSessionByHost(state, ownerByPrefix())
expect(slices[LOCAL_EXECUTION_HOST_ID]?.terminalTopologyRevisionByRepoId).toBeUndefined()
expect(slices[RUNTIME_A]?.terminalTopologyRevisionByRepoId).toBeUndefined()
})
})
describe('mergeWorkspaceSessionsFromHosts', () => {
it('takes global fields from the local slice', () => {
const merged = mergeWorkspaceSessionsFromHosts({
[LOCAL_EXECUTION_HOST_ID]: {
...getDefaultWorkspaceSession(),
activeRepoId: 'local-repo',
activeWorktreeId: 'local-wt'
},
[RUNTIME_A]: {
...getDefaultWorkspaceSession(),
// A non-local slice's global fields must lose to local.
activeRepoId: 'runtime-repo',
tabsByWorktree: { 'a-wt': [makeTab('t-a', 'a-wt')] }
}
})
expect(merged.activeRepoId).toBe('local-repo')
expect(merged.tabsByWorktree).toHaveProperty('a-wt')
})
it('falls back to a non-local slice for globals when local is absent', () => {
const merged = mergeWorkspaceSessionsFromHosts({
[RUNTIME_A]: {
...getDefaultWorkspaceSession(),
activeRepoId: 'runtime-repo'
}
})
expect(merged.activeRepoId).toBe('runtime-repo')
})
it('tolerates missing and empty slices', () => {
const merged = mergeWorkspaceSessionsFromHosts({})
expect(merged.tabsByWorktree).toBeUndefined()
expect(() => mergeWorkspaceSessionsFromHosts({ [RUNTIME_A]: undefined })).not.toThrow()
})
it('does not merge host-private topology authority into unified renderer state', () => {
const merged = mergeWorkspaceSessionsFromHosts({
[LOCAL_EXECUTION_HOST_ID]: {
...getDefaultWorkspaceSession(),
terminalTopologyRevisionByRepoId: { duplicate: 3 }
},
[RUNTIME_A]: {
...getDefaultWorkspaceSession(),
terminalTopologyRevisionByRepoId: { duplicate: 9 }
}
})
expect(merged.terminalTopologyRevisionByRepoId).toBeUndefined()
})
})
describe('split → merge round trip', () => {
function roundTrip(state: WorkspaceSessionState): WorkspaceSessionState {
return mergeWorkspaceSessionsFromHosts(splitWorkspaceSessionByHost(state, ownerByPrefix()))
}
it('preserves a representative multi-host state', () => {
const state: WorkspaceSessionState = {
...getDefaultWorkspaceSession(),
activeRepoId: 'repo-1',
activeWorktreeId: 'a-wt',
activeTabId: 't-a',
tabsByWorktree: {
'local-wt': [makeTab('t-local', 'local-wt')],
'a-wt': [makeTab('t-a', 'a-wt')],
'b-wt': [makeTab('t-b', 'b-wt')]
},
unifiedTabs: { 'b-wt': [makeUnifiedTab('t-b', 'b-wt')] },
terminalLayoutsByTabId: { 't-local': makeLayout(), 't-a': makeLayout(), 't-b': makeLayout() },
remoteSessionIdsByTabId: { 't-a': 'sess-a' },
activeTabIdByWorktree: { 'local-wt': 't-local', 'a-wt': 't-a' },
activeWorktreeIdsOnShutdown: ['a-wt', 'b-wt'],
lastVisitedAtByWorktreeId: { 'a-wt': 10, 'local-wt': 5 },
defaultTerminalTabsAppliedByWorktreeId: { 'a-wt': true },
browserTabsByWorktree: {},
browserPagesByWorkspace: {},
browserUrlHistory: [
{ url: 'u', normalizedUrl: 'u', title: 't', lastVisitedAt: 1, visitCount: 1 }
]
}
expect(roundTrip(state)).toEqual(state)
})
it('preserves the default (empty) session', () => {
const state = getDefaultWorkspaceSession()
expect(roundTrip(state)).toEqual(state)
})
it('keeps orphan-owned entries in local and preserves them', () => {
const state: WorkspaceSessionState = {
...getDefaultWorkspaceSession(),
terminalLayoutsByTabId: { orphan: makeLayout() },
remoteSessionIdsByTabId: { orphan: 'sess' }
}
const result = roundTrip(state)
expect(result.terminalLayoutsByTabId).toEqual(state.terminalLayoutsByTabId)
expect(result.remoteSessionIdsByTabId).toEqual(state.remoteSessionIdsByTabId)
})
it('handles a host with only runtime-owned worktrees (empty local maps)', () => {
const state: WorkspaceSessionState = {
...getDefaultWorkspaceSession(),
tabsByWorktree: { 'a-wt': [makeTab('t-a', 'a-wt')] },
terminalLayoutsByTabId: { 't-a': makeLayout() }
}
expect(roundTrip(state)).toEqual(state)
})
})
/** Pin both local-precedence rules used by main’s nonlocal-global pruning or it could discard values renderer merge would read. */
describe('mergeWorkspaceSessionsFromHosts global-field precedence', () => {
const localEntry = {
url: 'local',
normalizedUrl: 'local',
title: 'l',
lastVisitedAt: 2,
visitCount: 1
}
const hostEntry = {
url: 'host',
normalizedUrl: 'host',
title: 'h',
lastVisitedAt: 1,
visitCount: 1
}
it("takes a global field from 'local' whenever local has one, ignoring every other slice", () => {
const merged = mergeWorkspaceSessionsFromHosts({
[LOCAL_EXECUTION_HOST_ID]: {
...getDefaultWorkspaceSession(),
browserUrlHistory: [localEntry]
},
[RUNTIME_A]: { ...getDefaultWorkspaceSession(), browserUrlHistory: [hostEntry] }
})
expect(merged.browserUrlHistory).toEqual([localEntry])
})
it('falls back to another slice only when local does not have the field', () => {
const local = getDefaultWorkspaceSession()
delete local.browserUrlHistory
const merged = mergeWorkspaceSessionsFromHosts({
[LOCAL_EXECUTION_HOST_ID]: local,
[RUNTIME_A]: { ...getDefaultWorkspaceSession(), browserUrlHistory: [hostEntry] }
})
expect(merged.browserUrlHistory).toEqual([hostEntry])
})
})
@@ -1,208 +0,0 @@
import { describe, expect, it } from 'vitest'
import type { WorkspaceSessionState } from '../../../shared/workspace-session-state-types'
import {
collectFolderWorkspaceKeysFromSession,
collectWorktreeHydrationRepoIdsFromSession
} from './workspace-session-hydration-keys'
describe('collectFolderWorkspaceKeysFromSession', () => {
it('keeps folder workspace selection markers valid without scheduling a Git scan', () => {
const session = {
activeRepoId: null,
activeWorktreeId: null,
activeTabId: null,
tabsByWorktree: {},
activeTabTypeByWorktree: { 'folder:folder-1': 'terminal' }
} as unknown as WorkspaceSessionState
expect(collectFolderWorkspaceKeysFromSession(session)).toEqual(['folder:folder-1'])
expect(collectWorktreeHydrationRepoIdsFromSession(session)).toEqual([])
})
})
describe('collectWorktreeHydrationRepoIdsFromSession', () => {
it('includes persisted terminal tabs and ignores folder workspaces', () => {
const session = {
activeWorktreeIdsOnShutdown: [
'repo-a::/worktree-a',
'repo-b::/worktree-b',
'folder:folder-1'
],
tabsByWorktree: {
'repo-a::/worktree-a': [{ ptyId: 'pty-a' }],
'repo-b::/worktree-b': [{ ptyId: null }],
'folder:folder-1': [{ ptyId: 'pty-folder' }]
}
} as unknown as WorkspaceSessionState
expect(collectWorktreeHydrationRepoIdsFromSession(session)).toEqual(['repo-a', 'repo-b'])
})
it('recognizes split-pane and remote persisted sessions', () => {
const session = {
activeRepoId: null,
activeWorktreeId: null,
activeTabId: null,
terminalLayoutsByTabId: {
'tab-a': { ptyIdsByLeafId: { 'pane:1': 'pty-a' } }
},
tabsByWorktree: {
'repo-a::/worktree-a': [{ id: 'tab-a', ptyId: null }],
'repo-b::/worktree-b': [{ id: 'tab-b', ptyId: null }]
},
remoteSessionIdsByTabId: { 'tab-b': 'remote-session' }
} as unknown as WorkspaceSessionState
expect(collectWorktreeHydrationRepoIdsFromSession(session)).toEqual(['repo-a', 'repo-b'])
})
it('matches canonical session keys against raw shutdown worktree IDs', () => {
const rawWorktreeId = 'repo-a::/worktree-a'
const session = {
activeRepoId: null,
activeWorktreeId: null,
activeTabId: null,
activeWorktreeIdsOnShutdown: [rawWorktreeId],
tabsByWorktree: {
[`worktree:${rawWorktreeId}`]: [{ ptyId: 'pty-a' }]
}
} as unknown as WorkspaceSessionState
expect(collectWorktreeHydrationRepoIdsFromSession(session)).toEqual(['repo-a'])
})
it('excludes runtime-owned session worktrees for raw and canonical owner keys', () => {
const rawWorktreeId = 'repo-a::/remote/worktree'
const canonicalWorktreeKey = `worktree:${rawWorktreeId}`
const session = {
activeRepoId: null,
activeWorktreeId: null,
activeTabId: null,
tabsByWorktree: {
[canonicalWorktreeKey]: [{ ptyId: 'pty-a' }]
}
} as unknown as WorkspaceSessionState
expect(
collectWorktreeHydrationRepoIdsFromSession(session, {
[rawWorktreeId]: 'runtime:env-1'
})
).toEqual([])
expect(
collectWorktreeHydrationRepoIdsFromSession(session, {
[canonicalWorktreeKey]: 'runtime:env-1'
})
).toEqual([])
})
it('keeps SSH-owned session worktrees eligible for local recovery routing', () => {
const rawWorktreeId = 'repo-a::/ssh/worktree'
const session = {
activeRepoId: null,
activeWorktreeId: null,
activeTabId: null,
tabsByWorktree: {
[rawWorktreeId]: [{ ptyId: 'ssh:ssh-target@@pty-a' }]
}
} as unknown as WorkspaceSessionState
expect(
collectWorktreeHydrationRepoIdsFromSession(session, {
[rawWorktreeId]: 'ssh:ssh-target'
})
).toEqual(['repo-a'])
})
it('returns repository IDs in deterministic order', () => {
const session = {
activeRepoId: null,
activeWorktreeId: null,
activeTabId: null,
tabsByWorktree: {
'repo-b::/worktree-b': [{ ptyId: 'pty-b' }],
'repo-a::/worktree-a': [{ ptyId: 'pty-a' }]
}
} as unknown as WorkspaceSessionState
expect(collectWorktreeHydrationRepoIdsFromSession(session)).toEqual(['repo-a', 'repo-b'])
})
it('excludes repositories referenced only by unbounded history maps (visit recency, default tabs)', () => {
const session = {
activeRepoId: null,
activeWorktreeId: null,
activeTabId: null,
tabsByWorktree: { 'repo-chrome::/wt': [{ ptyId: 'pty-a' }] },
lastVisitedAtByWorktreeId: {
'repo-chrome::/wt': 10,
'repo-history-only::/visited': 20
},
defaultTerminalTabsAppliedByWorktreeId: {
'repo-default-only::/applied': true
}
} as unknown as WorkspaceSessionState
// Why: history-only repos hydrate their maps unfiltered and would bloat the selective fetch.
expect(collectWorktreeHydrationRepoIdsFromSession(session)).toEqual(['repo-chrome'])
})
it('does not scan repositories retained only by empty chrome maps or selection markers', () => {
const emptyTabsByWorktree = Object.fromEntries(
Array.from({ length: 326 }, (_, index) => [`repo-empty-${index}::/wt`, []])
)
const staleActiveTabTypes = Object.fromEntries(
Array.from({ length: 326 }, (_, index) => [`repo-empty-${index}::/wt`, 'terminal'])
)
const session = {
activeRepoId: null,
activeWorktreeId: null,
activeTabId: null,
tabsByWorktree: {
...emptyTabsByWorktree,
'repo-live::/wt': [{ ptyId: 'pty-a' }]
},
openFilesByWorktree: { 'repo-empty-editor::/wt': [] },
browserTabsByWorktree: { 'repo-empty-browser::/wt': [] },
activeFileIdByWorktree: { 'repo-empty-active-file::/wt': '/stale/file.ts' },
activeBrowserTabIdByWorktree: { 'repo-empty-active-browser::/wt': 'stale-browser' },
activeTabIdByWorktree: { 'repo-empty-active-terminal::/wt': 'stale-terminal' },
activeGroupIdByWorktree: { 'repo-empty-active-group::/wt': 'stale-group' },
activeTabTypeByWorktree: staleActiveTabTypes
} as unknown as WorkspaceSessionState
expect(collectWorktreeHydrationRepoIdsFromSession(session)).toEqual(['repo-live'])
})
it('includes a repository referenced only by activeRepoId (no active worktree, no tabs)', () => {
const session = {
activeRepoId: 'repo-active-only',
activeWorktreeId: null,
activeTabId: null,
tabsByWorktree: {}
} as unknown as WorkspaceSessionState
expect(collectWorktreeHydrationRepoIdsFromSession(session)).toEqual(['repo-active-only'])
})
it('includes repositories referenced only by active, editor, browser, or sleeping-agent state', () => {
const session = {
activeRepoId: null,
activeWorktreeId: 'repo-active::/active',
activeTabId: null,
tabsByWorktree: {},
terminalLayoutsByTabId: {},
openFilesByWorktree: { 'repo-editor::/editor': [{ filePath: '/editor/file.ts' }] },
browserTabsByWorktree: { 'repo-browser::/browser': [{ id: 'browser-a' }] },
sleepingAgentSessionsByPaneKey: {
'tab:leaf': { worktreeId: 'repo-agent::/agent' }
}
} as unknown as WorkspaceSessionState
expect(collectWorktreeHydrationRepoIdsFromSession(session)).toEqual([
'repo-active',
'repo-agent',
'repo-browser',
'repo-editor'
])
})
})
-5
View File
@@ -57,11 +57,6 @@ export function getStandardEmojiShortcodeEntries(): readonly StandardEmojiShortc
return loadCatalog().entries
}
/** Test-only probe for the lazy-boundary guard; never branch on this in product code. */
export function isEmojiShortcodeCatalogBuiltForTest(): boolean {
return catalog !== null
}
/** Prefer spelled-out names over symbolic +1/-1, cryptic no/v and flag_xx namespaces for branch/directory labels. */
function primaryShortcode(shortcodes: readonly string[]): string {
const named = shortcodes.filter((candidate) => /^[a-z]/i.test(candidate))