Skip to content

Commit 4e272d0

Browse files
committed
Bridge handler regression net: pre-read map, ack, degrades
Six mocked-boundary tests for the paths the live e2e exercised: @token pre-reads into the embed map, @@/@- passthrough, missing-file and cold-machine degrades on both directions, ack-only stdout replacement, and no write on command failure. Claude-Session: https://claude.ai/code/session_01CgaxNAaeD3taGdghbXn17w
1 parent 09aa745 commit 4e272d0

1 file changed

Lines changed: 102 additions & 0 deletions

File tree

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/**
2+
* @vitest-environment node
3+
*/
4+
import { beforeEach, describe, expect, it, vi } from 'vitest'
5+
6+
const { mockRead, mockWrite, mockRunEmbeddedCli, mockMint } = vi.hoisted(() => ({
7+
mockRead: vi.fn(),
8+
mockWrite: vi.fn(),
9+
mockRunEmbeddedCli: vi.fn(),
10+
mockMint: vi.fn(),
11+
}))
12+
13+
vi.mock('@/lib/execution/remote-sandbox/session-files', () => ({
14+
readSessionSandboxFile: mockRead,
15+
writeSessionSandboxFile: mockWrite,
16+
}))
17+
vi.mock('sim/embed', () => ({
18+
runEmbeddedCli: mockRunEmbeddedCli,
19+
createEmbeddedClient: vi.fn(),
20+
}))
21+
vi.mock('@/lib/mothership/chat/delegation', () => ({ mintDelegationToken: mockMint }))
22+
vi.mock('@/lib/core/utils/urls', () => ({ getInternalApiBaseUrl: () => 'http://internal' }))
23+
24+
import { executeSimCli } from '@/lib/mothership/tools/handlers/sim-cli'
25+
26+
const context = { workspaceId: 'ws-1', userId: 'u-1', chatId: 'chat-1' } as Parameters<
27+
typeof executeSimCli
28+
>[1]
29+
30+
describe('sim-cli machine file bridge', () => {
31+
beforeEach(() => {
32+
vi.clearAllMocks()
33+
mockMint.mockResolvedValue('key')
34+
mockRunEmbeddedCli.mockResolvedValue({ exitCode: 0, stdout: 'BIG OUTPUT', stderr: '' })
35+
})
36+
37+
it('pre-reads @tokens from the machine into the embed file map', async () => {
38+
mockRead.mockResolvedValue({ outcome: 'read', content: '{"text":"hi"}' })
39+
await executeSimCli({ args: ['workflows', 'run', 'wf1', '--input', '@env.json'] }, context)
40+
expect(mockRead).toHaveBeenCalledWith('mothership-chat:chat-1', 'env.json')
41+
expect(mockRunEmbeddedCli).toHaveBeenCalledWith(
42+
['workflows', 'run', 'wf1', '--input', '@env.json'],
43+
expect.anything(),
44+
{ fileArguments: { 'env.json': '{"text":"hi"}' } }
45+
)
46+
})
47+
48+
it('leaves @@ literals and @- alone, and omits missing files from the map', async () => {
49+
mockRead.mockResolvedValue({ outcome: 'no-file', detail: 'nope' })
50+
await executeSimCli({ args: ['x', '@@literal', '@missing.json'] }, context)
51+
expect(mockRead).toHaveBeenCalledTimes(1)
52+
expect(mockRunEmbeddedCli).toHaveBeenCalledWith(
53+
['x', '@@literal', '@missing.json'],
54+
expect.anything(),
55+
{
56+
fileArguments: {},
57+
}
58+
)
59+
})
60+
61+
it('cold machine on read degrades via the empty map (CLI core words the refusal)', async () => {
62+
mockRead.mockResolvedValue({ outcome: 'no-session' })
63+
await executeSimCli({ args: ['x', '@env.json'] }, context)
64+
expect(mockRunEmbeddedCli).toHaveBeenCalledWith(['x', '@env.json'], expect.anything(), {
65+
fileArguments: {},
66+
})
67+
})
68+
69+
it('outputFile lands stdout on the machine and returns only the ack', async () => {
70+
mockWrite.mockResolvedValue({ outcome: 'written', path: '/home/user/trace.json' })
71+
const result = await executeSimCli(
72+
{ args: ['logs', 'get', 'r1'], outputFile: 'trace.json' },
73+
context
74+
)
75+
expect(mockWrite).toHaveBeenCalledWith('mothership-chat:chat-1', 'trace.json', 'BIG OUTPUT')
76+
const output = result.output as { stdout: string }
77+
expect(output.stdout).toContain('written to trace.json')
78+
expect(output.stdout).toContain('10 chars')
79+
expect(output.stdout).not.toContain('BIG OUTPUT')
80+
})
81+
82+
it('outputFile on a cold machine returns output inline with boot guidance', async () => {
83+
mockWrite.mockResolvedValue({ outcome: 'no-session' })
84+
const result = await executeSimCli(
85+
{ args: ['logs', 'get', 'r1'], outputFile: 'trace.json' },
86+
context
87+
)
88+
const output = result.output as { stdout: string }
89+
expect(output.stdout).toContain('BIG OUTPUT')
90+
expect(output.stdout).toContain('not booted')
91+
})
92+
93+
it('outputFile is skipped on command failure so the error stays visible', async () => {
94+
mockRunEmbeddedCli.mockResolvedValue({ exitCode: 1, stdout: '', stderr: 'boom' })
95+
const result = await executeSimCli(
96+
{ args: ['logs', 'get', 'r1'], outputFile: 'trace.json' },
97+
context
98+
)
99+
expect(mockWrite).not.toHaveBeenCalled()
100+
expect(result.success).toBe(false)
101+
})
102+
})

0 commit comments

Comments
 (0)