Skip to content

Commit e922699

Browse files
authored
test(maintenance): switch layers to vitest (aws-powertools#3292)
1 parent 530be50 commit e922699

File tree

7 files changed

+100
-124
lines changed

7 files changed

+100
-124
lines changed

layers/jest.config.js

-28
This file was deleted.

layers/package.json

+7-3
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,19 @@
77
"private": true,
88
"description": "This CDK app is meant to be used to publish Powertools for AWS Lambda (TypeScript) Lambda Layer. It is composed of a single stack deploying the Layer into the target account.",
99
"scripts": {
10+
"test": "vitest --run tests/unit",
11+
"test:unit": "vitest --run tests/unit",
12+
"test:unit:coverage": "echo 'Not Implemented'",
13+
"test:unit:types": "echo 'Not Implemented'",
14+
"test:e2e:nodejs18x": "echo 'Not Implemented'",
15+
"test:e2e:nodejs20x": "echo 'Not Implemented'",
16+
"test:e2e": "vitest --run tests/e2e",
1017
"build": "echo 'Not applicable, run `npx cdk synth` instead to build the stack'",
11-
"test": "echo 'Not applicable'",
1218
"jest": "jest --detectOpenHandles",
1319
"cdk": "cdk",
1420
"package": "echo 'Not applicable'",
1521
"lint": "biome lint .",
1622
"lint:fix": "biome check --write .",
17-
"test:unit": "jest --group=unit",
18-
"test:e2e": "jest --group=e2e",
1923
"createLayerFolder": "cdk synth --context BuildFromLocal=true"
2024
},
2125
"repository": {

layers/tests/e2e/layerPublisher.test.ts

+74-80
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
/**
2-
* Test LayerPublisherStack class
3-
*
4-
* @group e2e/layers/all
5-
*/
61
import { join } from 'node:path';
72
import {
83
TestInvocationLogs,
@@ -13,23 +8,14 @@ import {
138
import { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils/resources/lambda';
149
import { App } from 'aws-cdk-lib';
1510
import { LayerVersion } from 'aws-cdk-lib/aws-lambda';
11+
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
1612
import packageJson from '../../package.json';
17-
import { LayerPublisherStack } from '../../src/layer-publisher-stack';
13+
import { LayerPublisherStack } from '../../src/layer-publisher-stack.js';
1814
import {
1915
RESOURCE_NAME_PREFIX,
2016
SETUP_TIMEOUT,
2117
TEARDOWN_TIMEOUT,
22-
} from './constants';
23-
24-
jest.spyOn(console, 'log').mockImplementation();
25-
26-
function assertLogs(
27-
logs: TestInvocationLogs | undefined
28-
): asserts logs is TestInvocationLogs {
29-
if (!logs) {
30-
throw new Error('Function logs are not available');
31-
}
32-
}
18+
} from './constants.js';
3319

3420
/**
3521
* This test has two stacks:
@@ -137,70 +123,78 @@ describe('Layers E2E tests', () => {
137123
}
138124
}, SETUP_TIMEOUT);
139125

140-
describe.each(cases)(
141-
'utilities tests for %s output format',
126+
it.each(cases)(
127+
'imports and instantiates all utilities (%s)',
128+
(outputFormat) => {
129+
const invocationLogs = invocationLogsMap.get(
130+
outputFormat
131+
) as TestInvocationLogs;
132+
133+
expect(invocationLogs.doesAnyFunctionLogsContains('ERROR')).toBe(false);
134+
}
135+
);
136+
137+
it.each(cases)(
138+
'emits a warning log for missing Metrics namespace (%s)',
142139
(outputFormat) => {
143-
it('should have no errors in the logs, which indicates the pacakges version matches the expected one', () => {
144-
const maybeInvocationLogs = invocationLogsMap.get(outputFormat);
145-
assertLogs(maybeInvocationLogs);
146-
const invocationLogs = maybeInvocationLogs;
147-
const logs = invocationLogs.getFunctionLogs('ERROR');
148-
149-
expect(logs.length).toBe(0);
150-
});
151-
152-
it('should have one warning related to missing Metrics namespace', () => {
153-
const maybeInvocationLogs = invocationLogsMap.get(outputFormat);
154-
assertLogs(maybeInvocationLogs);
155-
const invocationLogs = maybeInvocationLogs;
156-
const logs = invocationLogs.getFunctionLogs('WARN');
157-
158-
expect(logs.length).toBe(1);
159-
expect(logs[0]).toContain('Namespace should be defined, default used');
160-
});
161-
162-
it('should have one info log related to coldstart metric', () => {
163-
const maybeInvocationLogs = invocationLogsMap.get(outputFormat);
164-
assertLogs(maybeInvocationLogs);
165-
const invocationLogs = maybeInvocationLogs;
166-
const logs = invocationLogs.getFunctionLogs();
167-
168-
const emfLogEntry = logs.find((log) =>
169-
log.match(
170-
/{"_aws":{"Timestamp":\d+,"CloudWatchMetrics":\[\{"Namespace":"\S+","Dimensions":\[\["service"\]\],"Metrics":\[\{"Name":"ColdStart","Unit":"Count"\}\]\}\]},"service":"\S+","ColdStart":1}/
171-
)
172-
);
173-
174-
expect(emfLogEntry).toBeDefined();
175-
});
176-
177-
it('should have one debug log with tracer subsegment info', () => {
178-
const maybeInvocationLogs = invocationLogsMap.get(outputFormat);
179-
assertLogs(maybeInvocationLogs);
180-
const invocationLogs = maybeInvocationLogs;
181-
const logs = invocationLogs.getFunctionLogs('DEBUG');
182-
183-
expect(logs.length).toBe(1);
184-
const logEntry = TestInvocationLogs.parseFunctionLog(logs[0]);
185-
expect(logEntry.message).toContain('subsegment');
186-
expect(logEntry.subsegment).toBeDefined();
187-
const subsegment = JSON.parse(logEntry.subsegment as string);
188-
const traceIdFromLog = subsegment.trace_id;
189-
expect(subsegment).toEqual(
190-
expect.objectContaining({
191-
id: expect.any(String),
192-
name: '### index.handler',
193-
start_time: expect.any(Number),
194-
end_time: expect.any(Number),
195-
type: 'subsegment',
196-
annotations: {
197-
ColdStart: true,
198-
},
199-
parent_id: expect.any(String),
200-
trace_id: traceIdFromLog,
201-
})
202-
);
203-
});
140+
const invocationLogs = invocationLogsMap.get(
141+
outputFormat
142+
) as TestInvocationLogs;
143+
const logs = invocationLogs.getFunctionLogs('WARN');
144+
145+
expect(logs.length).toBe(1);
146+
expect(
147+
invocationLogs.doesAnyFunctionLogsContains(
148+
/Namespace should be defined, default used/,
149+
'WARN'
150+
)
151+
).toBe(true);
152+
/* expect(logEntry.message).toEqual(
153+
'Namespace should be defined, default used'
154+
); */
155+
}
156+
);
157+
158+
it.each(cases)('emits an EMF log (%s)', (outputFormat) => {
159+
const invocationLogs = invocationLogsMap.get(
160+
outputFormat
161+
) as TestInvocationLogs;
162+
163+
expect(
164+
invocationLogs.doesAnyFunctionLogsContains(
165+
/{"_aws":{"Timestamp":\d+,"CloudWatchMetrics":\[\{"Namespace":"\S+","Dimensions":\[\["service"\]\],"Metrics":\[\{"Name":"ColdStart","Unit":"Count"\}\]\}\]},"service":"\S+","ColdStart":1}/
166+
)
167+
).toBe(true);
168+
});
169+
170+
it.each(cases)(
171+
'emits a debug log with tracer subsegment info (%s)',
172+
(outputFormat) => {
173+
const invocationLogs = invocationLogsMap.get(
174+
outputFormat
175+
) as TestInvocationLogs;
176+
const logs = invocationLogs.getFunctionLogs('DEBUG');
177+
178+
expect(logs.length).toBe(1);
179+
const logEntry = TestInvocationLogs.parseFunctionLog(logs[0]);
180+
expect(logEntry.message).toContain('subsegment');
181+
expect(logEntry.subsegment).toBeDefined();
182+
const subsegment = JSON.parse(logEntry.subsegment as string);
183+
const traceIdFromLog = subsegment.trace_id;
184+
expect(subsegment).toEqual(
185+
expect.objectContaining({
186+
id: expect.any(String),
187+
name: '### index.handler',
188+
start_time: expect.any(Number),
189+
end_time: expect.any(Number),
190+
type: 'subsegment',
191+
annotations: {
192+
ColdStart: true,
193+
},
194+
parent_id: expect.any(String),
195+
trace_id: traceIdFromLog,
196+
})
197+
);
204198
}
205199
);
206200

layers/tests/unit/layer-publisher.test.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
/**
2-
* Test LayerPublisherStack class
3-
*
4-
* @group unit/layers/all
5-
*/
6-
71
import { App } from 'aws-cdk-lib';
82
import { Template } from 'aws-cdk-lib/assertions';
3+
import { describe, it } from 'vitest';
94
import { LayerPublisherStack } from '../../src/layer-publisher-stack';
105

116
describe('Class: LayerPublisherStack', () => {

layers/vitest.config.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { defineProject } from 'vitest/config';
2+
3+
export default defineProject({
4+
test: {
5+
environment: 'node',
6+
},
7+
});

packages/testing/src/TestInvocationLogs.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,17 @@ class TestInvocationLogs {
3434
}
3535

3636
/**
37-
* Find all functional logs whether it contains a given text
38-
* @param text
39-
* @param levelToFilter level to filter
40-
* @returns
37+
* Test whether any of the function logs contain the provided text or regex.
38+
*
39+
* @param needle - text or regex to search for in the logs
40+
* @param levelToFilter - level to filter
4141
*/
4242
public doesAnyFunctionLogsContains(
43-
text: string,
43+
needle: string | RegExp,
4444
levelToFilter?: keyof typeof LogLevel
4545
): boolean {
4646
const filteredLogs = this.getFunctionLogs(levelToFilter).filter((log) =>
47-
log.includes(text)
47+
typeof needle === 'string' ? log.includes(needle) : needle.test(log)
4848
);
4949

5050
return filteredLogs.length > 0;

vitest.workspace.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1-
export default ['packages/*/vitest.config.ts', 'examples/app/vitest.config.ts'];
1+
export default [
2+
'packages/*/vitest.config.ts',
3+
'examples/app/vitest.config.ts',
4+
'layers/vitest.config.ts',
5+
];

0 commit comments

Comments
 (0)