mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
* fix(runtime): stamp a runtime's own project setups as local, and report remote status about the remote (STA-4792) Two independent frame-of-reference bugs, both from code describing one machine while labelled as another. #15366 — projectHostSetup.* persisted the caller's host id verbatim. Those `runtime:<environment-id>` ids are minted by the calling client's own pairing store, so they name a machine only relative to that client. A client sending one is addressing this runtime, and runtimes do not proxy these calls onward, so the host it names is us. Storing the client's spelling made one machine look like a different host to every other client, hid its rows from them, and defeated the (projectId, hostId) duplicate check — two laptops paired to one server each created their own setup for the same checkout. Re-spell it as `local` at the RPC boundary. Rows written earlier keep their old stamp; readers already project `local` back to `runtime:<their-id>`, so the client-visible model is unchanged and no ids are rewritten. STA-4792 defect 4 — `status --environment <name>` hardcoded app.running:false to mean "no desktop on THIS machine" while every other field in the same object described the target, including a desktopWindowStatus echoed straight from it. The result contradicted itself and read as "that run was headless" when the remote GUI was up. `app` now describes the target, keyed off the one window status that requires a live renderer, and the result names its own subject so the frame can't be misread again. The remote pid is not knowable, so it stays null. STA-4792 defect 2 gets a regression test rather than a fix: routing already made the client remote, which is what stops a Windows destination being joined to the local cwd. The test pins the exact reported invocation. * fix(status): share the remote app projection with the SSH host passthrough, and name the version gap on project host setup Two review follow-ups. The SSH host passthrough answered `app.running: true` unconditionally for the Orca host a caller reached over SSH, claiming a desktop app even for a headless `serve`. That is the same defect as the paired-server path, one transport over, so the projection moved to shared and both now answer the question the same way. `--host runtime:<id>` routes project commands to a paired server, which means a client can reach a server that predates project host setup without meaning to. That answered a raw `method_not_found`, which reads as an Orca bug rather than a version gap; the CLI now names it the way the desktop already does. Reverted a third change: making the persistence duplicate check treat `local` and `runtime:*` as one machine. That assumption holds at the RPC boundary, where a `runtime:` host means the runtime being addressed, but not in the store, which also records independent provisioning metadata for machines that are not itself. An existing test covers exactly that, and it was right. The duplicate convergence therefore stays bounded to rows written after the normalization.
563 lines
19 KiB
TypeScript
563 lines
19 KiB
TypeScript
import { mkdtempSync, writeFileSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { createServer, type Socket } from 'node:net'
|
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
import { ORCHESTRATION_CONTRACT_RUNTIME_CAPABILITY } from '../shared/protocol-version'
|
|
import { RuntimeClient, RuntimeClientError, RuntimeRpcFailureError } from './runtime-client'
|
|
import { launchOrcaApp } from './runtime/launch'
|
|
|
|
vi.mock('./runtime/launch', () => ({
|
|
launchOrcaApp: vi.fn()
|
|
}))
|
|
|
|
const servers = new Set<ReturnType<typeof createServer>>()
|
|
const sockets = new Set<Socket>()
|
|
|
|
afterEach(async () => {
|
|
vi.mocked(launchOrcaApp).mockClear()
|
|
for (const socket of sockets) {
|
|
socket.destroy()
|
|
}
|
|
sockets.clear()
|
|
await Promise.all(
|
|
[...servers].map(
|
|
(server) =>
|
|
new Promise<void>((resolve) => {
|
|
server.close(() => resolve())
|
|
})
|
|
)
|
|
)
|
|
servers.clear()
|
|
})
|
|
|
|
function writeMetadata(
|
|
userDataPath: string,
|
|
endpoint: string,
|
|
authToken = 'token',
|
|
pid = 123
|
|
): void {
|
|
writeFileSync(
|
|
join(userDataPath, 'orca-runtime.json'),
|
|
JSON.stringify({
|
|
runtimeId: 'runtime-1',
|
|
pid,
|
|
transports: [
|
|
{
|
|
kind: 'unix',
|
|
endpoint
|
|
}
|
|
],
|
|
authToken,
|
|
startedAt: 1
|
|
}),
|
|
'utf8'
|
|
)
|
|
}
|
|
|
|
function findUnusedPid(seed = 200_000): number {
|
|
// Why: the stale-bootstrap test must point metadata at a definitely-dead
|
|
// process. Hard-coding a small PID is host-dependent and flakes when that
|
|
// PID happens to be alive on the machine running the suite.
|
|
let pid = Math.max(seed, process.pid + 10_000)
|
|
while (pid < 2_000_000) {
|
|
try {
|
|
process.kill(pid, 0)
|
|
pid += 1
|
|
} catch {
|
|
return pid
|
|
}
|
|
}
|
|
return 2_000_000
|
|
}
|
|
|
|
// Why: these tests create Unix domain socket servers in temp directories.
|
|
// Windows does not support Unix domain sockets in the same way, causing
|
|
// EACCES errors on listen(), so the suite is skipped on that platform.
|
|
describe.skipIf(process.platform === 'win32')('RuntimeClient', () => {
|
|
it('adds an opaque durable request ID only to orchestration mutations', async () => {
|
|
const userDataPath = mkdtempSync(join(tmpdir(), 'orca-runtime-client-'))
|
|
const endpoint = join(userDataPath, 'runtime.sock')
|
|
const requests: Record<string, unknown>[] = []
|
|
const server = createServer((socket) => {
|
|
sockets.add(socket)
|
|
socket.once('close', () => sockets.delete(socket))
|
|
socket.once('data', (data) => {
|
|
const request = JSON.parse(String(data).trim()) as Record<string, unknown>
|
|
requests.push(request)
|
|
const result =
|
|
request.method === 'status.get'
|
|
? { capabilities: [ORCHESTRATION_CONTRACT_RUNTIME_CAPABILITY] }
|
|
: {}
|
|
socket.write(
|
|
`${JSON.stringify({
|
|
id: request.id,
|
|
ok: true,
|
|
result,
|
|
_meta: { runtimeId: 'runtime-1' }
|
|
})}\n`
|
|
)
|
|
})
|
|
})
|
|
servers.add(server)
|
|
await new Promise<void>((resolve) => server.listen(endpoint, resolve))
|
|
writeMetadata(userDataPath, endpoint)
|
|
|
|
const priorLaunchToken = process.env.ORCA_AGENT_LAUNCH_TOKEN
|
|
process.env.ORCA_AGENT_LAUNCH_TOKEN = 'launch-secret'
|
|
const client = new RuntimeClient(userDataPath, 500)
|
|
try {
|
|
await client.call(
|
|
'orchestration.send',
|
|
{ subject: 'hello' },
|
|
{
|
|
orchestrationRequestId: 'mutation_explicit'
|
|
}
|
|
)
|
|
await client.call('orchestration.taskList', {})
|
|
const secondClient = new RuntimeClient(userDataPath, 500)
|
|
await secondClient.call('orchestration.taskList', {})
|
|
} finally {
|
|
if (priorLaunchToken === undefined) {
|
|
delete process.env.ORCA_AGENT_LAUNCH_TOKEN
|
|
} else {
|
|
process.env.ORCA_AGENT_LAUNCH_TOKEN = priorLaunchToken
|
|
}
|
|
}
|
|
|
|
expect(requests[0]?.method).toBe('status.get')
|
|
expect(requests[0]?.compatibilityInvocationId).toBeUndefined()
|
|
expect(requests[1]?.orchestrationRequestId).toBe('mutation_explicit')
|
|
expect(requests[1]?.orchestrationContractVersion).toBe(1)
|
|
expect(requests[1]?.compatibilityInvocationId).toBe('mutation_explicit')
|
|
expect(requests[1]?.orchestrationCompatibilityEvidence).toMatchObject({
|
|
launchToken: 'launch-secret'
|
|
})
|
|
expect(requests[2]?.orchestrationRequestId).toBeUndefined()
|
|
expect(requests[2]?.compatibilityInvocationId).not.toBe(requests[1]?.compatibilityInvocationId)
|
|
expect(requests[3]?.method).toBe('orchestration.taskList')
|
|
expect(requests[3]?.compatibilityInvocationId).not.toBe(requests[1]?.compatibilityInvocationId)
|
|
})
|
|
|
|
it('rejects an old local runtime before sending an orchestration mutation', async () => {
|
|
const userDataPath = mkdtempSync(join(tmpdir(), 'orca-runtime-client-'))
|
|
const endpoint = join(userDataPath, 'runtime.sock')
|
|
const requests: Record<string, unknown>[] = []
|
|
const server = createServer((socket) => {
|
|
sockets.add(socket)
|
|
socket.once('close', () => sockets.delete(socket))
|
|
socket.once('data', (data) => {
|
|
const request = JSON.parse(String(data).trim()) as Record<string, unknown>
|
|
requests.push(request)
|
|
socket.write(
|
|
`${JSON.stringify({
|
|
id: request.id,
|
|
ok: true,
|
|
result: { capabilities: [] },
|
|
_meta: { runtimeId: 'runtime-1' }
|
|
})}\n`
|
|
)
|
|
})
|
|
})
|
|
servers.add(server)
|
|
await new Promise<void>((resolve) => server.listen(endpoint, resolve))
|
|
writeMetadata(userDataPath, endpoint)
|
|
|
|
const client = new RuntimeClient(userDataPath, 500)
|
|
await expect(client.call('orchestration.send', { subject: 'hello' })).rejects.toMatchObject({
|
|
code: 'orchestration_migration_required',
|
|
data: {
|
|
reason: 'runtime_capability_missing',
|
|
effectsApplied: false,
|
|
nextCommandArgs: ['skills', 'get', 'orchestration', '--full']
|
|
}
|
|
})
|
|
expect(requests).toHaveLength(1)
|
|
expect(requests[0]?.method).toBe('status.get')
|
|
})
|
|
|
|
it('returns the full RPC envelope for successful calls', async () => {
|
|
const userDataPath = mkdtempSync(join(tmpdir(), 'orca-runtime-client-'))
|
|
const endpoint = join(userDataPath, 'runtime.sock')
|
|
const server = createServer((socket) => {
|
|
sockets.add(socket)
|
|
socket.once('close', () => sockets.delete(socket))
|
|
socket.once('data', (data) => {
|
|
const request = JSON.parse(String(data).trim()) as { id: string }
|
|
socket.write(
|
|
`${JSON.stringify({
|
|
id: request.id,
|
|
ok: true,
|
|
result: { running: true },
|
|
_meta: { runtimeId: 'runtime-1' }
|
|
})}\n`
|
|
)
|
|
})
|
|
})
|
|
servers.add(server)
|
|
await new Promise<void>((resolve) => server.listen(endpoint, resolve))
|
|
writeMetadata(userDataPath, endpoint)
|
|
|
|
const client = new RuntimeClient(userDataPath, 500)
|
|
const response = await client.call<{ running: boolean }>('status.get')
|
|
|
|
expect(response).toMatchObject({
|
|
ok: true,
|
|
result: { running: true },
|
|
_meta: { runtimeId: 'runtime-1' }
|
|
})
|
|
expect(response.id).toBeTruthy()
|
|
})
|
|
|
|
it('reports not_running when no runtime metadata exists', async () => {
|
|
const userDataPath = mkdtempSync(join(tmpdir(), 'orca-runtime-client-'))
|
|
const client = new RuntimeClient(userDataPath, 100)
|
|
|
|
const status = await client.getCliStatus()
|
|
|
|
expect(status.result).toEqual({
|
|
target: { kind: 'local' },
|
|
app: {
|
|
running: false,
|
|
pid: null
|
|
},
|
|
runtime: {
|
|
state: 'not_running',
|
|
reachable: false,
|
|
runtimeId: null
|
|
},
|
|
graph: {
|
|
state: 'not_running'
|
|
}
|
|
})
|
|
})
|
|
|
|
it('reports stale_bootstrap when bootstrap artifacts exist but no runtime is reachable', async () => {
|
|
const userDataPath = mkdtempSync(join(tmpdir(), 'orca-runtime-client-'))
|
|
writeMetadata(userDataPath, join(userDataPath, 'missing.sock'), 'token', findUnusedPid())
|
|
|
|
const client = new RuntimeClient(userDataPath, 100)
|
|
const status = await client.getCliStatus()
|
|
|
|
expect(status.result.runtime.state).toBe('stale_bootstrap')
|
|
expect(status.result.runtime.reachable).toBe(false)
|
|
})
|
|
|
|
it('reports graph_not_ready when the runtime is reachable but graph is unavailable', async () => {
|
|
const userDataPath = mkdtempSync(join(tmpdir(), 'orca-runtime-client-'))
|
|
const endpoint = join(userDataPath, 'runtime.sock')
|
|
const server = createServer((socket) => {
|
|
sockets.add(socket)
|
|
socket.once('close', () => sockets.delete(socket))
|
|
socket.once('data', (data) => {
|
|
const request = JSON.parse(String(data).trim()) as { id: string }
|
|
socket.write(
|
|
`${JSON.stringify({
|
|
id: request.id,
|
|
ok: true,
|
|
result: {
|
|
runtimeId: 'runtime-1',
|
|
rendererGraphEpoch: 0,
|
|
graphStatus: 'unavailable',
|
|
authoritativeWindowId: null,
|
|
liveTabCount: 0,
|
|
liveLeafCount: 0
|
|
},
|
|
_meta: { runtimeId: 'runtime-1' }
|
|
})}\n`
|
|
)
|
|
})
|
|
})
|
|
servers.add(server)
|
|
await new Promise<void>((resolve) => server.listen(endpoint, resolve))
|
|
writeMetadata(userDataPath, endpoint)
|
|
|
|
const client = new RuntimeClient(userDataPath, 100)
|
|
const status = await client.getCliStatus()
|
|
|
|
expect(status.result.runtime.state).toBe('graph_not_ready')
|
|
expect(status.result.graph.state).toBe('unavailable')
|
|
})
|
|
|
|
it('openOrca activates the app even when a desktop runtime is already reachable', async () => {
|
|
const userDataPath = mkdtempSync(join(tmpdir(), 'orca-runtime-client-'))
|
|
const endpoint = join(userDataPath, 'runtime.sock')
|
|
const server = createServer((socket) => {
|
|
sockets.add(socket)
|
|
socket.once('close', () => sockets.delete(socket))
|
|
socket.once('data', (data) => {
|
|
const request = JSON.parse(String(data).trim()) as { id: string }
|
|
socket.write(
|
|
`${JSON.stringify({
|
|
id: request.id,
|
|
ok: true,
|
|
result: {
|
|
runtimeId: 'runtime-1',
|
|
rendererGraphEpoch: 0,
|
|
graphStatus: 'ready',
|
|
authoritativeWindowId: 1,
|
|
liveTabCount: 1,
|
|
liveLeafCount: 1
|
|
},
|
|
_meta: { runtimeId: 'runtime-1' }
|
|
})}\n`
|
|
)
|
|
})
|
|
})
|
|
servers.add(server)
|
|
await new Promise<void>((resolve) => server.listen(endpoint, resolve))
|
|
writeMetadata(userDataPath, endpoint)
|
|
|
|
const client = new RuntimeClient(userDataPath, 100)
|
|
const status = await client.openOrca(100)
|
|
|
|
expect(status.result.runtime.state).toBe('ready')
|
|
expect(status.result.runtime.reachable).toBe(true)
|
|
expect(launchOrcaApp).toHaveBeenCalledOnce()
|
|
})
|
|
|
|
it('openOrca waits for a reachable headless runtime to expose a desktop window', async () => {
|
|
const userDataPath = mkdtempSync(join(tmpdir(), 'orca-runtime-client-'))
|
|
const endpoint = join(userDataPath, 'runtime.sock')
|
|
let statusRequests = 0
|
|
const server = createServer((socket) => {
|
|
sockets.add(socket)
|
|
socket.once('close', () => sockets.delete(socket))
|
|
socket.once('data', (data) => {
|
|
const request = JSON.parse(String(data).trim()) as { id: string }
|
|
statusRequests += 1
|
|
const available = statusRequests > 1
|
|
socket.write(
|
|
`${JSON.stringify({
|
|
id: request.id,
|
|
ok: true,
|
|
result: {
|
|
runtimeId: 'runtime-1',
|
|
rendererGraphEpoch: available ? 1 : 0,
|
|
graphStatus: available ? 'reloading' : 'ready',
|
|
authoritativeWindowId: available ? 1 : 0,
|
|
desktopWindowStatus: available ? 'available' : 'initializing',
|
|
liveTabCount: 0,
|
|
liveLeafCount: 0
|
|
},
|
|
_meta: { runtimeId: 'runtime-1' }
|
|
})}\n`
|
|
)
|
|
})
|
|
})
|
|
servers.add(server)
|
|
await new Promise<void>((resolve) => server.listen(endpoint, resolve))
|
|
writeMetadata(userDataPath, endpoint)
|
|
|
|
const client = new RuntimeClient(userDataPath, 100)
|
|
const status = await client.openOrca(1_000)
|
|
|
|
expect(launchOrcaApp).toHaveBeenCalledOnce()
|
|
expect(status.result.app.desktopWindowStatus).toBe('available')
|
|
expect(statusRequests).toBeGreaterThan(1)
|
|
})
|
|
|
|
it('openOrca fails explicitly when the serve owner cannot promote safely', async () => {
|
|
const userDataPath = mkdtempSync(join(tmpdir(), 'orca-runtime-client-'))
|
|
const endpoint = join(userDataPath, 'runtime.sock')
|
|
const server = createServer((socket) => {
|
|
sockets.add(socket)
|
|
socket.once('close', () => sockets.delete(socket))
|
|
socket.once('data', (data) => {
|
|
const request = JSON.parse(String(data).trim()) as { id: string }
|
|
socket.write(
|
|
`${JSON.stringify({
|
|
id: request.id,
|
|
ok: true,
|
|
result: {
|
|
runtimeId: 'runtime-1',
|
|
rendererGraphEpoch: 0,
|
|
graphStatus: 'ready',
|
|
authoritativeWindowId: 0,
|
|
desktopWindowStatus: 'blocked',
|
|
liveTabCount: 1,
|
|
liveLeafCount: 1
|
|
},
|
|
_meta: { runtimeId: 'runtime-1' }
|
|
})}\n`
|
|
)
|
|
})
|
|
})
|
|
servers.add(server)
|
|
await new Promise<void>((resolve) => server.listen(endpoint, resolve))
|
|
writeMetadata(userDataPath, endpoint)
|
|
|
|
const client = new RuntimeClient(userDataPath, 100)
|
|
|
|
await expect(client.openOrca(100)).rejects.toMatchObject({
|
|
code: 'desktop_activation_blocked'
|
|
})
|
|
// A blocked runtime can't promote, so we bail before spawning the app.
|
|
expect(launchOrcaApp).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('times out if the runtime never responds', async () => {
|
|
const userDataPath = mkdtempSync(join(tmpdir(), 'orca-runtime-client-'))
|
|
const endpoint = join(userDataPath, 'runtime.sock')
|
|
const server = createServer((socket) => {
|
|
sockets.add(socket)
|
|
socket.once('close', () => sockets.delete(socket))
|
|
// Why: keep the socket open without replying so the client timeout path
|
|
// is exercised against a real hung runtime connection.
|
|
})
|
|
servers.add(server)
|
|
await new Promise<void>((resolve) => server.listen(endpoint, resolve))
|
|
writeMetadata(userDataPath, endpoint)
|
|
|
|
const client = new RuntimeClient(userDataPath, 25)
|
|
|
|
await expect(client.call('status.get')).rejects.toMatchObject({
|
|
code: 'runtime_timeout'
|
|
})
|
|
})
|
|
|
|
it('preserves a dropped read-only orchestration failure exactly', async () => {
|
|
const userDataPath = mkdtempSync(join(tmpdir(), 'orca-runtime-client-'))
|
|
const endpoint = join(userDataPath, 'runtime.sock')
|
|
let request: Record<string, unknown> | undefined
|
|
const server = createServer((socket) => {
|
|
sockets.add(socket)
|
|
socket.once('close', () => sockets.delete(socket))
|
|
socket.once('data', (data) => {
|
|
request = JSON.parse(String(data).trim()) as Record<string, unknown>
|
|
socket.end()
|
|
})
|
|
})
|
|
servers.add(server)
|
|
await new Promise<void>((resolve) => server.listen(endpoint, resolve))
|
|
writeMetadata(userDataPath, endpoint)
|
|
const client = new RuntimeClient(userDataPath, 100)
|
|
|
|
const failure = await client
|
|
.call('orchestration.workerShow', { dispatch: 'ctx_1' })
|
|
.catch((error: unknown) => error)
|
|
|
|
expect(failure).toBeInstanceOf(RuntimeClientError)
|
|
expect((failure as RuntimeClientError).message).toBe(
|
|
'The Orca runtime closed the connection before responding. Restart Orca and try again.'
|
|
)
|
|
expect((failure as RuntimeClientError).data).toBeUndefined()
|
|
expect(request).toMatchObject({ method: 'orchestration.workerShow' })
|
|
expect(request).not.toHaveProperty('orchestrationRequestId')
|
|
})
|
|
|
|
it('allows a per-call timeout override for long runtime requests', async () => {
|
|
const userDataPath = mkdtempSync(join(tmpdir(), 'orca-runtime-client-'))
|
|
const endpoint = join(userDataPath, 'runtime.sock')
|
|
const server = createServer((socket) => {
|
|
sockets.add(socket)
|
|
socket.once('close', () => sockets.delete(socket))
|
|
socket.once('data', (data) => {
|
|
const request = JSON.parse(String(data).trim()) as { id: string }
|
|
setTimeout(() => {
|
|
socket.write(
|
|
`${JSON.stringify({
|
|
id: request.id,
|
|
ok: true,
|
|
result: { satisfied: true },
|
|
_meta: { runtimeId: 'runtime-1' }
|
|
})}\n`
|
|
)
|
|
}, 40)
|
|
})
|
|
})
|
|
servers.add(server)
|
|
await new Promise<void>((resolve) => server.listen(endpoint, resolve))
|
|
writeMetadata(userDataPath, endpoint)
|
|
|
|
const client = new RuntimeClient(userDataPath, 25)
|
|
const response = await client.call<{ satisfied: boolean }>('terminal.wait', undefined, {
|
|
timeoutMs: 250
|
|
})
|
|
|
|
expect(response.result).toEqual({ satisfied: true })
|
|
})
|
|
|
|
it('preserves structured runtime failures', async () => {
|
|
const userDataPath = mkdtempSync(join(tmpdir(), 'orca-runtime-client-'))
|
|
const endpoint = join(userDataPath, 'runtime.sock')
|
|
const server = createServer((socket) => {
|
|
sockets.add(socket)
|
|
socket.once('close', () => sockets.delete(socket))
|
|
socket.once('data', (data) => {
|
|
const request = JSON.parse(String(data).trim()) as { id: string }
|
|
socket.write(
|
|
`${JSON.stringify({
|
|
id: request.id,
|
|
ok: false,
|
|
error: { code: 'selector_not_found', message: 'selector_not_found' },
|
|
_meta: { runtimeId: 'runtime-1' }
|
|
})}\n`
|
|
)
|
|
})
|
|
})
|
|
servers.add(server)
|
|
await new Promise<void>((resolve) => server.listen(endpoint, resolve))
|
|
writeMetadata(userDataPath, endpoint)
|
|
|
|
const client = new RuntimeClient(userDataPath, 100)
|
|
|
|
await expect(client.call('worktree.show')).rejects.toBeInstanceOf(RuntimeRpcFailureError)
|
|
await expect(client.call('worktree.show')).rejects.toMatchObject({
|
|
response: {
|
|
ok: false,
|
|
_meta: { runtimeId: 'runtime-1' }
|
|
}
|
|
})
|
|
})
|
|
|
|
it('rejects invalid runtime response frames', async () => {
|
|
const userDataPath = mkdtempSync(join(tmpdir(), 'orca-runtime-client-'))
|
|
const endpoint = join(userDataPath, 'runtime.sock')
|
|
const server = createServer((socket) => {
|
|
sockets.add(socket)
|
|
socket.once('close', () => sockets.delete(socket))
|
|
socket.once('data', () => {
|
|
socket.write('not json\n')
|
|
})
|
|
})
|
|
servers.add(server)
|
|
await new Promise<void>((resolve) => server.listen(endpoint, resolve))
|
|
writeMetadata(userDataPath, endpoint)
|
|
|
|
const client = new RuntimeClient(userDataPath, 100)
|
|
|
|
await expect(client.call('status.get')).rejects.toMatchObject({
|
|
code: 'invalid_runtime_response'
|
|
})
|
|
})
|
|
|
|
it('rejects mismatched response ids from the runtime', async () => {
|
|
const userDataPath = mkdtempSync(join(tmpdir(), 'orca-runtime-client-'))
|
|
const endpoint = join(userDataPath, 'runtime.sock')
|
|
const server = createServer((socket) => {
|
|
sockets.add(socket)
|
|
socket.once('close', () => sockets.delete(socket))
|
|
socket.once('data', () => {
|
|
socket.write(
|
|
`${JSON.stringify({
|
|
id: 'not-the-request-id',
|
|
ok: true,
|
|
result: { running: true },
|
|
_meta: { runtimeId: 'runtime-1' }
|
|
})}\n`
|
|
)
|
|
})
|
|
})
|
|
servers.add(server)
|
|
await new Promise<void>((resolve) => server.listen(endpoint, resolve))
|
|
writeMetadata(userDataPath, endpoint)
|
|
|
|
const client = new RuntimeClient(userDataPath, 100)
|
|
|
|
await expect(client.call('status.get')).rejects.toMatchObject({
|
|
code: 'invalid_runtime_response'
|
|
})
|
|
})
|
|
})
|