Skip to content

Commit

Permalink
Merge pull request #544 from zimicjs/canary
Browse files Browse the repository at this point in the history
  • Loading branch information
diego-aquino authored Jan 30, 2025
2 parents 2f4f6b4 + 45f79f3 commit 889ee21
Show file tree
Hide file tree
Showing 17 changed files with 232 additions and 79 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "zimic-root",
"description": "TypeScript-first, statically inferred HTTP mocks",
"version": "0.12.4",
"version": "0.12.5-canary.2",
"repository": {
"type": "git",
"url": "https://github.com/zimicjs/zimic.git"
Expand Down
3 changes: 2 additions & 1 deletion packages/zimic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"mock",
"static"
],
"version": "0.12.4",
"version": "0.12.5-canary.2",
"repository": {
"type": "git",
"url": "https://github.com/zimicjs/zimic.git",
Expand Down Expand Up @@ -98,6 +98,7 @@
"typegen:fixtures": "tsx ./scripts/dev/typegen/generateFixtureTypes.js",
"deps:install-playwright": "playwright install chromium",
"deps:init-msw": "msw init ./public --no-save",
"deps:init": "pnpm deps:install-playwright && pnpm deps:init-msw",
"postinstall": "node -e \"try{require('./dist/scripts/postinstall')}catch(error){console.error(error)}\"",
"prepublish:patch-relative-paths": "sed -E -i 's/\\]\\(\\.\\/([^\\)]+)\\)/](..\\/..\\/\\1)/g;s/\"\\.\\/([^\"]+)\"/\"..\\/..\\/\\1\"/g'",
"prepublishOnly": "cp ../../README.md ../../LICENSE.md . && pnpm prepublish:patch-relative-paths README.md"
Expand Down
4 changes: 3 additions & 1 deletion packages/zimic/scripts/dev/typegen/generateFixtureTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ async function generateFixtureCasesTypes({ fixtureType, fixtureNames }: FixtureT
batchFixtureCases.map((fixtureCase) => generateFixtureCaseTypes(fixtureType, fixtureCase)),
);

outputFilePaths.push(...newOutputFilePaths);
for (const outputFilePath of newOutputFilePaths) {
outputFilePaths.push(outputFilePath);
}
}

await lintGeneratedFiles(outputFilePaths);
Expand Down
70 changes: 70 additions & 0 deletions packages/zimic/src/cli/server/__tests__/server.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -811,5 +811,75 @@ describe('CLI (server)', async () => {
}
});
});

it('should abort waiting for a worker reply if its internal web socket client was closed before responding', async () => {
processArgvSpy.mockReturnValue([
'node',
'./dist/cli.js',
'server',
'start',
'--port',
'5001',
'--log-unhandled-requests',
]);

const interceptor = createHttpInterceptor<{
'/users': {
GET: { response: { 204: {} } };
};
}>({
type: 'remote',
baseURL: 'http://localhost:5001',
});

await usingIgnoredConsole(['error', 'log'], async (spies) => {
await runCLI();

expect(server).toBeDefined();
expect(server!.isRunning()).toBe(true);
expect(server!.hostname()).toBe('localhost');
expect(server!.port()).toBe(5001);

try {
await interceptor.start();
expect(interceptor.isRunning()).toBe(true);

let wasResponseFactoryCalled = false;

const responseFactory = vi.fn(async () => {
wasResponseFactoryCalled = true;

await waitForDelay(5000);

/* istanbul ignore next -- @preserve
* This code is unreachable because the request is aborted before the response is sent. */
return { status: 204 } as const;
});

await interceptor.get('/users').respond(responseFactory);

const onFetchError = vi.fn<(error: unknown) => void>();
const responsePromise = fetch('http://localhost:5001/users', { method: 'GET' }).catch(onFetchError);

await waitFor(() => {
expect(wasResponseFactoryCalled).toBe(true);
});

// @ts-expect-error Force the internal web socket client to stop.
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
await interceptor._client.worker._webSocketClient.stop();

await responsePromise;

await waitFor(() => {
expect(onFetchError).toHaveBeenCalled();
});

expect(spies.error).not.toHaveBeenCalled();
} finally {
await interceptor.stop();
}
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,12 @@ class HttpInterceptorClient<
const clearResults: PossiblePromise<AnyHttpRequestHandlerClient | void>[] = [];

for (const method of HTTP_METHODS) {
clearResults.push(...this.clearMethodHandlers(method));
const newClearResults = this.clearMethodHandlers(method);

for (const result of newClearResults) {
clearResults.push(result);
}

const handlersByPath = this.handlerClientsByMethod[method];
handlersByPath.clear();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export async function declareTimesHttpInterceptorTests(options: RuntimeSharedHtt
firstLine: `Expected exactly ${
numberOfRequestsIncludingPreflight
} request${numberOfRequestsIncludingPreflight === 1 ? '' : 's'}, but got 0.`,
numberOfRequests: numberOfRequestsIncludingPreflight,
},
);

Expand Down Expand Up @@ -139,7 +140,10 @@ export async function declareTimesHttpInterceptorTests(options: RuntimeSharedHtt
async () => {
await promiseIfRemote(interceptor.checkTimes(), interceptor);
},
{ firstLine: `Expected exactly ${numberOfRequestsIncludingPreflight * 2} requests, but got 0.` },
{
firstLine: `Expected exactly ${numberOfRequestsIncludingPreflight * 2} requests, but got 0.`,
numberOfRequests: numberOfRequestsIncludingPreflight * 2,
},
);

let response = await fetch(joinURL(baseURL, '/users'), { method });
Expand All @@ -156,6 +160,7 @@ export async function declareTimesHttpInterceptorTests(options: RuntimeSharedHtt
firstLine: `Expected exactly ${
numberOfRequestsIncludingPreflight * 2
} requests, but got ${numberOfRequestsIncludingPreflight}.`,
numberOfRequests: numberOfRequestsIncludingPreflight * 2,
},
);

Expand Down Expand Up @@ -183,6 +188,7 @@ export async function declareTimesHttpInterceptorTests(options: RuntimeSharedHtt
firstLine: `Expected exactly ${
numberOfRequestsIncludingPreflight * 4
} requests, but got ${numberOfRequestsIncludingPreflight * 3}.`,
numberOfRequests: numberOfRequestsIncludingPreflight * 4,
},
);
});
Expand Down Expand Up @@ -219,6 +225,7 @@ export async function declareTimesHttpInterceptorTests(options: RuntimeSharedHtt
firstLine: `Expected exactly ${
numberOfRequestsIncludingPreflight
} request${numberOfRequestsIncludingPreflight === 1 ? '' : 's'}, but got 0.`,
numberOfRequests: numberOfRequestsIncludingPreflight,
},
);

