Skip to content

Commit 9371dbf

Browse files
committed
Merge remote-tracking branch 'origin/chore-unskip-e2e-use-mcp-tool' into chore-unskip-e2e-subtasks
2 parents 00b9cac + fa293b2 commit 9371dbf

15 files changed

Lines changed: 1198 additions & 110 deletions

File tree

apps/vscode-e2e/AGENTS.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,13 @@ Background API calls from the extension (usage collection, initialization) hit a
137137
138138
Some suites can't redirect their provider through aimock. These suites patch `globalThis.fetch` directly — the OpenAI SDK resolves `fetch` at API client construction time (which happens lazily at task start), so installing the interceptor before `api.startNewTask()` is sufficient. Installing it before `api.setConfiguration()` (as done below) is the conservative, recommended order.
139139
140+
Keep fetch-interceptor suites hermetic across test cases:
141+
142+
- reset in-memory request/event capture in `setup()` or allocate a fresh per-test buffer instead of reusing shared mutable state implicitly
143+
- scope request-shape assertions to the current probe or test tag only; do not pull in older requests just because they contain tool outputs
144+
- assume late async requests from the prior task can still arrive after a shared array/map was cleared, so tags or other per-probe identity should be the source of truth
145+
- when changing persisted provider/model settings in tests, use the path that clears prior provider fields instead of partial mutation
146+
140147
### Z.ai GLM (`suite/providers/zai.test.ts`)
141148
142149
Z.ai doesn't expose a user-configurable base URL (it uses a fixed set of regional endpoints), so we deliberately avoided adding a hidden test-only override to the schema. The suite instead patches `globalThis.fetch` to intercept requests to `api.z.ai` and return a crafted OpenAI-compatible SSE response.
@@ -153,6 +160,39 @@ ZAI_API_KEY=<key> TEST_FILE=zai.test pnpm --filter @roo-code/vscode-e2e test:ci
153160
154161
When adding a new test to this suite, add a matching fixture to the `installZAiFetchInterceptor` call in `suiteSetup`. Use a short unique prefix (e.g. `"zai-glm-e2e-mytest:"`) that won't appear in `<environment_details>`.
155162
163+
### xAI Grok (`suite/providers/xai.test.ts`)
164+
165+
xAI uses the **Responses API** (`POST https://api.x.ai/v1/responses`), which is not OpenAI-compatible. aimock can't intercept it. The suite instead patches `globalThis.fetch` to intercept requests to that endpoint. By default it replays hand-crafted SSE events; when a local `fixtures/xai.json` recording exists, it can replay recorded real-API SSE events for reference.
166+
167+
The local `fixtures/xai.json` file is gitignored. It keeps an empty top-level `fixtures` array so aimock can scan the directory without warnings. xAI recordings live under `xaiResponses`, which maps each model ID to `{ readCallId, turn1, turn2 }`:
168+
169+
- **`turn1`** — raw SSE events from the real API for the first API call (should contain a `read_file` function call)
170+
- **`readCallId`** — the `call_id` extracted from turn 1's `response.output_item.done` event; used to match the turn 2 request
171+
- **`turn2`** — raw SSE events for the second API call (should contain an `attempt_completion` call with the correct result text)
172+
173+
Having real-API events in the fixture guarantees:
174+
175+
1. The SSE format expected by the stream processor is correct.
176+
2. The `attempt_completion.result` field contains the actual marker value (not an empty string).
177+
178+
**Recording** (populate or refresh the fixture from the real xAI API):
179+
180+
```sh
181+
XAI_API_KEY=<key> XAI_RECORD=true TEST_FILE=providers/xai.test.js pnpm --filter @roo-code/vscode-e2e test:run
182+
```
183+
184+
This routes all xAI requests through the real API, captures the SSE events per turn, and writes the local gitignored `fixtures/xai.json` on teardown.
185+
186+
**Verifying the recorded fixture in mock mode** (no API key needed):
187+
188+
```sh
189+
TEST_FILE=xai.test pnpm --filter @roo-code/vscode-e2e test:ci:mock
190+
```
191+
192+
When no local recording exists for a model, the interceptor falls back to hand-crafted SSE events (using a hardcoded `readCallId`). CI should use this fallback so the suite remains deterministic and does not depend on large raw provider recordings.
193+
194+
When adding a new test to this suite, update the hand-crafted interceptor response unless the behavior specifically needs a real xAI SSE recording for local debugging. Use a short unique probe tag (e.g. `"xai-e2e:grok-4.20"`) that won't appear in `<environment_details>`.
195+
156196
### DeepSeek V4 (`suite/providers/deepseek-v4.test.ts`)
157197
158198
DeepSeek exposes `deepSeekBaseUrl`, so the suite redirects the OpenAI-compatible DeepSeek client through aimock with `deepSeekBaseUrl: ${AIMOCK_URL}/v1`. The test still installs a lightweight fetch capture for request-shape assertions, but responses should come from aimock fixtures or aimock record mode.

apps/vscode-e2e/fixtures/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
# Contributors should extract stable fixtures manually from these files, then delete them.
33
openai-*.json
44
anthropic-*.json
5+
xai.json

apps/vscode-e2e/src/fixtures/use-mcp-tool.ts

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import * as path from "path"
2-
31
import { LLMock } from "@copilotkit/aimock"
42

53
const TEST_DIR_NAME = "use-mcp-tool-fixture"
64
const FILESYSTEM_SERVER_NAME = "filesystem"
5+
const READ_FILE_RELATIVE_PATH = `${TEST_DIR_NAME}/mcp-read-target.txt`
6+
const WRITE_FILE_RELATIVE_PATH = `${TEST_DIR_NAME}/mcp-write-target.txt`
77

