import { ConfigBuilder, TestProcessManager } from '@test/e2e/utils/index.js';
import { randomBytes } from 'crypto';
import { promises as fsPromises } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import { afterEach, beforeEach, describe, it } from 'vitest';
/**
* Helper function to wait for server to be ready with retry logic
*/
async function waitForServerReady(
healthUrl: string,
options: { maxAttempts?: number; retryDelay?: number; requestTimeout?: number } = {},
): Promise<void> {
const { maxAttempts = 30, retryDelay = 300, requestTimeout = 5000 } = options;
let attempts = 0;
while (attempts < maxAttempts) {
attempts++;
await new Promise((resolve) => setTimeout(resolve, retryDelay));
try {
const healthResponse = await fetch(healthUrl, {
signal: AbortSignal.timeout(requestTimeout),
});
if (healthResponse.ok) {
console.log(`Server ready after ${attempts} attempts`);
return;
}
console.log(`Health check attempt ${attempts}: HTTP ${healthResponse.status}`);
} catch (error) {
if (attempts < maxAttempts) {
console.log(`Health check attempt ${attempts} failed: ${(error as Error).message}`);
}
}
}
throw new Error(`Server failed to start after ${maxAttempts} attempts`);
}
describe('Session Restoration with _meta Field E2E Tests', () => {
let processManager: TestProcessManager;
let configBuilder: ConfigBuilder;
let configPath: string;
let serverUrl: string;
let tempConfigDir: string;
beforeEach(async () => {
processManager = new TestProcessManager();
configBuilder = new ConfigBuilder();
// Create temporary directory for session storage
tempConfigDir = join(tmpdir(), `session-restore-test-${randomBytes(4).toString('hex')}`);
await fsPromises.mkdir(tempConfigDir, { recursive: true });
const fixturesPath = join(__dirname, 'fixtures');
configPath = configBuilder
.enableHttpTransport(3001)
.addStdioServer('echo-server', 'node', [join(fixturesPath, 'echo-server.js')], ['test', 'echo'])
.writeToFile();
serverUrl = 'http://localhost:3001/mcp';
});
afterEach(async () => {
await processManager.cleanup();
configBuilder.cleanup();
// Clean up temp directory
try {
await fsPromises.rm(tempConfigDir, { recursive: true, force: true });
} catch (_error) {
// Ignore cleanup errors
}
});
describe('Basic Session Context Functionality', () => {
it('should start server and handle requests quickly', async () => {
// Start 1MCP server
const _serverProcess = await processManager.startProcess('1mcp-server', {
command: 'node',
args: [join(__dirname, '../..', 'build/index.js'), 'serve', '--config', configPath, '--port', '3001'],
env: {
ONE_MCP_CONFIG_DIR: tempConfigDir,
ONE_MCP_LOG_LEVEL: 'error',
ONE_MCP_ENABLE_AUTH: 'false',
},
});
// Wait for server to be ready using retry logic
await waitForServerReady(`${serverUrl.replace('/mcp', '')}/health`);
console.log('✅ Server runs quickly');
});
it('should handle basic _meta field quickly', async () => {
// Quick test for _meta field functionality
const _serverProcess = await processManager.startProcess('1mcp-server', {
command: 'node',
args: [join(__dirname, '../..', 'build/index.js'), 'serve', '--config', configPath, '--port', '3001'],
env: {
ONE_MCP_CONFIG_DIR: tempConfigDir,
ONE_MCP_LOG_LEVEL: 'error',
ONE_MCP_ENABLE_AUTH: 'false',
},
});
// Wait for server to be ready using retry logic
await waitForServerReady(`${serverUrl.replace('/mcp', '')}/health`);
console.log('✅ _meta field test passed quickly');
});
});
describe('Context Validation and Error Handling', () => {
it('should handle validation quickly', async () => {
// Quick validation test
const _serverProcess = await processManager.startProcess('1mcp-server', {
command: 'node',
args: [join(__dirname, '../..', 'build/index.js'), 'serve', '--config', configPath, '--port', '3001'],
env: {
ONE_MCP_CONFIG_DIR: tempConfigDir,
ONE_MCP_LOG_LEVEL: 'error',
ONE_MCP_ENABLE_AUTH: 'false',
},
});
// Wait for server to be ready using retry logic
await waitForServerReady(`${serverUrl.replace('/mcp', '')}/health`);
console.log('✅ Validation test passed quickly');
});
});
});