← back to Designer Wallcoverings
DW-Agents/__tests__/helpers/test-server.ts
233 lines
/**
* Test Server Helper
* Utilities for setting up and managing test servers
*/
import axios, { AxiosInstance } from 'axios';
export interface TestServerConfig {
baseURL: string;
port: string;
timeout?: number;
retries?: number;
}
export class TestServer {
private client: AxiosInstance;
private config: TestServerConfig;
constructor(config: TestServerConfig) {
this.config = {
timeout: 5000,
retries: 3,
...config
};
this.client = axios.create({
baseURL: `${config.baseURL}:${config.port}`,
timeout: this.config.timeout,
validateStatus: () => true // Don't throw on non-2xx
});
}
/**
* Check if server is healthy
*/
async isHealthy(): Promise<boolean> {
try {
const response = await this.client.get('/api/health');
return response.status === 200;
} catch {
return false;
}
}
/**
* Wait for server to be ready
*/
async waitUntilReady(timeout = 30000): Promise<void> {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
if (await this.isHealthy()) {
return;
}
await new Promise(resolve => setTimeout(resolve, 500));
}
throw new Error(`Server not ready after ${timeout}ms`);
}
/**
* Make a GET request
*/
async get(path: string, config?: any) {
return await this.client.get(path, config);
}
/**
* Make a POST request
*/
async post(path: string, data?: any, config?: any) {
return await this.client.post(path, data, config);
}
/**
* Make a PUT request
*/
async put(path: string, data?: any, config?: any) {
return await this.client.put(path, data, config);
}
/**
* Make a DELETE request
*/
async delete(path: string, config?: any) {
return await this.client.delete(path, config);
}
/**
* Make a request with retry logic
*/
async requestWithRetry(
method: string,
path: string,
data?: any,
retries = this.config.retries
): Promise<any> {
for (let i = 0; i < retries!; i++) {
try {
const response = await this.client.request({
method,
url: path,
data
});
if (response.status >= 200 && response.status < 300) {
return response;
}
if (i === retries! - 1) {
return response;
}
} catch (error) {
if (i === retries! - 1) {
throw error;
}
}
// Wait before retry
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
/**
* Get server metrics
*/
async getMetrics(): Promise<{
uptime: number;
responseTime: number;
requestCount: number;
}> {
const start = Date.now();
const response = await this.client.get('/api/health');
const responseTime = Date.now() - start;
return {
uptime: response.data.uptime || 0,
responseTime,
requestCount: 1
};
}
/**
* Measure response time
*/
async measureResponseTime(path: string, iterations = 10): Promise<{
min: number;
max: number;
avg: number;
p95: number;
}> {
const times: number[] = [];
for (let i = 0; i < iterations; i++) {
const start = Date.now();
await this.client.get(path);
times.push(Date.now() - start);
}
times.sort((a, b) => a - b);
return {
min: times[0],
max: times[times.length - 1],
avg: times.reduce((a, b) => a + b, 0) / times.length,
p95: times[Math.floor(times.length * 0.95)]
};
}
/**
* Test server under load
*/
async loadTest(config: {
path: string;
concurrent: number;
duration: number;
}): Promise<{
totalRequests: number;
successful: number;
failed: number;
avgResponseTime: number;
}> {
const { path, concurrent, duration } = config;
const startTime = Date.now();
const results: { success: boolean; time: number }[] = [];
const workers = Array(concurrent).fill(null).map(async () => {
while (Date.now() - startTime < duration) {
const reqStart = Date.now();
try {
const response = await this.client.get(path);
results.push({
success: response.status === 200,
time: Date.now() - reqStart
});
} catch {
results.push({
success: false,
time: Date.now() - reqStart
});
}
await new Promise(resolve => setTimeout(resolve, 100));
}
});
await Promise.all(workers);
const successful = results.filter(r => r.success).length;
const failed = results.length - successful;
const avgResponseTime = results.reduce((sum, r) => sum + r.time, 0) / results.length;
return {
totalRequests: results.length,
successful,
failed,
avgResponseTime
};
}
}
/**
* Create a test server instance
*/
export function createTestServer(config?: Partial<TestServerConfig>): TestServer {
const defaultConfig: TestServerConfig = {
baseURL: process.env.BASE_URL || 'http://localhost',
port: process.env.MASTER_HUB_PORT || '9800',
...config
};
return new TestServer(defaultConfig);
}