Skip to content

Commit d6e92cc

Browse files
am29ddreamorosi
andauthored
docs(parser): add section on how to test parser using decorator (#2755)
Co-authored-by: Andrea Amorosi <[email protected]>
1 parent 92d2201 commit d6e92cc

File tree

7 files changed

+194
-2
lines changed

7 files changed

+194
-2
lines changed

docs/utilities/parser.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,3 +249,51 @@ The package `@types/aws-lambda` is a popular project that contains type definiti
249249
Powertools parser utility also bring AWS Lambda event types based on the built-in schema definitions.
250250

251251
We recommend to use the types provided by the parser utility. If you encounter any issues or have any feedback, please [submit an issue](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose).
252+
253+
## Testing your code
254+
255+
When testing your handler with [**parser decorator**](#parse-events) you need to use double assetion to bypass TypeScript type checking in your tests.
256+
This is useful when you want to test the handler for invalid payloads or when you want to test the error handling.
257+
If you are you use middy middleware, you don't need to do this.
258+
259+
=== "handlerDecorator.test.ts"
260+
261+
```typescript hl_lines="26"
262+
--8<-- "examples/snippets/parser/unitTestDecorator.ts"
263+
```
264+
265+
1. Use double assertion `as unknown as X` to bypass TypeScript type checking in your tests
266+
267+
=== "handlerDecorator.ts"
268+
269+
```typescript
270+
--8<-- "examples/snippets/parser/handlerDecorator.ts"
271+
```
272+
273+
=== "schema.ts"
274+
275+
```typescript
276+
--8<-- "examples/snippets/parser/schema.ts"
277+
```
278+
279+
This also works when using `safeParse` option.
280+
281+
=== "handlerSafeParse.test.ts"
282+
283+
```typescript hl_lines="21-29 35 45"
284+
--8<-- "examples/snippets/parser/unitTestSafeParse.ts"
285+
```
286+
287+
1. Use double assertion to pass expected types to the handler
288+
289+
=== "handlerSafeParse.ts"
290+
291+
```typescript
292+
--8<-- "examples/snippets/parser/handlerSafeParseDecorator.ts"
293+
```
294+
295+
=== "schema.ts"
296+
297+
```typescript
298+
--8<-- "examples/snippets/parser/schema.ts"
299+
```
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { Context } from 'aws-lambda';
2+
import type { LambdaInterface } from '@aws-lambda-powertools/commons/types';
3+
import { parser } from '@aws-lambda-powertools/parser';
4+
import { Logger } from '@aws-lambda-powertools/logger';
5+
import { orderSchema, type Order } from './schema.js';
6+
7+
const logger = new Logger();
8+
9+
class Lambda implements LambdaInterface {
10+
@parser({ schema: orderSchema })
11+
public async handler(event: Order, _context: Context): Promise<number> {
12+
logger.info('Processing event', { event });
13+
14+
// ... business logic
15+
return event.id;
16+
}
17+
}
18+
19+
const myFunction = new Lambda();
20+
export const handler = myFunction.handler.bind(myFunction);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import type { Context } from 'aws-lambda';
2+
import type { LambdaInterface } from '@aws-lambda-powertools/commons/types';
3+
import { parser } from '@aws-lambda-powertools/parser';
4+
import { Logger } from '@aws-lambda-powertools/logger';
5+
import { orderSchema, type Order } from './schema.js';
6+
import { EventBridgeEnvelope } from '@aws-lambda-powertools/parser/envelopes';
7+
import type {
8+
ParsedResult,
9+
EventBridgeEvent,
10+
} from '@aws-lambda-powertools/parser/types';
11+
12+
const logger = new Logger();
13+
14+
class Lambda implements LambdaInterface {
15+
@parser({
16+
schema: orderSchema,
17+
envelope: EventBridgeEnvelope,
18+
safeParse: true,
19+
})
20+
public async handler(
21+
event: ParsedResult<EventBridgeEvent, Order>,
22+
_context: Context
23+
): Promise<number> {
24+
logger.info('Processing event', { event });
25+
if (event.success) {
26+
// ... business logic
27+
return event.data.id;
28+
} else {
29+
logger.error('Failed to parse event', { event });
30+
throw new Error('Failed to parse event');
31+
}
32+
}
33+
}
34+
35+
const myFunction = new Lambda();
36+
export const handler = myFunction.handler.bind(myFunction);

examples/snippets/parser/safeParseDecorator.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
EventBridgeEvent,
88
} from '@aws-lambda-powertools/parser/types';
99
import { Logger } from '@aws-lambda-powertools/logger';
10+
import { EventBridgeEnvelope } from '@aws-lambda-powertools/parser/envelopes';
1011

1112
const logger = new Logger();
1213

@@ -26,7 +27,11 @@ const orderSchema = z.object({
2627
type Order = z.infer<typeof orderSchema>;
2728

2829
class Lambda implements LambdaInterface {
29-
@parser({ schema: orderSchema, safeParse: true }) // (1)!
30+
@parser({
31+
schema: orderSchema,
32+
envelope: EventBridgeEnvelope,
33+
safeParse: true,
34+
}) // (1)!
3035
public async handler(
3136
event: ParsedResult<EventBridgeEvent, Order>,
3237
_context: Context

examples/snippets/parser/schema.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@ const orderSchema = z.object({
1313
optionalField: z.string().optional(),
1414
});
1515

16-
export { orderSchema };
16+
type Order = z.infer<typeof orderSchema>;
17+
18+
export { orderSchema, type Order };
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { Context } from 'aws-lambda';
2+
import type { Order } from './schema.js';
3+
import { handler } from './decorator.js';
4+
5+
describe('Test handler', () => {
6+
it('should parse event successfully', async () => {
7+
const testEvent = {
8+
id: 123,
9+
description: 'test',
10+
items: [
11+
{
12+
id: 1,
13+
quantity: 1,
14+
description: 'item1',
15+
},
16+
],
17+
};
18+
19+
await expect(handler(testEvent, {} as Context)).resolves.toEqual(123);
20+
});
21+
22+
it('should throw error if event is invalid', async () => {
23+
const testEvent = { foo: 'bar' };
24+
await expect(
25+
handler(
26+
testEvent as unknown as Order, // (1)!
27+
{} as Context
28+
)
29+
).rejects.toThrow();
30+
});
31+
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import type { Order } from './schema.js';
2+
import type { Context } from 'aws-lambda';
3+
import { handler } from './safeParseDecorator.js';
4+
import {
5+
ParsedResult,
6+
EventBridgeEvent,
7+
} from '@aws-lambda-powertools/parser/types';
8+
9+
describe('Test handler', () => {
10+
it('should parse event successfully', async () => {
11+
const testEvent = {
12+
version: '0',
13+
id: '6a7e8feb-b491-4cf7-a9f1-bf3703467718',
14+
'detail-type': 'OrderPurchased',
15+
source: 'OrderService',
16+
account: '111122223333',
17+
time: '2020-10-22T18:43:48Z',
18+
region: 'us-west-1',
19+
resources: ['some_additional'],
20+
detail: {
21+
id: 10876546789,
22+
description: 'My order',
23+
items: [
24+
{
25+
id: 1015938732,
26+
quantity: 1,
27+
description: 'item xpto',
28+
},
29+
],
30+
},
31+
};
32+
33+
await expect(
34+
handler(
35+
testEvent as unknown as ParsedResult<EventBridgeEvent, Order>, // (1)!
36+
{} as Context
37+
)
38+
).resolves.toEqual(10876546789);
39+
});
40+
41+
it('should throw error if event is invalid', async () => {
42+
const testEvent = { foo: 'bar' };
43+
await expect(
44+
handler(
45+
testEvent as unknown as ParsedResult<EventBridgeEvent, Order>,
46+
{} as Context
47+
)
48+
).rejects.toThrow();
49+
});
50+
});

0 commit comments

Comments
 (0)