import { describe, it, expect, vi } from 'vitest'; import { PaseoClient, PaseoClientError } from '../paseo-client.js'; /** * Create a PaseoClient whose runCli method is replaced with a mock. * The mock is returned as the second tuple element so tests can * control and inspect it directly. */ function makeClient(config?: { paseoBin?: string; cliHost?: string }): { client: PaseoClient; mockRunCli: ReturnType; } { const client = new PaseoClient(config); const mockRunCli = vi.fn(); (client as any).runCli = mockRunCli; return { client, mockRunCli }; } describe('PaseoClient', () => { describe('listAgents', () => { it('returns parsed agent list from paseo ls --json', async () => { const agents = [ { id: 'abc-123', shortId: 'abc', name: 'Agent 1', provider: 'opencode', status: 'running' }, { id: 'def-456', shortId: 'def', name: 'Agent 2', provider: 'claude', status: 'idle' }, ]; const { client, mockRunCli } = makeClient(); mockRunCli.mockResolvedValue(JSON.stringify(agents)); const result = await client.listAgents(); expect(mockRunCli).toHaveBeenCalledWith(['ls', '--json']); expect(result).toEqual(agents); }); it('throws PaseoClientError on non-JSON output', async () => { const { client, mockRunCli } = makeClient(); mockRunCli.mockResolvedValue('not json'); await expect(client.listAgents()).rejects.toThrow(PaseoClientError); await expect(client.listAgents()).rejects.toThrow(/invalid JSON/); }); it('propagates runCli rejection as-is', async () => { const { client, mockRunCli } = makeClient(); const err = new PaseoClientError('ls failed: connection refused', 'ls', 1, 'connection refused'); mockRunCli.mockRejectedValue(err); await expect(client.listAgents()).rejects.toThrow(PaseoClientError); await expect(client.listAgents()).rejects.toThrow(/ls failed/); }); }); describe('getAgentStatus', () => { it('returns parsed agent detail from paseo inspect --json', async () => { const detail = { Id: 'abc-123', Name: 'Agent 1', Provider: 'opencode', Status: 'idle', Archived: false, CreatedAt: '2026-01-01T00:00:00Z', UpdatedAt: '2026-01-01T01:00:00Z', }; const { client, mockRunCli } = makeClient(); mockRunCli.mockResolvedValue(JSON.stringify(detail)); const result = await client.getAgentStatus('abc-123'); expect(mockRunCli).toHaveBeenCalledWith(['inspect', '--json', 'abc-123']); expect(result.Id).toBe('abc-123'); expect(result.Status).toBe('idle'); }); }); describe('health', () => { it('returns ok when paseo ls succeeds', async () => { const { client, mockRunCli } = makeClient(); mockRunCli.mockResolvedValue('[]'); const result = await client.health(); expect(result).toEqual({ status: 'ok' }); }); it('returns error when runCli throws', async () => { const { client, mockRunCli } = makeClient(); mockRunCli.mockRejectedValue(new Error('connection refused')); const result = await client.health(); expect(result).toEqual({ status: 'error' }); }); }); describe('importAgent', () => { it('calls paseo import with provider and labels', async () => { const agentResult = { Id: 'new-789', Name: 'Imported', Provider: 'opencode', Status: 'idle' }; const { client, mockRunCli } = makeClient(); mockRunCli.mockResolvedValue(JSON.stringify(agentResult)); const result = await client.importAgent('ses-001', 'opencode', { origin: 'boocode', project: 'proj-1', }); expect(mockRunCli).toHaveBeenCalledWith([ 'import', '--json', '--provider', 'opencode', '--label', 'origin=boocode', '--label', 'project=proj-1', 'ses-001', ]); expect(result.Id).toBe('new-789'); }); it('works without labels', async () => { const { client, mockRunCli } = makeClient(); mockRunCli.mockResolvedValue(JSON.stringify({ Id: 'new-789' })); const result = await client.importAgent('ses-001', 'claude'); expect(mockRunCli).toHaveBeenCalledWith([ 'import', '--json', '--provider', 'claude', 'ses-001', ]); expect(result.Id).toBe('new-789'); }); }); describe('archiveAgent', () => { it('calls paseo archive --json', async () => { const { client, mockRunCli } = makeClient(); mockRunCli.mockResolvedValue('{}'); await client.archiveAgent('abc-123'); expect(mockRunCli).toHaveBeenCalledWith(['archive', '--json', 'abc-123']); }); }); describe('sendPrompt', () => { it('sends prompt and parses JSON result', async () => { const sendResult = { text: 'Hello!', ok: true }; const { client, mockRunCli } = makeClient(); mockRunCli.mockResolvedValue(JSON.stringify(sendResult)); const result = await client.sendPrompt('abc-123', 'Hello'); expect(mockRunCli).toHaveBeenCalledWith(['send', '--json', 'abc-123', 'Hello'], undefined); expect(result).toEqual(sendResult); }); it('falls back to plain text on non-JSON output', async () => { const { client, mockRunCli } = makeClient(); mockRunCli.mockResolvedValue('plain text response'); const result = await client.sendPrompt('abc-123', 'Hi'); expect(result).toEqual({ text: 'plain text response', ok: true }); }); it('supports --no-wait flag', async () => { const { client, mockRunCli } = makeClient(); mockRunCli.mockResolvedValue('{}'); await client.sendPrompt('abc-123', 'Hi', { noWait: true }); expect(mockRunCli).toHaveBeenCalledWith([ 'send', '--json', '--no-wait', 'abc-123', 'Hi', ], undefined); }); }); describe('stopAgent', () => { it('calls paseo stop', async () => { const { client, mockRunCli } = makeClient(); mockRunCli.mockResolvedValue(''); await client.stopAgent('abc-123'); expect(mockRunCli).toHaveBeenCalledWith(['stop', 'abc-123']); }); }); describe('cliHost config', () => { it('includes --host flag in args when cliHost is set', async () => { const { client, mockRunCli } = makeClient({ cliHost: 'tcp://localhost:6767?ssl=true' }); mockRunCli.mockResolvedValue('[]'); await client.listAgents(); expect(mockRunCli).toHaveBeenCalledWith([ 'ls', '--json', '--host', 'tcp://localhost:6767?ssl=true', ]); }); }); });