Skip to content

Commit 43bf71c

Browse files
authored
test(batch): migrate unit tests to Vitest (#2938)
1 parent 8b0e77f commit 43bf71c

15 files changed

+175
-277
lines changed

.github/workflows/reusable-run-linting-check-and-unit-tests.yml

+51
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,63 @@
11
name: Run unit tests
22

3+
#
4+
# PROCESS
5+
#
6+
# 1. Checkout code
7+
# 2. Install dependencies & build project
8+
# 3. Run linting
9+
# 4. Run unit tests
10+
#
11+
# NOTES
12+
# We create different jobs for different workspaces of the monorepo, since we have slightly different requirements for each.
13+
# For example, the docs (`check-docs`), runs markdown linting, while the layer (`check-layer-publisher`), examples (`check-examples`),
14+
# and code snippets (`check-docs-snippets`) jobs run linting and unit tests but only for the current LTS version of Node.js.
15+
#
16+
# For the Powertools for AWS main features (aka `packages/*`), instead we run linting and unit tests for all the supported
17+
# versions of Node.js.
18+
#
19+
# Since #2938, we are in the process of improving our test suite, so we are gradually extracting the tests for each package
20+
# from (`run-linting-check-and-unit-tests-on-utilities`) to their own job, so we can run them in parallel using the matrix
21+
# strategy and reduce the time it takes to run the tests, as well as improve maintainer experience in case of failures.
22+
#
23+
# USAGE
24+
#
25+
# NOTE: meant to be called by ./.github/workflows/pr-run-linting-check-and-unit-tests.yml when a PR is opened or updated,
26+
# or by ./.github/workflows/make-release.yml when a release is made.
27+
#
28+
329
on:
430
workflow_call:
531

632
permissions:
733
contents: read
834

935
jobs:
36+
code-quality:
37+
runs-on: ubuntu-latest
38+
env:
39+
NODE_ENV: dev
40+
strategy:
41+
matrix:
42+
version: [18, 20]
43+
workspace: ["packages/batch"]
44+
fail-fast: false
45+
steps:
46+
- name: Checkout code
47+
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
48+
- name: Setup NodeJS
49+
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
50+
with:
51+
node-version: ${{ matrix.version }}
52+
cache: "npm"
53+
- name: Setup dependencies
54+
uses: aws-powertools/actions/.github/actions/cached-node-modules@d406bac5563f1d8c793519a3eedfe620f6a14872
55+
with:
56+
nodeVersion: ${{ matrix.version }}
57+
- name: Linting
58+
run: npm run lint -w ${{ matrix.workspace }}
59+
- name: Unit tests
60+
run: npm run test:unit:coverage -w ${{ matrix.workspace }}
1061
run-linting-check-and-unit-tests-on-utilities:
1162
runs-on: ubuntu-latest
1263
env:

.husky/pre-push

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ npm t \
77
-w packages/idempotency \
88
-w packages/parameters \
99
-w packages/parser \
10-
-w packages/event-handler
10+
-w packages/event-handler
11+
12+
npx vitest --run --coverage --changed="$(git merge-base HEAD main)"

packages/batch/jest.config.cjs

-31
This file was deleted.

packages/batch/package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
"access": "public"
1111
},
1212
"scripts": {
13-
"test": "npm run test:unit",
14-
"test:unit": "jest --group=unit --detectOpenHandles --coverage --verbose",
15-
"jest": "jest --detectOpenHandles --verbose",
13+
"test": "vitest --run",
14+
"test:unit": "vitest --run",
15+
"test:unit:coverage": "vitest --run --coverage.enabled --coverage.thresholds.100 --coverage.include='src/**'",
1616
"test:e2e:nodejs18x": "echo 'Not Implemented'",
1717
"test:e2e:nodejs20x": "echo 'Not Implemented'",
1818
"test:e2e": "echo 'Not Implemented'",
@@ -75,4 +75,4 @@
7575
"devDependencies": {
7676
"@aws-lambda-powertools/testing-utils": "file:../testing"
7777
}
78-
}
78+
}

