Merge pull request #768 from olaservo/cli-header-followups
Add CLI header integration tests and update test scripts
This commit is contained in:
commit
05b279a2e6
4 changed files with 258 additions and 4 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -17,3 +17,4 @@ client/results.json
|
|||
client/test-results/
|
||||
client/e2e/test-results/
|
||||
mcp.json
|
||||
.claude/settings.local.json
|
||||
|
|
|
|||
|
|
@ -17,9 +17,10 @@
|
|||
"scripts": {
|
||||
"build": "tsc",
|
||||
"postbuild": "node scripts/make-executable.js",
|
||||
"test": "node scripts/cli-tests.js && node scripts/cli-tool-tests.js",
|
||||
"test": "node scripts/cli-tests.js && node scripts/cli-tool-tests.js && node scripts/cli-header-tests.js",
|
||||
"test:cli": "node scripts/cli-tests.js",
|
||||
"test:cli-tools": "node scripts/cli-tool-tests.js"
|
||||
"test:cli-tools": "node scripts/cli-tool-tests.js",
|
||||
"test:cli-headers": "node scripts/cli-header-tests.js"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"dependencies": {
|
||||
|
|
|
|||
252
cli/scripts/cli-header-tests.js
Normal file
252
cli/scripts/cli-header-tests.js
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Integration tests for header functionality
|
||||
* Tests the CLI header parsing end-to-end
|
||||
*/
|
||||
|
||||
import { spawn } from "node:child_process";
|
||||
import { resolve, dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const CLI_PATH = resolve(__dirname, "..", "build", "index.js");
|
||||
|
||||
// ANSI colors for output
|
||||
const colors = {
|
||||
GREEN: "\x1b[32m",
|
||||
RED: "\x1b[31m",
|
||||
YELLOW: "\x1b[33m",
|
||||
BLUE: "\x1b[34m",
|
||||
NC: "\x1b[0m", // No Color
|
||||
};
|
||||
|
||||
let testsPassed = 0;
|
||||
let testsFailed = 0;
|
||||
|
||||
/**
|
||||
* Run a CLI test with given arguments and check for expected behavior
|
||||
*/
|
||||
function runHeaderTest(
|
||||
testName,
|
||||
args,
|
||||
expectSuccess = false,
|
||||
expectedInOutput = null,
|
||||
) {
|
||||
return new Promise((resolve) => {
|
||||
console.log(`\n${colors.BLUE}Testing: ${testName}${colors.NC}`);
|
||||
console.log(
|
||||
`${colors.BLUE}Command: node ${CLI_PATH} ${args.join(" ")}${colors.NC}`,
|
||||
);
|
||||
|
||||
const child = spawn("node", [CLI_PATH, ...args], {
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
|
||||
child.stdout.on("data", (data) => {
|
||||
stdout += data.toString();
|
||||
});
|
||||
|
||||
child.stderr.on("data", (data) => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
|
||||
child.on("close", (code) => {
|
||||
const output = stdout + stderr;
|
||||
let passed = true;
|
||||
let reason = "";
|
||||
|
||||
// Check exit code expectation
|
||||
if (expectSuccess && code !== 0) {
|
||||
passed = false;
|
||||
reason = `Expected success (exit code 0) but got ${code}`;
|
||||
} else if (!expectSuccess && code === 0) {
|
||||
passed = false;
|
||||
reason = `Expected failure (non-zero exit code) but got success`;
|
||||
}
|
||||
|
||||
// Check expected output
|
||||
if (passed && expectedInOutput && !output.includes(expectedInOutput)) {
|
||||
passed = false;
|
||||
reason = `Expected output to contain "${expectedInOutput}"`;
|
||||
}
|
||||
|
||||
if (passed) {
|
||||
console.log(`${colors.GREEN}PASS: ${testName}${colors.NC}`);
|
||||
testsPassed++;
|
||||
} else {
|
||||
console.log(`${colors.RED}FAIL: ${testName}${colors.NC}`);
|
||||
console.log(`${colors.RED}Reason: ${reason}${colors.NC}`);
|
||||
console.log(`${colors.RED}Exit code: ${code}${colors.NC}`);
|
||||
console.log(`${colors.RED}Output: ${output}${colors.NC}`);
|
||||
testsFailed++;
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
|
||||
child.on("error", (error) => {
|
||||
console.log(
|
||||
`${colors.RED}ERROR: ${testName} - ${error.message}${colors.NC}`,
|
||||
);
|
||||
testsFailed++;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function runHeaderIntegrationTests() {
|
||||
console.log(
|
||||
`${colors.YELLOW}=== MCP Inspector CLI Header Integration Tests ===${colors.NC}`,
|
||||
);
|
||||
console.log(
|
||||
`${colors.BLUE}Testing header parsing and validation${colors.NC}`,
|
||||
);
|
||||
|
||||
// Test 1: Valid header format should parse successfully (connection will fail)
|
||||
await runHeaderTest(
|
||||
"Valid single header",
|
||||
[
|
||||
"https://example.com",
|
||||
"--method",
|
||||
"tools/list",
|
||||
"--transport",
|
||||
"http",
|
||||
"--header",
|
||||
"Authorization: Bearer token123",
|
||||
],
|
||||
false,
|
||||
);
|
||||
|
||||
// Test 2: Multiple headers should parse successfully
|
||||
await runHeaderTest(
|
||||
"Multiple headers",
|
||||
[
|
||||
"https://example.com",
|
||||
"--method",
|
||||
"tools/list",
|
||||
"--transport",
|
||||
"http",
|
||||
"--header",
|
||||
"Authorization: Bearer token123",
|
||||
"--header",
|
||||
"X-API-Key: secret123",
|
||||
],
|
||||
false,
|
||||
);
|
||||
|
||||
// Test 3: Invalid header format - no colon
|
||||
await runHeaderTest(
|
||||
"Invalid header format - no colon",
|
||||
[
|
||||
"https://example.com",
|
||||
"--method",
|
||||
"tools/list",
|
||||
"--transport",
|
||||
"http",
|
||||
"--header",
|
||||
"InvalidHeader",
|
||||
],
|
||||
false,
|
||||
"Invalid header format",
|
||||
);
|
||||
|
||||
// Test 4: Invalid header format - empty name
|
||||
await runHeaderTest(
|
||||
"Invalid header format - empty name",
|
||||
[
|
||||
"https://example.com",
|
||||
"--method",
|
||||
"tools/list",
|
||||
"--transport",
|
||||
"http",
|
||||
"--header",
|
||||
": value",
|
||||
],
|
||||
false,
|
||||
"Invalid header format",
|
||||
);
|
||||
|
||||
// Test 5: Invalid header format - empty value
|
||||
await runHeaderTest(
|
||||
"Invalid header format - empty value",
|
||||
[
|
||||
"https://example.com",
|
||||
"--method",
|
||||
"tools/list",
|
||||
"--transport",
|
||||
"http",
|
||||
"--header",
|
||||
"Header:",
|
||||
],
|
||||
false,
|
||||
"Invalid header format",
|
||||
);
|
||||
|
||||
// Test 6: Header with colons in value
|
||||
await runHeaderTest(
|
||||
"Header with colons in value",
|
||||
[
|
||||
"https://example.com",
|
||||
"--method",
|
||||
"tools/list",
|
||||
"--transport",
|
||||
"http",
|
||||
"--header",
|
||||
"X-Time: 2023:12:25:10:30:45",
|
||||
],
|
||||
false,
|
||||
);
|
||||
|
||||
// Test 7: Whitespace handling
|
||||
await runHeaderTest(
|
||||
"Whitespace handling in headers",
|
||||
[
|
||||
"https://example.com",
|
||||
"--method",
|
||||
"tools/list",
|
||||
"--transport",
|
||||
"http",
|
||||
"--header",
|
||||
" X-Header : value with spaces ",
|
||||
],
|
||||
false,
|
||||
);
|
||||
|
||||
console.log(`\n${colors.YELLOW}=== Test Results ===${colors.NC}`);
|
||||
console.log(`${colors.GREEN}Tests passed: ${testsPassed}${colors.NC}`);
|
||||
console.log(`${colors.RED}Tests failed: ${testsFailed}${colors.NC}`);
|
||||
|
||||
if (testsFailed === 0) {
|
||||
console.log(
|
||||
`${colors.GREEN}All header integration tests passed!${colors.NC}`,
|
||||
);
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.log(
|
||||
`${colors.RED}Some header integration tests failed.${colors.NC}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle graceful shutdown
|
||||
process.on("SIGINT", () => {
|
||||
console.log(`\n${colors.YELLOW}Test interrupted by user${colors.NC}`);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
process.on("SIGTERM", () => {
|
||||
console.log(`\n${colors.YELLOW}Test terminated${colors.NC}`);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// Run the tests
|
||||
runHeaderIntegrationTests().catch((error) => {
|
||||
console.error(`${colors.RED}Test runner error: ${error.message}${colors.NC}`);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -352,8 +352,8 @@ async function main(): Promise<void> {
|
|||
const args = parseArgs();
|
||||
await callMethod(args);
|
||||
|
||||
// Let Node.js naturally exit instead of force-exiting
|
||||
// process.exit(0) was causing stdout truncation
|
||||
// Explicitly exit to ensure process terminates in CI
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue