mirror of
https://github.com/stablyai/orca
synced 2026-04-21 14:17:16 +00:00
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.
98 lines
2.7 KiB
TypeScript
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)
|
|
})
|
|
})
|