packages/batch/src/BasePartialProcessor.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,16 @@ abstract class BasePartialProcessor {
115115
/**
116116
* If this is a sync processor, user should have called processSync instead,
117117
* so we call the method early to throw the error early thus failing fast.
118+
*
119+
* The type casting is necessary to ensure that we have test coverage for the
120+
* block of code that throws the error, without having to change the return type
121+
* of the method. This is because this call will always throw an error.
118122
*/
119123
if (this.constructor.name === 'BatchProcessorSync') {
120-
await this.processRecord(this.records[0]);
124+
return (await this.processRecord(this.records[0])) as (
125+
| SuccessResponse
126+
| FailureResponse
127+
)[];
121128
}
122129
this.prepare();
123130

packages/batch/tests/helpers/populateEnvironmentVariables.ts

-12
This file was deleted.

packages/batch/tests/unit/BasePartialProcessor.test.ts

+5-10
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
/**
2-
* Test BasePartialBatchProcessor class
3-
*
4-
* @group unit/batch/class/basepartialbatchprocessor
5-
*/
1+
import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest';
62
import { BasePartialBatchProcessor, EventType } from '../../src/index.js';
73
import type {
84
BaseRecord,
@@ -16,8 +12,7 @@ describe('Class: BasePartialBatchProcessor', () => {
1612
const ENVIRONMENT_VARIABLES = process.env;
1713

1814
beforeEach(() => {
19-
jest.clearAllMocks();
20-
jest.resetModules();
15+
vi.clearAllMocks();
2116
process.env = { ...ENVIRONMENT_VARIABLES };
2217
});
2318

@@ -46,19 +41,19 @@ describe('Class: BasePartialBatchProcessor', () => {
4641
}
4742

4843
describe('create custom batch partial processor', () => {
49-
it('should create a custom batch partial processor', () => {
44+
it('creates a custom batch partial processor', () => {
5045
// Act
5146
const processor = new MyPartialProcessor();
5247

5348
// Assess
5449
expect(processor).toBeInstanceOf(BasePartialBatchProcessor);
5550
});
5651

57-
it('should process a batch of records', () => {
52+
it('processes a batch of records', () => {
5853
// Prepare
5954
const processor = new MyPartialProcessor();
6055
const records = [sqsRecordFactory('success')];
61-
const consoleSpy = jest.spyOn(console, 'log');
56+
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
6257

6358
// Act
6459
processor.register(records, sqsRecordHandler);

packages/batch/tests/unit/BatchProcessor.test.ts

+14-37
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
/**
2-
* Test BatchProcessor class
3-
*
4-
* @group unit/batch/class/batchprocessor
5-
*/
61
import context from '@aws-lambda-powertools/testing-utils/context';
72
import type { Context } from 'aws-lambda';
3+
import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest';
84
import {
95
BatchProcessingError,
106
BatchProcessor,
@@ -31,8 +27,7 @@ describe('Class: AsyncBatchProcessor', () => {
3127
};
3228

3329
beforeEach(() => {
34-
jest.clearAllMocks();
35-
jest.resetModules();
30+
vi.clearAllMocks();
3631
process.env = { ...ENVIRONMENT_VARIABLES };
3732
});
3833

@@ -41,7 +36,7 @@ describe('Class: AsyncBatchProcessor', () => {
4136
});
4237

4338
describe('Asynchronously processing SQS Records', () => {
44-
test('Batch processing SQS records with no failures', async () => {
39+
it('completes processing with no failures', async () => {
4540
// Prepare
4641
const firstRecord = sqsRecordFactory('success');
4742
const secondRecord = sqsRecordFactory('success');
@@ -59,7 +54,7 @@ describe('Class: AsyncBatchProcessor', () => {
5954
]);
6055
});
6156

62-
test('Batch processing SQS records with some failures', async () => {
57+
it('completes processing with with some failures', async () => {
6358
// Prepare
6459
const firstRecord = sqsRecordFactory('failure');
6560
const secondRecord = sqsRecordFactory('success');
@@ -86,7 +81,7 @@ describe('Class: AsyncBatchProcessor', () => {
8681
});
8782
});
8883

89-
test('Batch processing SQS records with all failures', async () => {
84+
it('completes processing with all failures', async () => {
9085
// Prepare
9186
const firstRecord = sqsRecordFactory('failure');
9287
const secondRecord = sqsRecordFactory('failure');
@@ -106,7 +101,7 @@ describe('Class: AsyncBatchProcessor', () => {
106101
});
107102

108103
describe('Asynchronously processing Kinesis Records', () => {
109-
test('Batch processing Kinesis records with no failures', async () => {
104+
it('completes processing with no failures', async () => {
110105
// Prepare
111106
const firstRecord = kinesisRecordFactory('success');
112107
const secondRecord = kinesisRecordFactory('success');
@@ -124,7 +119,7 @@ describe('Class: AsyncBatchProcessor', () => {
124119
]);
125120
});
126121

127-
test('Batch processing Kinesis records with some failures', async () => {
122+
it('completes processing with some failures', async () => {
128123
// Prepare
129124
const firstRecord = kinesisRecordFactory('failure');
130125
const secondRecord = kinesisRecordFactory('success');
@@ -151,7 +146,7 @@ describe('Class: AsyncBatchProcessor', () => {
151146
});
152147
});
153148

154-
test('Batch processing Kinesis records with all failures', async () => {
149+
it('completes processing with all failures', async () => {
155150
// Prepare
156151
const firstRecord = kinesisRecordFactory('failure');
157152
const secondRecord = kinesisRecordFactory('failure');
@@ -171,7 +166,7 @@ describe('Class: AsyncBatchProcessor', () => {
171166
});
172167

173168
describe('Asynchronously processing DynamoDB Records', () => {
174-
test('Batch processing DynamoDB records with no failures', async () => {
169+
it('completes processing with no failures', async () => {
175170
// Prepare
176171
const firstRecord = dynamodbRecordFactory('success');
177172
const secondRecord = dynamodbRecordFactory('success');
@@ -189,7 +184,7 @@ describe('Class: AsyncBatchProcessor', () => {
189184
]);
190185
});
191186

192-
test('Batch processing DynamoDB records with some failures', async () => {
187+
it('completes processing with some failures', async () => {
193188
// Prepare
194189
const firstRecord = dynamodbRecordFactory('failure');
195190
const secondRecord = dynamodbRecordFactory('success');
@@ -216,7 +211,7 @@ describe('Class: AsyncBatchProcessor', () => {
216211
});
217212
});
218213

219-
test('Batch processing DynamoDB records with all failures', async () => {
214+
it('completes processing with all failures', async () => {
220215
// Prepare
221216
const firstRecord = dynamodbRecordFactory('failure');
222217
const secondRecord = dynamodbRecordFactory('failure');
@@ -236,7 +231,7 @@ describe('Class: AsyncBatchProcessor', () => {
236231
});
237232

238233
describe('Batch processing with Lambda context', () => {
239-
test('Batch processing when context is provided and handler accepts', async () => {
234+
it('passes the context to the record handler', async () => {
240235
// Prepare
241236
const firstRecord = sqsRecordFactory('success');
242237
const secondRecord = sqsRecordFactory('success');
@@ -254,25 +249,7 @@ describe('Class: AsyncBatchProcessor', () => {
254249
]);
255250
});
256251

257-
test('Batch processing when context is provided and handler does not accept', async () => {
258-
// Prepare
259-
const firstRecord = sqsRecordFactory('success');
260-
const secondRecord = sqsRecordFactory('success');
261-
const records = [firstRecord, secondRecord];
262-
const processor = new BatchProcessor(EventType.SQS);
263-
264-
// Act
265-
processor.register(records, asyncSqsRecordHandler, options);
266-
const processedMessages = await processor.process();
267-
268-
// Assess
269-
expect(processedMessages).toStrictEqual([
270-
['success', firstRecord.body, firstRecord],
271-
['success', secondRecord.body, secondRecord],
272-
]);
273-
});
274-
275-
test('Batch processing when malformed context is provided and handler attempts to use', async () => {
252+
it('throws an error when passing an invalid context object', async () => {
276253
// Prepare
277254
const firstRecord = sqsRecordFactory('success');
278255
const secondRecord = sqsRecordFactory('success');
@@ -289,7 +266,7 @@ describe('Class: AsyncBatchProcessor', () => {
289266
});
290267
});
291268

292-
test('When calling the sync process method, it should throw an error', () => {
269+
it('throws an error when the sync process method is called', () => {
293270
// Prepare
294271
const processor = new BatchProcessor(EventType.SQS);
295272

0 commit comments

Comments
 (0)