Skip to content

Commit

Permalink
feat(#zimic)!: improve unhandled request API (#467) (#469)
Browse files Browse the repository at this point in the history
Closes #467.
  • Loading branch information
diego-aquino authored Nov 10, 2024
1 parent 56a7849 commit e53cb89
Show file tree
Hide file tree
Showing 45 changed files with 508 additions and 325 deletions.
1 change: 0 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ jobs:

strategy:
fail-fast: false
max-parallel: 3
matrix:
typescript-version:
- '4.8'
Expand Down
12 changes: 9 additions & 3 deletions apps/zimic-test-client/tests/exports/exports.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,19 @@ describe('Exports', () => {
expect(typeof httpInterceptor.create).toBe('function');

expectTypeOf(httpInterceptor.default).toEqualTypeOf<Readonly<HttpInterceptorNamespace['default']>>();
expect(typeof httpInterceptor.default.onUnhandledRequest).toBe('function');
expect(typeof httpInterceptor.default.local.onUnhandledRequest).toBe('object');
expect(typeof httpInterceptor.default.remote.onUnhandledRequest).toBe('object');

expectTypeOf<UnhandledRequestStrategy>().not.toBeAny();
expectTypeOf<UnhandledRequestStrategy.Action>().not.toBeAny();
expectTypeOf<UnhandledRequestStrategy.Declaration>().not.toBeAny();
expectTypeOf<UnhandledRequestStrategy.Handler>().not.toBeAny();
expectTypeOf<UnhandledRequestStrategy.HandlerContext>().not.toBeAny();
expectTypeOf<UnhandledRequestStrategy.DeclarationFactory>().not.toBeAny();
expectTypeOf<UnhandledRequestStrategy.Local>().not.toBeAny();
expectTypeOf<UnhandledRequestStrategy.LocalDeclaration>().not.toBeAny();
expectTypeOf<UnhandledRequestStrategy.LocalDeclarationFactory>().not.toBeAny();
expectTypeOf<UnhandledRequestStrategy.Remote>().not.toBeAny();
expectTypeOf<UnhandledRequestStrategy.RemoteDeclaration>().not.toBeAny();
expectTypeOf<UnhandledRequestStrategy.RemoteDeclarationFactory>().not.toBeAny();

expectTypeOf<HttpInterceptor<never>>().not.toBeAny();
expectTypeOf<LocalHttpInterceptor<never>>().not.toBeAny();
Expand Down
11 changes: 6 additions & 5 deletions examples/with-jest-node/tests/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { httpInterceptor } from 'zimic/interceptor/http';

import githubInterceptor from './interceptors/github';

httpInterceptor.default.onUnhandledRequest(async (request, context) => {
httpInterceptor.default.local.onUnhandledRequest = (request) => {
const url = new URL(request.url);

if (url.hostname !== '127.0.0.1') {
await context.log();
}
});
return {
action: 'bypass',
logWarning: url.hostname !== '127.0.0.1',
};
};

beforeAll(async () => {
await githubInterceptor.start();
Expand Down
14 changes: 6 additions & 8 deletions examples/with-next-js-pages/tests/interceptors/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@ import { httpInterceptor } from 'zimic/interceptor/http';

import githubInterceptor, { githubFixtures } from './github';

httpInterceptor.default.onUnhandledRequest(async (request, context) => {
httpInterceptor.default.local.onUnhandledRequest = (request) => {
const url = new URL(request.url);

// Ignore requests to the same host
if (url.host === window.location.host) {
return;
}

await context.log();
});
return {
action: 'bypass',
logWarning: url.host !== window.location.host,
};
};

export async function loadInterceptors() {
await githubInterceptor.start();
Expand Down
11 changes: 6 additions & 5 deletions examples/with-openapi-typegen/tests/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { httpInterceptor } from 'zimic/interceptor/http';

import githubInterceptor from './interceptors/github';

httpInterceptor.default.onUnhandledRequest(async (request, context) => {
httpInterceptor.default.local.onUnhandledRequest = (request) => {
const url = new URL(request.url);

if (url.hostname !== '127.0.0.1') {
await context.log();
}
});
return {
action: 'bypass',
logWarning: url.hostname !== '127.0.0.1',
};
};

beforeAll(async () => {
await githubInterceptor.start();
Expand Down
11 changes: 6 additions & 5 deletions examples/with-vitest-node/tests/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { httpInterceptor } from 'zimic/interceptor/http';

import githubInterceptor from './interceptors/github';

httpInterceptor.default.onUnhandledRequest(async (request, context) => {
httpInterceptor.default.local.onUnhandledRequest = (request) => {
const url = new URL(request.url);

if (url.hostname !== '127.0.0.1') {
await context.log();
}
});
return {
action: 'bypass',
logWarning: url.hostname !== '127.0.0.1',
};
};

beforeAll(async () => {
await githubInterceptor.start();
Expand Down
4 changes: 1 addition & 3 deletions packages/zimic/src/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,7 @@ async function runCLI() {
hostname: cliArguments.hostname,
port: cliArguments.port,
ephemeral: cliArguments.ephemeral,
onUnhandledRequest: {
log: cliArguments.logUnhandledRequests,
},
logUnhandledRequests: cliArguments.logUnhandledRequests,
onReady: onReadyCommand
? {
command: onReadyCommand.toString(),
Expand Down
41 changes: 11 additions & 30 deletions packages/zimic/src/cli/server/__tests__/server.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import filesystem from 'fs/promises';
import path from 'path';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import { httpInterceptor } from '@/interceptor/http';
import { verifyUnhandledRequestMessage } from '@/interceptor/http/interceptor/__tests__/shared/utils';
import { createHttpInterceptor } from '@/interceptor/http/interceptor/factory';
import { DEFAULT_SERVER_LIFE_CYCLE_TIMEOUT } from '@/interceptor/server/constants';
Expand Down Expand Up @@ -581,39 +580,25 @@ describe('CLI (server)', async () => {
});
});

it.each([
{ overrideDefault: false as const },
{ overrideDefault: 'static' as const },
{ overrideDefault: 'static-empty' as const },
{ overrideDefault: 'function' as const },
])(
'should show an error if logging is enabled when a request is received and does not match any interceptors (override default $overrideDefault)',
async ({ overrideDefault }) => {
it.each([undefined, 'true'])(
'should show an error if logging is enabled when a request is received and does not match any interceptors (flag %s)',
async (flagValue) => {
processArgvSpy.mockReturnValue([
'node',
'./dist/cli.js',
'server',
'start',
...(overrideDefault === false ? ['--log-unhandled-requests'] : []),
...(flagValue === undefined ? [] : ['--log-unhandled-requests', flagValue]),
]);

if (overrideDefault === 'static') {
httpInterceptor.default.onUnhandledRequest({ log: true });
} else if (overrideDefault === 'static-empty') {
httpInterceptor.default.onUnhandledRequest({});
} else if (overrideDefault === 'function') {
httpInterceptor.default.onUnhandledRequest(async (_request, context) => {
await context.log();
});
}

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

expect(server).toBeDefined();
expect(server!.isRunning()).toBe(true);
expect(server!.hostname()).toBe('localhost');
expect(server!.port()).toBeGreaterThan(0);
expect(server!.logUnhandledRequests()).toBe(true);

expect(spies.log).toHaveBeenCalledTimes(1);
expect(spies.warn).toHaveBeenCalledTimes(0);
Expand Down Expand Up @@ -643,30 +628,26 @@ describe('CLI (server)', async () => {
},
);

it.each([{ overrideDefault: false }, { overrideDefault: 'static' }, { overrideDefault: 'function' }])(
'should not show an error if logging is disabled when a request is received and does not match any interceptors (override default $overrideDefault)',
async ({ overrideDefault }) => {
it.each(['false'])(
'should not show an error if logging is disabled when a request is received and does not match any interceptors (flag %s)',
async (flagValue) => {
processArgvSpy.mockReturnValue([
'node',
'./dist/cli.js',
'server',
'start',
...(overrideDefault === false ? ['--log-unhandled-requests', 'false'] : []),
'--log-unhandled-requests',
flagValue,
]);

if (overrideDefault === 'static') {
httpInterceptor.default.onUnhandledRequest({ log: false });
} else if (overrideDefault === 'function') {
httpInterceptor.default.onUnhandledRequest(vi.fn());
}

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

expect(server).toBeDefined();
expect(server!.isRunning()).toBe(true);
expect(server!.hostname()).toBe('localhost');
expect(server!.port()).toBeGreaterThan(0);
expect(server!.logUnhandledRequests()).toBe(false);

expect(spies.log).toHaveBeenCalledTimes(1);
expect(spies.warn).toHaveBeenCalledTimes(0);
Expand Down
4 changes: 2 additions & 2 deletions packages/zimic/src/cli/server/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ async function startInterceptorServer({
hostname,
port,
ephemeral,
onUnhandledRequest,
logUnhandledRequests,
onReady,
}: InterceptorServerStartOptions) {
const server = interceptorServer.create({
hostname,
port,
onUnhandledRequest,
logUnhandledRequests,
});

async function handleExitEvent(exitEvent: ProcessExitEvent | undefined) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ describe('Type generation (OpenAPI)', () => {
expect(successMessage).toBeDefined();
expect(successMessage).toMatch(/.*\[zimic\].* /);
expect(successMessage).toContain(`Generated ${outputLabel}`);
expect(successMessage).toMatch(/.*(\d+ms).*$/);
expect(successMessage).toMatch(/.*([\d.]+m?s).*$/);
}

async function getGeneratedOutputContent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { declareBaseURLHttpInterceptorTests } from './shared/baseURLs';
import testMatrix from './shared/matrix';

describe.each(testMatrix)('HttpInterceptor (node, $type) > Base URLs', ({ type }) => {
const server = createInternalInterceptorServer();
const server = createInternalInterceptorServer({ logUnhandledRequests: false });

let baseURL: ExtendedURL;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { declareBlobBodyHttpInterceptorTests } from './shared/bodies/blob';
import testMatrix from './shared/matrix';

describe.each(testMatrix)('HttpInterceptor (node, $type) > Blob', async ({ type }) => {
const server = createInternalInterceptorServer();
const server = createInternalInterceptorServer({ logUnhandledRequests: false });

let baseURL: ExtendedURL;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { declareFormDataBodyHttpInterceptorTests } from './shared/bodies/formDat
import testMatrix from './shared/matrix';

describe.each(testMatrix)('HttpInterceptor (node, $type) > Form data', async ({ type }) => {
const server = createInternalInterceptorServer();
const server = createInternalInterceptorServer({ logUnhandledRequests: false });

let baseURL: ExtendedURL;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { declareJSONBodyHttpInterceptorTests } from './shared/bodies/json';
import testMatrix from './shared/matrix';

describe.each(testMatrix)('HttpInterceptor (node, $type) > Bodies > Json', async ({ type }) => {
const server = createInternalInterceptorServer();
const server = createInternalInterceptorServer({ logUnhandledRequests: false });

let baseURL: ExtendedURL;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { declarePlainTextBodyHttpInterceptorTests } from './shared/bodies/plainT
import testMatrix from './shared/matrix';

describe.each(testMatrix)('HttpInterceptor (node, $type) > Bodies > Plain text', async ({ type }) => {
const server = createInternalInterceptorServer();
const server = createInternalInterceptorServer({ logUnhandledRequests: false });

let baseURL: ExtendedURL;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { declareSearchParamsBodyHttpInterceptorTests } from './shared/bodies/sea
import testMatrix from './shared/matrix';

describe.each(testMatrix)('HttpInterceptor (node, $type) > Bodies > Search params', async ({ type }) => {
const server = createInternalInterceptorServer();
const server = createInternalInterceptorServer({ logUnhandledRequests: false });

let baseURL: ExtendedURL;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { declareBypassHttpInterceptorTests } from './shared/bypass';
import testMatrix from './shared/matrix';

describe.each(testMatrix)('HttpInterceptor (node, $type) > Bypass', ({ type }) => {
const server = createInternalInterceptorServer();
const server = createInternalInterceptorServer({ logUnhandledRequests: false });

let baseURL: ExtendedURL;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { declareClearHttpInterceptorTests } from './shared/clear';
import testMatrix from './shared/matrix';

describe.each(testMatrix)('HttpInterceptor (node, $type) > Clear', ({ type }) => {
const server = createInternalInterceptorServer();
const server = createInternalInterceptorServer({ logUnhandledRequests: false });

let baseURL: ExtendedURL;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { declareHandlerHttpInterceptorTests } from './shared/handlers';
import testMatrix from './shared/matrix';

describe.each(testMatrix)('HttpInterceptor (node, $type) > Handlers', ({ type }) => {
const server = createInternalInterceptorServer();
const server = createInternalInterceptorServer({ logUnhandledRequests: false });

let baseURL: ExtendedURL;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { declareLifeCycleHttpInterceptorTests } from './shared/lifeCycle';
import testMatrix from './shared/matrix';

describe.each(testMatrix)('HttpInterceptor (node, $type) > Life cycle', ({ type }) => {
const server = createInternalInterceptorServer();
const server = createInternalInterceptorServer({ logUnhandledRequests: false });

let baseURL: ExtendedURL;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { declareDeclareHttpInterceptorTests } from './shared/default';
import testMatrix from './shared/matrix';

describe.each(testMatrix)('HttpInterceptor (node, $type)', ({ type }) => {
const server = createInternalInterceptorServer();
const server = createInternalInterceptorServer({ logUnhandledRequests: false });

let baseURL: ExtendedURL;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import testMatrix from './shared/matrix';
import { declarePathParamsHttpInterceptorTests } from './shared/pathParams';

describe.each(testMatrix)('HttpInterceptor (node, $type) > Path params', async ({ type }) => {
const server = createInternalInterceptorServer();
const server = createInternalInterceptorServer({ logUnhandledRequests: false });

let baseURL: ExtendedURL;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import testMatrix from './shared/matrix';
import { declareRestrictionsHttpInterceptorTests } from './shared/restrictions';

describe.each(testMatrix)('HttpInterceptor (node, $type) > Restrictions', async ({ type }) => {
const server = createInternalInterceptorServer();
const server = createInternalInterceptorServer({ logUnhandledRequests: false });

let baseURL: ExtendedURL;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import testMatrix from './shared/matrix';
import { declareTypeHttpInterceptorTests } from './shared/types';

describe.each(testMatrix)('HttpInterceptor (node, $type) > Types', ({ type }) => {
const server = createInternalInterceptorServer();
const server = createInternalInterceptorServer({ logUnhandledRequests: false });

let baseURL: ExtendedURL;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import testMatrix from './shared/matrix';
import { declareUnhandledRequestHttpInterceptorTests } from './shared/unhandledRequests';

describe.each(testMatrix)('HttpInterceptor (node, $type) > Unhandled requests', ({ type }) => {
const server = createInternalInterceptorServer();
const server = createInternalInterceptorServer({ logUnhandledRequests: false });

let baseURL: ExtendedURL;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ export function declareHandlerHttpInterceptorTests(options: RuntimeSharedHttpInt
});

it(`should log an error if a ${method} request is intercepted with a computed response and the handler throws`, async () => {
const extendedInterceptorOptions: HttpInterceptorOptions =
type === 'local'
? { ...interceptorOptions, type, onUnhandledRequest: { action: 'bypass', logWarning: true } }
: { ...interceptorOptions, type, onUnhandledRequest: { action: 'reject', logWarning: true } };

await usingHttpInterceptor<{
'/users': {
GET: MethodSchema;
Expand All @@ -149,7 +154,7 @@ export function declareHandlerHttpInterceptorTests(options: RuntimeSharedHttpInt
HEAD: MethodSchema;
OPTIONS: MethodSchema;
};
}>({ ...interceptorOptions, onUnhandledRequest: { log: true } }, async (interceptor) => {
}>(extendedInterceptorOptions, async (interceptor) => {
const error = new Error('An error occurred.');

const handler = await promiseIfRemote(
Expand Down
Loading

0 comments on commit e53cb89

Please sign in to comment.