orca/src/shared/workspace-session-schema.test.ts
Neil a29dc3e9d1 feat(settings): gate persistent terminal daemon behind experimental toggle
The out-of-process terminal daemon (PR #729) can leave sessions visibly
unresponsive when its internal state drifts. Rather than revert, hide it
behind an Experimental setting (default OFF) so users get a fast off-ramp
while the daemon stabilizes.

- New `experimentalTerminalDaemon` setting with an Experimental settings
  pane, "Restart required" banner, and `app:relaunch` IPC.
- Graceful v1.3.0 upgrade path: `cleanupOrphanedDaemon()` on startup
  kills surviving sessions and shuts down the stale daemon; a one-shot
  toast informs the user and links to the new pane.
- Zod validation (`parseWorkspaceSession`) at the persistence read
  boundary so a schema drift or truncated write falls back to defaults
  instead of poisoning Zustand state.
2026-04-17 15:51:32 -07:00

98 lines
2.7 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { parseWorkspaceSession } from './workspace-session-schema'
describe('parseWorkspaceSession', () => {
it('accepts a minimal valid session', () => {
const result = parseWorkspaceSession({
activeRepoId: null,
activeWorktreeId: null,
activeTabId: null,
tabsByWorktree: {},
terminalLayoutsByTabId: {}
})
expect(result.ok).toBe(true)
})
it('accepts a fully populated session with optional fields', () => {
const result = parseWorkspaceSession({
activeRepoId: 'repo1',
activeWorktreeId: 'repo1::/path/wt1',
activeTabId: 'tab1',
tabsByWorktree: {
'repo1::/path/wt1': [
{
id: 'tab1',
ptyId: 'daemon-session-abc',
worktreeId: 'repo1::/path/wt1',
title: 'bash',
customTitle: null,
color: null,
sortOrder: 0,
createdAt: 1_700_000_000_000
}
]
},
terminalLayoutsByTabId: {
tab1: {
root: {
type: 'split',
direction: 'vertical',
first: { type: 'leaf', leafId: 'pane:1' },
second: { type: 'leaf', leafId: 'pane:2' }
},
activeLeafId: 'pane:1',
expandedLeafId: null,
ptyIdsByLeafId: { 'pane:1': 'daemon-session-A' }
}
},
activeWorktreeIdsOnShutdown: ['repo1::/path/wt1']
})
expect(result.ok).toBe(true)
})
it('rejects a session where ptyId is a number (schema drift)', () => {
const result = parseWorkspaceSession({
activeRepoId: null,
activeWorktreeId: null,
activeTabId: null,
tabsByWorktree: {
wt: [
{
id: 'tab1',
ptyId: 42,
worktreeId: 'wt',
title: 'bash',
customTitle: null,
color: null,
sortOrder: 0,
createdAt: 0
}
]
},
terminalLayoutsByTabId: {}
})
expect(result.ok).toBe(false)
if (!result.ok) {
expect(result.error).toContain('ptyId')
}
})
it('rejects a session with missing required top-level fields', () => {
const result = parseWorkspaceSession({
activeRepoId: null
// missing activeWorktreeId, tabsByWorktree, etc.
})
expect(result.ok).toBe(false)
})
it('rejects a truncated JSON object', () => {
const result = parseWorkspaceSession({})
expect(result.ok).toBe(false)
})
it('rejects non-object input (e.g. corrupted file contents)', () => {
expect(parseWorkspaceSession(null).ok).toBe(false)
expect(parseWorkspaceSession('garbage').ok).toBe(false)
expect(parseWorkspaceSession(42).ok).toBe(false)
})
})