skuba 11.1.0-jest30-20250620003740 → 11.1.0
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/lib/cli/migrate/nodeVersion/index.js +18 -18
- package/lib/cli/migrate/nodeVersion/index.js.map +2 -2
- package/package.json +15 -15
- package/template/express-rest-api/.buildkite/pipeline.yml +1 -1
- package/template/express-rest-api/Dockerfile.dev-deps +1 -1
- package/template/express-rest-api/package.json +1 -1
- package/template/greeter/.buildkite/pipeline.yml +1 -1
- package/template/greeter/Dockerfile +1 -1
- package/template/greeter/package.json +2 -2
- package/template/koa-rest-api/.buildkite/pipeline.yml +1 -1
- package/template/koa-rest-api/Dockerfile.dev-deps +1 -1
- package/template/koa-rest-api/package.json +7 -7
- package/template/koa-rest-api/src/api/jobs/postJob.test.ts +1 -1
- package/template/koa-rest-api/src/config.ts +1 -1
- package/template/koa-rest-api/src/framework/logging.ts +21 -12
- package/template/koa-rest-api/src/framework/server.test.ts +91 -60
- package/template/koa-rest-api/src/framework/validation.test.ts +31 -31
- package/template/koa-rest-api/src/framework/validation.ts +14 -21
- package/template/koa-rest-api/src/testing/types.ts +1 -1
- package/template/koa-rest-api/src/types/jobs.ts +1 -1
- package/template/lambda-sqs-worker-cdk/.buildkite/pipeline.yml +2 -2
- package/template/lambda-sqs-worker-cdk/Dockerfile +1 -1
- package/template/lambda-sqs-worker-cdk/infra/__snapshots__/appStack.test.ts.snap +6 -0
- package/template/lambda-sqs-worker-cdk/package.json +5 -5
- package/template/lambda-sqs-worker-cdk/src/app.test.ts +76 -17
- package/template/lambda-sqs-worker-cdk/src/config.ts +1 -1
- package/template/lambda-sqs-worker-cdk/src/framework/handler.test.ts +35 -15
- package/template/lambda-sqs-worker-cdk/src/framework/logging.ts +22 -11
- package/template/lambda-sqs-worker-cdk/src/framework/validation.test.ts +6 -9
- package/template/lambda-sqs-worker-cdk/src/framework/validation.ts +3 -7
- package/template/lambda-sqs-worker-cdk/src/testing/types.ts +1 -1
- package/template/lambda-sqs-worker-cdk/src/types/jobScorer.ts +1 -1
- package/template/lambda-sqs-worker-cdk/src/types/pipelineEvents.ts +1 -1
- package/template/koa-rest-api/src/testing/logging.ts +0 -16
- package/template/lambda-sqs-worker-cdk/src/testing/logging.ts +0 -19
|
@@ -43,15 +43,15 @@ describe('validate', () => {
|
|
|
43
43
|
.expect(422)
|
|
44
44
|
.expect(({ body }) =>
|
|
45
45
|
expect(body).toMatchInlineSnapshot(`
|
|
46
|
-
{
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
`),
|
|
46
|
+
{
|
|
47
|
+
"invalidFields": {
|
|
48
|
+
"~union0/id": "Invalid input: expected string, received null",
|
|
49
|
+
"~union1/id": "Invalid input: expected number, received null",
|
|
50
|
+
"~union1/summary": "Invalid input: expected string, received undefined",
|
|
51
|
+
},
|
|
52
|
+
"message": "Input validation failed",
|
|
53
|
+
}
|
|
54
|
+
`),
|
|
55
55
|
);
|
|
56
56
|
});
|
|
57
57
|
|
|
@@ -62,17 +62,17 @@ describe('validate', () => {
|
|
|
62
62
|
.expect(422)
|
|
63
63
|
.expect(({ body }) =>
|
|
64
64
|
expect(body).toMatchInlineSnapshot(`
|
|
65
|
-
{
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
`),
|
|
65
|
+
{
|
|
66
|
+
"invalidFields": {
|
|
67
|
+
"~union0/description~union0": "Invalid input: expected string, received undefined",
|
|
68
|
+
"~union0/description~union1": "Invalid input: expected object, received undefined",
|
|
69
|
+
"~union0/id": "Invalid input: expected string, received undefined",
|
|
70
|
+
"~union1/id": "Invalid input: expected number, received undefined",
|
|
71
|
+
"~union1/summary": "Invalid input: expected string, received undefined",
|
|
72
|
+
},
|
|
73
|
+
"message": "Input validation failed",
|
|
74
|
+
}
|
|
75
|
+
`),
|
|
76
76
|
));
|
|
77
77
|
|
|
78
78
|
it('blocks invalid nested union prop', () => {
|
|
@@ -89,17 +89,17 @@ describe('validate', () => {
|
|
|
89
89
|
.expect(422)
|
|
90
90
|
.expect(({ body }) =>
|
|
91
91
|
expect(body).toMatchInlineSnapshot(`
|
|
92
|
-
{
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
`),
|
|
92
|
+
{
|
|
93
|
+
"invalidFields": {
|
|
94
|
+
"~union0/description~union0": "Invalid input: expected string, received object",
|
|
95
|
+
"~union0/description~union1/content": "Invalid input: expected string, received undefined",
|
|
96
|
+
"~union0/id": "Invalid input: expected string, received null",
|
|
97
|
+
"~union1/id": "Invalid input: expected number, received null",
|
|
98
|
+
"~union1/summary": "Invalid input: expected string, received undefined",
|
|
99
|
+
},
|
|
100
|
+
"message": "Input validation failed",
|
|
101
|
+
}
|
|
102
|
+
`),
|
|
103
103
|
);
|
|
104
104
|
});
|
|
105
105
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ErrorMiddleware } from 'seek-koala';
|
|
2
|
-
import {
|
|
2
|
+
import type { core, z } from 'zod/v4';
|
|
3
3
|
|
|
4
4
|
import type { Context } from 'src/types/koa';
|
|
5
5
|
|
|
@@ -35,41 +35,38 @@ type InvalidFields = Record<string, string>;
|
|
|
35
35
|
* @see [union error example](./validation.test.ts)
|
|
36
36
|
*/
|
|
37
37
|
const parseInvalidFieldsFromError = (err: z.ZodError): InvalidFields =>
|
|
38
|
-
Object.fromEntries(parseTuples(err
|
|
38
|
+
Object.fromEntries(parseTuples(err.issues));
|
|
39
39
|
|
|
40
40
|
const parseTuples = (
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
errors: core.$ZodIssue[],
|
|
42
|
+
basePath: Array<string | number | symbol> = [],
|
|
43
|
+
unions: Record<number, number[]> = {},
|
|
43
44
|
): Array<readonly [string, string]> =>
|
|
44
45
|
errors.flatMap((issue) => {
|
|
45
|
-
if (issue.code ===
|
|
46
|
-
return issue.
|
|
47
|
-
parseTuples(err, {
|
|
46
|
+
if (issue.code === 'invalid_union') {
|
|
47
|
+
return issue.errors.flatMap((err, idx) =>
|
|
48
|
+
parseTuples(err, issue.path, {
|
|
48
49
|
...unions,
|
|
49
50
|
[issue.path.length]: [...(unions[issue.path.length] ?? []), idx],
|
|
50
51
|
}),
|
|
51
52
|
);
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
const path = ['', ...issue.path]
|
|
55
|
+
const path = ['', ...basePath, ...issue.path]
|
|
55
56
|
.map((prop, idx) => [prop, ...(unions[idx] ?? [])].join('~union'))
|
|
56
57
|
.join('/');
|
|
57
58
|
|
|
58
59
|
return [[path, issue.message]] as const;
|
|
59
60
|
});
|
|
60
61
|
|
|
61
|
-
export const validate = <
|
|
62
|
-
Output,
|
|
63
|
-
Def extends z.ZodTypeDef = z.ZodTypeDef,
|
|
64
|
-
Input = Output,
|
|
65
|
-
>({
|
|
62
|
+
export const validate = <Output, Input = Output>({
|
|
66
63
|
ctx,
|
|
67
64
|
input,
|
|
68
65
|
schema,
|
|
69
66
|
}: {
|
|
70
67
|
ctx: Context;
|
|
71
68
|
input: unknown;
|
|
72
|
-
schema: z.ZodSchema<Output,
|
|
69
|
+
schema: z.ZodSchema<Output, Input>;
|
|
73
70
|
}): Output => {
|
|
74
71
|
const parseResult = schema.safeParse(input);
|
|
75
72
|
if (parseResult.success === false) {
|
|
@@ -85,15 +82,11 @@ export const validate = <
|
|
|
85
82
|
return parseResult.data;
|
|
86
83
|
};
|
|
87
84
|
|
|
88
|
-
export const validateRequestBody = <
|
|
89
|
-
Output,
|
|
90
|
-
Def extends z.ZodTypeDef = z.ZodTypeDef,
|
|
91
|
-
Input = Output,
|
|
92
|
-
>(
|
|
85
|
+
export const validateRequestBody = <Output, Input = Output>(
|
|
93
86
|
ctx: Context,
|
|
94
|
-
schema: z.ZodSchema<Output,
|
|
87
|
+
schema: z.ZodSchema<Output, Input>,
|
|
95
88
|
): Output =>
|
|
96
|
-
validate<Output,
|
|
89
|
+
validate<Output, Input>({
|
|
97
90
|
ctx,
|
|
98
91
|
input: ctx.request.body as unknown,
|
|
99
92
|
schema,
|
|
@@ -23,7 +23,7 @@ configs:
|
|
|
23
23
|
concurrency: 1
|
|
24
24
|
plugins:
|
|
25
25
|
- *docker-ecr-cache
|
|
26
|
-
- docker-compose#v5.
|
|
26
|
+
- docker-compose#v5.10.0:
|
|
27
27
|
dependencies: false
|
|
28
28
|
run: app
|
|
29
29
|
environment:
|
|
@@ -52,7 +52,7 @@ steps:
|
|
|
52
52
|
GET_NPM_TOKEN: please
|
|
53
53
|
plugins:
|
|
54
54
|
- *docker-ecr-cache
|
|
55
|
-
- docker-compose#v5.
|
|
55
|
+
- docker-compose#v5.10.0:
|
|
56
56
|
run: app
|
|
57
57
|
environment:
|
|
58
58
|
- GITHUB_API_TOKEN
|
|
@@ -369,6 +369,9 @@ exports[`returns expected CloudFormation stack for dev 1`] = `
|
|
|
369
369
|
"Type": "AWS::Lambda::EventSourceMapping",
|
|
370
370
|
},
|
|
371
371
|
"workerCurrentVersionxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": {
|
|
372
|
+
"Metadata": {
|
|
373
|
+
"aws:cdk:do-not-refactor": true,
|
|
374
|
+
},
|
|
372
375
|
"Properties": {
|
|
373
376
|
"FunctionName": {
|
|
374
377
|
"Ref": "worker28EA3E30",
|
|
@@ -1099,6 +1102,9 @@ exports[`returns expected CloudFormation stack for prod 1`] = `
|
|
|
1099
1102
|
"Type": "AWS::Lambda::EventSourceMapping",
|
|
1100
1103
|
},
|
|
1101
1104
|
"workerCurrentVersionxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": {
|
|
1105
|
+
"Metadata": {
|
|
1106
|
+
"aws:cdk:do-not-refactor": true,
|
|
1107
|
+
},
|
|
1102
1108
|
"Properties": {
|
|
1103
1109
|
"FunctionName": {
|
|
1104
1110
|
"Ref": "worker28EA3E30",
|
|
@@ -17,15 +17,15 @@
|
|
|
17
17
|
"@aws-sdk/client-codedeploy": "^3.363.0",
|
|
18
18
|
"@aws-sdk/client-lambda": "^3.363.0",
|
|
19
19
|
"@aws-sdk/client-sns": "^3.363.0",
|
|
20
|
-
"@seek/aws-codedeploy-hooks": "^
|
|
20
|
+
"@seek/aws-codedeploy-hooks": "^2.0.0",
|
|
21
21
|
"@seek/logger": "^10.0.0",
|
|
22
22
|
"datadog-lambda-js": "^10.0.0",
|
|
23
23
|
"dd-trace": "^5.0.0",
|
|
24
24
|
"skuba-dive": "^2.0.0",
|
|
25
|
-
"zod": "^3.
|
|
25
|
+
"zod": "^3.25.67"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@seek/aws-codedeploy-infra": "^
|
|
28
|
+
"@seek/aws-codedeploy-infra": "^3.0.0",
|
|
29
29
|
"@types/aws-lambda": "^8.10.82",
|
|
30
30
|
"@types/chance": "^1.1.3",
|
|
31
31
|
"@types/node": "^22.13.10",
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
"constructs": "^10.0.17",
|
|
38
38
|
"datadog-cdk-constructs-v2": "^2.0.0",
|
|
39
39
|
"pino-pretty": "^13.0.0",
|
|
40
|
-
"skuba": "
|
|
40
|
+
"skuba": "*"
|
|
41
41
|
},
|
|
42
|
-
"packageManager": "pnpm@10.12.
|
|
42
|
+
"packageManager": "pnpm@10.12.4",
|
|
43
43
|
"engines": {
|
|
44
44
|
"node": ">=22"
|
|
45
45
|
}
|
|
@@ -2,11 +2,11 @@ import { PublishCommand } from '@aws-sdk/client-sns';
|
|
|
2
2
|
|
|
3
3
|
import { metricsClient } from 'src/framework/metrics';
|
|
4
4
|
import { createCtx, createSqsEvent } from 'src/testing/handler';
|
|
5
|
-
import { logger } from 'src/testing/logging';
|
|
6
5
|
import { scoringService, sns } from 'src/testing/services';
|
|
7
6
|
import { chance, mockJobPublishedEvent } from 'src/testing/types';
|
|
8
7
|
|
|
9
8
|
import * as app from './app';
|
|
9
|
+
import { stdoutMock } from './framework/logging';
|
|
10
10
|
|
|
11
11
|
describe('app', () => {
|
|
12
12
|
it('exports a handler', () => expect(app).toHaveProperty('handler'));
|
|
@@ -23,7 +23,6 @@ describe('handler', () => {
|
|
|
23
23
|
.spyOn(metricsClient, 'distribution')
|
|
24
24
|
.mockReturnValue();
|
|
25
25
|
|
|
26
|
-
beforeAll(logger.spy);
|
|
27
26
|
beforeAll(scoringService.spy);
|
|
28
27
|
|
|
29
28
|
beforeEach(() => {
|
|
@@ -32,7 +31,7 @@ describe('handler', () => {
|
|
|
32
31
|
});
|
|
33
32
|
|
|
34
33
|
afterEach(() => {
|
|
35
|
-
|
|
34
|
+
stdoutMock.clear();
|
|
36
35
|
distribution.mockClear();
|
|
37
36
|
scoringService.clear();
|
|
38
37
|
sns.clear();
|
|
@@ -45,12 +44,24 @@ describe('handler', () => {
|
|
|
45
44
|
|
|
46
45
|
expect(scoringService.request).toHaveBeenCalledTimes(1);
|
|
47
46
|
|
|
48
|
-
expect(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
47
|
+
expect(stdoutMock.calls).toMatchObject([
|
|
48
|
+
{
|
|
49
|
+
awsRequestId: '-',
|
|
50
|
+
count: 1,
|
|
51
|
+
level: 20,
|
|
52
|
+
msg: 'Received jobs',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
awsRequestId: '-',
|
|
56
|
+
level: 20,
|
|
57
|
+
msg: 'Scored job',
|
|
58
|
+
snsMessageId: expect.any(String),
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
awsRequestId: '-',
|
|
62
|
+
level: 20,
|
|
63
|
+
msg: 'Function succeeded',
|
|
64
|
+
},
|
|
54
65
|
]);
|
|
55
66
|
|
|
56
67
|
expect(distribution.mock.calls).toEqual([
|
|
@@ -76,7 +87,23 @@ describe('handler', () => {
|
|
|
76
87
|
|
|
77
88
|
await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
|
|
78
89
|
|
|
79
|
-
expect(
|
|
90
|
+
expect(stdoutMock.calls).toMatchObject([
|
|
91
|
+
{
|
|
92
|
+
awsRequestId: '-',
|
|
93
|
+
count: 1,
|
|
94
|
+
level: 20,
|
|
95
|
+
msg: 'Received jobs',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
awsRequestId: '-',
|
|
99
|
+
err: {
|
|
100
|
+
message: err.message,
|
|
101
|
+
type: 'Error',
|
|
102
|
+
},
|
|
103
|
+
level: 50,
|
|
104
|
+
msg: 'Function failed',
|
|
105
|
+
},
|
|
106
|
+
]);
|
|
80
107
|
});
|
|
81
108
|
|
|
82
109
|
it('bubbles up SNS error', async () => {
|
|
@@ -88,22 +115,44 @@ describe('handler', () => {
|
|
|
88
115
|
|
|
89
116
|
await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
|
|
90
117
|
|
|
91
|
-
expect(
|
|
118
|
+
expect(stdoutMock.calls).toMatchObject([
|
|
119
|
+
{
|
|
120
|
+
awsRequestId: '-',
|
|
121
|
+
count: 1,
|
|
122
|
+
level: 20,
|
|
123
|
+
msg: 'Received jobs',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
awsRequestId: '-',
|
|
127
|
+
err: {
|
|
128
|
+
message: err.message,
|
|
129
|
+
type: 'Error',
|
|
130
|
+
},
|
|
131
|
+
level: 50,
|
|
132
|
+
msg: 'Function failed',
|
|
133
|
+
},
|
|
134
|
+
]);
|
|
92
135
|
});
|
|
93
136
|
|
|
94
137
|
it('throws on zero records', async () => {
|
|
95
|
-
const err = new Error('Received 0 records');
|
|
96
|
-
|
|
97
138
|
const event = createSqsEvent([]);
|
|
98
139
|
|
|
99
140
|
await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
|
|
100
141
|
|
|
101
|
-
expect(
|
|
142
|
+
expect(stdoutMock.calls).toMatchObject([
|
|
143
|
+
{
|
|
144
|
+
awsRequestId: '-',
|
|
145
|
+
err: {
|
|
146
|
+
message: 'Received 0 records',
|
|
147
|
+
type: 'Error',
|
|
148
|
+
},
|
|
149
|
+
level: 50,
|
|
150
|
+
msg: 'Function failed',
|
|
151
|
+
},
|
|
152
|
+
]);
|
|
102
153
|
});
|
|
103
154
|
|
|
104
155
|
it('throws on multiple records', async () => {
|
|
105
|
-
const err = new Error('Received 2 records');
|
|
106
|
-
|
|
107
156
|
const event = createSqsEvent([
|
|
108
157
|
JSON.stringify(jobPublished),
|
|
109
158
|
JSON.stringify(jobPublished),
|
|
@@ -111,6 +160,16 @@ describe('handler', () => {
|
|
|
111
160
|
|
|
112
161
|
await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
|
|
113
162
|
|
|
114
|
-
expect(
|
|
163
|
+
expect(stdoutMock.calls).toMatchObject([
|
|
164
|
+
{
|
|
165
|
+
awsRequestId: '-',
|
|
166
|
+
err: {
|
|
167
|
+
message: 'Received 2 records',
|
|
168
|
+
type: 'Error',
|
|
169
|
+
},
|
|
170
|
+
level: 50,
|
|
171
|
+
msg: 'Function failed',
|
|
172
|
+
},
|
|
173
|
+
]);
|
|
115
174
|
});
|
|
116
175
|
});
|
|
@@ -29,7 +29,7 @@ const configs: Record<Environment, () => Omit<Config, 'environment'>> = {
|
|
|
29
29
|
}),
|
|
30
30
|
|
|
31
31
|
test: () => ({
|
|
32
|
-
logLevel: Env.string('LOG_LEVEL', { default: '
|
|
32
|
+
logLevel: Env.string('LOG_LEVEL', { default: 'debug' }),
|
|
33
33
|
metrics: false,
|
|
34
34
|
name: '<%- serviceName %>',
|
|
35
35
|
version: 'test',
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { createCtx } from 'src/testing/handler';
|
|
2
|
-
import { logger } from 'src/testing/logging';
|
|
3
2
|
import { chance } from 'src/testing/types';
|
|
4
3
|
|
|
5
4
|
import { createHandler } from './handler';
|
|
5
|
+
import { logger, stdoutMock } from './logging';
|
|
6
6
|
|
|
7
7
|
describe('createHandler', () => {
|
|
8
8
|
const ctx = createCtx();
|
|
9
9
|
const input = chance.paragraph();
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
afterEach(logger.clear);
|
|
11
|
+
afterEach(stdoutMock.clear);
|
|
14
12
|
|
|
15
13
|
it('handles happy path', async () => {
|
|
16
14
|
const output = chance.paragraph();
|
|
@@ -25,11 +23,17 @@ describe('createHandler', () => {
|
|
|
25
23
|
|
|
26
24
|
await expect(handler(input, ctx)).resolves.toBe(output);
|
|
27
25
|
|
|
28
|
-
expect(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
expect(stdoutMock.calls).toMatchObject([
|
|
27
|
+
{
|
|
28
|
+
awsRequestId: '-',
|
|
29
|
+
level: 20,
|
|
30
|
+
msg: 'Handler invoked',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
awsRequestId: '-',
|
|
34
|
+
level: 20,
|
|
35
|
+
msg: 'Function succeeded',
|
|
36
|
+
},
|
|
33
37
|
]);
|
|
34
38
|
});
|
|
35
39
|
|
|
@@ -40,9 +44,17 @@ describe('createHandler', () => {
|
|
|
40
44
|
|
|
41
45
|
await expect(handler(input, ctx)).rejects.toThrow('Function failed');
|
|
42
46
|
|
|
43
|
-
expect(
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
expect(stdoutMock.calls).toMatchObject([
|
|
48
|
+
{
|
|
49
|
+
awsRequestId: '-',
|
|
50
|
+
err: {
|
|
51
|
+
message: err.message,
|
|
52
|
+
type: 'Error',
|
|
53
|
+
},
|
|
54
|
+
level: 50,
|
|
55
|
+
msg: 'Function failed',
|
|
56
|
+
},
|
|
57
|
+
]);
|
|
46
58
|
});
|
|
47
59
|
|
|
48
60
|
it('handles sync error', async () => {
|
|
@@ -54,8 +66,16 @@ describe('createHandler', () => {
|
|
|
54
66
|
|
|
55
67
|
await expect(handler(input, ctx)).rejects.toThrow('Function failed');
|
|
56
68
|
|
|
57
|
-
expect(
|
|
58
|
-
|
|
59
|
-
|
|
69
|
+
expect(stdoutMock.calls).toMatchObject([
|
|
70
|
+
{
|
|
71
|
+
awsRequestId: '-',
|
|
72
|
+
err: {
|
|
73
|
+
message: err.message,
|
|
74
|
+
type: 'Error',
|
|
75
|
+
},
|
|
76
|
+
level: 50,
|
|
77
|
+
msg: 'Function failed',
|
|
78
|
+
},
|
|
79
|
+
]);
|
|
60
80
|
});
|
|
61
81
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
2
2
|
|
|
3
|
-
import createLogger from '@seek/logger';
|
|
3
|
+
import createLogger, { createDestination } from '@seek/logger';
|
|
4
4
|
|
|
5
5
|
import { config } from 'src/config';
|
|
6
6
|
|
|
@@ -10,18 +10,29 @@ interface LoggerContext {
|
|
|
10
10
|
|
|
11
11
|
export const loggerContext = new AsyncLocalStorage<LoggerContext>();
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
version: config.version,
|
|
13
|
+
const { destination, stdoutMock } = createDestination({
|
|
14
|
+
mock: config.environment === 'test' && {
|
|
15
|
+
redact: ['awsRequestId'],
|
|
17
16
|
},
|
|
17
|
+
});
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
export { stdoutMock };
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
export const logger = createLogger(
|
|
22
|
+
{
|
|
23
|
+
base: {
|
|
24
|
+
environment: config.environment,
|
|
25
|
+
version: config.version,
|
|
26
|
+
},
|
|
22
27
|
|
|
23
|
-
|
|
28
|
+
level: config.logLevel,
|
|
24
29
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
mixin: () => ({ ...loggerContext.getStore() }),
|
|
31
|
+
|
|
32
|
+
name: config.name,
|
|
33
|
+
|
|
34
|
+
transport:
|
|
35
|
+
config.environment === 'local' ? { target: 'pino-pretty' } : undefined,
|
|
36
|
+
},
|
|
37
|
+
destination,
|
|
38
|
+
);
|
|
@@ -32,13 +32,12 @@ describe('validateJson', () => {
|
|
|
32
32
|
.toThrowErrorMatchingInlineSnapshot(`
|
|
33
33
|
"[
|
|
34
34
|
{
|
|
35
|
-
"code": "invalid_type",
|
|
36
35
|
"expected": "string",
|
|
37
|
-
"
|
|
36
|
+
"code": "invalid_type",
|
|
38
37
|
"path": [
|
|
39
38
|
"id"
|
|
40
39
|
],
|
|
41
|
-
"message": "
|
|
40
|
+
"message": "Invalid input: expected string, received null"
|
|
42
41
|
}
|
|
43
42
|
]"
|
|
44
43
|
`);
|
|
@@ -51,22 +50,20 @@ describe('validateJson', () => {
|
|
|
51
50
|
.toThrowErrorMatchingInlineSnapshot(`
|
|
52
51
|
"[
|
|
53
52
|
{
|
|
54
|
-
"code": "invalid_type",
|
|
55
53
|
"expected": "string",
|
|
56
|
-
"
|
|
54
|
+
"code": "invalid_type",
|
|
57
55
|
"path": [
|
|
58
56
|
"id"
|
|
59
57
|
],
|
|
60
|
-
"message": "
|
|
58
|
+
"message": "Invalid input: expected string, received undefined"
|
|
61
59
|
},
|
|
62
60
|
{
|
|
63
|
-
"code": "invalid_type",
|
|
64
61
|
"expected": "string",
|
|
65
|
-
"
|
|
62
|
+
"code": "invalid_type",
|
|
66
63
|
"path": [
|
|
67
64
|
"description"
|
|
68
65
|
],
|
|
69
|
-
"message": "
|
|
66
|
+
"message": "Invalid input: expected string, received undefined"
|
|
70
67
|
}
|
|
71
68
|
]"
|
|
72
69
|
`);
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import type { z } from 'zod';
|
|
1
|
+
import type { z } from 'zod/v4';
|
|
2
2
|
|
|
3
|
-
export const validateJson = <
|
|
4
|
-
Output,
|
|
5
|
-
Def extends z.ZodTypeDef = z.ZodTypeDef,
|
|
6
|
-
Input = Output,
|
|
7
|
-
>(
|
|
3
|
+
export const validateJson = <Output, Input>(
|
|
8
4
|
input: string,
|
|
9
|
-
schema: z.ZodSchema<Output,
|
|
5
|
+
schema: z.ZodSchema<Output, Input>,
|
|
10
6
|
): Output => schema.parse(JSON.parse(input));
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import * as logging from 'src/framework/logging';
|
|
2
|
-
|
|
3
|
-
export const logger = {
|
|
4
|
-
error: jest.fn(),
|
|
5
|
-
info: jest.fn(),
|
|
6
|
-
|
|
7
|
-
clear: () => {
|
|
8
|
-
logger.error.mockClear();
|
|
9
|
-
logger.info.mockClear();
|
|
10
|
-
},
|
|
11
|
-
|
|
12
|
-
spy: () => {
|
|
13
|
-
jest.spyOn(logging.logger, 'error').mockImplementation(logger.error);
|
|
14
|
-
jest.spyOn(logging.logger, 'info').mockImplementation(logger.info);
|
|
15
|
-
},
|
|
16
|
-
};
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import * as logging from 'src/framework/logging';
|
|
2
|
-
|
|
3
|
-
export const logger = {
|
|
4
|
-
error: jest.fn(),
|
|
5
|
-
info: jest.fn(),
|
|
6
|
-
debug: jest.fn(),
|
|
7
|
-
|
|
8
|
-
clear: () => {
|
|
9
|
-
logger.error.mockClear();
|
|
10
|
-
logger.info.mockClear();
|
|
11
|
-
logger.debug.mockClear();
|
|
12
|
-
},
|
|
13
|
-
|
|
14
|
-
spy: () => {
|
|
15
|
-
jest.spyOn(logging.logger, 'error').mockImplementation(logger.error);
|
|
16
|
-
jest.spyOn(logging.logger, 'info').mockImplementation(logger.info);
|
|
17
|
-
jest.spyOn(logging.logger, 'debug').mockImplementation(logger.debug);
|
|
18
|
-
},
|
|
19
|
-
};
|