mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-15 18:21:09 +00:00
fix(tests): address edge cases in mock infrastructure
- Prevent spy leak on double mockFetch() call - Differentiate body types (URLSearchParams, ArrayBuffer, Blob, ReadableStream) - Fix route path parsing to preserve spaces - Escape regex special chars before wildcard replacement - Remove unused MockResponse.error and MockRoute interface
This commit is contained in:
@@ -27,7 +27,13 @@ let capturedRequests: CapturedRequest[] = [];
|
||||
* afterEach(() => restoreFetch());
|
||||
*/
|
||||
export function mockFetch(handlers: MockFetchHandler[]): void {
|
||||
// Store original if not already mocked
|
||||
// Restore previous mock if exists (prevents spy leak on double-call)
|
||||
if (mockInstance) {
|
||||
mockInstance.mockRestore();
|
||||
mockInstance = null;
|
||||
}
|
||||
|
||||
// Store original if not already stored
|
||||
if (!originalFetch) {
|
||||
originalFetch = globalThis.fetch;
|
||||
}
|
||||
@@ -64,12 +70,20 @@ export function mockFetch(handlers: MockFetchHandler[]): void {
|
||||
}
|
||||
}
|
||||
|
||||
// Extract body
|
||||
// Extract body with type differentiation
|
||||
if (init?.body) {
|
||||
if (typeof init.body === 'string') {
|
||||
captured.body = init.body;
|
||||
} else if (init.body instanceof FormData) {
|
||||
captured.body = '[FormData]';
|
||||
} else if (init.body instanceof URLSearchParams) {
|
||||
captured.body = '[URLSearchParams]';
|
||||
} else if (init.body instanceof ArrayBuffer || ArrayBuffer.isView(init.body)) {
|
||||
captured.body = '[ArrayBuffer]';
|
||||
} else if (init.body instanceof Blob) {
|
||||
captured.body = '[Blob]';
|
||||
} else if (typeof init.body === 'object' && 'getReader' in init.body) {
|
||||
captured.body = '[ReadableStream]';
|
||||
} else {
|
||||
captured.body = '[Binary]';
|
||||
}
|
||||
|
||||
@@ -55,12 +55,18 @@ export function createMockHttpServer(config: MockHttpServerConfig): MockHttpServ
|
||||
|
||||
// Try pattern matching for routes with wildcards
|
||||
for (const [key, response] of Object.entries(routes)) {
|
||||
const [routeMethod, routePath] = key.split(' ', 2);
|
||||
// Split on first space only to preserve spaces in path
|
||||
const spaceIndex = key.indexOf(' ');
|
||||
if (spaceIndex === -1) continue;
|
||||
const routeMethod = key.slice(0, spaceIndex);
|
||||
const routePath = key.slice(spaceIndex + 1);
|
||||
if (routeMethod !== method) continue;
|
||||
|
||||
// Check if route path is a pattern (contains *)
|
||||
if (routePath.includes('*')) {
|
||||
const pattern = routePath.replace(/\*/g, '.*');
|
||||
// Escape regex special chars, then replace * with .*
|
||||
const escaped = routePath.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
|
||||
const pattern = escaped.replace(/\*/g, '.*');
|
||||
const regex = new RegExp(`^${pattern}$`);
|
||||
if (regex.test(path)) {
|
||||
return response;
|
||||
|
||||
@@ -21,23 +21,11 @@ export interface MockResponse {
|
||||
headers?: Record<string, string>;
|
||||
/** Delay in ms before responding (for timeout testing) */
|
||||
delay?: number;
|
||||
/** Throw error instead of responding */
|
||||
error?: Error;
|
||||
}
|
||||
|
||||
/** Route key format: "METHOD /path" */
|
||||
export type RouteKey = `${HttpMethod} ${string}`;
|
||||
|
||||
/** Mock route configuration */
|
||||
export interface MockRoute {
|
||||
/** HTTP method */
|
||||
method: HttpMethod;
|
||||
/** URL path pattern (string or regex) */
|
||||
path: string | RegExp;
|
||||
/** Response to return */
|
||||
response: MockResponse;
|
||||
}
|
||||
|
||||
/** Configuration for createMockHttpServer */
|
||||
export interface MockHttpServerConfig {
|
||||
/** Route definitions - key format: "METHOD /path" */
|
||||
|
||||
Reference in New Issue
Block a user