Expand Down Expand Up @@ -254,6 +261,7 @@ export async function declareTimesHttpInterceptorTests(options: RuntimeSharedHtt
} request${numberOfRequestsIncludingPreflight === 1 ? '' : 's'}, but got ${
numberOfRequestsIncludingPreflight * 2
}.`,
numberOfRequests: numberOfRequestsIncludingPreflight,
},
);

Expand All @@ -278,6 +286,7 @@ export async function declareTimesHttpInterceptorTests(options: RuntimeSharedHtt
} request${numberOfRequestsIncludingPreflight === 1 ? '' : 's'}, but got ${
numberOfRequestsIncludingPreflight * 3
}.`,
numberOfRequests: numberOfRequestsIncludingPreflight,
},
);
});
Expand Down Expand Up @@ -367,6 +376,8 @@ export async function declareTimesHttpInterceptorTests(options: RuntimeSharedHtt
firstLine: `Expected at least ${
numberOfRequestsIncludingPreflight * 2
} and at most ${numberOfRequestsIncludingPreflight * 3} requests, but got 0.`,
minNumberOfRequests: numberOfRequestsIncludingPreflight * 2,
maxNumberOfRequests: numberOfRequestsIncludingPreflight * 3,
},
);

Expand All @@ -386,6 +397,8 @@ export async function declareTimesHttpInterceptorTests(options: RuntimeSharedHtt
} and at most ${numberOfRequestsIncludingPreflight * 3} requests, but got ${
numberOfRequestsIncludingPreflight
}.`,
minNumberOfRequests: numberOfRequestsIncludingPreflight * 2,
maxNumberOfRequests: numberOfRequestsIncludingPreflight * 3,
},
);

Expand Down Expand Up @@ -493,6 +506,8 @@ export async function declareTimesHttpInterceptorTests(options: RuntimeSharedHtt
} and at most ${numberOfRequestsIncludingPreflight * 3} requests, but got ${
numberOfRequestsIncludingPreflight * 4
}.`,
minNumberOfRequests: numberOfRequestsIncludingPreflight * 2,
maxNumberOfRequests: numberOfRequestsIncludingPreflight * 3,
},
);
});
Expand Down Expand Up @@ -526,6 +541,8 @@ export async function declareTimesHttpInterceptorTests(options: RuntimeSharedHtt
firstLine: `Expected exactly ${numberOfRequestsIncludingPreflight} request${
numberOfRequestsIncludingPreflight === 1 ? '' : 's'
}, but got 0.`,
minNumberOfRequests: numberOfRequestsIncludingPreflight,
maxNumberOfRequests: numberOfRequestsIncludingPreflight,
},
);

Expand Down Expand Up @@ -559,6 +576,8 @@ export async function declareTimesHttpInterceptorTests(options: RuntimeSharedHtt
firstLine: `Expected exactly ${numberOfRequestsIncludingPreflight} request${
numberOfRequestsIncludingPreflight === 1 ? '' : 's'
}, but got ${numberOfRequestsIncludingPreflight * 2}.`,
minNumberOfRequests: numberOfRequestsIncludingPreflight,
maxNumberOfRequests: numberOfRequestsIncludingPreflight,
},
);
});
Expand Down Expand Up @@ -594,7 +613,7 @@ export async function declareTimesHttpInterceptorTests(options: RuntimeSharedHtt
async () => {
await promiseIfRemote(interceptor.checkTimes(), interceptor);
},
{ firstLine: 'Expected exactly 1 request, but got 0.' },
{ firstLine: 'Expected exactly 1 request, but got 0.', numberOfRequests: 1 },
);

