Files
tkmind_go/ui/desktop/src/acp/__tests__/gooseAcpClient.test.ts
T
2026-08-14 03:48:34 +00:00

130 lines
3.6 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import type { AnyMessage, Stream } from '@agentclientprotocol/sdk';
import { connectGooseAcpClient, type GooseAcpCallbacks } from '../gooseAcpClient';
function createTestStream(): Stream & {
push(message: AnyMessage): void;
writes: AnyMessage[];
} {
let controller: ReadableStreamDefaultController<AnyMessage> | undefined;
const writes: AnyMessage[] = [];
return {
readable: new ReadableStream<AnyMessage>({
start(nextController) {
controller = nextController;
},
}),
writable: new WritableStream<AnyMessage>({
write(message) {
writes.push(message);
},
}),
push(message) {
controller?.enqueue(message);
},
writes,
};
}
function callbacks(): GooseAcpCallbacks {
return {
requestPermission: vi.fn().mockResolvedValue({
outcome: { outcome: 'selected', optionId: 'allow' },
}),
sessionUpdate: vi.fn(),
unstable_createElicitation: vi.fn(),
unstable_sessionRecipeRequestParams: vi.fn().mockResolvedValue({
action: 'submit',
values: { name: 'Ada' },
}),
unstable_sessionUpdate: vi.fn(),
};
}
async function waitForWrites(stream: { writes: AnyMessage[] }, count: number): Promise<void> {
await vi.waitFor(() => {
expect(stream.writes).toHaveLength(count);
});
}
describe('Goose ACP client composition', () => {
it('registers standard and Goose-specific handlers on a live ACP connection', async () => {
const stream = createTestStream();
const handlers = callbacks();
const client = connectGooseAcpClient(stream, handlers);
stream.push({
jsonrpc: '2.0',
id: 1,
method: 'session/request_permission',
params: {
sessionId: 'session-1',
toolCall: { toolCallId: 'tool-1' },
options: [{ optionId: 'allow', name: 'Allow', kind: 'allow_once' }],
},
});
await waitForWrites(stream, 1);
expect(handlers.requestPermission).toHaveBeenCalledOnce();
expect(stream.writes[0]).toMatchObject({
id: 1,
result: { outcome: { outcome: 'selected', optionId: 'allow' } },
});
stream.push({
jsonrpc: '2.0',
method: '_goose/unstable/session/update',
params: {
sessionId: 'session-1',
update: {
sessionUpdate: 'status_message',
status: { type: 'notice', message: 'ready' },
},
},
});
await vi.waitFor(() => {
expect(handlers.unstable_sessionUpdate).toHaveBeenCalledOnce();
});
stream.push({
jsonrpc: '2.0',
id: 2,
method: '_goose/unstable/session/recipe/request-params',
params: {
sessionId: 'session-1',
parameters: [
{
key: 'name',
input_type: 'string',
requirement: 'user_prompt',
description: 'Name',
},
],
},
});
await waitForWrites(stream, 2);
expect(handlers.unstable_sessionRecipeRequestParams).toHaveBeenCalledOnce();
expect(stream.writes[1]).toMatchObject({
id: 2,
result: { action: 'submit', values: { name: 'Ada' } },
});
const toolsRequest = client.goose.toolsList_unstable({ sessionId: 'session-1' });
await waitForWrites(stream, 3);
const outboundRequest = stream.writes[2] as { id: number; method: string };
expect(outboundRequest.method).toBe('_goose/unstable/tools/list');
stream.push({
jsonrpc: '2.0',
id: outboundRequest.id,
result: { tools: [] },
});
await expect(toolsRequest).resolves.toEqual({ tools: [] });
client.connection.close();
await client.connection.closed;
});
});