Skip to content

Commit bdfa53d

Browse files
authored
Avoid timeouts in client integration test fixtures (#74)
1 parent cad63bd commit bdfa53d

File tree

4 files changed

+64
-77
lines changed

4 files changed

+64
-77
lines changed

client/src/lib/integration-test-runner.js

Lines changed: 35 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,27 @@ export class IntegrationTestRunner {
6060
this.options = options;
6161
}
6262

63+
/**
64+
* Call an MCP tool with an appropriate timeout.
65+
*
66+
* All codeql_* tools invoke the CodeQL CLI or language server JVM, which
67+
* can be slow in CI (cold JVM start, network pack downloads, Windows
68+
* runner overhead). A generous 5-minute timeout avoids intermittent
69+
* -32001 RequestTimeout failures.
70+
*/
71+
async callTool(toolName, args) {
72+
const isCodeQLTool = toolName.startsWith("codeql_");
73+
const requestOptions = {
74+
timeout: isCodeQLTool ? 300000 : 60000,
75+
resetTimeoutOnProgress: isCodeQLTool
76+
};
77+
return await this.client.callTool(
78+
{ name: toolName, arguments: args },
79+
undefined,
80+
requestOptions
81+
);
82+
}
83+
6384
/**
6485
* Run integration tests for a specific tool
6586
*/
@@ -268,12 +289,9 @@ export class IntegrationTestRunner {
268289
}
269290

270291
// Run the tool
271-
const result = await this.client.callTool({
272-
name: toolName,
273-
arguments: {
274-
"files": files,
275-
"in-place": true
276-
}
292+
const result = await this.callTool(toolName, {
293+
"files": files,
294+
"in-place": true
277295
});
278296

279297
this.logger.log(`Tool ${toolName} result: ${result.content?.[0]?.text || "No output"}`);
@@ -337,11 +355,8 @@ export class IntegrationTestRunner {
337355
const _afterContent = fs.readFileSync(afterPath, "utf8");
338356

339357
// Run the codeql_lsp_diagnostics tool on the before content
340-
const result = await this.client.callTool({
341-
name: toolName,
342-
arguments: {
343-
ql_code: beforeContent
344-
}
358+
const result = await this.callTool(toolName, {
359+
ql_code: beforeContent
345360
});
346361

347362
this.logger.log(
@@ -472,10 +487,7 @@ export class IntegrationTestRunner {
472487
resolvePathPlaceholders(testConfig.arguments, this.logger);
473488

474489
// Run the tool with custom arguments
475-
const result = await this.client.callTool({
476-
name: toolName,
477-
arguments: testConfig.arguments
478-
});
490+
const result = await this.callTool(toolName, testConfig.arguments);
479491

480492
this.logger.log(`Tool ${toolName} result: ${result.content?.[0]?.text || "No output"}`);
481493

@@ -659,10 +671,7 @@ export class IntegrationTestRunner {
659671
// Check if qlpack.yml exists and install dependencies
660672
if (fs.existsSync(path.join(packDir, "codeql-pack.yml"))) {
661673
try {
662-
await this.client.callTool({
663-
name: "codeql_pack_install",
664-
arguments: { packDir: packDir }
665-
});
674+
await this.callTool("codeql_pack_install", { packDir: packDir });
666675
} catch (installError) {
667676
this.logger.log(
668677
` Warning: Could not install pack dependencies: ${installError.message}`
@@ -708,9 +717,8 @@ export class IntegrationTestRunner {
708717

709718
if (fs.existsSync(absoluteTestSourceDir)) {
710719
this.logger.log(`Database not found, extracting from ${testSourceDir}`);
711-
const extractResult = await this.client.callTool({
712-
name: "codeql_test_extract",
713-
arguments: { tests: [testSourceDir] }
720+
const extractResult = await this.callTool("codeql_test_extract", {
721+
tests: [testSourceDir]
714722
});
715723
if (extractResult.isError) {
716724
const errorText = extractResult.content?.[0]?.text || "Unknown error";
@@ -739,38 +747,10 @@ export class IntegrationTestRunner {
739747
params = await this.getToolSpecificParams(toolName, testCase);
740748
}
741749

742-
// Call the tool with appropriate parameters
743-
// Set extended timeout for long-running operations
744-
const longRunningTools = [
745-
"codeql_database_analyze",
746-
"codeql_database_create",
747-
"codeql_lsp_completion",
748-
"codeql_lsp_definition",
749-
"codeql_lsp_diagnostics",
750-
"codeql_lsp_references",
751-
"codeql_query_run",
752-
"codeql_test_run"
753-
];
754-
755-
const requestOptions = longRunningTools.includes(toolName)
756-
? {
757-
timeout: 300000, // 5 minutes for long-running tools
758-
resetTimeoutOnProgress: true
759-
}
760-
: {
761-
timeout: 60000 // 60 seconds for other tools
762-
};
763-
764-
this.logger.log(`Calling tool ${toolName} with timeout: ${requestOptions.timeout}ms`);
750+
// Call the tool with appropriate parameters (timeout is handled by this.callTool)
751+
this.logger.log(`Calling tool ${toolName}`);
765752

766-
const result = await this.client.callTool(
767-
{
768-
name: toolName,
769-
arguments: params
770-
},
771-
undefined,
772-
requestOptions
773-
);
753+
const result = await this.callTool(toolName, params);
774754

775755
// For monitoring tests, we primarily check if the tool executed successfully
776756
// Special handling for session management tools that expect sessions to exist
@@ -979,9 +959,8 @@ export class IntegrationTestRunner {
979959
if (!fs.existsSync(databaseDir) && fs.existsSync(testDir)) {
980960
this.logger.log(`Database not found for query run, extracting first: ${databaseDir}`);
981961
// Call codeql test extract to create the database
982-
const extractResult = await this.client.callTool({
983-
name: "codeql_test_extract",
984-
arguments: { tests: [testDir] }
962+
const extractResult = await this.callTool("codeql_test_extract", {
963+
tests: [testDir]
985964
});
986965
if (extractResult.isError) {
987966
throw new Error(`Failed to extract database: ${extractResult.content[0].text}`);

client/src/lib/mcp-test-suite.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ export class MCPTestSuite {
3232
this.logger.log("Testing tool execution...");
3333

3434
// Try to call a simple resolve languages tool
35-
const result = await this.client.callTool({
36-
name: "codeql_resolve_languages",
37-
arguments: {}
38-
});
35+
const result = await this.client.callTool(
36+
{ name: "codeql_resolve_languages", arguments: {} },
37+
undefined,
38+
{ timeout: 300000, resetTimeoutOnProgress: true }
39+
);
3940

4041
this.logger.log(`Tool result: ${JSON.stringify(result, null, 2)}`);
4142

client/src/lib/monitoring-integration-test-runner.js

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,23 @@ export class MonitoringIntegrationTestRunner {
1717
}
1818

1919
/**
20-
* Helper method to call MCP tools with correct format
20+
* Helper method to call MCP tools with correct format and timeout.
21+
*
22+
* All codeql_* tools invoke the CodeQL CLI or language server JVM, which
23+
* can be slow in CI. A generous 5-minute timeout avoids intermittent
24+
* -32001 RequestTimeout failures.
2125
*/
2226
async callTool(toolName, parameters = {}) {
23-
return await this.client.callTool({
24-
name: toolName,
25-
arguments: parameters
26-
});
27+
const isCodeQLTool = toolName.startsWith("codeql_");
28+
const requestOptions = {
29+
timeout: isCodeQLTool ? 300000 : 60000,
30+
resetTimeoutOnProgress: isCodeQLTool
31+
};
32+
return await this.client.callTool(
33+
{ name: toolName, arguments: parameters },
34+
undefined,
35+
requestOptions
36+
);
2737
}
2838

2939
/**

client/src/ql-mcp-client.js

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,20 +49,17 @@ class CodeQLMCPClient {
4949
* Helper method to call MCP tools with correct format
5050
*/
5151
async callTool(toolName, parameters = {}, options = {}) {
52-
// Set appropriate timeout based on tool type
53-
// Long-running tools like query_run and test_run need extended timeouts
54-
const longRunningTools = [
55-
"codeql_query_run",
56-
"codeql_test_run",
57-
"codeql_database_analyze",
58-
"codeql_database_create"
59-
];
52+
// All codeql_* tools invoke the CodeQL CLI or language server JVM, which
53+
// can be slow in CI (cold JVM start, network pack downloads, Windows
54+
// runner overhead). Use a generous 5-minute timeout for every CodeQL
55+
// tool to avoid intermittent -32001 RequestTimeout failures.
56+
const isCodeQLTool = toolName.startsWith("codeql_");
6057

6158
const defaultOptions = {
62-
// Use 5 minute timeout for long-running tools, 60 seconds for others
63-
timeout: longRunningTools.includes(toolName) ? 300000 : 60000,
64-
// Reset timeout on progress notifications for long-running operations
65-
resetTimeoutOnProgress: longRunningTools.includes(toolName)
59+
// Use 5 minute timeout for CodeQL tools, 60 seconds for others
60+
timeout: isCodeQLTool ? 300000 : 60000,
61+
// Reset timeout on progress notifications for CodeQL operations
62+
resetTimeoutOnProgress: isCodeQLTool
6663
};
6764

6865
const requestOptions = { ...defaultOptions, ...options };

0 commit comments

Comments
 (0)