88
type UseMcpToolFixture = {
99
userMessagePattern: string
@@ -15,48 +15,45 @@ type UseMcpToolFixture = {
1515
id: string
1616
}
1717

18-
export function addUseMcpToolResultFixtures(mock: InstanceType<typeof LLMock>, workspaceDir: string) {
19-
const readFilePath = path.join(workspaceDir, TEST_DIR_NAME, "mcp-read-target.txt")
20-
const writeFilePath = path.join(workspaceDir, TEST_DIR_NAME, "mcp-write-target.txt")
21-
18+
export function addUseMcpToolResultFixtures(mock: InstanceType<typeof LLMock>) {
2219
const fixtures: UseMcpToolFixture[] = [
2320
{
2421
userMessagePattern: "USE_MCP_TOOL_READ_FILE_SMOKE",
2522
toolCallId: "call_use_mcp_tool_read_file_001",
2623
toolName: "read_file",
27-
toolArguments: { path: readFilePath },
24+
toolArguments: { path: READ_FILE_RELATIVE_PATH },
2825
result: "Read the requested file through the MCP filesystem server.",
2926
id: "call_use_mcp_tool_read_file_002",
3027
},
3128
{
3229
userMessagePattern: "USE_MCP_TOOL_WRITE_FILE_SMOKE",
3330
toolCallId: "call_use_mcp_tool_write_file_001",
3431
toolName: "write_file",
35-
toolArguments: { path: writeFilePath, content: "Hello from MCP!" },
32+
toolArguments: { path: WRITE_FILE_RELATIVE_PATH, content: "Hello from MCP!" },
3633
result: "Created the requested file through the MCP filesystem server.",
3734
id: "call_use_mcp_tool_write_file_002",
3835
},
3936
{
4037
userMessagePattern: "USE_MCP_TOOL_LIST_DIRECTORY_SMOKE",
4138
toolCallId: "call_use_mcp_tool_list_directory_001",
4239
toolName: "list_directory",
43-
toolArguments: { path: path.join(workspaceDir, TEST_DIR_NAME) },
40+
toolArguments: { path: TEST_DIR_NAME },
4441
result: "Listed the requested directory through the MCP filesystem server.",
4542
id: "call_use_mcp_tool_list_directory_002",
4643
},
4744
{
4845
userMessagePattern: "USE_MCP_TOOL_DIRECTORY_TREE_SMOKE",
4946
toolCallId: "call_use_mcp_tool_directory_tree_001",
5047
toolName: "directory_tree",
51-
toolArguments: { path: path.join(workspaceDir, TEST_DIR_NAME) },
48+
toolArguments: { path: TEST_DIR_NAME },
5249
result: "Returned the directory tree through the MCP filesystem server.",
5350
id: "call_use_mcp_tool_directory_tree_002",
5451
},
5552
{
5653
userMessagePattern: "USE_MCP_TOOL_GET_FILE_INFO_SMOKE",
5754
toolCallId: "call_use_mcp_tool_get_file_info_001",
5855
toolName: "get_file_info",
59-
toolArguments: { path: readFilePath },
56+
toolArguments: { path: READ_FILE_RELATIVE_PATH },
6057
result: "Returned the requested file metadata through the MCP filesystem server.",
6158
id: "call_use_mcp_tool_get_file_info_002",
6259
},
@@ -65,26 +62,35 @@ export function addUseMcpToolResultFixtures(mock: InstanceType<typeof LLMock>, w
6562
toolCallId: "call_use_mcp_tool_unknown_server_001",
6663
serverName: "nonexistent-server",
6764
toolName: "read_file",
68-
toolArguments: { path: readFilePath },
69-
result: "Handled the missing MCP server gracefully.",
65+
toolArguments: { path: READ_FILE_RELATIVE_PATH },
66+
result: "MCP server 'nonexistent-server' is not configured. Available servers: filesystem",
7067
id: "call_use_mcp_tool_unknown_server_002",
7168
},
7269
]
7370

7471
for (const fixture of fixtures) {
72+
const serverName = fixture.serverName ?? FILESYSTEM_SERVER_NAME
73+
const isConfiguredFilesystemTool = serverName === FILESYSTEM_SERVER_NAME
74+
7575
mock.addFixture({
7676
match: {
7777
userMessage: new RegExp(fixture.userMessagePattern),
7878
},
7979
response: {
8080
toolCalls: [
8181
{
82-
name: "use_mcp_tool",
83-
arguments: JSON.stringify({
84-
server_name: fixture.serverName ?? FILESYSTEM_SERVER_NAME,
85-
tool_name: fixture.toolName,
86-
arguments: fixture.toolArguments,
87-
}),
82+
name: isConfiguredFilesystemTool
83+
? `mcp--${FILESYSTEM_SERVER_NAME}--${fixture.toolName}`
84+
: "use_mcp_tool",
85+
arguments: JSON.stringify(
86+
isConfiguredFilesystemTool
87+
? fixture.toolArguments
88+
: {
89+
server_name: serverName,
90+
tool_name: fixture.toolName,
91+
arguments: fixture.toolArguments,
92+
},
93+
),
8894
id: fixture.toolCallId,
8995
},
9096
],

apps/vscode-e2e/src/runTest.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ async function main() {
9494
addReadFileResultFixtures(mock)
9595
addSearchFilesResultFixtures(mock)
9696
addSubtaskFixtures(mock)
97-
addUseMcpToolResultFixtures(mock, testWorkspace)
97+
addUseMcpToolResultFixtures(mock)
9898
addWriteToFileResultFixtures(mock)
9999

100100
// The modes test (switch_mode → ask) triggers a second API call whose last

0 commit comments

Comments
 (0)