Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(maintenance): switch layers to vitest #3292

Merged
merged 1 commit into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 0 additions & 28 deletions layers/jest.config.js

This file was deleted.

10 changes: 7 additions & 3 deletions layers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,19 @@
"private": true,
"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.",
"scripts": {
"test": "vitest --run tests/unit",
"test:unit": "vitest --run tests/unit",
"test:unit:coverage": "echo 'Not Implemented'",
"test:unit:types": "echo 'Not Implemented'",
"test:e2e:nodejs18x": "echo 'Not Implemented'",
"test:e2e:nodejs20x": "echo 'Not Implemented'",
"test:e2e": "vitest --run tests/e2e",
"build": "echo 'Not applicable, run `npx cdk synth` instead to build the stack'",
"test": "echo 'Not applicable'",
"jest": "jest --detectOpenHandles",
"cdk": "cdk",
"package": "echo 'Not applicable'",
"lint": "biome lint .",
"lint:fix": "biome check --write .",
"test:unit": "jest --group=unit",
"test:e2e": "jest --group=e2e",
"createLayerFolder": "cdk synth --context BuildFromLocal=true"
},
"repository": {
Expand Down
154 changes: 74 additions & 80 deletions layers/tests/e2e/layerPublisher.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
/**
* Test LayerPublisherStack class
*
* @group e2e/layers/all
*/
import { join } from 'node:path';
import {
TestInvocationLogs,
Expand All @@ -13,23 +8,14 @@ import {
import { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils/resources/lambda';
import { App } from 'aws-cdk-lib';
import { LayerVersion } from 'aws-cdk-lib/aws-lambda';
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
import packageJson from '../../package.json';
import { LayerPublisherStack } from '../../src/layer-publisher-stack';
import { LayerPublisherStack } from '../../src/layer-publisher-stack.js';
import {
RESOURCE_NAME_PREFIX,
SETUP_TIMEOUT,
TEARDOWN_TIMEOUT,
} from './constants';

jest.spyOn(console, 'log').mockImplementation();

function assertLogs(
logs: TestInvocationLogs | undefined
): asserts logs is TestInvocationLogs {
if (!logs) {
throw new Error('Function logs are not available');
}
}
} from './constants.js';

/**
* This test has two stacks:
Expand Down Expand Up @@ -137,70 +123,78 @@ describe('Layers E2E tests', () => {
}
}, SETUP_TIMEOUT);

describe.each(cases)(
'utilities tests for %s output format',
it.each(cases)(
'imports and instantiates all utilities (%s)',
(outputFormat) => {
const invocationLogs = invocationLogsMap.get(
outputFormat
) as TestInvocationLogs;

expect(invocationLogs.doesAnyFunctionLogsContains('ERROR')).toBe(false);
}
);

it.each(cases)(
'emits a warning log for missing Metrics namespace (%s)',
(outputFormat) => {
it('should have no errors in the logs, which indicates the pacakges version matches the expected one', () => {
const maybeInvocationLogs = invocationLogsMap.get(outputFormat);
assertLogs(maybeInvocationLogs);
const invocationLogs = maybeInvocationLogs;
const logs = invocationLogs.getFunctionLogs('ERROR');

expect(logs.length).toBe(0);
});

it('should have one warning related to missing Metrics namespace', () => {
const maybeInvocationLogs = invocationLogsMap.get(outputFormat);
assertLogs(maybeInvocationLogs);
const invocationLogs = maybeInvocationLogs;
const logs = invocationLogs.getFunctionLogs('WARN');

expect(logs.length).toBe(1);
expect(logs[0]).toContain('Namespace should be defined, default used');
});

it('should have one info log related to coldstart metric', () => {
const maybeInvocationLogs = invocationLogsMap.get(outputFormat);
assertLogs(maybeInvocationLogs);
const invocationLogs = maybeInvocationLogs;
const logs = invocationLogs.getFunctionLogs();

const emfLogEntry = logs.find((log) =>
log.match(
/{"_aws":{"Timestamp":\d+,"CloudWatchMetrics":\[\{"Namespace":"\S+","Dimensions":\[\["service"\]\],"Metrics":\[\{"Name":"ColdStart","Unit":"Count"\}\]\}\]},"service":"\S+","ColdStart":1}/
)
);

expect(emfLogEntry).toBeDefined();
});

it('should have one debug log with tracer subsegment info', () => {
const maybeInvocationLogs = invocationLogsMap.get(outputFormat);
assertLogs(maybeInvocationLogs);
const invocationLogs = maybeInvocationLogs;
const logs = invocationLogs.getFunctionLogs('DEBUG');

expect(logs.length).toBe(1);
const logEntry = TestInvocationLogs.parseFunctionLog(logs[0]);
expect(logEntry.message).toContain('subsegment');
expect(logEntry.subsegment).toBeDefined();
const subsegment = JSON.parse(logEntry.subsegment as string);
const traceIdFromLog = subsegment.trace_id;
expect(subsegment).toEqual(
expect.objectContaining({
id: expect.any(String),
name: '### index.handler',
start_time: expect.any(Number),
end_time: expect.any(Number),
type: 'subsegment',
annotations: {
ColdStart: true,
},
parent_id: expect.any(String),
trace_id: traceIdFromLog,
})
);
});
const invocationLogs = invocationLogsMap.get(
outputFormat
) as TestInvocationLogs;
const logs = invocationLogs.getFunctionLogs('WARN');

expect(logs.length).toBe(1);
expect(
invocationLogs.doesAnyFunctionLogsContains(
/Namespace should be defined, default used/,
'WARN'
)
).toBe(true);
/* expect(logEntry.message).toEqual(
'Namespace should be defined, default used'
); */
}
);

it.each(cases)('emits an EMF log (%s)', (outputFormat) => {
const invocationLogs = invocationLogsMap.get(
outputFormat
) as TestInvocationLogs;

expect(
invocationLogs.doesAnyFunctionLogsContains(
/{"_aws":{"Timestamp":\d+,"CloudWatchMetrics":\[\{"Namespace":"\S+","Dimensions":\[\["service"\]\],"Metrics":\[\{"Name":"ColdStart","Unit":"Count"\}\]\}\]},"service":"\S+","ColdStart":1}/
)
).toBe(true);
});

it.each(cases)(
'emits a debug log with tracer subsegment info (%s)',
(outputFormat) => {
const invocationLogs = invocationLogsMap.get(
outputFormat
) as TestInvocationLogs;
const logs = invocationLogs.getFunctionLogs('DEBUG');

expect(logs.length).toBe(1);
const logEntry = TestInvocationLogs.parseFunctionLog(logs[0]);
expect(logEntry.message).toContain('subsegment');
expect(logEntry.subsegment).toBeDefined();
const subsegment = JSON.parse(logEntry.subsegment as string);
const traceIdFromLog = subsegment.trace_id;
expect(subsegment).toEqual(
expect.objectContaining({
id: expect.any(String),
name: '### index.handler',
start_time: expect.any(Number),
end_time: expect.any(Number),
type: 'subsegment',
annotations: {
ColdStart: true,
},
parent_id: expect.any(String),
trace_id: traceIdFromLog,
})
);
}
);

Expand Down
7 changes: 1 addition & 6 deletions layers/tests/unit/layer-publisher.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
/**
* Test LayerPublisherStack class
*
* @group unit/layers/all
*/

import { App } from 'aws-cdk-lib';
import { Template } from 'aws-cdk-lib/assertions';
import { describe, it } from 'vitest';
import { LayerPublisherStack } from '../../src/layer-publisher-stack';

describe('Class: LayerPublisherStack', () => {
Expand Down
7 changes: 7 additions & 0 deletions layers/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineProject } from 'vitest/config';

export default defineProject({
test: {
environment: 'node',
},
});
12 changes: 6 additions & 6 deletions packages/testing/src/TestInvocationLogs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,17 @@ class TestInvocationLogs {
}

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

return filteredLogs.length > 0;
Expand Down
6 changes: 5 additions & 1 deletion vitest.workspace.ts
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export default ['packages/*/vitest.config.ts', 'examples/app/vitest.config.ts'];
export default [
'packages/*/vitest.config.ts',
'examples/app/vitest.config.ts',
'layers/vitest.config.ts',
];