diff --git a/integration-tests/acp-jsonrpc.test.ts b/integration-tests/acp-jsonrpc.test.ts new file mode 100644 index 00000000000..6003dfee044 --- /dev/null +++ b/integration-tests/acp-jsonrpc.test.ts @@ -0,0 +1,81 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { expect, describe, it, beforeEach, afterEach } from 'vitest'; +import { TestRig } from './test-helper.js'; + +describe('ACP JSON-RPC Protocol', () => { + let rig: TestRig; + + beforeEach(async () => { + rig = new TestRig(); + await rig.setup('acp-jsonrpc-test'); + }); + + afterEach(async () => { + await rig.cleanup(); + }); + + it('should return valid JSON-RPC 2.0 response for initialize method', async () => { + // Prepare the JSON-RPC initialize request + const initializeRequest = { + jsonrpc: '2.0', + id: 0, + method: 'initialize', + params: { + protocolVersion: 1, + clientCapabilities: { + fs: { + readTextFile: false, + writeTextFile: false, + }, + terminal: false, + }, + }, + }; + + // Send the request via stdin with --experimental-acp flag + const result = await rig.run( + { + stdin: JSON.stringify(initializeRequest) + '\n', + yolo: false, + }, + '--experimental-acp', + ); + + // Extract JSON lines from output (filter out debug/log output) + const jsonLines = result + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.startsWith('{') && line.includes('"jsonrpc"')); + + // Should have at least one JSON-RPC response + expect(jsonLines.length).toBeGreaterThanOrEqual(1); + + // Parse the first JSON-RPC response + const parsed = JSON.parse(jsonLines[0]); + + // Verify it's a valid JSON-RPC 2.0 response + expect(parsed).toHaveProperty('jsonrpc'); + expect(parsed.jsonrpc).toBe('2.0'); + + // Verify response structure + expect(parsed).toHaveProperty('id'); + expect(parsed.id).toBe(0); + + // Should have either 'result' or 'error', but not both + const hasResult = 'result' in parsed; + const hasError = 'error' in parsed; + expect(hasResult || hasError).toBe(true); + expect(hasResult && hasError).toBe(false); + + // For a successful initialize, we expect a result + if (hasResult) { + expect(parsed.result).toBeDefined(); + expect(typeof parsed.result).toBe('object'); + } + }); +}); diff --git a/integration-tests/test-helper.ts b/integration-tests/test-helper.ts index 94d3e697b8c..59289f4c6d6 100644 --- a/integration-tests/test-helper.ts +++ b/integration-tests/test-helper.ts @@ -503,9 +503,10 @@ export class TestRig { const isJsonOutput = commandArgs.includes('--output-format') && commandArgs.includes('json'); + const isAcpMode = commandArgs.includes('--experimental-acp'); - // If we have stderr output and it's not a JSON test, include that also - if (stderr && !isJsonOutput) { + // If we have stderr output and it's not a JSON test or ACP mode (ACP uses JRPC), include that also + if (stderr && !isJsonOutput && !isAcpMode) { result += `\n\nStdErr:\n${stderr}`; }