Skip to content

Commit ed3d12f

Browse files
authored
test(e2e): Add e2e test for AWS lambda in ESM mode (#12833)
Add an E2E test app that simulates running an AWS lambda function in ESM mode. At the same time, this test app represents the state of how to manually use the `@sentry/aws-serverless` SDK for ESM-based lambda functions.
1 parent 339f25e commit ed3d12f

File tree

13 files changed

+216
-57
lines changed

13 files changed

+216
-57
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -995,6 +995,7 @@ jobs:
995995
'angular-17',
996996
'angular-18',
997997
'aws-lambda-layer-cjs',
998+
'aws-serverless-esm',
998999
'node-express',
9991000
'create-react-app',
10001001
'create-next-app',

dev-packages/e2e-tests/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
"esbuild": "0.20.0",
2424
"glob": "8.0.3",
2525
"ts-node": "10.9.1",
26-
"yaml": "2.2.2"
26+
"yaml": "2.2.2",
27+
"rimraf": "^5.0.0"
2728
},
2829
"volta": {
2930
"extends": "../../package.json",
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@sentry:registry=http://127.0.0.1:4873
2+
@sentry-internal:registry=http://127.0.0.1:4873
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "node-express-app",
3+
"version": "1.0.0",
4+
"private": true,
5+
"scripts": {
6+
"start": "node src/run.mjs",
7+
"test": "playwright test",
8+
"clean": "npx rimraf node_modules pnpm-lock.yaml",
9+
"test:build": "pnpm install",
10+
"test:assert": "pnpm test"
11+
},
12+
"dependencies": {
13+
"@sentry/aws-serverless": "* || latest"
14+
},
15+
"devDependencies": {
16+
"@sentry-internal/test-utils": "link:../../../test-utils",
17+
"@playwright/test": "^1.41.1",
18+
"wait-port": "1.0.4"
19+
},
20+
"volta": {
21+
"extends": "../../package.json"
22+
}
23+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { getPlaywrightConfig } from '@sentry-internal/test-utils';
2+
3+
// Fix urls not resolving to localhost on Node v17+
4+
// See: https://github.com/axios/axios/issues/3821#issuecomment-1413727575
5+
import { setDefaultResultOrder } from 'dns';
6+
setDefaultResultOrder('ipv4first');
7+
8+
const eventProxyPort = 3031;
9+
10+
/**
11+
* See https://playwright.dev/docs/test-configuration.
12+
*/
13+
const config = getPlaywrightConfig(
14+
{ startCommand: '' },
15+
{
16+
/* Run your local dev server before starting the tests */
17+
webServer: [
18+
{
19+
command: `node start-event-proxy.mjs && pnpm wait-port ${eventProxyPort}`,
20+
port: eventProxyPort,
21+
stdout: 'pipe',
22+
},
23+
],
24+
},
25+
);
26+
27+
export default config;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import * as http from 'node:http';
2+
import * as Sentry from '@sentry/aws-serverless';
3+
4+
const handler = Sentry.wrapHandler(async () => {
5+
await new Promise(resolve => {
6+
const req = http.request(
7+
{
8+
host: 'example.com',
9+
},
10+
res => {
11+
res.on('data', d => {
12+
process.stdout.write(d);
13+
});
14+
15+
res.on('end', () => {
16+
resolve();
17+
});
18+
},
19+
);
20+
req.end();
21+
});
22+
23+
Sentry.startSpan({ name: 'manual-span', op: 'manual' }, () => {});
24+
});
25+
26+
export { handler };
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"//": "This is a mock package.json file which is usually created by AWS when deploying the lambda. OTEL instrumentation tries to read this file to get the lambda version",
3+
"name": "lambda",
4+
"version": "1.0.0"
5+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { handler } from './lambda-function.mjs';
2+
3+
// Simulate minimal event and context objects being passed to the handler by the AWS runtime
4+
const event = {};
5+
const context = {
6+
invokedFunctionArn: 'arn:aws:lambda:us-east-1:123453789012:function:my-lambda',
7+
functionName: 'my-lambda',
8+
};
9+
10+
await handler(event, context);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import child_process from 'child_process';
2+
3+
child_process.execSync('node ./src/run-lambda.mjs', {
4+
stdio: 'inherit',
5+
env: {
6+
...process.env,
7+
// On AWS, LAMBDA_TASK_ROOT is usually /var/task but for testing, we set it to the CWD to correctly apply our handler
8+
LAMBDA_TASK_ROOT: process.cwd(),
9+
_HANDLER: 'src/lambda-function.handler',
10+
11+
NODE_OPTIONS: '--import @sentry/aws-serverless/awslambda-auto',
12+
SENTRY_DSN: 'http://public@localhost:3031/1337',
13+
SENTRY_TRACES_SAMPLE_RATE: '1.0',
14+
},
15+
cwd: process.cwd(),
16+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { startEventProxyServer } from '@sentry-internal/test-utils';
2+
3+
startEventProxyServer({
4+
port: 3031,
5+
proxyServerName: 'aws-serverless-esm',
6+
forwardToSentry: false,
7+
});
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import * as child_process from 'child_process';
2+
import { expect, test } from '@playwright/test';
3+
import { waitForTransaction } from '@sentry-internal/test-utils';
4+
5+
test('AWS Serverless SDK sends events in ESM mode', async ({ request }) => {
6+
const transactionEventPromise = waitForTransaction('aws-serverless-esm', transactionEvent => {
7+
return transactionEvent?.transaction === 'my-lambda';
8+
});
9+
10+
// Waiting for 1s here because attaching the listener for events in `waitForTransaction` is not synchronous
11+
// Since in this test, we don't start a browser via playwright, we don't have the usual delays (page.goto, etc)
12+
// which are usually enough for us to never have noticed this race condition before.
13+
// This is a workaround but probably sufficient as long as we only experience it in this test.
14+
await new Promise<void>(resolve =>
15+
setTimeout(() => {
16+
resolve();
17+
}, 1000),
18+
);
19+
20+
child_process.execSync('pnpm start', {
21+
stdio: 'ignore',
22+
});
23+
24+
const transactionEvent = await transactionEventPromise;
25+
26+
// shows the SDK sent a transaction
27+
expect(transactionEvent.transaction).toEqual('my-lambda'); // name should be the function name
28+
expect(transactionEvent.contexts?.trace).toEqual({
29+
data: {
30+
'sentry.sample_rate': 1,
31+
'sentry.source': 'component',
32+
'sentry.origin': 'auto.function.serverless',
33+
'sentry.op': 'function.aws.lambda',
34+
'otel.kind': 'INTERNAL',
35+
},
36+
op: 'function.aws.lambda',
37+
origin: 'auto.function.serverless',
38+
span_id: expect.any(String),
39+
status: 'ok',
40+
trace_id: expect.any(String),
41+
});
42+
43+
expect(transactionEvent.spans).toHaveLength(2);
44+
45+
// shows that the Otel Http instrumentation is working
46+
expect(transactionEvent.spans).toContainEqual(
47+
expect.objectContaining({
48+
data: expect.objectContaining({
49+
'sentry.op': 'http.client',
50+
'sentry.origin': 'auto.http.otel.http',
51+
url: 'http://example.com/',
52+
}),
53+
description: 'GET http://example.com/',
54+
op: 'http.client',
55+
}),
56+
);
57+
58+
// shows that the manual span creation is working
59+
expect(transactionEvent.spans).toContainEqual(
60+
expect.objectContaining({
61+
data: expect.objectContaining({
62+
'sentry.op': 'manual',
63+
'sentry.origin': 'manual',
64+
}),
65+
description: 'manual-span',
66+
op: 'manual',
67+
}),
68+
);
69+
});

packages/aws-serverless/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
"./awslambda-auto": {
4242
"require": {
4343
"default": "./build/npm/cjs/awslambda-auto.js"
44+
},
45+
"import": {
46+
"default": "./build/npm/esm/awslambda-auto.js"
4447
}
4548
},
4649
"./dist/awslambda-auto": {

yarn.lock

Lines changed: 25 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -9454,17 +9454,7 @@
94549454
dependencies:
94559455
"@types/unist" "*"
94569456

9457-
"@types/history-4@npm:@types/[email protected]":
9458-
version "4.7.8"
9459-
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934"
9460-
integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==
9461-
9462-
"@types/history-5@npm:@types/[email protected]":
9463-
version "4.7.8"
9464-
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934"
9465-
integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==
9466-
9467-
"@types/history@*":
9457+
"@types/history-4@npm:@types/[email protected]", "@types/history-5@npm:@types/[email protected]", "@types/history@*":
94689458
version "4.7.8"
94699459
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934"
94709460
integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==
@@ -9785,15 +9775,7 @@
97859775
"@types/history" "^3"
97869776
"@types/react" "*"
97879777

9788-
"@types/react-router-4@npm:@types/[email protected]":
9789-
version "5.1.14"
9790-
resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.14.tgz#e0442f4eb4c446541ad7435d44a97f8fe6df40da"
9791-
integrity sha512-LAJpqYUaCTMT2anZheoidiIymt8MuX286zoVFPM3DVb23aQBH0mAkFvzpd4LKqiolV8bBtZWT5Qp7hClCNDENw==
9792-
dependencies:
9793-
"@types/history" "*"
9794-
"@types/react" "*"
9795-
9796-
"@types/react-router-5@npm:@types/[email protected]":
9778+
"@types/react-router-4@npm:@types/[email protected]", "@types/react-router-5@npm:@types/[email protected]":
97979779
version "5.1.14"
97989780
resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.14.tgz#e0442f4eb4c446541ad7435d44a97f8fe6df40da"
97999781
integrity sha512-LAJpqYUaCTMT2anZheoidiIymt8MuX286zoVFPM3DVb23aQBH0mAkFvzpd4LKqiolV8bBtZWT5Qp7hClCNDENw==
@@ -19592,6 +19574,18 @@ glob@^10.3.4:
1959219574
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
1959319575
path-scurry "^1.10.1"
1959419576

19577+
glob@^10.3.7:
19578+
version "10.4.5"
19579+
resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956"
19580+
integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==
19581+
dependencies:
19582+
foreground-child "^3.1.0"
19583+
jackspeak "^3.1.2"
19584+
minimatch "^9.0.4"
19585+
minipass "^7.1.2"
19586+
package-json-from-dist "^1.0.0"
19587+
path-scurry "^1.11.1"
19588+
1959519589
glob@^5.0.10:
1959619590
version "5.0.15"
1959719591
resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
@@ -28978,7 +28972,7 @@ react-is@^18.0.0:
2897828972
dependencies:
2897928973
"@remix-run/router" "1.0.2"
2898028974

28981-
"react-router-6@npm:[email protected]":
28975+
"react-router-6@npm:[email protected]", [email protected]:
2898228976
version "6.3.0"
2898328977
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557"
2898428978
integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==
@@ -28993,13 +28987,6 @@ react-router-dom@^6.2.2:
2899328987
history "^5.2.0"
2899428988
react-router "6.3.0"
2899528989

28996-
28997-
version "6.3.0"
28998-
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557"
28999-
integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==
29000-
dependencies:
29001-
history "^5.2.0"
29002-
2900328990
react@^18.0.0:
2900428991
version "18.0.0"
2900528992
resolved "https://registry.yarnpkg.com/react/-/react-18.0.0.tgz#b468736d1f4a5891f38585ba8e8fb29f91c3cb96"
@@ -29922,6 +29909,13 @@ rimraf@^4.4.1:
2992229909
dependencies:
2992329910
glob "^9.2.0"
2992429911

29912+
rimraf@^5.0.0:
29913+
version "5.0.9"
29914+
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.9.tgz#c3baa1b886eadc2ec7981a06a593c3d01134ffe9"
29915+
integrity sha512-3i7b8OcswU6CpU8Ej89quJD4O98id7TtVM5U4Mybh84zQXdrFmDLouWBEEaD/QfO3gDDfH+AGFCGsR7kngzQnA==
29916+
dependencies:
29917+
glob "^10.3.7"
29918+
2992529919
rimraf@~2.6.2:
2992629920
version "2.6.3"
2992729921
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
@@ -31525,16 +31519,7 @@ string-template@~0.2.1:
3152531519
resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add"
3152631520
integrity sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=
3152731521

31528-
"string-width-cjs@npm:string-width@^4.2.0":
31529-
version "4.2.3"
31530-
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
31531-
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
31532-
dependencies:
31533-
emoji-regex "^8.0.0"
31534-
is-fullwidth-code-point "^3.0.0"
31535-
strip-ansi "^6.0.1"
31536-
31537-
[email protected], string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
31522+
"string-width-cjs@npm:string-width@^4.2.0", [email protected], string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
3153831523
version "4.2.3"
3153931524
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
3154031525
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -31646,14 +31631,7 @@ stringify-object@^3.2.1:
3164631631
is-obj "^1.0.1"
3164731632
is-regexp "^1.0.0"
3164831633

31649-
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
31650-
version "6.0.1"
31651-
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
31652-
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
31653-
dependencies:
31654-
ansi-regex "^5.0.1"
31655-
31656-
[email protected], strip-ansi@^6.0.0, strip-ansi@^6.0.1:
31634+
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", [email protected], strip-ansi@^6.0.0, strip-ansi@^6.0.1:
3165731635
version "6.0.1"
3165831636
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
3165931637
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -34746,16 +34724,7 @@ workerpool@^6.4.0:
3474634724
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.4.0.tgz#f8d5cfb45fde32fa3b7af72ad617c3369567a462"
3474734725
integrity sha512-i3KR1mQMNwY2wx20ozq2EjISGtQWDIfV56We+yGJ5yDs8jTwQiLLaqHlkBHITlCuJnYlVRmXegxFxZg7gqI++A==
3474834726

34749-
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
34750-
version "7.0.0"
34751-
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
34752-
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
34753-
dependencies:
34754-
ansi-styles "^4.0.0"
34755-
string-width "^4.1.0"
34756-
strip-ansi "^6.0.0"
34757-
34758-
[email protected], wrap-ansi@^7.0.0:
34727+
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", [email protected], wrap-ansi@^7.0.0:
3475934728
version "7.0.0"
3476034729
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
3476134730
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==

0 commit comments

Comments
 (0)