113 lines
3.0 KiB
TypeScript
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`);
|
|
}
|
|
} |