@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.unit.test.js","sourceRoot":"","sources":["../../../src/tests/unit/index.unit.test.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACpD,OAAO,EACN,uBAAuB,EACvB,cAAc,EACd,sCAAsC,EACtC,YAAY,EACZ,uBAAuB,GACvB,MAAM,gBAAgB,CAAC;AAQxB,QAAQ,CAAC,wCAAwC,EAAE,GAAG,EAAE;IACvD,IAAI,CAAC,0CAA0C,EAAE,GAAG,EAAE;QACrD,MAAM,OAAO,GAAG,sCAAsC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3E,MAAM,CAAC,OAAO,OAAO,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,iCAAiC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,sCAAsC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAC1E,sCAAsC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAC1D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAClE,MAAM,CAAC,GAAG,EAAE,CACX,sCAAsC,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAC3D,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,GAAG,EAAE,CACX,sCAAsC,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAChE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACxC,IAAI,CAAC,yCAAyC,EAAE,GAAG,EAAE;QAGpD,MAAM,CAAC,OAAO,uBAAuB,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,iCAAiC,EAAE,GAAG,EAAE;QAC5C,MAAM,QAAQ,GAAsB;YACnC,SAAS,EAAE,GAAc,EAAE,CAAC,CAAC,EAAE,CAAC;SAChC,CAAC;QACF,MAAM,MAAM,GAAG,uBAAuB,CAAC,YAAY,CAAC,QAAQ,EAAE;YAC7D,aAAa,EAAE,EAAE,CAAC,EAAE,CAOlB,KAAK,IAAI,EAAE,CAAC,SAAS,CAAC;SACxB,CAAC,CAAC;QACH,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC3C,IAAI,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACtC,MAAM,CAAC,OAAO,YAAY,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACxC,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,OAAO,uBAAuB,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
|
@@ -1,144 +1,218 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
};
|
|
16
|
-
vi.mock("@polkadot/util", async (importOriginal) => {
|
|
17
|
-
const actual = await importOriginal();
|
|
18
|
-
return {
|
|
19
|
-
...actual,
|
|
20
|
-
hexToU8a: vi.fn(),
|
|
21
|
-
isHex: vi.fn(),
|
|
22
|
-
};
|
|
1
|
+
import { ProsopoApiError } from "@prosopo/common";
|
|
2
|
+
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
3
|
+
import { authMiddleware, verifySignature, } from "../../../middlewares/authMiddleware.js";
|
|
4
|
+
import { captureNext } from "../testDoubles.js";
|
|
5
|
+
const pairThat = (verify) => ({
|
|
6
|
+
jwtVerify: vi.fn(verify),
|
|
7
|
+
address: "5Test",
|
|
8
|
+
publicKey: new Uint8Array([1, 2, 3]),
|
|
9
|
+
verify: vi.fn(() => true),
|
|
10
|
+
});
|
|
11
|
+
const accepts = () => pairThat(() => ({ isValid: true }));
|
|
12
|
+
const rejects = () => pairThat(() => ({ isValid: false }));
|
|
13
|
+
const explodes = () => pairThat(() => {
|
|
14
|
+
throw new Error("verifier unavailable");
|
|
23
15
|
});
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
16
|
+
const build = (headers = {}) => {
|
|
17
|
+
const json = vi.fn();
|
|
18
|
+
const status = vi.fn();
|
|
19
|
+
const res = { status, json };
|
|
20
|
+
status.mockReturnValue(res);
|
|
21
|
+
const logged = vi.fn();
|
|
22
|
+
const req = {
|
|
23
|
+
headers,
|
|
24
|
+
logger: { error: logged },
|
|
25
|
+
};
|
|
26
|
+
return { req, res, next: captureNext(), status, json, logged };
|
|
28
27
|
};
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
const bearer = (token) => ({
|
|
29
|
+
authorization: `Bearer ${token}`,
|
|
30
|
+
});
|
|
31
|
+
let harness;
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
harness = build(bearer("token-1"));
|
|
34
|
+
});
|
|
35
|
+
const run = async (pair, authAccount) => {
|
|
36
|
+
await authMiddleware(pair, authAccount)(harness.req, harness.res, harness.next.fn);
|
|
34
37
|
};
|
|
35
|
-
describe("
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}),
|
|
89
|
-
};
|
|
90
|
-
const mockRes = {
|
|
91
|
-
status: vi.fn().mockReturnThis(),
|
|
92
|
-
json: vi.fn(),
|
|
93
|
-
};
|
|
94
|
-
const mockNext = vi.fn();
|
|
95
|
-
vi.mocked(isHex).mockReturnValue(true);
|
|
96
|
-
vi.mocked(hexToU8a).mockReturnValue(new Uint8Array());
|
|
97
|
-
vi.mocked(mockPair.jwtVerify).mockReturnValue({
|
|
98
|
-
isValid: false,
|
|
99
|
-
});
|
|
100
|
-
const middleware = authMiddleware(mockEnv.pair, mockEnv.authAccount);
|
|
101
|
-
await middleware(mockReq, mockRes, mockNext);
|
|
102
|
-
expect(mockNext).not.toHaveBeenCalled();
|
|
103
|
-
expect(mockRes.status).toHaveBeenCalledWith(401);
|
|
104
|
-
expect(mockRes.json).toHaveBeenCalledWith({
|
|
105
|
-
error: new ProsopoEnvError("API.UNAUTHORIZED", {
|
|
106
|
-
context: { i18n: mockReq.i18n, code: 401 },
|
|
107
|
-
}),
|
|
108
|
-
});
|
|
38
|
+
describe("a token one of the keys accepts", () => {
|
|
39
|
+
test("passes the request through on the auth account", async () => {
|
|
40
|
+
await run(rejects(), accepts());
|
|
41
|
+
expect(harness.next.calls).toHaveLength(1);
|
|
42
|
+
expect(harness.status).not.toHaveBeenCalled();
|
|
43
|
+
});
|
|
44
|
+
test("passes the request through on the provider pair", async () => {
|
|
45
|
+
await run(accepts(), rejects());
|
|
46
|
+
expect(harness.next.calls).toHaveLength(1);
|
|
47
|
+
});
|
|
48
|
+
test("does not consult the provider pair once the auth account accepts", async () => {
|
|
49
|
+
const pair = accepts();
|
|
50
|
+
await run(pair, accepts());
|
|
51
|
+
expect(pair.jwtVerify).not.toHaveBeenCalled();
|
|
52
|
+
});
|
|
53
|
+
test("works when only the provider pair is configured", async () => {
|
|
54
|
+
await run(accepts(), undefined);
|
|
55
|
+
expect(harness.next.calls).toHaveLength(1);
|
|
56
|
+
});
|
|
57
|
+
test("works when only the auth account is configured", async () => {
|
|
58
|
+
await run(undefined, accepts());
|
|
59
|
+
expect(harness.next.calls).toHaveLength(1);
|
|
60
|
+
});
|
|
61
|
+
test("hands the verifier the token without its Bearer prefix", async () => {
|
|
62
|
+
const pair = accepts();
|
|
63
|
+
await run(pair, undefined);
|
|
64
|
+
expect(pair.jwtVerify).toHaveBeenCalledWith("token-1");
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe("a token neither key accepts", () => {
|
|
68
|
+
beforeEach(async () => {
|
|
69
|
+
await run(rejects(), rejects());
|
|
70
|
+
});
|
|
71
|
+
test("is answered 401 and never reaches the handler", () => {
|
|
72
|
+
expect(harness.status).toHaveBeenCalledWith(401);
|
|
73
|
+
expect(harness.next.calls).toHaveLength(0);
|
|
74
|
+
});
|
|
75
|
+
test("is answered with an error body carrying the 401 code", () => {
|
|
76
|
+
const body = harness.json.mock.calls[0]?.[0];
|
|
77
|
+
expect(body.error.context?.code).toBe(401);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe("a request with no usable credentials", () => {
|
|
81
|
+
test("no configured keys at all is rejected, not allowed through", async () => {
|
|
82
|
+
await run(undefined, undefined);
|
|
83
|
+
expect(harness.status).toHaveBeenCalledWith(401);
|
|
84
|
+
expect(harness.next.calls).toHaveLength(0);
|
|
85
|
+
});
|
|
86
|
+
test("a missing Authorization header is rejected", async () => {
|
|
87
|
+
harness = build({});
|
|
88
|
+
await run(accepts(), accepts());
|
|
89
|
+
expect(harness.status).toHaveBeenCalledWith(401);
|
|
90
|
+
expect(harness.next.calls).toHaveLength(0);
|
|
109
91
|
});
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
log: vi.fn().mockImplementation(loggerOuter.log.bind(loggerOuter)),
|
|
114
|
-
info: vi.fn().mockImplementation(loggerOuter.info.bind(loggerOuter)),
|
|
115
|
-
error: vi.fn().mockImplementation(loggerOuter.error.bind(loggerOuter)),
|
|
116
|
-
trace: vi.fn().mockImplementation(loggerOuter.trace.bind(loggerOuter)),
|
|
117
|
-
fatal: vi.fn().mockImplementation(loggerOuter.fatal.bind(loggerOuter)),
|
|
118
|
-
warn: vi.fn().mockImplementation(loggerOuter.warn.bind(loggerOuter)),
|
|
119
|
-
};
|
|
120
|
-
const mockReq = {
|
|
121
|
-
url: "/v1/prosopo/provider/captcha/image",
|
|
122
|
-
originalUrl: "/v1/prosopo/provider/captcha/image",
|
|
123
|
-
headers: {
|
|
124
|
-
Authorization: "Bearer mockToken",
|
|
125
|
-
},
|
|
126
|
-
logger: mockLogger,
|
|
127
|
-
};
|
|
128
|
-
const mockRes = {
|
|
129
|
-
status: vi.fn().mockReturnThis(),
|
|
130
|
-
json: vi.fn(),
|
|
131
|
-
};
|
|
132
|
-
const mockNext = vi.fn();
|
|
133
|
-
const middleware = authMiddleware(undefined, undefined);
|
|
134
|
-
await middleware(mockReq, mockRes, mockNext);
|
|
135
|
-
expect(mockNext).not.toHaveBeenCalled();
|
|
136
|
-
expect(mockRes.status).toHaveBeenCalledWith(401);
|
|
137
|
-
expect(mockRes.json).toHaveBeenCalledWith({
|
|
138
|
-
error: new ProsopoEnvError("API.UNAUTHORIZED", {
|
|
139
|
-
context: { i18n: mockReq.i18n, code: 401 },
|
|
140
|
-
}),
|
|
92
|
+
test("an Authorization header that is not a string is rejected", async () => {
|
|
93
|
+
harness = build({
|
|
94
|
+
authorization: ["Bearer a", "Bearer b"],
|
|
141
95
|
});
|
|
96
|
+
await run(accepts(), accepts());
|
|
97
|
+
expect(harness.status).toHaveBeenCalledWith(401);
|
|
98
|
+
});
|
|
99
|
+
test("an empty Authorization header is rejected", async () => {
|
|
100
|
+
harness = build({ authorization: "" });
|
|
101
|
+
await run(accepts(), accepts());
|
|
102
|
+
expect(harness.status).toHaveBeenCalledWith(401);
|
|
103
|
+
});
|
|
104
|
+
test("a Bearer prefix with no token after it is rejected", async () => {
|
|
105
|
+
harness = build({ authorization: "Bearer " });
|
|
106
|
+
await run(accepts(), accepts());
|
|
107
|
+
expect(harness.status).toHaveBeenCalledWith(401);
|
|
108
|
+
expect(harness.next.calls).toHaveLength(0);
|
|
109
|
+
});
|
|
110
|
+
test("the verifier is never reached for a malformed header", async () => {
|
|
111
|
+
harness = build({});
|
|
112
|
+
const pair = accepts();
|
|
113
|
+
await run(pair, undefined);
|
|
114
|
+
expect(pair.jwtVerify).not.toHaveBeenCalled();
|
|
115
|
+
});
|
|
116
|
+
test("a header rejection is logged", async () => {
|
|
117
|
+
harness = build({});
|
|
118
|
+
await run(accepts(), undefined);
|
|
119
|
+
expect(harness.logged).toHaveBeenCalledTimes(1);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
describe("header handling quirks worth pinning down", () => {
|
|
123
|
+
test("a bare token with no Bearer prefix is accepted as the token", async () => {
|
|
124
|
+
harness = build({ authorization: "token-1" });
|
|
125
|
+
const pair = accepts();
|
|
126
|
+
await run(pair, undefined);
|
|
127
|
+
expect(pair.jwtVerify).toHaveBeenCalledWith("token-1");
|
|
128
|
+
expect(harness.next.calls).toHaveLength(1);
|
|
129
|
+
});
|
|
130
|
+
test("a capitalised Authorization key is read as well as the lowercase one", async () => {
|
|
131
|
+
harness = build({ Authorization: "Bearer token-1" });
|
|
132
|
+
const pair = accepts();
|
|
133
|
+
await run(pair, undefined);
|
|
134
|
+
expect(pair.jwtVerify).toHaveBeenCalledWith("token-1");
|
|
135
|
+
expect(harness.next.calls).toHaveLength(1);
|
|
136
|
+
});
|
|
137
|
+
test("a Bearer prefix appearing inside the token is also stripped", async () => {
|
|
138
|
+
harness = build({ authorization: "abcBearer xyz" });
|
|
139
|
+
const pair = accepts();
|
|
140
|
+
await run(pair, undefined);
|
|
141
|
+
expect(pair.jwtVerify).toHaveBeenCalledWith("abcxyz");
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
describe("a verifier that throws", () => {
|
|
145
|
+
test("is answered 401 rather than crashing the request", async () => {
|
|
146
|
+
await run(explodes(), undefined);
|
|
147
|
+
expect(harness.status).toHaveBeenCalledWith(401);
|
|
148
|
+
expect(harness.next.calls).toHaveLength(0);
|
|
149
|
+
});
|
|
150
|
+
test("is logged against the request logger", async () => {
|
|
151
|
+
await run(explodes(), undefined);
|
|
152
|
+
expect(harness.logged).toHaveBeenCalledTimes(1);
|
|
153
|
+
const entry = harness.logged.mock.calls[0]?.[0];
|
|
154
|
+
expect(entry().msg).toBe("Auth Middleware Error");
|
|
155
|
+
});
|
|
156
|
+
test("a throwing auth account does not fall through to the provider pair", async () => {
|
|
157
|
+
const pair = accepts();
|
|
158
|
+
await run(pair, explodes());
|
|
159
|
+
expect(pair.jwtVerify).not.toHaveBeenCalled();
|
|
160
|
+
expect(harness.next.calls).toHaveLength(0);
|
|
161
|
+
});
|
|
162
|
+
test("does not resolve before it has answered", async () => {
|
|
163
|
+
const middleware = authMiddleware(explodes(), undefined);
|
|
164
|
+
await middleware(harness.req, harness.res, harness.next.fn);
|
|
165
|
+
expect(harness.json).toHaveBeenCalledTimes(1);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
describe("verifySignature", () => {
|
|
169
|
+
const message = "sign me";
|
|
170
|
+
const signature = "0x0102";
|
|
171
|
+
test("returns quietly when the pair verifies the signature", () => {
|
|
172
|
+
const pair = pairThat(() => ({ isValid: true }));
|
|
173
|
+
expect(() => verifySignature(signature, message, pair)).not.toThrow();
|
|
174
|
+
});
|
|
175
|
+
test("passes the decoded signature bytes to the pair", () => {
|
|
176
|
+
const pair = pairThat(() => ({ isValid: true }));
|
|
177
|
+
verifySignature(signature, message, pair);
|
|
178
|
+
expect(pair.verify).toHaveBeenCalledWith(message, new Uint8Array([1, 2]), pair.publicKey);
|
|
179
|
+
});
|
|
180
|
+
test("throws a 401 error when verification fails", () => {
|
|
181
|
+
const pair = pairThat(() => ({ isValid: true }));
|
|
182
|
+
vi.mocked(pair.verify).mockReturnValue(false);
|
|
183
|
+
try {
|
|
184
|
+
verifySignature(signature, message, pair);
|
|
185
|
+
expect.unreachable("verifySignature should have thrown");
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
expect(error).toBeInstanceOf(ProsopoApiError);
|
|
189
|
+
expect(error.context?.code).toBe(401);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
test("reports which account and message failed", () => {
|
|
193
|
+
const pair = pairThat(() => ({ isValid: true }));
|
|
194
|
+
vi.mocked(pair.verify).mockReturnValue(false);
|
|
195
|
+
try {
|
|
196
|
+
verifySignature(signature, message, pair);
|
|
197
|
+
expect.unreachable("verifySignature should have thrown");
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
const context = error.context;
|
|
201
|
+
expect(context?.account).toBe("5Test");
|
|
202
|
+
expect(context?.message).toBe(message);
|
|
203
|
+
expect(context?.signature).toBe(signature);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
test("a non-hex signature is decoded to garbage rather than rejected", () => {
|
|
207
|
+
const pair = pairThat(() => ({ isValid: true }));
|
|
208
|
+
vi.mocked(pair.verify).mockReturnValue(false);
|
|
209
|
+
expect(() => verifySignature("not hex", message, pair)).toThrow(ProsopoApiError);
|
|
210
|
+
expect(pair.verify).toHaveBeenCalledTimes(1);
|
|
211
|
+
});
|
|
212
|
+
test("an empty message is still passed through to the pair", () => {
|
|
213
|
+
const pair = pairThat(() => ({ isValid: true }));
|
|
214
|
+
verifySignature(signature, "", pair);
|
|
215
|
+
expect(pair.verify).toHaveBeenCalledWith("", new Uint8Array([1, 2]), pair.publicKey);
|
|
142
216
|
});
|
|
143
217
|
});
|
|
144
218
|
//# sourceMappingURL=authMiddleware.unit.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authMiddleware.unit.test.js","sourceRoot":"","sources":["../../../../src/tests/unit/middlewares/authMiddleware.unit.test.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"authMiddleware.unit.test.js","sourceRoot":"","sources":["../../../../src/tests/unit/middlewares/authMiddleware.unit.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requestLoggerMiddleware.unit.test.d.ts","sourceRoot":"","sources":["../../../../src/tests/unit/middlewares/requestLoggerMiddleware.unit.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
3
|
+
import { requestLoggerMiddleware } from "../../../middlewares/requestLoggerMiddleware.js";
|
|
4
|
+
import { captureNext } from "../testDoubles.js";
|
|
5
|
+
const recorded = { bindings: {}, infos: [] };
|
|
6
|
+
const { getLoggerMock } = vi.hoisted(() => ({
|
|
7
|
+
getLoggerMock: vi.fn(),
|
|
8
|
+
}));
|
|
9
|
+
vi.mock("@prosopo/logger", async () => {
|
|
10
|
+
const actual = await vi.importActual("@prosopo/logger");
|
|
11
|
+
return { ...actual, getLogger: getLoggerMock };
|
|
12
|
+
});
|
|
13
|
+
const makeLogger = () => {
|
|
14
|
+
const logger = {
|
|
15
|
+
with: (bindings) => {
|
|
16
|
+
recorded.bindings = bindings;
|
|
17
|
+
return logger;
|
|
18
|
+
},
|
|
19
|
+
info: (entry) => {
|
|
20
|
+
recorded.infos.push(entry());
|
|
21
|
+
},
|
|
22
|
+
error: vi.fn(),
|
|
23
|
+
debug: vi.fn(),
|
|
24
|
+
warn: vi.fn(),
|
|
25
|
+
};
|
|
26
|
+
return logger;
|
|
27
|
+
};
|
|
28
|
+
const env = { config: { logLevel: "info" } };
|
|
29
|
+
const build = (overrides = {}) => {
|
|
30
|
+
const emitter = new EventEmitter();
|
|
31
|
+
const setHeader = vi.fn();
|
|
32
|
+
const res = Object.assign(emitter, {
|
|
33
|
+
setHeader,
|
|
34
|
+
statusCode: overrides.statusCode ?? 200,
|
|
35
|
+
});
|
|
36
|
+
const req = {
|
|
37
|
+
headers: overrides.headers ?? {},
|
|
38
|
+
body: overrides.body,
|
|
39
|
+
path: overrides.path ?? "/verify",
|
|
40
|
+
method: overrides.method ?? "POST",
|
|
41
|
+
};
|
|
42
|
+
return { req, res, next: captureNext(), setHeader };
|
|
43
|
+
};
|
|
44
|
+
const run = (harness) => {
|
|
45
|
+
requestLoggerMiddleware(env)(harness.req, harness.res, harness.next.fn);
|
|
46
|
+
};
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
recorded.bindings = {};
|
|
49
|
+
recorded.infos = [];
|
|
50
|
+
getLoggerMock.mockReset();
|
|
51
|
+
getLoggerMock.mockImplementation(() => makeLogger());
|
|
52
|
+
});
|
|
53
|
+
describe("the request id", () => {
|
|
54
|
+
test("reuses the id an upstream proxy already assigned", () => {
|
|
55
|
+
const harness = build({ headers: { "x-request-id": "caddy-123" } });
|
|
56
|
+
run(harness);
|
|
57
|
+
expect(harness.req.requestId).toBe("caddy-123");
|
|
58
|
+
expect(recorded.bindings.requestId).toBe("caddy-123");
|
|
59
|
+
});
|
|
60
|
+
test("generates an e-prefixed uuid when there is no inbound id", () => {
|
|
61
|
+
const harness = build();
|
|
62
|
+
run(harness);
|
|
63
|
+
expect(harness.req.requestId).toMatch(/^e-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/);
|
|
64
|
+
});
|
|
65
|
+
test("generates a fresh id per request", () => {
|
|
66
|
+
const first = build();
|
|
67
|
+
const second = build();
|
|
68
|
+
run(first);
|
|
69
|
+
run(second);
|
|
70
|
+
expect(first.req.requestId).not.toBe(second.req.requestId);
|
|
71
|
+
});
|
|
72
|
+
test("falls back for an empty inbound id rather than using a blank one", () => {
|
|
73
|
+
const harness = build({ headers: { "x-request-id": "" } });
|
|
74
|
+
run(harness);
|
|
75
|
+
expect(harness.req.requestId).toMatch(/^e-/);
|
|
76
|
+
});
|
|
77
|
+
test("mirrors the id back onto the response", () => {
|
|
78
|
+
const harness = build({ headers: { "x-request-id": "caddy-123" } });
|
|
79
|
+
run(harness);
|
|
80
|
+
expect(harness.setHeader).toHaveBeenCalledWith("x-request-id", "caddy-123");
|
|
81
|
+
});
|
|
82
|
+
test("mirrors the generated id too, not just an inbound one", () => {
|
|
83
|
+
const harness = build();
|
|
84
|
+
run(harness);
|
|
85
|
+
expect(harness.setHeader).toHaveBeenCalledWith("x-request-id", harness.req.requestId);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe("the log bindings", () => {
|
|
89
|
+
test("carry the user and site key headers when present", () => {
|
|
90
|
+
const harness = build({
|
|
91
|
+
headers: { "prosopo-user": "alice", "prosopo-site-key": "site-1" },
|
|
92
|
+
});
|
|
93
|
+
run(harness);
|
|
94
|
+
expect(recorded.bindings.user).toBe("alice");
|
|
95
|
+
expect(recorded.bindings.siteKey).toBe("site-1");
|
|
96
|
+
});
|
|
97
|
+
test("omit absent fields entirely rather than binding undefined", () => {
|
|
98
|
+
const harness = build();
|
|
99
|
+
run(harness);
|
|
100
|
+
expect(Object.keys(recorded.bindings)).toEqual(["requestId"]);
|
|
101
|
+
});
|
|
102
|
+
test("omit headers present but empty", () => {
|
|
103
|
+
const harness = build({
|
|
104
|
+
headers: { "prosopo-user": "", "prosopo-site-key": "" },
|
|
105
|
+
});
|
|
106
|
+
run(harness);
|
|
107
|
+
expect(Object.keys(recorded.bindings)).toEqual(["requestId"]);
|
|
108
|
+
});
|
|
109
|
+
test("collapse a repeated header to a single string", () => {
|
|
110
|
+
const harness = build({
|
|
111
|
+
headers: { "prosopo-user": ["alice", "bob"] },
|
|
112
|
+
});
|
|
113
|
+
run(harness);
|
|
114
|
+
expect(recorded.bindings.user).toBe("alice,bob");
|
|
115
|
+
});
|
|
116
|
+
test("carry the session id off the request body", () => {
|
|
117
|
+
const harness = build({ body: { sessionId: "sess-9" } });
|
|
118
|
+
run(harness);
|
|
119
|
+
expect(recorded.bindings.sessionId).toBe("sess-9");
|
|
120
|
+
});
|
|
121
|
+
test("tolerate a missing body", () => {
|
|
122
|
+
const harness = build({ body: undefined });
|
|
123
|
+
expect(() => run(harness)).not.toThrow();
|
|
124
|
+
expect(recorded.bindings.sessionId).toBeUndefined();
|
|
125
|
+
});
|
|
126
|
+
test("tolerate a body that is not an object", () => {
|
|
127
|
+
const harness = build({ body: "raw text" });
|
|
128
|
+
expect(() => run(harness)).not.toThrow();
|
|
129
|
+
expect(recorded.bindings.sessionId).toBeUndefined();
|
|
130
|
+
});
|
|
131
|
+
test("omit an empty session id", () => {
|
|
132
|
+
const harness = build({ body: { sessionId: "" } });
|
|
133
|
+
run(harness);
|
|
134
|
+
expect(recorded.bindings.sessionId).toBeUndefined();
|
|
135
|
+
});
|
|
136
|
+
test("attach the bound logger to the request for handlers downstream", () => {
|
|
137
|
+
const harness = build();
|
|
138
|
+
run(harness);
|
|
139
|
+
expect(harness.req.logger).toBeDefined();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
describe("envelope logging", () => {
|
|
143
|
+
test("logs a request-received line before calling next", () => {
|
|
144
|
+
const harness = build({ path: "/verify", method: "POST" });
|
|
145
|
+
run(harness);
|
|
146
|
+
expect(recorded.infos[0]).toEqual({
|
|
147
|
+
msg: "Request received",
|
|
148
|
+
data: { method: "POST", path: "/verify" },
|
|
149
|
+
});
|
|
150
|
+
expect(harness.next.calls).toHaveLength(1);
|
|
151
|
+
});
|
|
152
|
+
test("logs a response-sent line with status and outcome when finish fires", () => {
|
|
153
|
+
const harness = build({ statusCode: 201 });
|
|
154
|
+
run(harness);
|
|
155
|
+
harness.res.emit("finish");
|
|
156
|
+
const sent = recorded.infos[1];
|
|
157
|
+
expect(sent?.msg).toBe("Response sent");
|
|
158
|
+
expect(sent?.data?.status).toBe(201);
|
|
159
|
+
expect(sent?.data?.outcome).toBe("finish");
|
|
160
|
+
expect(typeof sent?.data?.durationMs).toBe("number");
|
|
161
|
+
});
|
|
162
|
+
test("records a client disconnect as a close outcome", () => {
|
|
163
|
+
const harness = build();
|
|
164
|
+
run(harness);
|
|
165
|
+
harness.res.emit("close");
|
|
166
|
+
expect(recorded.infos[1]?.data?.outcome).toBe("close");
|
|
167
|
+
});
|
|
168
|
+
test("logs the response only once when both events fire", () => {
|
|
169
|
+
const harness = build();
|
|
170
|
+
run(harness);
|
|
171
|
+
harness.res.emit("finish");
|
|
172
|
+
harness.res.emit("close");
|
|
173
|
+
expect(recorded.infos.filter((e) => e.msg === "Response sent")).toHaveLength(1);
|
|
174
|
+
});
|
|
175
|
+
test("keeps the first outcome when close follows finish", () => {
|
|
176
|
+
const harness = build();
|
|
177
|
+
run(harness);
|
|
178
|
+
harness.res.emit("finish");
|
|
179
|
+
harness.res.emit("close");
|
|
180
|
+
expect(recorded.infos[1]?.data?.outcome).toBe("finish");
|
|
181
|
+
});
|
|
182
|
+
test("reads the status at emit time, not at middleware time", () => {
|
|
183
|
+
const harness = build({ statusCode: 200 });
|
|
184
|
+
run(harness);
|
|
185
|
+
harness.res.statusCode = 500;
|
|
186
|
+
harness.res.emit("finish");
|
|
187
|
+
expect(recorded.infos[1]?.data?.status).toBe(500);
|
|
188
|
+
});
|
|
189
|
+
test("reports a non-negative duration", () => {
|
|
190
|
+
const harness = build();
|
|
191
|
+
run(harness);
|
|
192
|
+
harness.res.emit("finish");
|
|
193
|
+
expect(recorded.infos[1]?.data?.durationMs).toBeGreaterThanOrEqual(0);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
describe("health probes", () => {
|
|
197
|
+
test.each(["/healthz", "/health", "/readyz"])("%s is not envelope-logged", (path) => {
|
|
198
|
+
const harness = build({ path });
|
|
199
|
+
run(harness);
|
|
200
|
+
harness.res.emit("finish");
|
|
201
|
+
expect(recorded.infos).toEqual([]);
|
|
202
|
+
expect(harness.next.calls).toHaveLength(1);
|
|
203
|
+
});
|
|
204
|
+
test("a health probe still gets a request id and a logger", () => {
|
|
205
|
+
const harness = build({ path: "/healthz" });
|
|
206
|
+
run(harness);
|
|
207
|
+
expect(harness.req.requestId).toMatch(/^e-/);
|
|
208
|
+
expect(harness.req.logger).toBeDefined();
|
|
209
|
+
expect(harness.setHeader).toHaveBeenCalledWith("x-request-id", harness.req.requestId);
|
|
210
|
+
});
|
|
211
|
+
test("a path that merely looks like a probe is still logged", () => {
|
|
212
|
+
const harness = build({ path: "/healthz/deep" });
|
|
213
|
+
run(harness);
|
|
214
|
+
expect(recorded.infos[0]?.msg).toBe("Request received");
|
|
215
|
+
});
|
|
216
|
+
test("registers no response listeners for a probe", () => {
|
|
217
|
+
const harness = build({ path: "/healthz" });
|
|
218
|
+
run(harness);
|
|
219
|
+
expect(harness.res.listenerCount("finish")).toBe(0);
|
|
220
|
+
expect(harness.res.listenerCount("close")).toBe(0);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
describe("the environment it is built from", () => {
|
|
224
|
+
test("builds its logger under the provider:request scope at the configured level", () => {
|
|
225
|
+
const harness = build();
|
|
226
|
+
run(harness);
|
|
227
|
+
expect(getLoggerMock).toHaveBeenCalledWith("info", "provider:request");
|
|
228
|
+
});
|
|
229
|
+
test("reads the log level from the environment on every request", () => {
|
|
230
|
+
const mutable = {
|
|
231
|
+
config: { logLevel: "info" },
|
|
232
|
+
};
|
|
233
|
+
const middleware = requestLoggerMiddleware(mutable);
|
|
234
|
+
const first = build();
|
|
235
|
+
middleware(first.req, first.res, first.next.fn);
|
|
236
|
+
mutable.config.logLevel = "debug";
|
|
237
|
+
const second = build();
|
|
238
|
+
middleware(second.req, second.res, second.next.fn);
|
|
239
|
+
expect(getLoggerMock).toHaveBeenLastCalledWith("debug", "provider:request");
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
//# sourceMappingURL=requestLoggerMiddleware.unit.test.js.map
|