move to vitest (#18)
* feat: move to vitest * fix: lock file * fix: tests running sequentially on same port * fix lock file --------- Co-authored-by: Paul Carleton <paulcarletonjr@gmail.com>
This commit is contained in:
parent
eed3b222a1
commit
aeceefa9cf
8 changed files with 2069 additions and 4025 deletions
|
|
@ -1,8 +0,0 @@
|
||||||
export default {
|
|
||||||
preset: 'ts-jest/presets/default-esm',
|
|
||||||
extensionsToTreatAsEsm: ['.ts'],
|
|
||||||
testEnvironment: 'node',
|
|
||||||
moduleNameMapper: {
|
|
||||||
'^(\\.{1,2}/.*)\\.js$': '$1'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
5067
package-lock.json
generated
5067
package-lock.json
generated
File diff suppressed because it is too large
Load diff
11
package.json
11
package.json
|
|
@ -12,7 +12,8 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "tsx src/index.ts",
|
"start": "tsx src/index.ts",
|
||||||
"test": "jest",
|
"test": "vitest run",
|
||||||
|
"test:watch": "vitest",
|
||||||
"build": "tsdown src/index.ts --minify --clean --target node20",
|
"build": "tsdown src/index.ts --minify --clean --target node20",
|
||||||
"lint": "eslint src/ examples/ && prettier --check .",
|
"lint": "eslint src/ examples/ && prettier --check .",
|
||||||
"lint:fix": "eslint src/ examples/ --fix && prettier --write .",
|
"lint:fix": "eslint src/ examples/ --fix && prettier --write .",
|
||||||
|
|
@ -29,19 +30,17 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.8.0",
|
"@eslint/js": "^9.8.0",
|
||||||
"@jest/globals": "^29.7.0",
|
|
||||||
"@types/express": "^5.0.3",
|
"@types/express": "^5.0.3",
|
||||||
"@types/jest": "^29.5.8",
|
"@types/node": "^22.10.2",
|
||||||
"@typescript/native-preview": "^7.0.0-dev.20251111.1",
|
"@typescript/native-preview": "^7.0.0-dev.20251111.1",
|
||||||
"eslint": "^9.8.0",
|
"eslint": "^9.8.0",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"jest": "^29.7.0",
|
|
||||||
"prettier": "3.6.2",
|
"prettier": "3.6.2",
|
||||||
"ts-jest": "^29.1.1",
|
|
||||||
"tsdown": "^0.16.2",
|
"tsdown": "^0.16.2",
|
||||||
"tsx": "^4.7.0",
|
"tsx": "^4.7.0",
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.5.4",
|
||||||
"typescript-eslint": "^8.0.0"
|
"typescript-eslint": "^8.0.0",
|
||||||
|
"vitest": "^2.1.8"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "^1.20.1",
|
"@modelcontextprotocol/sdk": "^1.20.1",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { describe, test } from '@jest/globals';
|
|
||||||
import {
|
import {
|
||||||
runClientAgainstScenario,
|
runClientAgainstScenario,
|
||||||
SpawnedClientRunner
|
SpawnedClientRunner
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { describe, test } from '@jest/globals';
|
|
||||||
import {
|
import {
|
||||||
runClientAgainstScenario,
|
runClientAgainstScenario,
|
||||||
SpawnedClientRunner
|
SpawnedClientRunner
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@ import { getClientScenario, listActiveClientScenarios } from '../index.js';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
describe('Server Scenarios', () => {
|
describe('Server Scenarios', () => {
|
||||||
let serverProcess: ChildProcess;
|
let serverProcess: ChildProcess | null = null;
|
||||||
const TEST_PORT = 3001;
|
const TEST_PORT = 3001;
|
||||||
const SERVER_URL = `http://localhost:${TEST_PORT}/mcp`;
|
const SERVER_URL = `http://localhost:${TEST_PORT}/mcp`;
|
||||||
const SERVER_STARTUP_TIMEOUT = 10000; // 10 seconds to start
|
const SERVER_STARTUP_TIMEOUT = 30000; // 30 seconds for CI
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
// Start the everything-server once for all scenarios
|
// Start the everything-server once for all scenarios in this file
|
||||||
const serverPath = path.join(
|
const serverPath = path.join(
|
||||||
process.cwd(),
|
process.cwd(),
|
||||||
'examples/servers/typescript/everything-server.ts'
|
'examples/servers/typescript/everything-server.ts'
|
||||||
|
|
@ -17,61 +17,85 @@ describe('Server Scenarios', () => {
|
||||||
|
|
||||||
serverProcess = spawn('npx', ['tsx', serverPath], {
|
serverProcess = spawn('npx', ['tsx', serverPath], {
|
||||||
env: { ...process.env, PORT: TEST_PORT.toString() },
|
env: { ...process.env, PORT: TEST_PORT.toString() },
|
||||||
stdio: ['ignore', 'pipe', 'pipe']
|
stdio: ['ignore', 'pipe', 'pipe'],
|
||||||
|
detached: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// Capture output for debugging
|
||||||
|
let stdoutData = '';
|
||||||
|
let stderrData = '';
|
||||||
|
|
||||||
|
serverProcess.stdout?.on('data', (data) => {
|
||||||
|
stdoutData += data.toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
serverProcess.stderr?.on('data', (data) => {
|
||||||
|
stderrData += data.toString();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait for server to be ready
|
// Wait for server to be ready
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
|
if (serverProcess) {
|
||||||
|
serverProcess.kill('SIGKILL');
|
||||||
|
}
|
||||||
reject(
|
reject(
|
||||||
new Error(`Server failed to start within ${SERVER_STARTUP_TIMEOUT}ms`)
|
new Error(`Server failed to start within ${SERVER_STARTUP_TIMEOUT}ms`)
|
||||||
);
|
);
|
||||||
}, SERVER_STARTUP_TIMEOUT);
|
}, SERVER_STARTUP_TIMEOUT);
|
||||||
|
|
||||||
serverProcess.stdout?.on('data', (data) => {
|
let resolved = false;
|
||||||
|
|
||||||
|
serverProcess!.stdout?.on('data', (data) => {
|
||||||
const output = data.toString();
|
const output = data.toString();
|
||||||
if (output.includes('running on')) {
|
if (output.includes('running on')) {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
|
resolved = true;
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
serverProcess.on('error', (error) => {
|
serverProcess!.on('error', (error) => {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
reject(new Error(`Failed to start server: ${error.message}`));
|
reject(new Error(`Failed to start server: ${error.message}`));
|
||||||
});
|
});
|
||||||
|
|
||||||
serverProcess.on('exit', (code) => {
|
serverProcess!.on('exit', (code) => {
|
||||||
if (code !== null && code !== 0) {
|
// Only reject if server exits unexpectedly during startup
|
||||||
|
if (!resolved && code !== null && code !== 0) {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
reject(new Error(`Server exited prematurely with code ${code}`));
|
reject(
|
||||||
|
new Error(
|
||||||
|
`Server exited prematurely with code ${code}. STDOUT: ${stdoutData}, STDERR: ${stderrData}`
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, SERVER_STARTUP_TIMEOUT + 5000);
|
}, SERVER_STARTUP_TIMEOUT + 5000);
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
// Stop the server
|
// Stop the server and clean up
|
||||||
if (serverProcess) {
|
if (serverProcess && !serverProcess.killed) {
|
||||||
|
// Try graceful shutdown first
|
||||||
serverProcess.kill('SIGTERM');
|
serverProcess.kill('SIGTERM');
|
||||||
|
|
||||||
// Wait for graceful shutdown
|
// Wait for graceful shutdown with timeout
|
||||||
await new Promise<void>((resolve) => {
|
await new Promise<void>((resolve) => {
|
||||||
const timeoutHandle = setTimeout(() => {
|
const killTimeout = setTimeout(() => {
|
||||||
if (!serverProcess.killed) {
|
if (serverProcess && !serverProcess.killed) {
|
||||||
serverProcess.kill('SIGKILL');
|
serverProcess.kill('SIGKILL');
|
||||||
}
|
}
|
||||||
resolve();
|
resolve();
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
const cleanUp = () => {
|
serverProcess!.once('exit', () => {
|
||||||
clearTimeout(timeoutHandle);
|
clearTimeout(killTimeout);
|
||||||
serverProcess.removeListener('exit', cleanUp);
|
|
||||||
resolve();
|
resolve();
|
||||||
};
|
});
|
||||||
|
|
||||||
serverProcess.on('exit', cleanUp);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
serverProcess = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"forceConsistentCasingInFileNames": true
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"types": ["node", "vitest/globals"]
|
||||||
},
|
},
|
||||||
"include": ["src/**/*", "examples/clients/typescript/**/*"],
|
"include": ["src/**/*", "examples/clients/typescript/**/*"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
|
|
|
||||||
15
vitest.config.ts
Normal file
15
vitest.config.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
globals: true,
|
||||||
|
environment: 'node',
|
||||||
|
include: ['**/*.test.ts'],
|
||||||
|
exclude: ['node_modules', 'dist'],
|
||||||
|
// Run test files sequentially to avoid port conflicts
|
||||||
|
fileParallelism: false,
|
||||||
|
// Increase timeout for server tests in CI
|
||||||
|
testTimeout: 15000,
|
||||||
|
hookTimeout: 30000
|
||||||
|
}
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue