Files
gridpilot.gg/tests/integration/harness/api-client.ts
2026-01-08 16:52:37 +01:00

113 lines
3.0 KiB
TypeScript

/**
* API Client for Integration Tests
* Provides typed HTTP client for testing API endpoints
*/
export interface ApiClientConfig {
baseUrl: string;
timeout?: number;
}
export class ApiClient {
private baseUrl: string;
private timeout: number;
constructor(config: ApiClientConfig) {
this.baseUrl = config.baseUrl.replace(/\/$/, ''); // Remove trailing slash
this.timeout = config.timeout || 30000;
}
/**
* Make HTTP request to API
*/
private async request<T>(method: string, path: string, body?: any, headers: Record<string, string> = {}): Promise<T> {
const url = `${this.baseUrl}${path}`;
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
try {
const response = await fetch(url, {
method,
headers: {
'Content-Type': 'application/json',
...headers,
},
body: body ? JSON.stringify(body) : undefined,
signal: controller.signal,
});
clearTimeout(timeoutId);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API Error ${response.status}: ${errorText}`);
}
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
return (await response.json()) as T;
}
return (await response.text()) as unknown as T;
} catch (error) {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
throw new Error(`Request timeout after ${this.timeout}ms`);
}
throw error;
}
}
// GET requests
async get<T>(path: string, headers?: Record<string, string>): Promise<T> {
return this.request<T>('GET', path, undefined, headers);
}
// POST requests
async post<T>(path: string, body: any, headers?: Record<string, string>): Promise<T> {
return this.request<T>('POST', path, body, headers);
}
// PUT requests
async put<T>(path: string, body: any, headers?: Record<string, string>): Promise<T> {
return this.request<T>('PUT', path, body, headers);
}
// PATCH requests
async patch<T>(path: string, body: any, headers?: Record<string, string>): Promise<T> {
return this.request<T>('PATCH', path, body, headers);
}
// DELETE requests
async delete<T>(path: string, headers?: Record<string, string>): Promise<T> {
return this.request<T>('DELETE', path, undefined, headers);
}
/**
* Health check
*/
async health(): Promise<boolean> {
try {
const response = await fetch(`${this.baseUrl}/health`);
return response.ok;
} catch {
return false;
}
}
/**
* Wait for API to be ready
*/
async waitForReady(timeout: number = 60000): Promise<void> {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
if (await this.health()) {
return;
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
throw new Error(`API failed to become ready within ${timeout}ms`);
}
}