const responsePromise = fetch(joinURL(baseURL, '/users'), { method });
Expand All @@ -612,7 +631,7 @@ export async function declareTimesHttpInterceptorTests(options: RuntimeSharedHtt
async () => {
await promiseIfRemote(interceptor.checkTimes(), interceptor);
},
{ firstLine: 'Expected exactly 1 request, but got 0.' },
{ firstLine: 'Expected exactly 1 request, but got 0.', numberOfRequests: 1 },
);
});
});
Expand All @@ -639,7 +658,7 @@ export async function declareTimesHttpInterceptorTests(options: RuntimeSharedHtt
async () => {
await promiseIfRemote(interceptor.checkTimes(), interceptor);
},
{ firstLine: 'Expected exactly 1 request, but got 0.' },
{ firstLine: 'Expected exactly 1 request, but got 0.', numberOfRequests: 1 },
);

const responsePromise = fetch(joinURL(baseURL, '/users'), { method });
Expand All @@ -657,7 +676,7 @@ export async function declareTimesHttpInterceptorTests(options: RuntimeSharedHtt
async () => {
await promiseIfRemote(interceptor.checkTimes(), interceptor);
},
{ firstLine: 'Expected exactly 1 request, but got 0.' },
{ firstLine: 'Expected exactly 1 request, but got 0.', numberOfRequests: 1 },
);
});
});
Expand Down Expand Up @@ -690,7 +709,7 @@ export async function declareTimesHttpInterceptorTests(options: RuntimeSharedHtt
async () => {
await promiseIfRemote(interceptor.checkTimes(), interceptor);
},
{ firstLine: 'Expected exactly 1 request, but got 0.' },
{ firstLine: 'Expected exactly 1 request, but got 0.', numberOfRequests: 1 },
);

const responsePromise = fetch(joinURL(baseURL, '/users/other'), { method });
Expand All @@ -708,7 +727,7 @@ export async function declareTimesHttpInterceptorTests(options: RuntimeSharedHtt
async () => {
await promiseIfRemote(interceptor.checkTimes(), interceptor);
},
{ firstLine: 'Expected exactly 1 request, but got 0.' },
{ firstLine: 'Expected exactly 1 request, but got 0.', numberOfRequests: 1 },
);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { HttpSchema, HttpSchemaMethod, HttpSchemaPath, HttpStatusCode } from '@/
import { Default } from '@/types/utils';
import { blobContains, blobEquals } from '@/utils/data';
import { jsonContains, jsonEquals } from '@/utils/json';
import { getCurrentStack } from '@/utils/runtime';

import HttpInterceptorClient from '../interceptor/HttpInterceptorClient';
import DisabledRequestSavingError from './errors/DisabledRequestSavingError';
import NoResponseDefinitionError from './errors/NoResponseDefinitionError';
import TimesCheckError from './errors/TimesCheckError';
import TimesDeclarationError from './errors/TimesDeclarationError';
import {
HttpRequestHandlerRestriction,
HttpRequestHandlerStaticRestriction,
Expand Down Expand Up @@ -38,7 +38,7 @@ class HttpRequestHandlerClient<
numberOfRequests: { min: 0, max: Infinity },
};

private timesStack?: string;
private timesDeclarationError?: TimesDeclarationError;

private numberOfMatchedRequests = 0;
private interceptedRequests: TrackedHttpInterceptorRequest<Path, Default<Schema[Path][Method]>, StatusCode>[] = [];
Expand Down Expand Up @@ -98,14 +98,14 @@ class HttpRequestHandlerClient<

times(
minNumberOfRequests: number,
maxNumberOfRequests = minNumberOfRequests,
maxNumberOfRequests?: number,
): HttpRequestHandlerClient<Schema, Method, Path, StatusCode> {
this.limits.numberOfRequests = {
min: minNumberOfRequests,
max: maxNumberOfRequests,
max: maxNumberOfRequests ?? minNumberOfRequests,
};

this.timesStack = getCurrentStack();
this.timesDeclarationError = new TimesDeclarationError(minNumberOfRequests, maxNumberOfRequests);

return this;
}
Expand All @@ -119,7 +119,7 @@ class HttpRequestHandlerClient<
throw new TimesCheckError({
limits: this.limits.numberOfRequests,
numberOfRequests: this.numberOfMatchedRequests,
timesStack: this.timesStack,
declarationError: this.timesDeclarationError,
});
}
}
Expand All @@ -131,7 +131,7 @@ class HttpRequestHandlerClient<
numberOfRequests: { min: 0, max: Infinity },
};

this.timesStack = undefined;
this.timesDeclarationError = undefined;

this.numberOfMatchedRequests = 0;
this.interceptedRequests = [];
Expand Down
Loading

0 comments on commit 889ee21

Please sign in to comment.