@prosopo/api-express-router 3.1.51 → 3.1.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build$colon$cjs.log +2 -2
- package/.turbo/turbo-build$colon$tsc.log +21 -21
- package/.turbo/turbo-build.log +3 -3
- package/CHANGELOG.md +6 -0
- package/dist/tests/apiExpressRouter.test-d.d.ts +2 -0
- package/dist/tests/apiExpressRouter.test-d.d.ts.map +1 -0
- package/dist/tests/apiExpressRouter.test-d.js +76 -0
- package/dist/tests/apiExpressRouter.test-d.js.map +1 -0
- package/dist/tests/unit/apiExpressDefaultEndpointAdapter.unit.test.d.ts +2 -0
- package/dist/tests/unit/apiExpressDefaultEndpointAdapter.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/apiExpressDefaultEndpointAdapter.unit.test.js +197 -0
- package/dist/tests/unit/apiExpressDefaultEndpointAdapter.unit.test.js.map +1 -0
- package/dist/tests/unit/apiExpressRouterFactory.unit.test.d.ts +2 -0
- package/dist/tests/unit/apiExpressRouterFactory.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/apiExpressRouterFactory.unit.test.js +108 -0
- package/dist/tests/unit/apiExpressRouterFactory.unit.test.js.map +1 -0
- package/dist/tests/unit/index.unit.test.d.ts +2 -0
- package/dist/tests/unit/index.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/index.unit.test.js +44 -0
- package/dist/tests/unit/index.unit.test.js.map +1 -0
- package/dist/tests/unit/middlewares/authMiddleware.unit.test.js +210 -136
- package/dist/tests/unit/middlewares/authMiddleware.unit.test.js.map +1 -1
- package/dist/tests/unit/middlewares/requestLoggerMiddleware.unit.test.d.ts +2 -0
- package/dist/tests/unit/middlewares/requestLoggerMiddleware.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/middlewares/requestLoggerMiddleware.unit.test.js +242 -0
- package/dist/tests/unit/middlewares/requestLoggerMiddleware.unit.test.js.map +1 -0
- package/dist/tests/unit/testDoubles.d.ts +8 -0
- package/dist/tests/unit/testDoubles.d.ts.map +1 -0
- package/dist/tests/unit/testDoubles.js +11 -0
- package/dist/tests/unit/testDoubles.js.map +1 -0
- package/package.json +2 -2
- package/src/tests/apiExpressRouter.test-d.ts +190 -0
- package/src/tests/unit/apiExpressDefaultEndpointAdapter.unit.test.ts +395 -0
- package/src/tests/unit/apiExpressRouterFactory.unit.test.ts +215 -0
- package/src/tests/unit/index.unit.test.ts +95 -0
- package/src/tests/unit/middlewares/authMiddleware.unit.test.ts +318 -159
- package/src/tests/unit/middlewares/requestLoggerMiddleware.unit.test.ts +368 -0
- package/src/tests/unit/testDoubles.ts +39 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @prosopo/api-express-router@3.1.
|
|
2
|
+
> @prosopo/api-express-router@3.1.52 build:cjs
|
|
3
3
|
> NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV
|
|
4
4
|
|
|
5
5
|
ViteCommonJSConfig: .
|
|
@@ -57,4 +57,4 @@ dist/cjs/index.cjs 1.25 kB │ gzip:
|
|
|
57
57
|
dist/cjs/middlewares/authMiddleware.cjs 1.71 kB │ gzip: 0.74 kB
|
|
58
58
|
dist/cjs/middlewares/requestLoggerMiddleware.cjs 1.81 kB │ gzip: 0.78 kB
|
|
59
59
|
|
|
60
|
-
[32m✓ built in
|
|
60
|
+
[32m✓ built in 50ms[39m
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
|
-
> @prosopo/api-express-router@3.1.
|
|
2
|
+
> @prosopo/api-express-router@3.1.52 build:tsc
|
|
3
3
|
> tsc --build --verbose
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
12:37:06 PM - Projects in this build:
|
|
6
6
|
* ../../dev/config/tsconfig.json
|
|
7
7
|
* ../util/tsconfig.json
|
|
8
8
|
* ../logger/tsconfig.json
|
|
@@ -22,41 +22,41 @@
|
|
|
22
22
|
* ../env/tsconfig.json
|
|
23
23
|
* tsconfig.json
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
12:37:06 PM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/webpack/index.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
12:37:06 PM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
12:37:06 PM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
12:37:06 PM - Project '../api-route/tsconfig.json' is up to date because newest input '../api-route/src/tests/apiRoute.unit.test.ts' is older than output '../api-route/tsconfig.tsbuildinfo'
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
12:37:06 PM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
12:37:06 PM - Project '../util-crypto/tsconfig.json' is up to date because newest input '../util-crypto/src/types.ts' is older than output '../util-crypto/tsconfig.tsbuildinfo'
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
12:37:06 PM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/provider/api.ts' is older than output '../types/tsconfig.tsbuildinfo'
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
12:37:06 PM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/index.ts' is older than output '../common/tsconfig.tsbuildinfo'
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
12:37:06 PM - Project '../ipinfo/tsconfig.json' is up to date because newest input '../ipinfo/src/IpInfoService.ts' is older than output '../ipinfo/tsconfig.tsbuildinfo'
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
12:37:06 PM - Project '../redis-client/tsconfig.json' is up to date because newest input '../redis-client/src/index.ts' is older than output '../redis-client/tsconfig.tsbuildinfo'
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
12:37:07 PM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/tests/apiHarness.ts' is older than output '../api/tsconfig.tsbuildinfo'
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
12:37:07 PM - Project '../user-access-policy/tsconfig.json' is up to date because newest input '../user-access-policy/src/transformRule.ts' is older than output '../user-access-policy/tsconfig.tsbuildinfo'
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
12:37:07 PM - Project '../types-database/tsconfig.json' is up to date because newest input '../types-database/src/types/bannedDomain.ts' is older than output '../types-database/tsconfig.tsbuildinfo'
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
12:37:07 PM - Project '../database/tsconfig.json' is up to date because newest input '../database/src/tests/unit/redisCacheEscalation.unit.test.ts' is older than output '../database/tsconfig.tsbuildinfo'
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
12:37:07 PM - Project '../keyring/tsconfig.json' is up to date because newest input '../keyring/src/keyring/keyring.ts' is older than output '../keyring/tsconfig.tsbuildinfo'
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
12:37:07 PM - Project '../types-env/tsconfig.json' is up to date because newest input '../types-env/src/index.ts' is older than output '../types-env/tsconfig.tsbuildinfo'
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
12:37:07 PM - Project '../env/tsconfig.json' is up to date because newest input '../env/src/env.ts' is older than output '../env/tsconfig.tsbuildinfo'
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
12:37:07 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
12:37:07 PM - Building project '/home/runner/work/captcha/captcha/packages/api-express-router/tsconfig.json'...
|
|
62
62
|
|
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
> @prosopo/api-express-router@3.1.
|
|
2
|
+
> @prosopo/api-express-router@3.1.52 build
|
|
3
3
|
> npm run build:cross-env -- --mode ${NODE_ENV:-development}
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
> @prosopo/api-express-router@3.1.
|
|
6
|
+
> @prosopo/api-express-router@3.1.52 build:cross-env
|
|
7
7
|
> vite build --config vite.esm.config.ts --mode production
|
|
8
8
|
|
|
9
9
|
ViteEsmConfig: .
|
|
@@ -62,4 +62,4 @@ dist/endpointAdapter/apiExpressDefaultEndpointAdapter.js 0.94 kB │ gzip: 0.47
|
|
|
62
62
|
dist/middlewares/authMiddleware.js 1.60 kB │ gzip: 0.72 kB
|
|
63
63
|
dist/middlewares/requestLoggerMiddleware.js 1.75 kB │ gzip: 0.76 kB
|
|
64
64
|
|
|
65
|
-
[32m✓ built in
|
|
65
|
+
[32m✓ built in 49ms[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @prosopo/api-express-router
|
|
2
2
|
|
|
3
|
+
## 3.1.52
|
|
4
|
+
### Patch Changes
|
|
5
|
+
|
|
6
|
+
- 508fee8: Add unit and type test coverage for the router factory, the default endpoint adapter, the auth and request-logger middleware, and the package barrel.
|
|
7
|
+
- @prosopo/env@3.6.21
|
|
8
|
+
|
|
3
9
|
## 3.1.51
|
|
4
10
|
### Patch Changes
|
|
5
11
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiExpressRouter.test-d.d.ts","sourceRoot":"","sources":["../../src/tests/apiExpressRouter.test-d.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { assertType, describe, expectTypeOf, test } from "vitest";
|
|
2
|
+
import { apiExpressRouterFactory, authMiddleware, createApiExpressDefaultEndpointAdapter, handleErrors, requestLoggerMiddleware, verifySignature, } from "../index.js";
|
|
3
|
+
describe("the barrel's shape", () => {
|
|
4
|
+
test("exports exactly the intended names", () => {
|
|
5
|
+
expectTypeOf().toEqualTypeOf();
|
|
6
|
+
});
|
|
7
|
+
});
|
|
8
|
+
describe("createApiExpressDefaultEndpointAdapter", () => {
|
|
9
|
+
test("takes a log level and an optional status code", () => {
|
|
10
|
+
expectTypeOf(createApiExpressDefaultEndpointAdapter).parameters.toEqualTypeOf();
|
|
11
|
+
});
|
|
12
|
+
test("returns the interface, not the concrete class", () => {
|
|
13
|
+
expectTypeOf(createApiExpressDefaultEndpointAdapter).returns.toEqualTypeOf();
|
|
14
|
+
});
|
|
15
|
+
test("rejects a log level that is not one of the known levels", () => {
|
|
16
|
+
createApiExpressDefaultEndpointAdapter("chatty");
|
|
17
|
+
});
|
|
18
|
+
test("rejects a non-numeric status code", () => {
|
|
19
|
+
createApiExpressDefaultEndpointAdapter("info", "500");
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
describe("the router factory", () => {
|
|
23
|
+
test("takes a routes provider and an adapter, and returns a Router", () => {
|
|
24
|
+
expectTypeOf(apiExpressRouterFactory.createRouter).toEqualTypeOf();
|
|
25
|
+
});
|
|
26
|
+
test("rejects a bare routes object in place of a provider", () => {
|
|
27
|
+
const adapter = {
|
|
28
|
+
handleRequest: async () => undefined,
|
|
29
|
+
};
|
|
30
|
+
apiExpressRouterFactory.createRouter({}, adapter);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe("the endpoint adapter interface", () => {
|
|
34
|
+
test("takes the endpoint and the express trio and resolves to void", () => {
|
|
35
|
+
expectTypeOf().toEqualTypeOf();
|
|
36
|
+
});
|
|
37
|
+
test("is satisfied by any object with a matching handleRequest", () => {
|
|
38
|
+
assertType({
|
|
39
|
+
handleRequest: async () => undefined,
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe("the middleware", () => {
|
|
44
|
+
test("authMiddleware takes an optional pair and an optional auth account", () => {
|
|
45
|
+
expectTypeOf(authMiddleware).parameters.toEqualTypeOf();
|
|
46
|
+
});
|
|
47
|
+
test("authMiddleware returns an async express handler", () => {
|
|
48
|
+
expectTypeOf(authMiddleware).returns.toEqualTypeOf();
|
|
49
|
+
});
|
|
50
|
+
test("authMiddleware still requires the first argument to be passed", () => {
|
|
51
|
+
authMiddleware();
|
|
52
|
+
});
|
|
53
|
+
test("requestLoggerMiddleware returns a synchronous express handler", () => {
|
|
54
|
+
expectTypeOf(requestLoggerMiddleware).returns.toEqualTypeOf();
|
|
55
|
+
});
|
|
56
|
+
test("the handler each factory builds is usable as express middleware", () => {
|
|
57
|
+
assertType(requestLoggerMiddleware({}));
|
|
58
|
+
assertType(authMiddleware(undefined));
|
|
59
|
+
});
|
|
60
|
+
test("verifySignature takes a signature, a message and a pair, and returns void", () => {
|
|
61
|
+
expectTypeOf(verifySignature).parameters.toEqualTypeOf();
|
|
62
|
+
expectTypeOf(verifySignature).returns.toEqualTypeOf();
|
|
63
|
+
});
|
|
64
|
+
test("verifySignature rejects a byte-array signature", () => {
|
|
65
|
+
verifySignature(new Uint8Array([1]), "msg", {});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
describe("the error handler", () => {
|
|
69
|
+
test("takes an error first, which is what makes express treat it as one", () => {
|
|
70
|
+
expectTypeOf(handleErrors).parameters.toMatchTypeOf();
|
|
71
|
+
});
|
|
72
|
+
test("rejects being mounted as an ordinary handler", () => {
|
|
73
|
+
assertType(handleErrors);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
//# sourceMappingURL=apiExpressRouter.test-d.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiExpressRouter.test-d.js","sourceRoot":"","sources":["../../src/tests/apiExpressRouter.test-d.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiExpressDefaultEndpointAdapter.unit.test.d.ts","sourceRoot":"","sources":["../../../src/tests/unit/apiExpressDefaultEndpointAdapter.unit.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { ApiEndpointResponseStatus, } from "@prosopo/api-route";
|
|
2
|
+
import { ProsopoApiError } from "@prosopo/common";
|
|
3
|
+
import { LogLevel } from "@prosopo/logger";
|
|
4
|
+
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { ApiExpressDefaultEndpointAdapter } from "../../endpointAdapter/apiExpressDefaultEndpointAdapter.js";
|
|
7
|
+
import { captureNext } from "./testDoubles.js";
|
|
8
|
+
const build = (body = {}, errorStatusCode = 500) => {
|
|
9
|
+
const logged = vi.fn();
|
|
10
|
+
const logger = {
|
|
11
|
+
error: logged,
|
|
12
|
+
info: vi.fn(),
|
|
13
|
+
debug: vi.fn(),
|
|
14
|
+
warn: vi.fn(),
|
|
15
|
+
};
|
|
16
|
+
const json = vi.fn();
|
|
17
|
+
const send = vi.fn();
|
|
18
|
+
const status = vi.fn();
|
|
19
|
+
const response = { json, send, status };
|
|
20
|
+
status.mockReturnValue(response);
|
|
21
|
+
return {
|
|
22
|
+
adapter: new ApiExpressDefaultEndpointAdapter(LogLevel.enum.info, errorStatusCode),
|
|
23
|
+
request: { body, logger },
|
|
24
|
+
response,
|
|
25
|
+
next: captureNext(),
|
|
26
|
+
logger,
|
|
27
|
+
json,
|
|
28
|
+
status,
|
|
29
|
+
send,
|
|
30
|
+
logged,
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
const endpoint = (schema, process = async () => ({
|
|
34
|
+
status: ApiEndpointResponseStatus.SUCCESS,
|
|
35
|
+
})) => ({
|
|
36
|
+
getRequestArgsSchema: () => schema,
|
|
37
|
+
processRequest: vi.fn(process),
|
|
38
|
+
});
|
|
39
|
+
let harness;
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
harness = build();
|
|
42
|
+
});
|
|
43
|
+
describe("a request the endpoint accepts", () => {
|
|
44
|
+
test("parses the body and passes the parsed value on", async () => {
|
|
45
|
+
harness = build({ count: "5" });
|
|
46
|
+
const schema = z.object({ count: z.coerce.number() });
|
|
47
|
+
const target = endpoint(schema);
|
|
48
|
+
await harness.adapter.handleRequest(target, harness.request, harness.response, harness.next.fn);
|
|
49
|
+
expect(target.processRequest).toHaveBeenCalledWith({ count: 5 }, harness.logger);
|
|
50
|
+
});
|
|
51
|
+
test("passes undefined args when the endpoint declares no schema", async () => {
|
|
52
|
+
const target = endpoint(undefined);
|
|
53
|
+
await harness.adapter.handleRequest(target, harness.request, harness.response, harness.next.fn);
|
|
54
|
+
expect(target.processRequest).toHaveBeenCalledWith(undefined, harness.logger);
|
|
55
|
+
});
|
|
56
|
+
test("hands the endpoint the request's own logger", async () => {
|
|
57
|
+
const target = endpoint(undefined);
|
|
58
|
+
await harness.adapter.handleRequest(target, harness.request, harness.response, harness.next.fn);
|
|
59
|
+
expect(vi.mocked(target.processRequest).mock.calls[0]?.[1]).toBe(harness.logger);
|
|
60
|
+
});
|
|
61
|
+
test("answers with the endpoint's response as JSON", async () => {
|
|
62
|
+
await harness.adapter.handleRequest(endpoint(undefined, async () => ({
|
|
63
|
+
status: ApiEndpointResponseStatus.SUCCESS,
|
|
64
|
+
data: { value: 1 },
|
|
65
|
+
})), harness.request, harness.response, harness.next.fn);
|
|
66
|
+
expect(harness.json).toHaveBeenCalledWith({
|
|
67
|
+
status: ApiEndpointResponseStatus.SUCCESS,
|
|
68
|
+
data: { value: 1 },
|
|
69
|
+
});
|
|
70
|
+
expect(harness.next.calls).toHaveLength(0);
|
|
71
|
+
});
|
|
72
|
+
test("stringifies bigints, which res.json() cannot serialise", async () => {
|
|
73
|
+
await harness.adapter.handleRequest(endpoint(undefined, async () => ({
|
|
74
|
+
status: ApiEndpointResponseStatus.SUCCESS,
|
|
75
|
+
data: { big: 10n },
|
|
76
|
+
})), harness.request, harness.response, harness.next.fn);
|
|
77
|
+
expect(harness.json).toHaveBeenCalledWith({
|
|
78
|
+
status: ApiEndpointResponseStatus.SUCCESS,
|
|
79
|
+
data: { big: "10" },
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
test("passes an empty data payload straight through", async () => {
|
|
83
|
+
await harness.adapter.handleRequest(endpoint(undefined, async () => ({
|
|
84
|
+
status: ApiEndpointResponseStatus.SUCCESS,
|
|
85
|
+
data: {},
|
|
86
|
+
})), harness.request, harness.response, harness.next.fn);
|
|
87
|
+
expect(harness.json).toHaveBeenCalledWith({
|
|
88
|
+
status: ApiEndpointResponseStatus.SUCCESS,
|
|
89
|
+
data: {},
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
test("an absent request body is still handed to the schema", async () => {
|
|
93
|
+
harness.request.body = undefined;
|
|
94
|
+
const schema = z.object({ a: z.string() }).optional();
|
|
95
|
+
const target = endpoint(schema);
|
|
96
|
+
await harness.adapter.handleRequest(target, harness.request, harness.response, harness.next.fn);
|
|
97
|
+
expect(target.processRequest).toHaveBeenCalledWith(undefined, harness.logger);
|
|
98
|
+
expect(harness.json).toHaveBeenCalledWith({
|
|
99
|
+
status: ApiEndpointResponseStatus.SUCCESS,
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
describe("a body the schema rejects", () => {
|
|
104
|
+
test("goes to the error handler rather than being answered here", async () => {
|
|
105
|
+
harness = build({ count: "not a number" });
|
|
106
|
+
const target = endpoint(z.object({ count: z.number() }));
|
|
107
|
+
await harness.adapter.handleRequest(target, harness.request, harness.response, harness.next.fn);
|
|
108
|
+
expect(harness.next.calls).toHaveLength(1);
|
|
109
|
+
expect(harness.json).not.toHaveBeenCalled();
|
|
110
|
+
expect(target.processRequest).not.toHaveBeenCalled();
|
|
111
|
+
});
|
|
112
|
+
test("is reported as a 400 parse error carrying the cause", async () => {
|
|
113
|
+
harness = build({ count: "not a number" });
|
|
114
|
+
await harness.adapter.handleRequest(endpoint(z.object({ count: z.number() })), harness.request, harness.response, harness.next.fn);
|
|
115
|
+
const [forwarded] = harness.next.calls[0] ?? [];
|
|
116
|
+
expect(forwarded).toBeInstanceOf(ProsopoApiError);
|
|
117
|
+
const error = forwarded;
|
|
118
|
+
expect(error.context?.code).toBe(400);
|
|
119
|
+
expect(error.context?.error).toBeDefined();
|
|
120
|
+
});
|
|
121
|
+
test("a schema that throws something other than a ZodError is treated the same", async () => {
|
|
122
|
+
const schema = {
|
|
123
|
+
parse: () => {
|
|
124
|
+
throw new Error("custom validator exploded");
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
await harness.adapter.handleRequest(endpoint(schema), harness.request, harness.response, harness.next.fn);
|
|
128
|
+
expect(harness.next.calls).toHaveLength(1);
|
|
129
|
+
expect(harness.next.calls[0]?.[0]).toBeInstanceOf(ProsopoApiError);
|
|
130
|
+
});
|
|
131
|
+
test("an endpoint that fails while building its schema is caught too", async () => {
|
|
132
|
+
const target = {
|
|
133
|
+
getRequestArgsSchema: () => {
|
|
134
|
+
throw new Error("schema construction failed");
|
|
135
|
+
},
|
|
136
|
+
processRequest: vi.fn(),
|
|
137
|
+
};
|
|
138
|
+
await harness.adapter.handleRequest(target, harness.request, harness.response, harness.next.fn);
|
|
139
|
+
expect(harness.next.calls[0]?.[0]).toBeInstanceOf(ProsopoApiError);
|
|
140
|
+
expect(target.processRequest).not.toHaveBeenCalled();
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
describe("an endpoint that fails", () => {
|
|
144
|
+
test("is answered with a 500 and a generic message", async () => {
|
|
145
|
+
await harness.adapter.handleRequest(endpoint(undefined, async () => {
|
|
146
|
+
throw new Error("database down");
|
|
147
|
+
}), harness.request, harness.response, harness.next.fn);
|
|
148
|
+
expect(harness.status).toHaveBeenCalledWith(500);
|
|
149
|
+
expect(harness.send).toHaveBeenCalledWith("An internal server error occurred.");
|
|
150
|
+
});
|
|
151
|
+
test("does not leak the failure's message to the client", async () => {
|
|
152
|
+
await harness.adapter.handleRequest(endpoint(undefined, async () => {
|
|
153
|
+
throw new Error("connection string: postgres://user:secret@host");
|
|
154
|
+
}), harness.request, harness.response, harness.next.fn);
|
|
155
|
+
expect(String(harness.send.mock.calls[0]?.[0])).not.toContain("secret");
|
|
156
|
+
});
|
|
157
|
+
test("logs the failure against the request's logger", async () => {
|
|
158
|
+
const failure = new Error("database down");
|
|
159
|
+
await harness.adapter.handleRequest(endpoint(undefined, async () => {
|
|
160
|
+
throw failure;
|
|
161
|
+
}), harness.request, harness.response, harness.next.fn);
|
|
162
|
+
expect(harness.logged).toHaveBeenCalledTimes(1);
|
|
163
|
+
const entry = harness.logged.mock.calls[0]?.[0];
|
|
164
|
+
expect(entry?.()).toEqual({ err: failure });
|
|
165
|
+
});
|
|
166
|
+
test("does not also call next(), so the error handler is not double-run", async () => {
|
|
167
|
+
await harness.adapter.handleRequest(endpoint(undefined, async () => {
|
|
168
|
+
throw new Error("database down");
|
|
169
|
+
}), harness.request, harness.response, harness.next.fn);
|
|
170
|
+
expect(harness.next.calls).toHaveLength(0);
|
|
171
|
+
});
|
|
172
|
+
test("ignores the configured errorStatusCode and always answers 500", async () => {
|
|
173
|
+
harness = build({}, 503);
|
|
174
|
+
await harness.adapter.handleRequest(endpoint(undefined, async () => {
|
|
175
|
+
throw new Error("unavailable");
|
|
176
|
+
}), harness.request, harness.response, harness.next.fn);
|
|
177
|
+
expect(harness.status).toHaveBeenCalledWith(500);
|
|
178
|
+
expect(harness.status).not.toHaveBeenCalledWith(503);
|
|
179
|
+
});
|
|
180
|
+
test("a rejection with a non-Error value is handled just the same", async () => {
|
|
181
|
+
await harness.adapter.handleRequest(endpoint(undefined, async () => {
|
|
182
|
+
throw "a bare string";
|
|
183
|
+
}), harness.request, harness.response, harness.next.fn);
|
|
184
|
+
expect(harness.status).toHaveBeenCalledWith(500);
|
|
185
|
+
expect(harness.logged).toHaveBeenCalledTimes(1);
|
|
186
|
+
});
|
|
187
|
+
test("a failure while serialising the response is caught too", async () => {
|
|
188
|
+
const hostile = {
|
|
189
|
+
get status() {
|
|
190
|
+
throw new Error("getter exploded");
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
await harness.adapter.handleRequest(endpoint(undefined, async () => hostile), harness.request, harness.response, harness.next.fn);
|
|
194
|
+
expect(harness.status).toHaveBeenCalledWith(500);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
//# sourceMappingURL=apiExpressDefaultEndpointAdapter.unit.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiExpressDefaultEndpointAdapter.unit.test.js","sourceRoot":"","sources":["../../../src/tests/unit/apiExpressDefaultEndpointAdapter.unit.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiExpressRouterFactory.unit.test.d.ts","sourceRoot":"","sources":["../../../src/tests/unit/apiExpressRouterFactory.unit.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { ApiEndpointResponseStatus, } from "@prosopo/api-route";
|
|
2
|
+
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
3
|
+
import { ApiExpressRouterFactory } from "../../apiExpressRouterFactory.js";
|
|
4
|
+
import { handleErrors } from "../../errorHandler.js";
|
|
5
|
+
import { captureNext } from "./testDoubles.js";
|
|
6
|
+
const endpoint = () => ({
|
|
7
|
+
getRequestArgsSchema: vi.fn(() => undefined),
|
|
8
|
+
processRequest: vi.fn(async () => ({ status: ApiEndpointResponseStatus.SUCCESS })),
|
|
9
|
+
});
|
|
10
|
+
const provider = (routes) => ({
|
|
11
|
+
getRoutes: () => routes,
|
|
12
|
+
});
|
|
13
|
+
const adapter = () => ({
|
|
14
|
+
handleRequest: vi.fn(async () => undefined),
|
|
15
|
+
});
|
|
16
|
+
const postRoutes = (router) => {
|
|
17
|
+
const registered = [];
|
|
18
|
+
for (const layer of router.stack) {
|
|
19
|
+
const route = layer.route;
|
|
20
|
+
if (!route)
|
|
21
|
+
continue;
|
|
22
|
+
const handler = route.stack[route.stack.length - 1]?.handle;
|
|
23
|
+
if (handler)
|
|
24
|
+
registered.push([route.path, handler]);
|
|
25
|
+
}
|
|
26
|
+
return registered;
|
|
27
|
+
};
|
|
28
|
+
let factory;
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
factory = new ApiExpressRouterFactory();
|
|
31
|
+
});
|
|
32
|
+
describe("createRouter", () => {
|
|
33
|
+
test("registers one POST route per entry the provider declares", () => {
|
|
34
|
+
const router = factory.createRouter(provider({ "/a": endpoint(), "/b": endpoint() }), adapter());
|
|
35
|
+
expect(postRoutes(router).map(([path]) => path)).toEqual(["/a", "/b"]);
|
|
36
|
+
});
|
|
37
|
+
test("registers the routes as POST only", () => {
|
|
38
|
+
const router = factory.createRouter(provider({ "/a": endpoint() }), adapter());
|
|
39
|
+
const route = router.stack.find((layer) => layer.route)?.route;
|
|
40
|
+
expect(route?.methods).toEqual({ post: true });
|
|
41
|
+
});
|
|
42
|
+
test("a provider with no routes still yields a router", () => {
|
|
43
|
+
const router = factory.createRouter(provider({}), adapter());
|
|
44
|
+
expect(postRoutes(router)).toEqual([]);
|
|
45
|
+
expect(router.stack).toHaveLength(1);
|
|
46
|
+
});
|
|
47
|
+
test("mounts the error handler last, after every route", () => {
|
|
48
|
+
const router = factory.createRouter(provider({ "/a": endpoint(), "/b": endpoint() }), adapter());
|
|
49
|
+
const last = router.stack[router.stack.length - 1];
|
|
50
|
+
expect(last?.handle).toBe(handleErrors);
|
|
51
|
+
expect(last?.route).toBeUndefined();
|
|
52
|
+
});
|
|
53
|
+
test("asks the provider for its routes exactly once", () => {
|
|
54
|
+
const getRoutes = vi.fn(() => ({ "/a": endpoint() }));
|
|
55
|
+
factory.createRouter({ getRoutes }, adapter());
|
|
56
|
+
expect(getRoutes).toHaveBeenCalledTimes(1);
|
|
57
|
+
});
|
|
58
|
+
test("each call builds a fresh router", () => {
|
|
59
|
+
const routes = provider({ "/a": endpoint() });
|
|
60
|
+
expect(factory.createRouter(routes, adapter())).not.toBe(factory.createRouter(routes, adapter()));
|
|
61
|
+
});
|
|
62
|
+
test("routes named oddly are passed to express verbatim", () => {
|
|
63
|
+
const router = factory.createRouter(provider({ "/": endpoint(), "/nested/:id": endpoint() }), adapter());
|
|
64
|
+
expect(postRoutes(router).map(([path]) => path)).toEqual([
|
|
65
|
+
"/",
|
|
66
|
+
"/nested/:id",
|
|
67
|
+
]);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe("the handler each route is given", () => {
|
|
71
|
+
test("hands the endpoint and the express trio to the adapter", async () => {
|
|
72
|
+
const target = endpoint();
|
|
73
|
+
const endpointAdapter = adapter();
|
|
74
|
+
const router = factory.createRouter(provider({ "/a": target }), endpointAdapter);
|
|
75
|
+
const request = {};
|
|
76
|
+
const response = {};
|
|
77
|
+
const next = captureNext();
|
|
78
|
+
const handler = postRoutes(router)[0]?.[1];
|
|
79
|
+
await handler?.(request, response, next.fn);
|
|
80
|
+
expect(endpointAdapter.handleRequest).toHaveBeenCalledWith(target, request, response, next.fn);
|
|
81
|
+
});
|
|
82
|
+
test("routes each path to its own endpoint, not the last one registered", () => {
|
|
83
|
+
const first = endpoint();
|
|
84
|
+
const second = endpoint();
|
|
85
|
+
const endpointAdapter = adapter();
|
|
86
|
+
const router = factory.createRouter(provider({ "/first": first, "/second": second }), endpointAdapter);
|
|
87
|
+
for (const [, handler] of postRoutes(router)) {
|
|
88
|
+
void handler({}, {}, captureNext().fn);
|
|
89
|
+
}
|
|
90
|
+
const handled = vi.mocked(endpointAdapter.handleRequest).mock.calls;
|
|
91
|
+
expect(handled.map(([called]) => called)).toEqual([first, second]);
|
|
92
|
+
});
|
|
93
|
+
test("propagates a rejection from the adapter to the caller", async () => {
|
|
94
|
+
const endpointAdapter = adapter();
|
|
95
|
+
vi.mocked(endpointAdapter.handleRequest).mockRejectedValue(new Error("adapter blew up"));
|
|
96
|
+
const router = factory.createRouter(provider({ "/a": endpoint() }), endpointAdapter);
|
|
97
|
+
const handler = postRoutes(router)[0]?.[1];
|
|
98
|
+
await expect(handler?.({}, {}, captureNext().fn)).rejects.toThrow("adapter blew up");
|
|
99
|
+
});
|
|
100
|
+
test("does not call the endpoint itself — that is the adapter's job", async () => {
|
|
101
|
+
const target = endpoint();
|
|
102
|
+
const router = factory.createRouter(provider({ "/a": target }), adapter());
|
|
103
|
+
const handler = postRoutes(router)[0]?.[1];
|
|
104
|
+
await handler?.({}, {}, captureNext().fn);
|
|
105
|
+
expect(target.processRequest).not.toHaveBeenCalled();
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
//# sourceMappingURL=apiExpressRouterFactory.unit.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiExpressRouterFactory.unit.test.js","sourceRoot":"","sources":["../../../src/tests/unit/apiExpressRouterFactory.unit.test.ts"],"names":[],"mappings":"AAcA,OAAO,EAEN,yBAAyB,GAGzB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAEhE,OAAO,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAE3E,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAe/C,MAAM,QAAQ,GAAG,GAAqC,EAAE,CAAC,CAAC;IACzD,oBAAoB,EAAE,EAAE,CAAC,EAAE,CAA4B,GAAG,EAAE,CAAC,SAAS,CAAC;IACvE,cAAc,EAAE,EAAE,CAAC,EAAE,CACpB,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,yBAAyB,CAAC,OAAO,EAAE,CAAC,CAC3D;CACD,CAAC,CAAC;AAEH,MAAM,QAAQ,GAAG,CAAC,MAAiB,EAAqB,EAAE,CAAC,CAAC;IAC3D,SAAS,EAAE,GAAc,EAAE,CAAC,MAAM;CAClC,CAAC,CAAC;AAEH,MAAM,OAAO,GAAG,GAA8B,EAAE,CAAC,CAAC;IACjD,aAAa,EAAE,EAAE,CAAC,EAAE,CACnB,KAAK,IAAI,EAAE,CAAC,SAAS,CACrB;CACD,CAAC,CAAC;AAGH,MAAM,UAAU,GAAG,CAAC,MAAc,EAAuB,EAAE;IAC1D,MAAM,UAAU,GAAwB,EAAE,CAAC;IAC3C,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QAC1B,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,MAEzC,CAAC;QACb,IAAI,OAAO;YAAE,UAAU,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,UAAU,CAAC;AACnB,CAAC,CAAC;AAEF,IAAI,OAAgC,CAAC;AAErC,UAAU,CAAC,GAAG,EAAE;IACf,OAAO,GAAG,IAAI,uBAAuB,EAAE,CAAC;AACzC,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC7B,IAAI,CAAC,0DAA0D,EAAE,GAAG,EAAE;QACrE,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAClC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC,EAChD,OAAO,EAAE,CACT,CAAC;QACF,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAG9C,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAClC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC,EAC9B,OAAO,EAAE,CACT,CAAC;QACF,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAE7C,CAAC;QACb,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,iDAAiD,EAAE,GAAG,EAAE;QAG5D,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QAC7D,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAG7D,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAClC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC,EAChD,OAAO,EAAE,CACT,CAAC;QACF,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACnD,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACxC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,aAAa,EAAE,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;QAC1D,MAAM,SAAS,GAAG,EAAE,CAAC,EAAE,CAAkB,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC;QACvE,OAAO,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QAC/C,MAAM,CAAC,SAAS,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,iCAAiC,EAAE,GAAG,EAAE;QAC5C,MAAM,MAAM,GAAG,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QAC9C,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CACvD,OAAO,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CACvC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAG9D,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAClC,QAAQ,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,aAAa,EAAE,QAAQ,EAAE,EAAE,CAAC,EACxD,OAAO,EAAE,CACT,CAAC;QACF,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;YACxD,GAAG;YACH,aAAa;SACb,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,iCAAiC,EAAE,GAAG,EAAE;IAChD,IAAI,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACzE,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAC;QAC1B,MAAM,eAAe,GAAG,OAAO,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAClC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAC1B,eAAe,CACf,CAAC;QACF,MAAM,OAAO,GAAG,EAAa,CAAC;QAC9B,MAAM,QAAQ,GAAG,EAAc,CAAC;QAChC,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;QAE3B,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,OAAO,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QAE5C,MAAM,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,oBAAoB,CACzD,MAAM,EACN,OAAO,EACP,QAAQ,EACR,IAAI,CAAC,EAAE,CACP,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAG9E,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAC;QAC1B,MAAM,eAAe,GAAG,OAAO,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAClC,QAAQ,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,EAChD,eAAe,CACf,CAAC;QAEF,KAAK,MAAM,CAAC,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9C,KAAK,OAAO,CAAC,EAAa,EAAE,EAAc,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;QACpE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;QAGxE,MAAM,eAAe,GAAG,OAAO,EAAE,CAAC;QAClC,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,iBAAiB,CACzD,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAC5B,CAAC;QACF,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAClC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC,EAC9B,eAAe,CACf,CAAC;QAEF,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,MAAM,CACX,OAAO,EAAE,CAAC,EAAa,EAAE,EAAc,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC,CAC1D,CAAC,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;QAChF,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3E,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,OAAO,EAAE,CAAC,EAAa,EAAE,EAAc,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC;QACjE,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACtD,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.unit.test.d.ts","sourceRoot":"","sources":["../../../src/tests/unit/index.unit.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { LogLevel } from "@prosopo/logger";
|
|
2
|
+
import { describe, expect, test, vi } from "vitest";
|
|
3
|
+
import { apiExpressRouterFactory, authMiddleware, createApiExpressDefaultEndpointAdapter, handleErrors, requestLoggerMiddleware, } from "../../index.js";
|
|
4
|
+
describe("createApiExpressDefaultEndpointAdapter", () => {
|
|
5
|
+
test("builds an adapter exposing handleRequest", () => {
|
|
6
|
+
const adapter = createApiExpressDefaultEndpointAdapter(LogLevel.enum.info);
|
|
7
|
+
expect(typeof adapter.handleRequest).toBe("function");
|
|
8
|
+
});
|
|
9
|
+
test("builds a fresh adapter per call", () => {
|
|
10
|
+
expect(createApiExpressDefaultEndpointAdapter(LogLevel.enum.info)).not.toBe(createApiExpressDefaultEndpointAdapter(LogLevel.enum.info));
|
|
11
|
+
});
|
|
12
|
+
test("defaults the error status code so callers may omit it", () => {
|
|
13
|
+
expect(() => createApiExpressDefaultEndpointAdapter(LogLevel.enum.debug)).not.toThrow();
|
|
14
|
+
});
|
|
15
|
+
test("accepts an explicit error status code", () => {
|
|
16
|
+
expect(() => createApiExpressDefaultEndpointAdapter(LogLevel.enum.debug, 503)).not.toThrow();
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
describe("apiExpressRouterFactory", () => {
|
|
20
|
+
test("is exported as a single shared instance", () => {
|
|
21
|
+
expect(typeof apiExpressRouterFactory.createRouter).toBe("function");
|
|
22
|
+
});
|
|
23
|
+
test("builds a router from a provider", () => {
|
|
24
|
+
const provider = {
|
|
25
|
+
getRoutes: () => ({}),
|
|
26
|
+
};
|
|
27
|
+
const router = apiExpressRouterFactory.createRouter(provider, {
|
|
28
|
+
handleRequest: vi.fn(async () => undefined),
|
|
29
|
+
});
|
|
30
|
+
expect(typeof router).toBe("function");
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe("the re-exported middleware", () => {
|
|
34
|
+
test("exposes the error handler", () => {
|
|
35
|
+
expect(typeof handleErrors).toBe("function");
|
|
36
|
+
});
|
|
37
|
+
test("exposes the auth middleware", () => {
|
|
38
|
+
expect(typeof authMiddleware).toBe("function");
|
|
39
|
+
});
|
|
40
|
+
test("exposes the request logger middleware", () => {
|
|
41
|
+
expect(typeof requestLoggerMiddleware).toBe("function");
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
//# sourceMappingURL=index.unit.test.js.map
|