Files
orca/src/main/ipc/ephemeral-vm.test.ts
T
mirror 34fa2cd5ca
Skill update round trip / roundtrip (false, macos-latest, copy, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (false, macos-latest, symlink, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (false, ubuntu-latest, copy, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (false, ubuntu-latest, symlink, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (false, ubuntu-latest, symlink, latest) (push) Canceled after 0s
Skill update round trip / roundtrip (false, windows-latest, copy, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (false, windows-latest, symlink, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (true, macos-latest, copy, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (true, macos-latest, symlink, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (true, ubuntu-latest, copy, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (true, ubuntu-latest, symlink, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (true, windows-latest, copy, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (true, windows-latest, symlink, 1.5.17) (push) Canceled after 0s
source snapshot 2026-09-02
2026-09-02 02:03:16 +08:00

852 lines
28 KiB
TypeScript

import { existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { encodePairingOffer, PAIRING_OFFER_VERSION } from '../../shared/pairing'
import { listEnvironments } from '../../shared/runtime-environment-store'
import { upsertEphemeralVmRuntime } from '../../shared/ephemeral-vm-runtime-store'
const handlers = new Map<string, (_event: unknown, args: never) => unknown>()
const {
handleMock,
removeHandlerMock,
getPathMock,
connectRuntimeOwnedSshTargetMock,
disconnectRuntimeOwnedSshTargetMock,
removeRuntimeOwnedSshTargetMock,
invalidateRuntimeEnvironmentTransportMock
} = vi.hoisted(() => ({
handleMock: vi.fn(),
removeHandlerMock: vi.fn(),
getPathMock: vi.fn(),
connectRuntimeOwnedSshTargetMock: vi.fn(),
disconnectRuntimeOwnedSshTargetMock: vi.fn(),
removeRuntimeOwnedSshTargetMock: vi.fn(),
invalidateRuntimeEnvironmentTransportMock: vi.fn()
}))
vi.mock('electron', () => ({
app: {
getPath: getPathMock
},
ipcMain: {
handle: handleMock,
removeHandler: removeHandlerMock
}
}))
vi.mock('../ephemeral-vm-runtime-ssh', () => ({
connectRuntimeOwnedSshTarget: connectRuntimeOwnedSshTargetMock,
disconnectRuntimeOwnedSshTarget: disconnectRuntimeOwnedSshTargetMock,
removeRuntimeOwnedSshTarget: removeRuntimeOwnedSshTargetMock
}))
vi.mock('./runtime-environments', () => ({
invalidateRuntimeEnvironmentTransport: invalidateRuntimeEnvironmentTransportMock
}))
import { registerEphemeralVmHandlers } from './ephemeral-vm'
const tempDirs: string[] = []
afterEach(() => {
for (const dir of tempDirs.splice(0)) {
rmSync(dir, { recursive: true, force: true })
}
})
function makeDir(prefix: string): string {
const dir = mkdtempSync(join(tmpdir(), prefix))
tempDirs.push(dir)
return dir
}
function makePairingCode(): string {
return encodePairingOffer({
v: PAIRING_OFFER_VERSION,
endpoint: 'wss://sandbox.example.com',
deviceToken: 'token',
publicKeyB64: 'public-key'
})
}
function makeStore(repoPath: string) {
const repo = {
id: 'repo-1',
path: repoPath,
displayName: 'Repo',
badgeColor: '#000',
addedAt: 0
}
let activeRuntimeEnvironmentId: string | null = null
return {
getRepo: vi.fn((repoId: string) => (repoId === 'repo-1' ? repo : null)),
getRepos: vi.fn(() => [repo]),
getSettings: vi.fn(() => ({ activeRuntimeEnvironmentId })),
updateSettings: vi.fn((updates: { activeRuntimeEnvironmentId: string | null }) => {
activeRuntimeEnvironmentId = updates.activeRuntimeEnvironmentId
})
}
}
function nodeCommand(scriptPath: string): string {
return `"${process.execPath}" "${scriptPath}"`
}
function pluginServiceWithRecipes(
recipes: { pluginKey: string; recipe: Record<string, unknown> }[]
) {
return {
whenReady: vi.fn().mockResolvedValue(undefined),
contentPacks: { vmRecipes: { list: vi.fn(() => recipes) } }
}
}
describe('registerEphemeralVmHandlers', () => {
const originalPlatform = Object.getOwnPropertyDescriptor(process, 'platform')
beforeEach(() => {
Object.defineProperty(process, 'platform', { configurable: true, value: 'linux' })
handlers.clear()
handleMock.mockReset()
removeHandlerMock.mockReset()
getPathMock.mockReset()
connectRuntimeOwnedSshTargetMock.mockReset()
disconnectRuntimeOwnedSshTargetMock.mockReset()
removeRuntimeOwnedSshTargetMock.mockReset()
invalidateRuntimeEnvironmentTransportMock.mockReset()
connectRuntimeOwnedSshTargetMock.mockResolvedValue({
targetId: 'runtime-ssh-orca-instance-1',
target: {
id: 'runtime-ssh-orca-instance-1',
label: 'Sandbox',
host: 'sandbox.example.com',
port: 22,
username: 'root'
}
})
disconnectRuntimeOwnedSshTargetMock.mockResolvedValue(undefined)
removeRuntimeOwnedSshTargetMock.mockResolvedValue(undefined)
handleMock.mockImplementation((channel: string, handler: never) => {
handlers.set(channel, handler)
})
})
afterEach(() => {
if (originalPlatform) {
Object.defineProperty(process, 'platform', originalPlatform)
}
})
it('lists recipes from local repo orca.yaml', async () => {
const repoPath = makeDir('orca-ephemeral-vm-ipc-repo-')
writeFileSync(
join(repoPath, 'orca.yaml'),
[
'environmentRecipes:',
' - id: cloud-sandbox',
' name: Cloud Sandbox',
' create: ./scripts/start.sh',
' destroy: none'
].join('\n')
)
const store = makeStore(repoPath)
registerEphemeralVmHandlers(store as never)
const result = await handlers.get('ephemeralVm:listRecipes')?.(null, {
repoId: 'repo-1'
} as never)
expect(removeHandlerMock).toHaveBeenCalledWith('ephemeralVm:listRecipes')
expect(result).toEqual({
status: 'ok',
repoPath,
diagnostics: [],
recipes: [
{
id: 'cloud-sandbox',
name: 'Cloud Sandbox',
create: './scripts/start.sh',
destroyDisabled: true
}
]
})
})
it('lists the recipe catalog across local git repos', async () => {
const repoPath = makeDir('orca-ephemeral-vm-ipc-repo-')
writeFileSync(
join(repoPath, 'orca.yaml'),
[
'environmentRecipes:',
' - id: cloud-sandbox',
' name: Cloud Sandbox',
' create: ./scripts/start.sh',
' destroy: none'
].join('\n')
)
const store = makeStore(repoPath)
registerEphemeralVmHandlers(store as never)
const result = await handlers.get('ephemeralVm:listRecipeCatalog')?.(null, undefined as never)
expect(result).toEqual([
{
repoId: 'repo-1',
repoName: 'Repo',
repoPath,
diagnostics: [],
recipes: [
{
id: 'cloud-sandbox',
name: 'Cloud Sandbox',
create: './scripts/start.sh',
destroyDisabled: true
}
]
}
])
})
it('merges approved plugin recipes while repository recipes shadow matching ids', async () => {
const repoPath = makeDir('orca-ephemeral-vm-ipc-repo-')
writeFileSync(
join(repoPath, 'orca.yaml'),
[
'environmentRecipes:',
' - id: shared',
' name: Repository Recipe',
' create: repo-create'
].join('\n')
)
const pluginService = pluginServiceWithRecipes([
{
pluginKey: 'orca-samples.recipes',
recipe: { id: 'shared', name: 'Plugin Shared', create: 'plugin-shared' }
},
{
pluginKey: 'orca-samples.recipes',
recipe: { id: 'global', name: 'Plugin Global', create: 'plugin-global' }
}
])
registerEphemeralVmHandlers(makeStore(repoPath) as never, pluginService as never)
const result = (await handlers.get('ephemeralVm:listRecipes')?.(null, {
repoId: 'repo-1'
} as never)) as { recipes: { id: string; name: string }[] }
expect(pluginService.whenReady).toHaveBeenCalled()
expect(result.recipes).toMatchObject([
{ id: 'shared', name: 'Repository Recipe' },
{ id: 'global', name: 'Plugin Global' }
])
})
it('uses an immutable plugin recipe snapshot after the plugin is removed', async () => {
const userDataPath = makeDir('orca-ephemeral-vm-ipc-user-data-')
const repoPath = makeDir('orca-ephemeral-vm-ipc-repo-')
getPathMock.mockReturnValue(userDataPath)
const startPath = join(repoPath, 'start.js')
const destroyPath = join(repoPath, 'destroy.js')
writeFileSync(
startPath,
`console.log(${JSON.stringify(
JSON.stringify({
schemaVersion: 1,
pairingCode: makePairingCode(),
projectRoot: '/workspace/repo'
})
)})`
)
writeFileSync(destroyPath, "require('fs').writeFileSync('plugin-cleaned.txt', 'yes')")
const registrations = [
{
pluginKey: 'orca-samples.recipes',
recipe: {
id: 'plugin-cloud',
name: 'Plugin Cloud',
create: nodeCommand(startPath),
destroy: nodeCommand(destroyPath)
}
}
]
const pluginService = pluginServiceWithRecipes(registrations)
registerEphemeralVmHandlers(makeStore(repoPath) as never, pluginService as never)
const provisioned = (await handlers.get('ephemeralVm:provision')?.(null, {
repoId: 'repo-1',
recipeId: 'plugin-cloud'
} as never)) as { ok: true; runtime: { id: string; recipe?: { id: string } } }
registrations.splice(0)
const cleaned = await handlers.get('ephemeralVm:cleanup')?.(null, {
runtimeId: provisioned.runtime.id
} as never)
expect(provisioned.runtime.recipe).toMatchObject({ id: 'plugin-cloud' })
expect(cleaned).toEqual(expect.objectContaining({ status: 'cleaned' }))
expect(readFileSync(join(repoPath, 'plugin-cleaned.txt'), 'utf8')).toBe('yes')
})
it('never substitutes a later same-id plugin recipe for a legacy runtime', async () => {
const userDataPath = makeDir('orca-ephemeral-vm-ipc-user-data-')
const repoPath = makeDir('orca-ephemeral-vm-ipc-repo-')
getPathMock.mockReturnValue(userDataPath)
const pluginDestroyPath = join(repoPath, 'plugin-destroy.js')
writeFileSync(
pluginDestroyPath,
"require('fs').writeFileSync('plugin-destroy-ran.txt', 'unsafe')"
)
upsertEphemeralVmRuntime(userDataPath, {
id: 'legacy-runtime',
recipeId: 'shared-id',
repoId: 'repo-1',
status: 'running',
cleanupStatus: 'not_started',
createdAt: 1,
updatedAt: 1,
recipeResult: {
schemaVersion: 1,
pairingCode: makePairingCode(),
projectRoot: '/workspace/repo'
}
})
const pluginService = pluginServiceWithRecipes([
{
pluginKey: 'orca-samples.recipes',
recipe: {
id: 'shared-id',
name: 'Later Plugin Recipe',
create: 'create',
destroy: nodeCommand(pluginDestroyPath)
}
}
])
registerEphemeralVmHandlers(makeStore(repoPath) as never, pluginService as never)
const cleaned = await handlers.get('ephemeralVm:cleanup')?.(null, {
runtimeId: 'legacy-runtime'
} as never)
expect(cleaned).toMatchObject({
status: 'cleanup_failed',
cleanupLastError: 'Recipe not found: shared-id'
})
expect(existsSync(join(repoPath, 'plugin-destroy-ran.txt'))).toBe(false)
})
it('provisions a recipe and persists the ephemeral runtime', async () => {
const userDataPath = makeDir('orca-ephemeral-vm-ipc-user-data-')
const repoPath = makeDir('orca-ephemeral-vm-ipc-repo-')
getPathMock.mockReturnValue(userDataPath)
mkdirSync(join(repoPath, 'scripts'), { recursive: true })
const startPath = join(repoPath, 'scripts', 'start.js')
writeFileSync(
startPath,
[
'console.log(JSON.stringify({',
' schemaVersion: 1,',
` pairingCode: ${JSON.stringify(makePairingCode())},`,
" projectRoot: '/workspace/repo',",
' userData: { providerResourceId: process.env.ORCA_VM_INSTANCE_ID }',
'}))'
].join('\n')
)
writeFileSync(
join(repoPath, 'orca.yaml'),
[
'environmentRecipes:',
' - id: cloud-sandbox',
' name: Cloud Sandbox',
` create: ${JSON.stringify(nodeCommand(startPath))}`,
' destroy: none'
].join('\n')
)
const store = makeStore(repoPath)
registerEphemeralVmHandlers(store as never)
const result = (await handlers.get('ephemeralVm:provision')?.(null, {
repoId: 'repo-1',
recipeId: 'cloud-sandbox',
workspaceName: 'Fix Login Race'
} as never)) as {
ok: boolean
runtime?: { id: string; repoId?: string; status?: string; runtimeEnvironmentId?: string }
environment?: { id: string; name: string }
}
expect(result).toMatchObject({
ok: true,
environment: {
name: expect.stringContaining('Repo VM ')
},
runtime: {
repoId: 'repo-1',
status: 'running',
runtimeEnvironmentId: result.environment?.id
}
})
const runtimes = await handlers.get('ephemeralVm:listRuntimes')?.(null, undefined as never)
expect(runtimes).toEqual([
expect.objectContaining({
repoId: 'repo-1',
recipeId: 'cloud-sandbox',
runtimeEnvironmentId: result.environment?.id
})
])
const attached = await handlers.get('ephemeralVm:attachWorkspace')?.(null, {
runtimeId: result.runtime?.id,
workspaceId: 'repo-1::/workspace/repo/worktree'
} as never)
expect(attached).toEqual(
expect.objectContaining({
id: result.runtime?.id,
workspaceId: 'repo-1::/workspace/repo/worktree'
})
)
store.updateSettings({ activeRuntimeEnvironmentId: result.environment!.id })
const cleaned = await handlers.get('ephemeralVm:cleanup')?.(null, {
runtimeId: result.runtime?.id
} as never)
expect(cleaned).toEqual(expect.objectContaining({ status: 'cleaned' }))
expect(listEnvironments(userDataPath)).toEqual([])
expect(store.getSettings().activeRuntimeEnvironmentId).toBe(result.environment!.id)
expect(store.updateSettings).toHaveBeenCalledTimes(1)
})
it('provisions an ssh recipe without creating a runtime environment', async () => {
const userDataPath = makeDir('orca-ephemeral-vm-ipc-user-data-')
const repoPath = makeDir('orca-ephemeral-vm-ipc-repo-')
getPathMock.mockReturnValue(userDataPath)
mkdirSync(join(repoPath, 'scripts'), { recursive: true })
const startPath = join(repoPath, 'scripts', 'start-ssh.js')
writeFileSync(
startPath,
[
'console.log(JSON.stringify({',
' schemaVersion: 1,',
' connection: {',
" type: 'ssh',",
" projectRoot: '/workspace/repo',",
' target: {',
" label: 'Sandbox',",
" host: 'sandbox.example.com',",
' port: 22,',
" username: 'root'",
' }',
' },',
" userData: { sandboxId: 'sandbox-123' }",
'}))'
].join('\n')
)
writeFileSync(
join(repoPath, 'orca.yaml'),
[
'environmentRecipes:',
' - id: cloud-sandbox',
' name: Cloud Sandbox',
` create: ${JSON.stringify(nodeCommand(startPath))}`,
' destroy: none'
].join('\n')
)
registerEphemeralVmHandlers(makeStore(repoPath) as never)
const result = (await handlers.get('ephemeralVm:provision')?.(null, {
repoId: 'repo-1',
recipeId: 'cloud-sandbox',
workspaceName: 'Fix Login Race'
} as never)) as {
ok: boolean
connectionType?: string
sshTargetId?: string
runtime?: {
id: string
repoId?: string
status?: string
connectionMode?: string
sshTargetId?: string
}
environment?: { id: string; name: string }
}
expect(result).toMatchObject({
ok: true,
connectionType: 'ssh',
sshTargetId: 'runtime-ssh-orca-instance-1',
runtime: {
repoId: 'repo-1',
status: 'running',
connectionMode: 'ssh',
sshTargetId: 'runtime-ssh-orca-instance-1'
}
})
expect(result.environment).toBeUndefined()
expect(connectRuntimeOwnedSshTargetMock).toHaveBeenCalledWith({
runtimeId: result.runtime?.id,
connection: {
type: 'ssh',
projectRoot: '/workspace/repo',
target: {
label: 'Sandbox',
host: 'sandbox.example.com',
port: 22,
username: 'root'
}
}
})
expect(listEnvironments(userDataPath)).toEqual([])
})
it('retains the runtime-owned SSH target when destroy fails', async () => {
const userDataPath = makeDir('orca-ephemeral-vm-ipc-user-data-')
const repoPath = makeDir('orca-ephemeral-vm-ipc-repo-')
getPathMock.mockReturnValue(userDataPath)
mkdirSync(join(repoPath, 'scripts'), { recursive: true })
const startPath = join(repoPath, 'scripts', 'start-ssh.js')
const destroyPath = join(repoPath, 'scripts', 'destroy.js')
writeFileSync(
startPath,
[
'console.log(JSON.stringify({',
' schemaVersion: 1,',
' connection: {',
" type: 'ssh',",
" projectRoot: '/workspace/repo',",
' target: {',
" label: 'Sandbox',",
" host: 'sandbox.example.com',",
' port: 22,',
" username: 'root'",
' }',
' }',
'}))'
].join('\n')
)
writeFileSync(destroyPath, 'process.exit(1)')
writeFileSync(
join(repoPath, 'orca.yaml'),
[
'environmentRecipes:',
' - id: cloud-sandbox',
' name: Cloud Sandbox',
` create: ${JSON.stringify(nodeCommand(startPath))}`,
` destroy: ${JSON.stringify(nodeCommand(destroyPath))}`
].join('\n')
)
registerEphemeralVmHandlers(makeStore(repoPath) as never)
const provisioned = (await handlers.get('ephemeralVm:provision')?.(null, {
repoId: 'repo-1',
recipeId: 'cloud-sandbox'
} as never)) as { ok: true; runtime: { id: string } }
const cleaned = (await handlers.get('ephemeralVm:cleanup')?.(null, {
runtimeId: provisioned.runtime.id
} as never)) as {
status?: string
cleanupLastError?: string
connectionMode?: string
sshTargetId?: string
}
expect(cleaned).toEqual(expect.objectContaining({ status: 'cleanup_failed' }))
expect(cleaned.cleanupLastError).toBeTruthy()
expect(removeRuntimeOwnedSshTargetMock).not.toHaveBeenCalled()
expect(cleaned.connectionMode).toBe('ssh')
expect(cleaned.sshTargetId).toBe('runtime-ssh-orca-instance-1')
})
it('retries hidden SSH teardown without rerunning completed provider cleanup', async () => {
const userDataPath = makeDir('orca-ephemeral-vm-ipc-user-data-')
const repoPath = makeDir('orca-ephemeral-vm-ipc-repo-')
getPathMock.mockReturnValue(userDataPath)
upsertEphemeralVmRuntime(userDataPath, {
id: 'runtime-cleanup-retry',
recipeId: 'cloud-sandbox',
repoId: 'repo-1',
status: 'cleanup_failed',
cleanupStatus: 'succeeded',
connectionMode: 'ssh',
sshTargetId: 'runtime-ssh-cleanup-retry',
createdAt: 1,
updatedAt: 1,
recipeResult: {
schemaVersion: 1,
connection: {
type: 'ssh',
projectRoot: '/workspace/repo',
target: { label: 'VM', host: 'host', port: 22, username: 'orca' }
}
}
})
registerEphemeralVmHandlers(makeStore(repoPath) as never)
removeRuntimeOwnedSshTargetMock.mockRejectedValueOnce(new Error('store unavailable'))
const failed = await handlers.get('ephemeralVm:cleanup')?.(null, {
runtimeId: 'runtime-cleanup-retry'
} as never)
expect(failed).toMatchObject({
status: 'cleanup_failed',
cleanupStatus: 'succeeded',
sshTargetId: 'runtime-ssh-cleanup-retry'
})
removeRuntimeOwnedSshTargetMock.mockResolvedValue(undefined)
const cleaned = await handlers.get('ephemeralVm:cleanup')?.(null, {
runtimeId: 'runtime-cleanup-retry'
} as never)
expect(cleaned).toMatchObject({
status: 'cleaned',
cleanupStatus: 'succeeded',
sshTargetId: undefined
})
expect(removeRuntimeOwnedSshTargetMock).toHaveBeenCalledTimes(2)
})
it('runs suspend and resume for an attached ephemeral VM workspace', async () => {
const userDataPath = makeDir('orca-ephemeral-vm-ipc-user-data-')
const repoPath = makeDir('orca-ephemeral-vm-ipc-repo-')
getPathMock.mockReturnValue(userDataPath)
mkdirSync(join(repoPath, 'scripts'), { recursive: true })
const startPath = join(repoPath, 'scripts', 'start.js')
const suspendPath = join(repoPath, 'scripts', 'suspend.js')
const resumePath = join(repoPath, 'scripts', 'resume.js')
const resumedPairingCode = encodePairingOffer({
v: PAIRING_OFFER_VERSION,
endpoint: 'wss://resumed.example.com',
deviceToken: 'resumed-token',
publicKeyB64: 'resumed-public-key'
})
writeFileSync(
startPath,
[
'console.log(JSON.stringify({',
' schemaVersion: 1,',
` pairingCode: ${JSON.stringify(makePairingCode())},`,
" projectRoot: '/workspace/repo'",
'}))'
].join('\n')
)
writeFileSync(
suspendPath,
[
"const fs = require('fs')",
"const payload = JSON.parse(fs.readFileSync(0, 'utf8'))",
"fs.writeFileSync('suspend-mode.txt', payload.mode)"
].join('\n')
)
writeFileSync(
resumePath,
[
"const fs = require('fs')",
"const payload = JSON.parse(fs.readFileSync(0, 'utf8'))",
'if (payload.mode !== "resume") process.exit(2)',
"fs.writeFileSync('resume-mode.txt', payload.mode)",
'console.log(JSON.stringify({',
' schemaVersion: 1,',
` pairingCode: ${JSON.stringify(resumedPairingCode)},`,
" projectRoot: '/workspace/resumed'",
'}))'
].join('\n')
)
writeFileSync(
join(repoPath, 'orca.yaml'),
[
'environmentRecipes:',
' - id: cloud-sandbox',
' name: Cloud Sandbox',
` create: ${JSON.stringify(nodeCommand(startPath))}`,
` suspend: ${JSON.stringify(nodeCommand(suspendPath))}`,
` resume: ${JSON.stringify(nodeCommand(resumePath))}`,
' destroy: none'
].join('\n')
)
registerEphemeralVmHandlers(makeStore(repoPath) as never)
const provisioned = (await handlers.get('ephemeralVm:provision')?.(null, {
repoId: 'repo-1',
recipeId: 'cloud-sandbox'
} as never)) as { ok: true; runtime: { id: string }; environment: { id: string } }
await handlers.get('ephemeralVm:attachWorkspace')?.(null, {
runtimeId: provisioned.runtime.id,
workspaceId: 'workspace-1'
} as never)
const runningResume = await handlers.get('ephemeralVm:resumeWorkspace')?.(null, {
workspaceId: 'workspace-1'
} as never)
expect(runningResume).toEqual(expect.objectContaining({ status: 'running' }))
expect(existsSync(join(repoPath, 'resume-mode.txt'))).toBe(false)
const suspended = await handlers.get('ephemeralVm:suspendWorkspace')?.(null, {
workspaceId: 'workspace-1'
} as never)
expect(suspended).toEqual(expect.objectContaining({ status: 'suspended' }))
expect(readFileSync(join(repoPath, 'suspend-mode.txt'), 'utf8')).toBe('suspend')
invalidateRuntimeEnvironmentTransportMock.mockImplementationOnce((environmentId: string) => {
const environment = listEnvironments(userDataPath).find((entry) => entry.id === environmentId)
expect(environment?.endpoints[0]?.endpoint).toBe('wss://resumed.example.com')
})
const resumed = await handlers.get('ephemeralVm:resumeWorkspace')?.(null, {
workspaceId: 'workspace-1'
} as never)
expect(resumed).toEqual(
expect.objectContaining({
status: 'running',
recipeResult: expect.objectContaining({ projectRoot: '/workspace/resumed' })
})
)
expect(readFileSync(join(repoPath, 'resume-mode.txt'), 'utf8')).toBe('resume')
const environment = listEnvironments(userDataPath).find(
(entry) => entry.id === provisioned.environment.id
)
expect(environment?.endpoints[0]?.endpoint).toBe('wss://resumed.example.com')
expect(invalidateRuntimeEnvironmentTransportMock).toHaveBeenCalledWith(
provisioned.environment.id
)
})
it('returns a copyable cleanup command for a persisted runtime', async () => {
const userDataPath = makeDir('orca-ephemeral-vm-ipc-user-data-')
const repoPath = makeDir('orca-ephemeral-vm-ipc-repo-')
getPathMock.mockReturnValue(userDataPath)
mkdirSync(join(repoPath, 'scripts'), { recursive: true })
const startPath = join(repoPath, 'scripts', 'start.js')
const cleanupPath = join(repoPath, 'scripts', 'cleanup.js')
writeFileSync(
startPath,
[
'console.log(JSON.stringify({',
' schemaVersion: 1,',
` pairingCode: ${JSON.stringify(makePairingCode())},`,
" projectRoot: '/workspace/repo'",
'}))'
].join('\n')
)
writeFileSync(cleanupPath, 'process.stdin.resume()\n')
writeFileSync(
join(repoPath, 'orca.yaml'),
[
'environmentRecipes:',
' - id: cloud-sandbox',
' name: Cloud Sandbox',
` create: ${JSON.stringify(nodeCommand(startPath))}`,
` destroy: ${JSON.stringify(nodeCommand(cleanupPath))}`
].join('\n')
)
registerEphemeralVmHandlers(makeStore(repoPath) as never)
const provisioned = (await handlers.get('ephemeralVm:provision')?.(null, {
repoId: 'repo-1',
recipeId: 'cloud-sandbox',
workspaceName: 'Fix Login Race'
} as never)) as { ok: true; runtime: { id: string } }
const result = await handlers.get('ephemeralVm:getCleanupCommand')?.(null, {
runtimeId: provisioned.runtime.id
} as never)
expect(result).toMatchObject({
runtimeId: provisioned.runtime.id,
cleanupDisabled: false,
payloadJson: expect.stringContaining('"workspaceName": "Fix Login Race"'),
command: expect.stringContaining(nodeCommand(cleanupPath))
})
})
it('streams provision logs and cancels an active provision', async () => {
const userDataPath = makeDir('orca-ephemeral-vm-ipc-user-data-')
const repoPath = makeDir('orca-ephemeral-vm-ipc-repo-')
getPathMock.mockReturnValue(userDataPath)
mkdirSync(join(repoPath, 'scripts'), { recursive: true })
const startPath = join(repoPath, 'scripts', 'start.js')
writeFileSync(
startPath,
[
"process.stderr.write('creating sandbox\\n')",
'setTimeout(() => {',
' console.log(JSON.stringify({',
' schemaVersion: 1,',
` pairingCode: ${JSON.stringify(makePairingCode())},`,
" projectRoot: '/workspace/repo'",
' }))',
'}, 30000)'
].join('\n')
)
writeFileSync(
join(repoPath, 'orca.yaml'),
[
'environmentRecipes:',
' - id: cloud-sandbox',
' name: Cloud Sandbox',
` create: ${JSON.stringify(nodeCommand(startPath))}`,
' destroy: none'
].join('\n')
)
registerEphemeralVmHandlers(makeStore(repoPath) as never)
const sender = { send: vi.fn() }
const provision = handlers.get('ephemeralVm:provision')?.({ sender }, {
repoId: 'repo-1',
recipeId: 'cloud-sandbox',
workspaceName: 'Fix Login Race',
provisionId: 'provision-1'
} as never) as Promise<{ ok: boolean; error?: string }>
await vi.waitFor(() =>
expect(sender.send).toHaveBeenCalledWith('ephemeralVm:provisionEvent', {
provisionId: 'provision-1',
stream: 'stderr',
chunk: 'creating sandbox\n'
})
)
const cancelled = await handlers.get('ephemeralVm:cancelProvision')?.(null, {
provisionId: 'provision-1'
} as never)
const result = await provision
expect(cancelled).toEqual({ cancelled: true })
expect(result.ok).toBe(false)
})
it('redacts recipe stdout when provisioning fails', async () => {
const userDataPath = makeDir('orca-ephemeral-vm-ipc-user-data-')
const repoPath = makeDir('orca-ephemeral-vm-ipc-repo-')
getPathMock.mockReturnValue(userDataPath)
mkdirSync(join(repoPath, 'scripts'), { recursive: true })
const startPath = join(repoPath, 'scripts', 'start.js')
writeFileSync(
startPath,
[
'console.log(JSON.stringify({',
` pairingCode: ${JSON.stringify(makePairingCode())},`,
" token: 'provider-token'",
'}))'
].join('\n')
)
writeFileSync(
join(repoPath, 'orca.yaml'),
[
'environmentRecipes:',
' - id: cloud-sandbox',
' name: Cloud Sandbox',
` create: ${JSON.stringify(nodeCommand(startPath))}`,
' destroy: none'
].join('\n')
)
registerEphemeralVmHandlers(makeStore(repoPath) as never)
const result = (await handlers.get('ephemeralVm:provision')?.(null, {
repoId: 'repo-1',
recipeId: 'cloud-sandbox'
} as never)) as { ok: false; stdout: string }
expect(result.ok).toBe(false)
expect(result.stdout).toContain('"pairingCode":"[redacted]"')
expect(result.stdout).toContain('"token":"[redacted]"')
expect(result.stdout).not.toContain('provider-token')
expect(result.stdout).not.toContain('public-key')
})
})