From ec8c39efa40fb866b419a06dc84148b9b247632a Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Fri, 8 Aug 2025 19:31:18 +0200 Subject: [PATCH 1/6] test(aws): Run E2E test in AWS SAM --- .github/workflows/build.yml | 6 + .../lambda-functions/basic/index.js | 9 ++ .../lambda-functions/esm/index.mjs | 10 ++ .../aws-lambda-sam/package.json | 31 +++++ .../aws-lambda-sam/playwright.config.ts | 3 + .../test-applications/aws-lambda-sam/stack.ts | 114 +++++++++++++++++ .../aws-lambda-sam/start-event-proxy.mjs | 6 + .../aws-lambda-sam/tests/lambda.test.ts | 120 ++++++++++++++++++ 8 files changed, 299 insertions(+) create mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions/basic/index.js create mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions/esm/index.mjs create mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-sam/package.json create mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-sam/playwright.config.ts create mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-sam/stack.ts create mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-sam/start-event-proxy.mjs create mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-sam/tests/lambda.test.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8ab3e3d77091..b21f488a6040 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1031,6 +1031,12 @@ jobs: uses: actions/setup-node@v4 with: node-version-file: 'dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}/package.json' + - name: Set up AWS SAM + if: matrix.test-application == 'aws-lambda-sam' + uses: aws-actions/setup-sam@v2 + with: + use-installer: true + token: ${{ secrets.GITHUB_TOKEN }} - name: Restore caches uses: ./.github/actions/restore-cache with: diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions/basic/index.js b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions/basic/index.js new file mode 100644 index 000000000000..a499eae5f410 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions/basic/index.js @@ -0,0 +1,9 @@ +module.exports.handler = async (event, context) => { + return { + statusCode: 200, + body: JSON.stringify({ + event, + context, + }), + }; +}; diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions/esm/index.mjs b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions/esm/index.mjs new file mode 100644 index 000000000000..0be852056a5c --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions/esm/index.mjs @@ -0,0 +1,10 @@ +import * as Sentry from '@sentry/aws-serverless'; + +export const handler = Sentry.wrapHandler(async (event, context) => { + return { + statusCode: 200, + body: JSON.stringify({ + event, + }), + }; +}); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/package.json b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/package.json new file mode 100644 index 000000000000..ec160f27602d --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/package.json @@ -0,0 +1,31 @@ +{ + "name": "aws-lambda-sam", + "version": "1.0.0", + "private": true, + "type": "commonjs", + "scripts": { + "test": "playwright test", + "clean": "npx rimraf node_modules pnpm-lock.yaml", + "test:build": "pnpm install && npx rimraf node_modules/@sentry/aws-serverless-layer/nodejs", + "test:assert": "pnpm test" + }, + "dependencies": { + "@sentry/aws-serverless": "* || latest" + }, + "//": "@sentry/aws-serverless-layer is not a package, but we use it to copy the layer zip file to the test app", + "devDependencies": { + "@aws-sdk/client-lambda": "^3.863.0", + "@playwright/test": "~1.53.2", + "@sentry-internal/test-utils": "link:../../../test-utils", + "@sentry/aws-serverless-layer": "link:../../../../packages/aws-serverless/build/aws/dist-serverless/", + "aws-cdk-lib": "^2.210.0", + "constructs": "^10.4.2", + "glob": "^11.0.3" + }, + "volta": { + "extends": "../../package.json" + }, + "sentryTest": { + "optional": true + } +} diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/playwright.config.ts b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/playwright.config.ts new file mode 100644 index 000000000000..174593c307df --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/playwright.config.ts @@ -0,0 +1,3 @@ +import { getPlaywrightConfig } from '@sentry-internal/test-utils'; + +export default getPlaywrightConfig(); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/stack.ts b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/stack.ts new file mode 100644 index 000000000000..f317a5f5dbc5 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/stack.ts @@ -0,0 +1,114 @@ +import { Stack, CfnResource, StackProps } from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as path from 'node:path'; +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as dns from 'node:dns/promises'; +import { platform } from 'node:process'; +import { globSync } from 'glob'; + +const LAMBDA_FUNCTION_DIR = './lambda-functions'; +const LAMBDA_FUNCTION_TIMEOUT = 10; +const LAYER_DIR = './node_modules/@sentry/aws-serverless-layer/'; +export const SAM_PORT = 3001; +const NODE_RUNTIME = `nodejs${process.version.split('.').at(0)?.replace('v', '')}.x`; + +export class LocalLambdaStack extends Stack { + sentryLayer: CfnResource; + + constructor(scope: Construct, id: string, props: StackProps, hostIp: string) { + console.log('[LocalLambdaStack] Creating local SAM Lambda Stack'); + super(scope, id, props); + + this.templateOptions.templateFormatVersion = '2010-09-09'; + this.templateOptions.transforms = ['AWS::Serverless-2016-10-31']; + + console.log('[LocalLambdaStack] Add Sentry Lambda layer containing the Sentry SDK to the SAM stack'); + + const [layerZipFile] = globSync('sentry-node-serverless-*.zip', { cwd: LAYER_DIR }); + + if (!layerZipFile) { + throw new Error(`[LocalLambdaStack] Could not find sentry-node-serverless zip file in ${LAYER_DIR}`); + } + + this.sentryLayer = new CfnResource(this, 'SentryNodeServerlessSDK', { + type: 'AWS::Serverless::LayerVersion', + properties: { + ContentUri: path.join(LAYER_DIR, layerZipFile), + CompatibleRuntimes: ['nodejs18.x', 'nodejs20.x', 'nodejs22.x'], + }, + }); + + const dsn = `http://public@${hostIp}:3031/1337`; + console.log(`[LocalLambdaStack] Using Sentry DSN: ${dsn}`); + + console.log('[LocalLambdaStack] Add all Lambda function defined in ./lambda-functions/ to the SAM stack'); + + const lambdaDirs = fs + .readdirSync(LAMBDA_FUNCTION_DIR) + .filter(dir => fs.statSync(path.join(LAMBDA_FUNCTION_DIR, dir)).isDirectory()); + + for (const lambdaDir of lambdaDirs) { + const isEsm = fs.existsSync(path.join(LAMBDA_FUNCTION_DIR, lambdaDir, 'index.mjs')); + + new CfnResource(this, lambdaDir, { + type: 'AWS::Serverless::Function', + properties: { + CodeUri: path.join(LAMBDA_FUNCTION_DIR, lambdaDir), + Handler: 'index.handler', + Runtime: NODE_RUNTIME, + Timeout: LAMBDA_FUNCTION_TIMEOUT, + Layers: [ + { + Ref: this.sentryLayer.logicalId, + }, + ], + Environment: { + Variables: { + SENTRY_DSN: dsn, + SENTRY_TRACES_SAMPLE_RATE: 1.0, + SENTRY_DEBUG: true, + NODE_OPTIONS: `--${isEsm ? 'import' : 'require'}=@sentry/aws-serverless/awslambda-auto`, + }, + }, + }, + }); + + console.log(`[LocalLambdaStack] Added Lambda function: ${lambdaDir}`); + } + } + + static async waitForStack(timeout = 60000, port = SAM_PORT) { + const startTime = Date.now(); + const maxWaitTime = timeout; + + while (Date.now() - startTime < maxWaitTime) { + try { + const response = await fetch(`http://127.0.0.1:${port}/`); + + if (response.ok || response.status === 404) { + console.log(`[LocalLambdaStack] SAM stack is ready`); + return; + } + } catch { + await new Promise(resolve => setTimeout(resolve, 1000)); + } + } + + throw new Error(`[LocalLambdaStack] Failed to start SAM stack after ${timeout}ms`); + } +} + +export async function getHostIp() { + if (process.env.GITHUB_ACTIONS) { + const host = await dns.lookup(os.hostname()); + return host.address; + } + + if (platform === 'darwin' || platform === 'win32') { + return 'host.docker.internal'; + } + + const host = await dns.lookup(os.hostname()); + return host.address; +} diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/start-event-proxy.mjs new file mode 100644 index 000000000000..196ae2471c69 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/start-event-proxy.mjs @@ -0,0 +1,6 @@ +import { startEventProxyServer } from '@sentry-internal/test-utils'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'aws-serverless-lambda-sam', +}); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/tests/lambda.test.ts b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/tests/lambda.test.ts new file mode 100644 index 000000000000..3633c7d4ca9d --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/tests/lambda.test.ts @@ -0,0 +1,120 @@ +import { test as base } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/test-utils'; +import { App } from 'aws-cdk-lib'; +import { LocalLambdaStack, SAM_PORT, getHostIp } from '../stack.js'; +import { writeFileSync, openSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import * as path from 'node:path'; +import { spawn, execSync } from 'node:child_process'; +import { InvokeCommand, LambdaClient } from '@aws-sdk/client-lambda'; + +const DOCKER_NETWORK_NAME = 'lambda-test-network'; +const SAM_TEMPLATE_FILE = 'sam.template.yml'; + +const test = base.extend<{ testEnvironment: LocalLambdaStack; lambdaClient: LambdaClient }>({ + testEnvironment: [ + async ({}, use) => { + console.log('[testEnvironment fixture] Setting up AWS Lambda test infrastructure'); + + execSync('docker network prune -f'); + execSync(`docker network create --driver bridge ${DOCKER_NETWORK_NAME}`); + + const hostIp = await getHostIp(); + const app = new App(); + + const stack = new LocalLambdaStack(app, 'LocalLambdaStack', {}, hostIp); + const template = app.synth().getStackByName('LocalLambdaStack').template; + writeFileSync(SAM_TEMPLATE_FILE, JSON.stringify(template, null, 2)); + + const debugLogFile = path.join(tmpdir(), 'sentry_aws_lambda_tests_sam_debug.log'); + const debugLogFd = openSync(debugLogFile, 'w'); + console.log(`[test_environment fixture] Writing SAM debug log to: ${debugLogFile}`); + + const process = spawn( + 'sam', + [ + 'local', + 'start-lambda', + '--debug', + '--template', + SAM_TEMPLATE_FILE, + '--warm-containers', + 'EAGER', + '--docker-network', + DOCKER_NETWORK_NAME, + ], + { + stdio: ['ignore', debugLogFd, debugLogFd], + }, + ); + + try { + await LocalLambdaStack.waitForStack(); + + await use(stack); + } finally { + console.log('[testEnvironment fixture] Tearing down AWS Lambda test infrastructure'); + + process.kill('SIGTERM'); + await new Promise(resolve => { + process.once('exit', resolve); + setTimeout(() => { + if (!process.killed) { + process.kill('SIGKILL'); + } + resolve(void 0); + }, 5000); + }); + } + }, + { scope: 'worker', auto: true }, + ], + lambdaClient: async ({}, use) => { + const lambdaClient = new LambdaClient({ + endpoint: `http://127.0.0.1:${SAM_PORT}`, + region: 'us-east-1', + credentials: { + accessKeyId: 'dummy', + secretAccessKey: 'dummy', + }, + }); + + await use(lambdaClient); + }, +}); + +test('basic no exception', async ({ lambdaClient }) => { + const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { + return transactionEvent?.transaction === 'basic'; + }); + + await lambdaClient.send( + new InvokeCommand({ + FunctionName: 'basic', + Payload: JSON.stringify({}), + }), + ); + + const transactionEvent = await transactionEventPromise; + + console.log('Transaction event received'); + + console.log(transactionEvent); +}); + +test('esm', async ({ lambdaClient }) => { + const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { + return transactionEvent?.transaction === 'esm'; + }); + + await lambdaClient.send( + new InvokeCommand({ + FunctionName: 'esm', + Payload: JSON.stringify({}), + }), + ); + + const transactionEvent = await transactionEventPromise; + + console.log(transactionEvent); +}); From 0dab9b38b7067f9e5904d61ad45b331fcc7a104e Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Mon, 11 Aug 2025 11:20:07 +0200 Subject: [PATCH 2/6] test both lambda layer and npm package in ESM and CJS --- .../Cjs}/index.js | 0 .../Esm}/index.mjs | 0 .../lambda-functions-npm/Cjs/index.js | 9 ++ .../lambda-functions-npm/Esm/index.mjs | 10 ++ .../aws-lambda-sam/package.json | 9 +- .../test-applications/aws-lambda-sam/stack.ts | 48 +++++--- .../aws-lambda-sam/tests/lambda.test.ts | 110 +++++++++++++----- 7 files changed, 139 insertions(+), 47 deletions(-) rename dev-packages/e2e-tests/test-applications/aws-lambda-sam/{lambda-functions/basic => lambda-functions-layer/Cjs}/index.js (100%) rename dev-packages/e2e-tests/test-applications/aws-lambda-sam/{lambda-functions/esm => lambda-functions-layer/Esm}/index.mjs (100%) create mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-npm/Cjs/index.js create mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-npm/Esm/index.mjs diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions/basic/index.js b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-layer/Cjs/index.js similarity index 100% rename from dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions/basic/index.js rename to dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-layer/Cjs/index.js diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions/esm/index.mjs b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-layer/Esm/index.mjs similarity index 100% rename from dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions/esm/index.mjs rename to dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-layer/Esm/index.mjs diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-npm/Cjs/index.js b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-npm/Cjs/index.js new file mode 100644 index 000000000000..a499eae5f410 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-npm/Cjs/index.js @@ -0,0 +1,9 @@ +module.exports.handler = async (event, context) => { + return { + statusCode: 200, + body: JSON.stringify({ + event, + context, + }), + }; +}; diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-npm/Esm/index.mjs b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-npm/Esm/index.mjs new file mode 100644 index 000000000000..0be852056a5c --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-npm/Esm/index.mjs @@ -0,0 +1,10 @@ +import * as Sentry from '@sentry/aws-serverless'; + +export const handler = Sentry.wrapHandler(async (event, context) => { + return { + statusCode: 200, + body: JSON.stringify({ + event, + }), + }; +}); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/package.json b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/package.json index ec160f27602d..f41f07a71764 100644 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/package.json +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/package.json @@ -6,18 +6,15 @@ "scripts": { "test": "playwright test", "clean": "npx rimraf node_modules pnpm-lock.yaml", - "test:build": "pnpm install && npx rimraf node_modules/@sentry/aws-serverless-layer/nodejs", + "test:build": "pnpm install && npx rimraf node_modules/@sentry/aws-serverless/nodejs", "test:assert": "pnpm test" }, - "dependencies": { - "@sentry/aws-serverless": "* || latest" - }, - "//": "@sentry/aws-serverless-layer is not a package, but we use it to copy the layer zip file to the test app", + "//": "We just need the AWS layer zip file, not the NPM package", "devDependencies": { + "@sentry/aws-serverless": "link:../../../../packages/aws-serverless/build/aws/dist-serverless/", "@aws-sdk/client-lambda": "^3.863.0", "@playwright/test": "~1.53.2", "@sentry-internal/test-utils": "link:../../../test-utils", - "@sentry/aws-serverless-layer": "link:../../../../packages/aws-serverless/build/aws/dist-serverless/", "aws-cdk-lib": "^2.210.0", "constructs": "^10.4.2", "glob": "^11.0.3" diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/stack.ts b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/stack.ts index f317a5f5dbc5..cd16d699d21a 100644 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/stack.ts +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/stack.ts @@ -6,10 +6,12 @@ import * as os from 'node:os'; import * as dns from 'node:dns/promises'; import { platform } from 'node:process'; import { globSync } from 'glob'; +import { execSync } from 'node:child_process'; -const LAMBDA_FUNCTION_DIR = './lambda-functions'; +const LAMBDA_FUNCTIONS_WITH_LAYER_DIR = './lambda-functions-layer'; +const LAMBDA_FUNCTIONS_WITH_NPM_DIR = './lambda-functions-npm'; const LAMBDA_FUNCTION_TIMEOUT = 10; -const LAYER_DIR = './node_modules/@sentry/aws-serverless-layer/'; +const LAYER_DIR = './node_modules/@sentry/aws-serverless/'; export const SAM_PORT = 3001; const NODE_RUNTIME = `nodejs${process.version.split('.').at(0)?.replace('v', '')}.x`; @@ -42,27 +44,45 @@ export class LocalLambdaStack extends Stack { const dsn = `http://public@${hostIp}:3031/1337`; console.log(`[LocalLambdaStack] Using Sentry DSN: ${dsn}`); - console.log('[LocalLambdaStack] Add all Lambda function defined in ./lambda-functions/ to the SAM stack'); + this.addLambdaFunctions({ functionsDir: LAMBDA_FUNCTIONS_WITH_LAYER_DIR, dsn, addLayer: true }); + this.addLambdaFunctions({ functionsDir: LAMBDA_FUNCTIONS_WITH_NPM_DIR, dsn, addLayer: false }); + } + + private addLambdaFunctions({ + functionsDir, + dsn, + addLayer, + }: { + functionsDir: string; + dsn: string; + addLayer: boolean; + }) { + console.log(`[LocalLambdaStack] Add all Lambda functions defined in ${functionsDir} to the SAM stack`); const lambdaDirs = fs - .readdirSync(LAMBDA_FUNCTION_DIR) - .filter(dir => fs.statSync(path.join(LAMBDA_FUNCTION_DIR, dir)).isDirectory()); + .readdirSync(functionsDir) + .filter(dir => fs.statSync(path.join(functionsDir, dir)).isDirectory()); for (const lambdaDir of lambdaDirs) { - const isEsm = fs.existsSync(path.join(LAMBDA_FUNCTION_DIR, lambdaDir, 'index.mjs')); + const functionName = `${lambdaDir}${addLayer ? 'Layer' : 'Npm'}`; + + if (!addLayer) { + console.log(`[LocalLambdaStack] Install dependencies for ${functionName}`); + const packageJson = { dependencies: { '@sentry/aws-serverless': '* || latest' } }; + fs.writeFileSync(path.join(functionsDir, lambdaDir, 'package.json'), JSON.stringify(packageJson, null, 2)); + execSync(`npm install --prefix ${path.join(functionsDir, lambdaDir)}`); + } + + const isEsm = fs.existsSync(path.join(functionsDir, lambdaDir, 'index.mjs')); - new CfnResource(this, lambdaDir, { + new CfnResource(this, functionName, { type: 'AWS::Serverless::Function', properties: { - CodeUri: path.join(LAMBDA_FUNCTION_DIR, lambdaDir), + CodeUri: path.join(functionsDir, lambdaDir), Handler: 'index.handler', Runtime: NODE_RUNTIME, Timeout: LAMBDA_FUNCTION_TIMEOUT, - Layers: [ - { - Ref: this.sentryLayer.logicalId, - }, - ], + Layers: addLayer ? [{ Ref: this.sentryLayer.logicalId }] : undefined, Environment: { Variables: { SENTRY_DSN: dsn, @@ -74,7 +94,7 @@ export class LocalLambdaStack extends Stack { }, }); - console.log(`[LocalLambdaStack] Added Lambda function: ${lambdaDir}`); + console.log(`[LocalLambdaStack] Added Lambda function: ${functionName}`); } } diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/tests/lambda.test.ts b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/tests/lambda.test.ts index 3633c7d4ca9d..26585f009d39 100644 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/tests/lambda.test.ts +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/tests/lambda.test.ts @@ -1,4 +1,4 @@ -import { test as base } from '@playwright/test'; +import { test as base, expect } from '@playwright/test'; import { waitForTransaction } from '@sentry-internal/test-utils'; import { App } from 'aws-cdk-lib'; import { LocalLambdaStack, SAM_PORT, getHostIp } from '../stack.js'; @@ -83,38 +83,94 @@ const test = base.extend<{ testEnvironment: LocalLambdaStack; lambdaClient: Lamb }, }); -test('basic no exception', async ({ lambdaClient }) => { - const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { - return transactionEvent?.transaction === 'basic'; - }); - - await lambdaClient.send( - new InvokeCommand({ - FunctionName: 'basic', - Payload: JSON.stringify({}), - }), - ); +test.describe('Lambda layer', () => { + test('CJS', async ({ lambdaClient }) => { + const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { + return transactionEvent?.transaction === 'CjsLayer'; + }); - const transactionEvent = await transactionEventPromise; + await lambdaClient.send( + new InvokeCommand({ + FunctionName: 'CjsLayer', + Payload: JSON.stringify({}), + }), + ); + + const transactionEvent = await transactionEventPromise; + + expect(transactionEvent.type).toEqual('transaction'); + expect(transactionEvent.transaction).toEqual('CjsLayer'); + expect(transactionEvent.sdk?.name).toEqual('sentry.javascript.aws-serverless'); + expect(transactionEvent.contexts?.trace?.origin).toEqual('auto.otel.aws-lambda'); + expect(transactionEvent.contexts?.trace?.op).toEqual('function.aws.lambda'); + expect(transactionEvent.spans).toHaveLength(0); + }); - console.log('Transaction event received'); + test('ESM', async ({ lambdaClient }) => { + const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { + return transactionEvent?.transaction === 'EsmLayer'; + }); - console.log(transactionEvent); + await lambdaClient.send( + new InvokeCommand({ + FunctionName: 'EsmLayer', + Payload: JSON.stringify({}), + }), + ); + + const transactionEvent = await transactionEventPromise; + + expect(transactionEvent.type).toEqual('transaction'); + expect(transactionEvent.transaction).toEqual('EsmLayer'); + expect(transactionEvent.sdk?.name).toEqual('sentry.javascript.aws-serverless'); + expect(transactionEvent.contexts?.trace?.origin).toEqual('auto.otel.aws-lambda'); + expect(transactionEvent.contexts?.trace?.op).toEqual('function.aws.lambda'); + expect(transactionEvent.spans).toHaveLength(0); + }); }); -test('esm', async ({ lambdaClient }) => { - const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { - return transactionEvent?.transaction === 'esm'; - }); +test.describe('NPM package', () => { + test('CJS', async ({ lambdaClient }) => { + const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { + return transactionEvent?.transaction === 'CjsNpm'; + }); - await lambdaClient.send( - new InvokeCommand({ - FunctionName: 'esm', - Payload: JSON.stringify({}), - }), - ); + await lambdaClient.send( + new InvokeCommand({ + FunctionName: 'CjsNpm', + Payload: JSON.stringify({}), + }), + ); + + const transactionEvent = await transactionEventPromise; + + expect(transactionEvent.type).toEqual('transaction'); + expect(transactionEvent.transaction).toEqual('CjsNpm'); + expect(transactionEvent.sdk?.name).toEqual('sentry.javascript.aws-serverless'); + expect(transactionEvent.contexts?.trace?.origin).toEqual('auto.otel.aws-lambda'); + expect(transactionEvent.contexts?.trace?.op).toEqual('function.aws.lambda'); + expect(transactionEvent.spans).toHaveLength(0); + }); - const transactionEvent = await transactionEventPromise; + test('ESM', async ({ lambdaClient }) => { + const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { + return transactionEvent?.transaction === 'EsmNpm'; + }); - console.log(transactionEvent); + await lambdaClient.send( + new InvokeCommand({ + FunctionName: 'EsmNpm', + Payload: JSON.stringify({}), + }), + ); + + const transactionEvent = await transactionEventPromise; + + expect(transactionEvent.type).toEqual('transaction'); + expect(transactionEvent.transaction).toEqual('EsmNpm'); + expect(transactionEvent.sdk?.name).toEqual('sentry.javascript.aws-serverless'); + expect(transactionEvent.contexts?.trace?.origin).toEqual('auto.otel.aws-lambda'); + expect(transactionEvent.contexts?.trace?.op).toEqual('function.aws.lambda'); + expect(transactionEvent.spans).toHaveLength(0); + }); }); From 0ea38b4939dbb40f51a77693982f823b8bd810b5 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Mon, 11 Aug 2025 11:33:56 +0200 Subject: [PATCH 3/6] security suggestions --- .../test-applications/aws-lambda-sam/package.json | 6 ++++-- .../test-applications/aws-lambda-sam/stack.ts | 4 ++-- .../aws-lambda-sam/tests/lambda.test.ts | 12 +++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/package.json b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/package.json index f41f07a71764..7ef68c3d96e7 100644 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/package.json +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/package.json @@ -11,13 +11,15 @@ }, "//": "We just need the AWS layer zip file, not the NPM package", "devDependencies": { - "@sentry/aws-serverless": "link:../../../../packages/aws-serverless/build/aws/dist-serverless/", "@aws-sdk/client-lambda": "^3.863.0", "@playwright/test": "~1.53.2", "@sentry-internal/test-utils": "link:../../../test-utils", + "@sentry/aws-serverless": "link:../../../../packages/aws-serverless/build/aws/dist-serverless/", + "@types/tmp": "^0.2.6", "aws-cdk-lib": "^2.210.0", "constructs": "^10.4.2", - "glob": "^11.0.3" + "glob": "^11.0.3", + "tmp": "^0.2.5" }, "volta": { "extends": "../../package.json" diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/stack.ts b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/stack.ts index cd16d699d21a..1c2671129437 100644 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/stack.ts +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/stack.ts @@ -6,7 +6,7 @@ import * as os from 'node:os'; import * as dns from 'node:dns/promises'; import { platform } from 'node:process'; import { globSync } from 'glob'; -import { execSync } from 'node:child_process'; +import { execFileSync } from 'node:child_process'; const LAMBDA_FUNCTIONS_WITH_LAYER_DIR = './lambda-functions-layer'; const LAMBDA_FUNCTIONS_WITH_NPM_DIR = './lambda-functions-npm'; @@ -70,7 +70,7 @@ export class LocalLambdaStack extends Stack { console.log(`[LocalLambdaStack] Install dependencies for ${functionName}`); const packageJson = { dependencies: { '@sentry/aws-serverless': '* || latest' } }; fs.writeFileSync(path.join(functionsDir, lambdaDir, 'package.json'), JSON.stringify(packageJson, null, 2)); - execSync(`npm install --prefix ${path.join(functionsDir, lambdaDir)}`); + execFileSync('npm', ['install', '--prefix', path.join(functionsDir, lambdaDir)], { stdio: 'inherit' }); } const isEsm = fs.existsSync(path.join(functionsDir, lambdaDir, 'index.mjs')); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/tests/lambda.test.ts b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/tests/lambda.test.ts index 26585f009d39..d99cb8ffac82 100644 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/tests/lambda.test.ts +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/tests/lambda.test.ts @@ -1,10 +1,9 @@ import { test as base, expect } from '@playwright/test'; import { waitForTransaction } from '@sentry-internal/test-utils'; import { App } from 'aws-cdk-lib'; +import * as tmp from 'tmp'; import { LocalLambdaStack, SAM_PORT, getHostIp } from '../stack.js'; -import { writeFileSync, openSync } from 'node:fs'; -import { tmpdir } from 'node:os'; -import * as path from 'node:path'; +import { writeFileSync } from 'node:fs'; import { spawn, execSync } from 'node:child_process'; import { InvokeCommand, LambdaClient } from '@aws-sdk/client-lambda'; @@ -26,9 +25,8 @@ const test = base.extend<{ testEnvironment: LocalLambdaStack; lambdaClient: Lamb const template = app.synth().getStackByName('LocalLambdaStack').template; writeFileSync(SAM_TEMPLATE_FILE, JSON.stringify(template, null, 2)); - const debugLogFile = path.join(tmpdir(), 'sentry_aws_lambda_tests_sam_debug.log'); - const debugLogFd = openSync(debugLogFile, 'w'); - console.log(`[test_environment fixture] Writing SAM debug log to: ${debugLogFile}`); + const debugLog = tmp.fileSync({ prefix: 'sentry_aws_lambda_tests_sam_debug_', postfix: '.log' }); + console.log(`[test_environment fixture] Writing SAM debug log to: ${debugLog.name}`); const process = spawn( 'sam', @@ -44,7 +42,7 @@ const test = base.extend<{ testEnvironment: LocalLambdaStack; lambdaClient: Lamb DOCKER_NETWORK_NAME, ], { - stdio: ['ignore', debugLogFd, debugLogFd], + stdio: ['ignore', debugLog.fd, debugLog.fd], }, ); From e54af55f15d6b3d8b1fedd90f67b11de217b7bb6 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Mon, 11 Aug 2025 17:12:09 +0200 Subject: [PATCH 4/6] replace exisiting e2e tests --- .github/workflows/build.yml | 12 +- .../lambda-functions-layer/Cjs/index.js | 9 - .../lambda-functions-layer/Esm/index.mjs | 10 - .../lambda-functions-npm/Cjs/index.js | 9 - .../lambda-functions-npm/Esm/index.mjs | 10 - .../aws-lambda-sam/tests/lambda.test.ts | 174 ------------------ .../test-applications/aws-serverless/.npmrc | 2 + .../package.json | 5 +- .../playwright.config.ts | 0 .../src/lambda-functions-layer/Error/index.js | 3 + .../TracingCjs/index.js | 18 ++ .../TracingEsm/index.mjs | 19 ++ .../lambda-functions-npm/TracingCjs/index.js | 24 +++ .../lambda-functions-npm/TracingEsm/index.mjs | 24 +++ .../src}/stack.ts | 6 +- .../start-event-proxy.mjs | 0 .../aws-serverless/tests/lambda-fixtures.ts | 83 +++++++++ .../aws-serverless/tests/layer.test.ts | 169 +++++++++++++++++ .../aws-serverless/tests/npm.test.ts | 141 ++++++++++++++ 19 files changed, 493 insertions(+), 225 deletions(-) delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-layer/Cjs/index.js delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-layer/Esm/index.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-npm/Cjs/index.js delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-npm/Esm/index.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-sam/tests/lambda.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless/.npmrc rename dev-packages/e2e-tests/test-applications/{aws-lambda-sam => aws-serverless}/package.json (87%) rename dev-packages/e2e-tests/test-applications/{aws-lambda-sam => aws-serverless}/playwright.config.ts (100%) create mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/Error/index.js create mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/TracingCjs/index.js create mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/TracingEsm/index.mjs create mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-npm/TracingCjs/index.js create mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-npm/TracingEsm/index.mjs rename dev-packages/e2e-tests/test-applications/{aws-lambda-sam => aws-serverless/src}/stack.ts (95%) rename dev-packages/e2e-tests/test-applications/{aws-lambda-sam => aws-serverless}/start-event-proxy.mjs (100%) create mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless/tests/lambda-fixtures.ts create mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless/tests/npm.test.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b21f488a6040..44057f39da11 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -910,6 +910,12 @@ jobs: - name: Set up Bun if: matrix.test-application == 'node-exports-test-app' uses: oven-sh/setup-bun@v2 + - name: Set up AWS SAM + if: matrix.test-application == 'aws-serverless' + uses: aws-actions/setup-sam@v2 + with: + use-installer: true + token: ${{ secrets.GITHUB_TOKEN }} - name: Restore caches uses: ./.github/actions/restore-cache with: @@ -1031,12 +1037,6 @@ jobs: uses: actions/setup-node@v4 with: node-version-file: 'dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}/package.json' - - name: Set up AWS SAM - if: matrix.test-application == 'aws-lambda-sam' - uses: aws-actions/setup-sam@v2 - with: - use-installer: true - token: ${{ secrets.GITHUB_TOKEN }} - name: Restore caches uses: ./.github/actions/restore-cache with: diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-layer/Cjs/index.js b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-layer/Cjs/index.js deleted file mode 100644 index a499eae5f410..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-layer/Cjs/index.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports.handler = async (event, context) => { - return { - statusCode: 200, - body: JSON.stringify({ - event, - context, - }), - }; -}; diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-layer/Esm/index.mjs b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-layer/Esm/index.mjs deleted file mode 100644 index 0be852056a5c..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-layer/Esm/index.mjs +++ /dev/null @@ -1,10 +0,0 @@ -import * as Sentry from '@sentry/aws-serverless'; - -export const handler = Sentry.wrapHandler(async (event, context) => { - return { - statusCode: 200, - body: JSON.stringify({ - event, - }), - }; -}); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-npm/Cjs/index.js b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-npm/Cjs/index.js deleted file mode 100644 index a499eae5f410..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-npm/Cjs/index.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports.handler = async (event, context) => { - return { - statusCode: 200, - body: JSON.stringify({ - event, - context, - }), - }; -}; diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-npm/Esm/index.mjs b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-npm/Esm/index.mjs deleted file mode 100644 index 0be852056a5c..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/lambda-functions-npm/Esm/index.mjs +++ /dev/null @@ -1,10 +0,0 @@ -import * as Sentry from '@sentry/aws-serverless'; - -export const handler = Sentry.wrapHandler(async (event, context) => { - return { - statusCode: 200, - body: JSON.stringify({ - event, - }), - }; -}); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/tests/lambda.test.ts b/dev-packages/e2e-tests/test-applications/aws-lambda-sam/tests/lambda.test.ts deleted file mode 100644 index d99cb8ffac82..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/tests/lambda.test.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { test as base, expect } from '@playwright/test'; -import { waitForTransaction } from '@sentry-internal/test-utils'; -import { App } from 'aws-cdk-lib'; -import * as tmp from 'tmp'; -import { LocalLambdaStack, SAM_PORT, getHostIp } from '../stack.js'; -import { writeFileSync } from 'node:fs'; -import { spawn, execSync } from 'node:child_process'; -import { InvokeCommand, LambdaClient } from '@aws-sdk/client-lambda'; - -const DOCKER_NETWORK_NAME = 'lambda-test-network'; -const SAM_TEMPLATE_FILE = 'sam.template.yml'; - -const test = base.extend<{ testEnvironment: LocalLambdaStack; lambdaClient: LambdaClient }>({ - testEnvironment: [ - async ({}, use) => { - console.log('[testEnvironment fixture] Setting up AWS Lambda test infrastructure'); - - execSync('docker network prune -f'); - execSync(`docker network create --driver bridge ${DOCKER_NETWORK_NAME}`); - - const hostIp = await getHostIp(); - const app = new App(); - - const stack = new LocalLambdaStack(app, 'LocalLambdaStack', {}, hostIp); - const template = app.synth().getStackByName('LocalLambdaStack').template; - writeFileSync(SAM_TEMPLATE_FILE, JSON.stringify(template, null, 2)); - - const debugLog = tmp.fileSync({ prefix: 'sentry_aws_lambda_tests_sam_debug_', postfix: '.log' }); - console.log(`[test_environment fixture] Writing SAM debug log to: ${debugLog.name}`); - - const process = spawn( - 'sam', - [ - 'local', - 'start-lambda', - '--debug', - '--template', - SAM_TEMPLATE_FILE, - '--warm-containers', - 'EAGER', - '--docker-network', - DOCKER_NETWORK_NAME, - ], - { - stdio: ['ignore', debugLog.fd, debugLog.fd], - }, - ); - - try { - await LocalLambdaStack.waitForStack(); - - await use(stack); - } finally { - console.log('[testEnvironment fixture] Tearing down AWS Lambda test infrastructure'); - - process.kill('SIGTERM'); - await new Promise(resolve => { - process.once('exit', resolve); - setTimeout(() => { - if (!process.killed) { - process.kill('SIGKILL'); - } - resolve(void 0); - }, 5000); - }); - } - }, - { scope: 'worker', auto: true }, - ], - lambdaClient: async ({}, use) => { - const lambdaClient = new LambdaClient({ - endpoint: `http://127.0.0.1:${SAM_PORT}`, - region: 'us-east-1', - credentials: { - accessKeyId: 'dummy', - secretAccessKey: 'dummy', - }, - }); - - await use(lambdaClient); - }, -}); - -test.describe('Lambda layer', () => { - test('CJS', async ({ lambdaClient }) => { - const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { - return transactionEvent?.transaction === 'CjsLayer'; - }); - - await lambdaClient.send( - new InvokeCommand({ - FunctionName: 'CjsLayer', - Payload: JSON.stringify({}), - }), - ); - - const transactionEvent = await transactionEventPromise; - - expect(transactionEvent.type).toEqual('transaction'); - expect(transactionEvent.transaction).toEqual('CjsLayer'); - expect(transactionEvent.sdk?.name).toEqual('sentry.javascript.aws-serverless'); - expect(transactionEvent.contexts?.trace?.origin).toEqual('auto.otel.aws-lambda'); - expect(transactionEvent.contexts?.trace?.op).toEqual('function.aws.lambda'); - expect(transactionEvent.spans).toHaveLength(0); - }); - - test('ESM', async ({ lambdaClient }) => { - const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { - return transactionEvent?.transaction === 'EsmLayer'; - }); - - await lambdaClient.send( - new InvokeCommand({ - FunctionName: 'EsmLayer', - Payload: JSON.stringify({}), - }), - ); - - const transactionEvent = await transactionEventPromise; - - expect(transactionEvent.type).toEqual('transaction'); - expect(transactionEvent.transaction).toEqual('EsmLayer'); - expect(transactionEvent.sdk?.name).toEqual('sentry.javascript.aws-serverless'); - expect(transactionEvent.contexts?.trace?.origin).toEqual('auto.otel.aws-lambda'); - expect(transactionEvent.contexts?.trace?.op).toEqual('function.aws.lambda'); - expect(transactionEvent.spans).toHaveLength(0); - }); -}); - -test.describe('NPM package', () => { - test('CJS', async ({ lambdaClient }) => { - const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { - return transactionEvent?.transaction === 'CjsNpm'; - }); - - await lambdaClient.send( - new InvokeCommand({ - FunctionName: 'CjsNpm', - Payload: JSON.stringify({}), - }), - ); - - const transactionEvent = await transactionEventPromise; - - expect(transactionEvent.type).toEqual('transaction'); - expect(transactionEvent.transaction).toEqual('CjsNpm'); - expect(transactionEvent.sdk?.name).toEqual('sentry.javascript.aws-serverless'); - expect(transactionEvent.contexts?.trace?.origin).toEqual('auto.otel.aws-lambda'); - expect(transactionEvent.contexts?.trace?.op).toEqual('function.aws.lambda'); - expect(transactionEvent.spans).toHaveLength(0); - }); - - test('ESM', async ({ lambdaClient }) => { - const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { - return transactionEvent?.transaction === 'EsmNpm'; - }); - - await lambdaClient.send( - new InvokeCommand({ - FunctionName: 'EsmNpm', - Payload: JSON.stringify({}), - }), - ); - - const transactionEvent = await transactionEventPromise; - - expect(transactionEvent.type).toEqual('transaction'); - expect(transactionEvent.transaction).toEqual('EsmNpm'); - expect(transactionEvent.sdk?.name).toEqual('sentry.javascript.aws-serverless'); - expect(transactionEvent.contexts?.trace?.origin).toEqual('auto.otel.aws-lambda'); - expect(transactionEvent.contexts?.trace?.op).toEqual('function.aws.lambda'); - expect(transactionEvent.spans).toHaveLength(0); - }); -}); diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/.npmrc b/dev-packages/e2e-tests/test-applications/aws-serverless/.npmrc new file mode 100644 index 000000000000..070f80f05092 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/.npmrc @@ -0,0 +1,2 @@ +@sentry:registry=http://127.0.0.1:4873 +@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/package.json b/dev-packages/e2e-tests/test-applications/aws-serverless/package.json similarity index 87% rename from dev-packages/e2e-tests/test-applications/aws-lambda-sam/package.json rename to dev-packages/e2e-tests/test-applications/aws-serverless/package.json index 7ef68c3d96e7..83437b2f9fbf 100644 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/package.json +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/package.json @@ -9,7 +9,7 @@ "test:build": "pnpm install && npx rimraf node_modules/@sentry/aws-serverless/nodejs", "test:assert": "pnpm test" }, - "//": "We just need the AWS layer zip file, not the NPM package", + "//": "We just need the @sentry/aws-serverless layer zip file, not the NPM package", "devDependencies": { "@aws-sdk/client-lambda": "^3.863.0", "@playwright/test": "~1.53.2", @@ -23,8 +23,5 @@ }, "volta": { "extends": "../../package.json" - }, - "sentryTest": { - "optional": true } } diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/playwright.config.ts b/dev-packages/e2e-tests/test-applications/aws-serverless/playwright.config.ts similarity index 100% rename from dev-packages/e2e-tests/test-applications/aws-lambda-sam/playwright.config.ts rename to dev-packages/e2e-tests/test-applications/aws-serverless/playwright.config.ts diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/Error/index.js b/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/Error/index.js new file mode 100644 index 000000000000..06a9c37a610c --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/Error/index.js @@ -0,0 +1,3 @@ +exports.handler = async (event, context) => { + throw new Error('test'); +}; diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/TracingCjs/index.js b/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/TracingCjs/index.js new file mode 100644 index 000000000000..a614387ddccd --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/TracingCjs/index.js @@ -0,0 +1,18 @@ +const Sentry = require('@sentry/aws-serverless'); +const http = require('http'); + +exports.handler = async (event, context) => { + await Sentry.startSpan({ name: 'manual-span', op: 'test' }, async () => { + await new Promise(resolve => { + http.get('http://example.com', res => { + res.on('data', d => { + process.stdout.write(d); + }); + + res.on('end', () => { + resolve(); + }); + }); + }); + }); +}; diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/TracingEsm/index.mjs b/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/TracingEsm/index.mjs new file mode 100644 index 000000000000..b13f30397b62 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-layer/TracingEsm/index.mjs @@ -0,0 +1,19 @@ +import * as Sentry from '@sentry/aws-serverless'; + +import * as http from 'node:http'; + +export const handler = Sentry.wrapHandler(async () => { + await Sentry.startSpan({ name: 'manual-span', op: 'test' }, async () => { + await new Promise(resolve => { + http.get('http://example.com', res => { + res.on('data', d => { + process.stdout.write(d); + }); + + res.on('end', () => { + resolve(); + }); + }); + }); + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-npm/TracingCjs/index.js b/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-npm/TracingCjs/index.js new file mode 100644 index 000000000000..534909d6764e --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-npm/TracingCjs/index.js @@ -0,0 +1,24 @@ +const http = require('http'); +const Sentry = require('@sentry/aws-serverless'); + +exports.handler = Sentry.wrapHandler(async () => { + await new Promise(resolve => { + const req = http.request( + { + host: 'example.com', + }, + res => { + res.on('data', d => { + process.stdout.write(d); + }); + + res.on('end', () => { + resolve(); + }); + }, + ); + req.end(); + }); + + Sentry.startSpan({ name: 'manual-span', op: 'manual' }, () => {}); +}); diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-npm/TracingEsm/index.mjs b/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-npm/TracingEsm/index.mjs new file mode 100644 index 000000000000..346613025497 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/src/lambda-functions-npm/TracingEsm/index.mjs @@ -0,0 +1,24 @@ +import * as http from 'node:http'; +import * as Sentry from '@sentry/aws-serverless'; + +export const handler = Sentry.wrapHandler(async () => { + await new Promise(resolve => { + const req = http.request( + { + host: 'example.com', + }, + res => { + res.on('data', d => { + process.stdout.write(d); + }); + + res.on('end', () => { + resolve(); + }); + }, + ); + req.end(); + }); + + Sentry.startSpan({ name: 'manual-span', op: 'manual' }, () => {}); +}); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/stack.ts b/dev-packages/e2e-tests/test-applications/aws-serverless/src/stack.ts similarity index 95% rename from dev-packages/e2e-tests/test-applications/aws-lambda-sam/stack.ts rename to dev-packages/e2e-tests/test-applications/aws-serverless/src/stack.ts index 1c2671129437..825c9648ee66 100644 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/stack.ts +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/src/stack.ts @@ -8,8 +8,8 @@ import { platform } from 'node:process'; import { globSync } from 'glob'; import { execFileSync } from 'node:child_process'; -const LAMBDA_FUNCTIONS_WITH_LAYER_DIR = './lambda-functions-layer'; -const LAMBDA_FUNCTIONS_WITH_NPM_DIR = './lambda-functions-npm'; +const LAMBDA_FUNCTIONS_WITH_LAYER_DIR = './src/lambda-functions-layer'; +const LAMBDA_FUNCTIONS_WITH_NPM_DIR = './src/lambda-functions-npm'; const LAMBDA_FUNCTION_TIMEOUT = 10; const LAYER_DIR = './node_modules/@sentry/aws-serverless/'; export const SAM_PORT = 3001; @@ -64,7 +64,7 @@ export class LocalLambdaStack extends Stack { .filter(dir => fs.statSync(path.join(functionsDir, dir)).isDirectory()); for (const lambdaDir of lambdaDirs) { - const functionName = `${lambdaDir}${addLayer ? 'Layer' : 'Npm'}`; + const functionName = `${addLayer ? 'Layer' : 'Npm'}${lambdaDir}`; if (!addLayer) { console.log(`[LocalLambdaStack] Install dependencies for ${functionName}`); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-sam/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/aws-serverless/start-event-proxy.mjs similarity index 100% rename from dev-packages/e2e-tests/test-applications/aws-lambda-sam/start-event-proxy.mjs rename to dev-packages/e2e-tests/test-applications/aws-serverless/start-event-proxy.mjs diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/tests/lambda-fixtures.ts b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/lambda-fixtures.ts new file mode 100644 index 000000000000..a4828dfbcb1e --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/lambda-fixtures.ts @@ -0,0 +1,83 @@ +import { test as base, expect } from '@playwright/test'; +import { App } from 'aws-cdk-lib'; +import * as tmp from 'tmp'; +import { LocalLambdaStack, SAM_PORT, getHostIp } from '../src/stack.js'; +import { writeFileSync } from 'node:fs'; +import { spawn, execSync } from 'node:child_process'; +import { LambdaClient } from '@aws-sdk/client-lambda'; + +const DOCKER_NETWORK_NAME = 'lambda-test-network'; +const SAM_TEMPLATE_FILE = 'sam.template.yml'; + +export { expect }; + +export const test = base.extend<{ testEnvironment: LocalLambdaStack; lambdaClient: LambdaClient }>({ + testEnvironment: [ + async ({}, use) => { + console.log('[testEnvironment fixture] Setting up AWS Lambda test infrastructure'); + + execSync('docker network prune -f'); + execSync(`docker network create --driver bridge ${DOCKER_NETWORK_NAME}`); + + const hostIp = await getHostIp(); + const app = new App(); + + const stack = new LocalLambdaStack(app, 'LocalLambdaStack', {}, hostIp); + const template = app.synth().getStackByName('LocalLambdaStack').template; + writeFileSync(SAM_TEMPLATE_FILE, JSON.stringify(template, null, 2)); + + const debugLog = tmp.fileSync({ prefix: 'sentry_aws_lambda_tests_sam_debug', postfix: '.log' }); + console.log(`[test_environment fixture] Writing SAM debug log to: ${debugLog.name}`); + + const process = spawn( + 'sam', + [ + 'local', + 'start-lambda', + '--debug', + '--template', + SAM_TEMPLATE_FILE, + '--warm-containers', + 'EAGER', + '--docker-network', + DOCKER_NETWORK_NAME, + ], + { + stdio: ['ignore', debugLog.fd, debugLog.fd], + }, + ); + + try { + await LocalLambdaStack.waitForStack(); + + await use(stack); + } finally { + console.log('[testEnvironment fixture] Tearing down AWS Lambda test infrastructure'); + + process.kill('SIGTERM'); + await new Promise(resolve => { + process.once('exit', resolve); + setTimeout(() => { + if (!process.killed) { + process.kill('SIGKILL'); + } + resolve(void 0); + }, 5000); + }); + } + }, + { scope: 'worker', auto: true }, + ], + lambdaClient: async ({}, use) => { + const lambdaClient = new LambdaClient({ + endpoint: `http://127.0.0.1:${SAM_PORT}`, + region: 'us-east-1', + credentials: { + accessKeyId: 'dummy', + secretAccessKey: 'dummy', + }, + }); + + await use(lambdaClient); + }, +}); diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts new file mode 100644 index 000000000000..6f38e3d46a4f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts @@ -0,0 +1,169 @@ +import { waitForTransaction, waitForError } from '@sentry-internal/test-utils'; +import { InvokeCommand } from '@aws-sdk/client-lambda'; +import { test, expect } from './lambda-fixtures.js'; + +test.describe('Lambda layer', () => { + test('tracing in CJS works', async ({ lambdaClient }) => { + const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { + return transactionEvent?.transaction === 'LayerTracingCjs'; + }); + + await lambdaClient.send( + new InvokeCommand({ + FunctionName: 'LayerTracingCjs', + Payload: JSON.stringify({}), + }), + ); + + const transactionEvent = await transactionEventPromise; + + // shows the SDK sent a transaction + expect(transactionEvent.transaction).toEqual('LayerTracingCjs'); + expect(transactionEvent.contexts?.trace).toEqual({ + data: { + 'sentry.sample_rate': 1, + 'sentry.source': 'custom', + 'sentry.origin': 'auto.otel.aws-lambda', + 'sentry.op': 'function.aws.lambda', + 'cloud.account.id': '012345678912', + 'faas.execution': expect.any(String), + 'faas.id': 'arn:aws:lambda:us-east-1:012345678912:function:LayerTracingCjs', + 'faas.coldstart': true, + 'otel.kind': 'SERVER', + }, + op: 'function.aws.lambda', + origin: 'auto.otel.aws-lambda', + span_id: expect.stringMatching(/[a-f0-9]{16}/), + status: 'ok', + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + }); + + expect(transactionEvent.spans).toHaveLength(2); + + // shows that the Otel Http instrumentation is working + expect(transactionEvent.spans).toContainEqual( + expect.objectContaining({ + data: expect.objectContaining({ + 'sentry.op': 'http.client', + 'sentry.origin': 'auto.http.otel.http', + url: 'http://example.com/', + }), + description: 'GET http://example.com/', + op: 'http.client', + }), + ); + + // shows that the manual span creation is working + expect(transactionEvent.spans).toContainEqual( + expect.objectContaining({ + data: expect.objectContaining({ + 'sentry.op': 'test', + 'sentry.origin': 'manual', + }), + description: 'manual-span', + op: 'test', + }), + ); + + // shows that the SDK source is correctly detected + expect(transactionEvent.sdk?.packages).toContainEqual( + expect.objectContaining({ name: 'aws-lambda-layer:@sentry/aws-serverless' }), + ); + }); + + test('tracing in ESM works', async ({ lambdaClient }) => { + const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { + return transactionEvent?.transaction === 'LayerTracingEsm'; + }); + + await lambdaClient.send( + new InvokeCommand({ + FunctionName: 'LayerTracingEsm', + Payload: JSON.stringify({}), + }), + ); + + const transactionEvent = await transactionEventPromise; + + // shows the SDK sent a transaction + expect(transactionEvent.transaction).toEqual('LayerTracingEsm'); // name should be the function name + expect(transactionEvent.contexts?.trace).toEqual({ + data: { + 'sentry.sample_rate': 1, + 'sentry.source': 'custom', + 'sentry.origin': 'auto.otel.aws-lambda', + 'sentry.op': 'function.aws.lambda', + 'cloud.account.id': '012345678912', + 'faas.execution': expect.any(String), + 'faas.id': 'arn:aws:lambda:us-east-1:012345678912:function:LayerTracingEsm', + 'faas.coldstart': true, + 'otel.kind': 'SERVER', + }, + op: 'function.aws.lambda', + origin: 'auto.otel.aws-lambda', + span_id: expect.stringMatching(/[a-f0-9]{16}/), + status: 'ok', + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + }); + + expect(transactionEvent.spans).toHaveLength(2); + + // shows that the Otel Http instrumentation is working + expect(transactionEvent.spans).toContainEqual( + expect.objectContaining({ + data: expect.objectContaining({ + 'sentry.op': 'http.client', + 'sentry.origin': 'auto.http.otel.http', + url: 'http://example.com/', + }), + description: 'GET http://example.com/', + op: 'http.client', + }), + ); + + // shows that the manual span creation is working + expect(transactionEvent.spans).toContainEqual( + expect.objectContaining({ + data: expect.objectContaining({ + 'sentry.op': 'test', + 'sentry.origin': 'manual', + }), + description: 'manual-span', + op: 'test', + }), + ); + + // shows that the SDK source is correctly detected + expect(transactionEvent.sdk?.packages).toContainEqual( + expect.objectContaining({ name: 'aws-lambda-layer:@sentry/aws-serverless' }), + ); + }); + + test('capturing errors works', async ({ lambdaClient }) => { + const errorEventPromise = waitForError('aws-serverless-lambda-sam', errorEvent => { + return errorEvent?.exception?.values?.[0]?.value === 'test'; + }); + + await lambdaClient.send( + new InvokeCommand({ + FunctionName: 'LayerError', + Payload: JSON.stringify({}), + }), + ); + + const errorEvent = await errorEventPromise; + + // shows the SDK sent an error event + expect(errorEvent.exception?.values).toHaveLength(1); + expect(errorEvent.exception?.values?.[0]).toEqual( + expect.objectContaining({ + type: 'Error', + value: 'test', + mechanism: { + type: 'auto.function.aws-serverless.handler', + handled: false, + }, + }), + ); + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/tests/npm.test.ts b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/npm.test.ts new file mode 100644 index 000000000000..f071e65913e1 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/npm.test.ts @@ -0,0 +1,141 @@ +import { waitForTransaction } from '@sentry-internal/test-utils'; +import { InvokeCommand } from '@aws-sdk/client-lambda'; +import { test, expect } from './lambda-fixtures.js'; + +test.describe('NPM package', () => { + test('tracing in CJS works', async ({ lambdaClient }) => { + const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { + return transactionEvent?.transaction === 'NpmTracingCjs'; + }); + + await lambdaClient.send( + new InvokeCommand({ + FunctionName: 'NpmTracingCjs', + Payload: JSON.stringify({}), + }), + ); + + const transactionEvent = await transactionEventPromise; + + // shows the SDK sent a transaction + expect(transactionEvent.transaction).toEqual('NpmTracingCjs'); // name should be the function name + expect(transactionEvent.contexts?.trace).toEqual({ + data: { + 'sentry.sample_rate': 1, + 'sentry.source': 'custom', + 'sentry.origin': 'auto.otel.aws-lambda', + 'sentry.op': 'function.aws.lambda', + 'cloud.account.id': '012345678912', + 'faas.execution': expect.any(String), + 'faas.id': 'arn:aws:lambda:us-east-1:012345678912:function:NpmTracingCjs', + 'faas.coldstart': true, + 'otel.kind': 'SERVER', + }, + op: 'function.aws.lambda', + origin: 'auto.otel.aws-lambda', + span_id: expect.stringMatching(/[a-f0-9]{16}/), + status: 'ok', + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + }); + + expect(transactionEvent.spans).toHaveLength(2); + + // shows that the Otel Http instrumentation is working + expect(transactionEvent.spans).toContainEqual( + expect.objectContaining({ + data: expect.objectContaining({ + 'sentry.op': 'http.client', + 'sentry.origin': 'auto.http.otel.http', + url: 'http://example.com/', + }), + description: 'GET http://example.com/', + op: 'http.client', + }), + ); + + // shows that the manual span creation is working + expect(transactionEvent.spans).toContainEqual( + expect.objectContaining({ + data: expect.objectContaining({ + 'sentry.op': 'manual', + 'sentry.origin': 'manual', + }), + description: 'manual-span', + op: 'manual', + }), + ); + + // shows that the SDK source is correctly detected + expect(transactionEvent.sdk?.packages).toContainEqual( + expect.objectContaining({ name: 'npm:@sentry/aws-serverless' }), + ); + }); + + test('tracing in ESM works', async ({ lambdaClient }) => { + const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => { + return transactionEvent?.transaction === 'NpmTracingEsm'; + }); + + await lambdaClient.send( + new InvokeCommand({ + FunctionName: 'NpmTracingEsm', + Payload: JSON.stringify({}), + }), + ); + + const transactionEvent = await transactionEventPromise; + + // shows the SDK sent a transaction + expect(transactionEvent.transaction).toEqual('NpmTracingEsm'); // name should be the function name + expect(transactionEvent.contexts?.trace).toEqual({ + data: { + 'sentry.sample_rate': 1, + 'sentry.source': 'custom', + 'sentry.origin': 'auto.otel.aws-lambda', + 'sentry.op': 'function.aws.lambda', + 'cloud.account.id': '012345678912', + 'faas.execution': expect.any(String), + 'faas.id': 'arn:aws:lambda:us-east-1:012345678912:function:NpmTracingEsm', + 'faas.coldstart': true, + 'otel.kind': 'SERVER', + }, + op: 'function.aws.lambda', + origin: 'auto.otel.aws-lambda', + span_id: expect.stringMatching(/[a-f0-9]{16}/), + status: 'ok', + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + }); + + expect(transactionEvent.spans).toHaveLength(2); + + // shows that the Otel Http instrumentation is working + expect(transactionEvent.spans).toContainEqual( + expect.objectContaining({ + data: expect.objectContaining({ + 'sentry.op': 'http.client', + 'sentry.origin': 'auto.http.otel.http', + url: 'http://example.com/', + }), + description: 'GET http://example.com/', + op: 'http.client', + }), + ); + + // shows that the manual span creation is working + expect(transactionEvent.spans).toContainEqual( + expect.objectContaining({ + data: expect.objectContaining({ + 'sentry.op': 'manual', + 'sentry.origin': 'manual', + }), + description: 'manual-span', + op: 'manual', + }), + ); + + // shows that the SDK source is correctly detected + expect(transactionEvent.sdk?.packages).toContainEqual( + expect.objectContaining({ name: 'npm:@sentry/aws-serverless' }), + ); + }); +}); From 3393bce7d504d7f5e17df9c704738e734d9c20c8 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Mon, 11 Aug 2025 17:23:04 +0200 Subject: [PATCH 5/6] remove old tests --- .../aws-lambda-layer-cjs/.npmrc | 2 - .../aws-lambda-layer-cjs/package.json | 23 ------ .../aws-lambda-layer-cjs/playwright.config.ts | 3 - .../src/lambda-function.js | 21 ----- .../aws-lambda-layer-cjs/src/run-lambda.js | 7 -- .../aws-lambda-layer-cjs/src/run.js | 17 ---- .../start-event-proxy.mjs | 6 -- .../aws-lambda-layer-cjs/tests/basic.test.ts | 77 ------------------- .../aws-lambda-layer-esm/.npmrc | 2 - .../aws-lambda-layer-esm/package.json | 23 ------ .../aws-lambda-layer-esm/playwright.config.ts | 3 - .../src/lambda-function.mjs | 21 ----- .../aws-lambda-layer-esm/src/run-lambda.mjs | 8 -- .../aws-lambda-layer-esm/src/run.mjs | 17 ---- .../start-event-proxy.mjs | 6 -- .../aws-lambda-layer-esm/tests/basic.test.ts | 77 ------------------- .../aws-serverless-esm/.npmrc | 2 - .../aws-serverless-esm/package.json | 22 ------ .../aws-serverless-esm/playwright.config.ts | 3 - .../src/lambda-function.mjs | 26 ------- .../aws-serverless-esm/src/package.json | 5 -- .../aws-serverless-esm/src/run-lambda.mjs | 10 --- .../aws-serverless-esm/src/run.mjs | 16 ---- .../aws-serverless-esm/start-event-proxy.mjs | 6 -- .../aws-serverless-esm/tests/basic.test.ts | 77 ------------------- 25 files changed, 480 deletions(-) delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/package.json delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/playwright.config.ts delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/src/lambda-function.js delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/src/run-lambda.js delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/src/run.js delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/start-event-proxy.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/tests/basic.test.ts delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/package.json delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/playwright.config.ts delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/lambda-function.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/run-lambda.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/run.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/start-event-proxy.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/tests/basic.test.ts delete mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless-esm/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless-esm/package.json delete mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless-esm/playwright.config.ts delete mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless-esm/src/lambda-function.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless-esm/src/package.json delete mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless-esm/src/run-lambda.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless-esm/src/run.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless-esm/start-event-proxy.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/aws-serverless-esm/tests/basic.test.ts diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/.npmrc b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/package.json b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/package.json deleted file mode 100644 index 25489cf0a35e..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "aws-lambda-layer-cjs", - "version": "1.0.0", - "private": true, - "type": "commonjs", - "scripts": { - "start": "node src/run.js", - "test": "playwright test", - "clean": "npx rimraf node_modules pnpm-lock.yaml", - "test:build": "pnpm install", - "test:assert": "pnpm test" - }, - "dependencies": { - "@sentry/aws-serverless": "link:../../../../packages/aws-serverless/build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless" - }, - "devDependencies": { - "@sentry-internal/test-utils": "link:../../../test-utils", - "@playwright/test": "~1.53.2" - }, - "volta": { - "extends": "../../package.json" - } -} diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/playwright.config.ts b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/playwright.config.ts deleted file mode 100644 index 174593c307df..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/playwright.config.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { getPlaywrightConfig } from '@sentry-internal/test-utils'; - -export default getPlaywrightConfig(); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/src/lambda-function.js b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/src/lambda-function.js deleted file mode 100644 index c688ed35a0c4..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/src/lambda-function.js +++ /dev/null @@ -1,21 +0,0 @@ -const Sentry = require('@sentry/aws-serverless'); - -const http = require('http'); - -async function handle() { - await Sentry.startSpan({ name: 'manual-span', op: 'test' }, async () => { - await new Promise(resolve => { - http.get('http://example.com', res => { - res.on('data', d => { - process.stdout.write(d); - }); - - res.on('end', () => { - resolve(); - }); - }); - }); - }); -} - -module.exports = { handle }; diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/src/run-lambda.js b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/src/run-lambda.js deleted file mode 100644 index 1d6e059e78f3..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/src/run-lambda.js +++ /dev/null @@ -1,7 +0,0 @@ -const { handle } = require('./lambda-function'); -const event = {}; -const context = { - invokedFunctionArn: 'arn:aws:lambda:us-east-1:123453789012:function:my-lambda', - functionName: 'my-lambda', -}; -handle(event, context); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/src/run.js b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/src/run.js deleted file mode 100644 index 2605f624ca9a..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/src/run.js +++ /dev/null @@ -1,17 +0,0 @@ -const child_process = require('child_process'); - -child_process.execSync('node ./src/run-lambda.js', { - stdio: 'inherit', - env: { - ...process.env, - // On AWS, LAMBDA_TASK_ROOT is usually /var/task but for testing, we set it to the CWD to correctly apply our handler - LAMBDA_TASK_ROOT: process.cwd(), - _HANDLER: 'src/lambda-function.handle', - - NODE_OPTIONS: '--require @sentry/aws-serverless/dist/awslambda-auto', - SENTRY_DSN: 'http://public@localhost:3031/1337', - SENTRY_TRACES_SAMPLE_RATE: '1.0', - SENTRY_DEBUG: 'true', - }, - cwd: process.cwd(), -}); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/start-event-proxy.mjs deleted file mode 100644 index fc4ac82aa7c6..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/start-event-proxy.mjs +++ /dev/null @@ -1,6 +0,0 @@ -import { startEventProxyServer } from '@sentry-internal/test-utils'; - -startEventProxyServer({ - port: 3031, - proxyServerName: 'aws-serverless-lambda-layer-cjs', -}); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/tests/basic.test.ts b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/tests/basic.test.ts deleted file mode 100644 index b8f7a4b4d51e..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-cjs/tests/basic.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import * as child_process from 'child_process'; -import { expect, test } from '@playwright/test'; -import { waitForTransaction } from '@sentry-internal/test-utils'; - -test('Lambda layer SDK bundle sends events', async ({ request }) => { - const transactionEventPromise = waitForTransaction('aws-serverless-lambda-layer-cjs', transactionEvent => { - return transactionEvent?.transaction === 'my-lambda'; - }); - - // Waiting for 1s here because attaching the listener for events in `waitForTransaction` is not synchronous - // Since in this test, we don't start a browser via playwright, we don't have the usual delays (page.goto, etc) - // which are usually enough for us to never have noticed this race condition before. - // This is a workaround but probably sufficient as long as we only experience it in this test. - await new Promise(resolve => - setTimeout(() => { - resolve(); - }, 1000), - ); - - child_process.execSync('pnpm start', { - stdio: 'inherit', - }); - - const transactionEvent = await transactionEventPromise; - - // shows the SDK sent a transaction - expect(transactionEvent.transaction).toEqual('my-lambda'); // name should be the function name - expect(transactionEvent.contexts?.trace).toEqual({ - data: { - 'sentry.sample_rate': 1, - 'sentry.source': 'custom', - 'sentry.origin': 'auto.otel.aws-lambda', - 'sentry.op': 'function.aws.lambda', - 'cloud.account.id': '123453789012', - 'faas.id': 'arn:aws:lambda:us-east-1:123453789012:function:my-lambda', - 'faas.coldstart': true, - 'otel.kind': 'SERVER', - }, - op: 'function.aws.lambda', - origin: 'auto.otel.aws-lambda', - span_id: expect.stringMatching(/[a-f0-9]{16}/), - status: 'ok', - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - }); - - expect(transactionEvent.spans).toHaveLength(2); - - // shows that the Otel Http instrumentation is working - expect(transactionEvent.spans).toContainEqual( - expect.objectContaining({ - data: expect.objectContaining({ - 'sentry.op': 'http.client', - 'sentry.origin': 'auto.http.otel.http', - url: 'http://example.com/', - }), - description: 'GET http://example.com/', - op: 'http.client', - }), - ); - - // shows that the manual span creation is working - expect(transactionEvent.spans).toContainEqual( - expect.objectContaining({ - data: expect.objectContaining({ - 'sentry.op': 'test', - 'sentry.origin': 'manual', - }), - description: 'manual-span', - op: 'test', - }), - ); - - // shows that the SDK source is correctly detected - expect(transactionEvent.sdk?.packages).toContainEqual( - expect.objectContaining({ name: 'aws-lambda-layer:@sentry/aws-serverless' }), - ); -}); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/.npmrc b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/package.json b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/package.json deleted file mode 100644 index 7a25061dde1c..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "aws-lambda-layer-esm", - "version": "1.0.0", - "private": true, - "scripts": { - "start": "node src/run.mjs", - "test": "playwright test", - "clean": "npx rimraf node_modules pnpm-lock.yaml", - "test:build": "pnpm install", - "test:assert": "pnpm test" - }, - "//": "Link from local Lambda layer build", - "dependencies": { - "@sentry/aws-serverless": "link:../../../../packages/aws-serverless/build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless" - }, - "devDependencies": { - "@sentry-internal/test-utils": "link:../../../test-utils", - "@playwright/test": "~1.53.2" - }, - "volta": { - "extends": "../../package.json" - } -} diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/playwright.config.ts b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/playwright.config.ts deleted file mode 100644 index 174593c307df..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/playwright.config.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { getPlaywrightConfig } from '@sentry-internal/test-utils'; - -export default getPlaywrightConfig(); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/lambda-function.mjs b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/lambda-function.mjs deleted file mode 100644 index a9cdd48c1197..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/lambda-function.mjs +++ /dev/null @@ -1,21 +0,0 @@ -import * as Sentry from '@sentry/aws-serverless'; - -import * as http from 'node:http'; - -async function handle() { - await Sentry.startSpan({ name: 'manual-span', op: 'test' }, async () => { - await new Promise(resolve => { - http.get('http://example.com', res => { - res.on('data', d => { - process.stdout.write(d); - }); - - res.on('end', () => { - resolve(); - }); - }); - }); - }); -} - -export { handle }; diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/run-lambda.mjs b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/run-lambda.mjs deleted file mode 100644 index c30903f9883d..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/run-lambda.mjs +++ /dev/null @@ -1,8 +0,0 @@ -import { handle } from './lambda-function.mjs'; - -const event = {}; -const context = { - invokedFunctionArn: 'arn:aws:lambda:us-east-1:123453789012:function:my-lambda', - functionName: 'my-lambda', -}; -await handle(event, context); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/run.mjs b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/run.mjs deleted file mode 100644 index 4bcd5886a865..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/run.mjs +++ /dev/null @@ -1,17 +0,0 @@ -import child_process from 'node:child_process'; - -child_process.execSync('node ./src/run-lambda.mjs', { - stdio: 'inherit', - env: { - ...process.env, - // On AWS, LAMBDA_TASK_ROOT is usually /var/task but for testing, we set it to the CWD to correctly apply our handler - LAMBDA_TASK_ROOT: process.cwd(), - _HANDLER: 'src/lambda-function.handle', - - NODE_OPTIONS: '--import @sentry/aws-serverless/awslambda-auto', - SENTRY_DSN: 'http://public@localhost:3031/1337', - SENTRY_TRACES_SAMPLE_RATE: '1.0', - SENTRY_DEBUG: 'true', - }, - cwd: process.cwd(), -}); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/start-event-proxy.mjs deleted file mode 100644 index 03fc10269998..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/start-event-proxy.mjs +++ /dev/null @@ -1,6 +0,0 @@ -import { startEventProxyServer } from '@sentry-internal/test-utils'; - -startEventProxyServer({ - port: 3031, - proxyServerName: 'aws-lambda-layer-esm', -}); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/tests/basic.test.ts b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/tests/basic.test.ts deleted file mode 100644 index 14ae8f9b81b0..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/tests/basic.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import * as child_process from 'child_process'; -import { expect, test } from '@playwright/test'; -import { waitForTransaction } from '@sentry-internal/test-utils'; - -test('Lambda layer SDK bundle sends events', async ({ request }) => { - const transactionEventPromise = waitForTransaction('aws-lambda-layer-esm', transactionEvent => { - return transactionEvent?.transaction === 'my-lambda'; - }); - - // Waiting for 1s here because attaching the listener for events in `waitForTransaction` is not synchronous - // Since in this test, we don't start a browser via playwright, we don't have the usual delays (page.goto, etc) - // which are usually enough for us to never have noticed this race condition before. - // This is a workaround but probably sufficient as long as we only experience it in this test. - await new Promise(resolve => - setTimeout(() => { - resolve(); - }, 1000), - ); - - child_process.execSync('pnpm start', { - stdio: 'inherit', - }); - - const transactionEvent = await transactionEventPromise; - - // shows the SDK sent a transaction - expect(transactionEvent.transaction).toEqual('my-lambda'); // name should be the function name - expect(transactionEvent.contexts?.trace).toEqual({ - data: { - 'sentry.sample_rate': 1, - 'sentry.source': 'custom', - 'sentry.origin': 'auto.otel.aws-lambda', - 'sentry.op': 'function.aws.lambda', - 'cloud.account.id': '123453789012', - 'faas.id': 'arn:aws:lambda:us-east-1:123453789012:function:my-lambda', - 'faas.coldstart': true, - 'otel.kind': 'SERVER', - }, - op: 'function.aws.lambda', - origin: 'auto.otel.aws-lambda', - span_id: expect.stringMatching(/[a-f0-9]{16}/), - status: 'ok', - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - }); - - expect(transactionEvent.spans).toHaveLength(2); - - // shows that the Otel Http instrumentation is working - expect(transactionEvent.spans).toContainEqual( - expect.objectContaining({ - data: expect.objectContaining({ - 'sentry.op': 'http.client', - 'sentry.origin': 'auto.http.otel.http', - url: 'http://example.com/', - }), - description: 'GET http://example.com/', - op: 'http.client', - }), - ); - - // shows that the manual span creation is working - expect(transactionEvent.spans).toContainEqual( - expect.objectContaining({ - data: expect.objectContaining({ - 'sentry.op': 'test', - 'sentry.origin': 'manual', - }), - description: 'manual-span', - op: 'test', - }), - ); - - // shows that the SDK source is correctly detected - expect(transactionEvent.sdk?.packages).toContainEqual( - expect.objectContaining({ name: 'aws-lambda-layer:@sentry/aws-serverless' }), - ); -}); diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/.npmrc b/dev-packages/e2e-tests/test-applications/aws-serverless-esm/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@sentry:registry=http://127.0.0.1:4873 -@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/package.json b/dev-packages/e2e-tests/test-applications/aws-serverless-esm/package.json deleted file mode 100644 index c9dc4c959d09..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "node-express-app", - "version": "1.0.0", - "private": true, - "scripts": { - "start": "node src/run.mjs", - "test": "playwright test", - "clean": "npx rimraf node_modules pnpm-lock.yaml", - "test:build": "pnpm install", - "test:assert": "pnpm test" - }, - "dependencies": { - "@sentry/aws-serverless": "* || latest" - }, - "devDependencies": { - "@sentry-internal/test-utils": "link:../../../test-utils", - "@playwright/test": "~1.53.2" - }, - "volta": { - "extends": "../../package.json" - } -} diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/playwright.config.ts b/dev-packages/e2e-tests/test-applications/aws-serverless-esm/playwright.config.ts deleted file mode 100644 index 174593c307df..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/playwright.config.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { getPlaywrightConfig } from '@sentry-internal/test-utils'; - -export default getPlaywrightConfig(); diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/src/lambda-function.mjs b/dev-packages/e2e-tests/test-applications/aws-serverless-esm/src/lambda-function.mjs deleted file mode 100644 index 4d248c4432c7..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/src/lambda-function.mjs +++ /dev/null @@ -1,26 +0,0 @@ -import * as http from 'node:http'; -import * as Sentry from '@sentry/aws-serverless'; - -const handler = Sentry.wrapHandler(async () => { - await new Promise(resolve => { - const req = http.request( - { - host: 'example.com', - }, - res => { - res.on('data', d => { - process.stdout.write(d); - }); - - res.on('end', () => { - resolve(); - }); - }, - ); - req.end(); - }); - - Sentry.startSpan({ name: 'manual-span', op: 'manual' }, () => {}); -}); - -export { handler }; diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/src/package.json b/dev-packages/e2e-tests/test-applications/aws-serverless-esm/src/package.json deleted file mode 100644 index 43afe1b9fe77..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/src/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "//": "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", - "name": "lambda", - "version": "1.0.0" -} diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/src/run-lambda.mjs b/dev-packages/e2e-tests/test-applications/aws-serverless-esm/src/run-lambda.mjs deleted file mode 100644 index 8356a5ef9bff..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/src/run-lambda.mjs +++ /dev/null @@ -1,10 +0,0 @@ -import { handler } from './lambda-function.mjs'; - -// Simulate minimal event and context objects being passed to the handler by the AWS runtime -const event = {}; -const context = { - invokedFunctionArn: 'arn:aws:lambda:us-east-1:123453789012:function:my-lambda', - functionName: 'my-lambda', -}; - -await handler(event, context); diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/src/run.mjs b/dev-packages/e2e-tests/test-applications/aws-serverless-esm/src/run.mjs deleted file mode 100644 index 2f67c14a54f7..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/src/run.mjs +++ /dev/null @@ -1,16 +0,0 @@ -import child_process from 'child_process'; - -child_process.execSync('node ./src/run-lambda.mjs', { - stdio: 'inherit', - env: { - ...process.env, - // On AWS, LAMBDA_TASK_ROOT is usually /var/task but for testing, we set it to the CWD to correctly apply our handler - LAMBDA_TASK_ROOT: process.cwd(), - _HANDLER: 'src/lambda-function.handler', - - NODE_OPTIONS: '--import @sentry/aws-serverless/awslambda-auto', - SENTRY_DSN: 'http://public@localhost:3031/1337', - SENTRY_TRACES_SAMPLE_RATE: '1.0', - }, - cwd: process.cwd(), -}); diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/aws-serverless-esm/start-event-proxy.mjs deleted file mode 100644 index 86605fcb7b9a..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/start-event-proxy.mjs +++ /dev/null @@ -1,6 +0,0 @@ -import { startEventProxyServer } from '@sentry-internal/test-utils'; - -startEventProxyServer({ - port: 3031, - proxyServerName: 'aws-serverless-esm', -}); diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/tests/basic.test.ts b/dev-packages/e2e-tests/test-applications/aws-serverless-esm/tests/basic.test.ts deleted file mode 100644 index 38c6e82043cf..000000000000 --- a/dev-packages/e2e-tests/test-applications/aws-serverless-esm/tests/basic.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import * as child_process from 'child_process'; -import { expect, test } from '@playwright/test'; -import { waitForTransaction } from '@sentry-internal/test-utils'; - -test('AWS Serverless SDK sends events in ESM mode', async ({ request }) => { - const transactionEventPromise = waitForTransaction('aws-serverless-esm', transactionEvent => { - return transactionEvent?.transaction === 'my-lambda'; - }); - - // Waiting for 1s here because attaching the listener for events in `waitForTransaction` is not synchronous - // Since in this test, we don't start a browser via playwright, we don't have the usual delays (page.goto, etc) - // which are usually enough for us to never have noticed this race condition before. - // This is a workaround but probably sufficient as long as we only experience it in this test. - await new Promise(resolve => - setTimeout(() => { - resolve(); - }, 1000), - ); - - child_process.execSync('pnpm start', { - stdio: 'inherit', - }); - - const transactionEvent = await transactionEventPromise; - - // shows the SDK sent a transaction - expect(transactionEvent.transaction).toEqual('my-lambda'); // name should be the function name - expect(transactionEvent.contexts?.trace).toEqual({ - data: { - 'sentry.sample_rate': 1, - 'sentry.source': 'custom', - 'sentry.origin': 'auto.otel.aws-lambda', - 'sentry.op': 'function.aws.lambda', - 'cloud.account.id': '123453789012', - 'faas.id': 'arn:aws:lambda:us-east-1:123453789012:function:my-lambda', - 'faas.coldstart': true, - 'otel.kind': 'SERVER', - }, - op: 'function.aws.lambda', - origin: 'auto.otel.aws-lambda', - span_id: expect.stringMatching(/[a-f0-9]{16}/), - status: 'ok', - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - }); - - expect(transactionEvent.spans).toHaveLength(2); - - // shows that the Otel Http instrumentation is working - expect(transactionEvent.spans).toContainEqual( - expect.objectContaining({ - data: expect.objectContaining({ - 'sentry.op': 'http.client', - 'sentry.origin': 'auto.http.otel.http', - url: 'http://example.com/', - }), - description: 'GET http://example.com/', - op: 'http.client', - }), - ); - - // shows that the manual span creation is working - expect(transactionEvent.spans).toContainEqual( - expect.objectContaining({ - data: expect.objectContaining({ - 'sentry.op': 'manual', - 'sentry.origin': 'manual', - }), - description: 'manual-span', - op: 'manual', - }), - ); - - // shows that the SDK source is correctly detected - expect(transactionEvent.sdk?.packages).toContainEqual( - expect.objectContaining({ name: 'npm:@sentry/aws-serverless' }), - ); -}); From 4de82cf0906d98c081102766e4acb2909e0cec51 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Tue, 12 Aug 2025 10:40:15 +0200 Subject: [PATCH 6/6] remove js file endings --- .../test-applications/aws-serverless/tests/lambda-fixtures.ts | 2 +- .../test-applications/aws-serverless/tests/layer.test.ts | 2 +- .../test-applications/aws-serverless/tests/npm.test.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/tests/lambda-fixtures.ts b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/lambda-fixtures.ts index a4828dfbcb1e..707f808218fb 100644 --- a/dev-packages/e2e-tests/test-applications/aws-serverless/tests/lambda-fixtures.ts +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/lambda-fixtures.ts @@ -1,7 +1,7 @@ import { test as base, expect } from '@playwright/test'; import { App } from 'aws-cdk-lib'; import * as tmp from 'tmp'; -import { LocalLambdaStack, SAM_PORT, getHostIp } from '../src/stack.js'; +import { LocalLambdaStack, SAM_PORT, getHostIp } from '../src/stack'; import { writeFileSync } from 'node:fs'; import { spawn, execSync } from 'node:child_process'; import { LambdaClient } from '@aws-sdk/client-lambda'; diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts index 6f38e3d46a4f..79ad0fa31070 100644 --- a/dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts @@ -1,6 +1,6 @@ import { waitForTransaction, waitForError } from '@sentry-internal/test-utils'; import { InvokeCommand } from '@aws-sdk/client-lambda'; -import { test, expect } from './lambda-fixtures.js'; +import { test, expect } from './lambda-fixtures'; test.describe('Lambda layer', () => { test('tracing in CJS works', async ({ lambdaClient }) => { diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless/tests/npm.test.ts b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/npm.test.ts index f071e65913e1..9b4183425c95 100644 --- a/dev-packages/e2e-tests/test-applications/aws-serverless/tests/npm.test.ts +++ b/dev-packages/e2e-tests/test-applications/aws-serverless/tests/npm.test.ts @@ -1,6 +1,6 @@ import { waitForTransaction } from '@sentry-internal/test-utils'; import { InvokeCommand } from '@aws-sdk/client-lambda'; -import { test, expect } from './lambda-fixtures.js'; +import { test, expect } from './lambda-fixtures'; test.describe('NPM package', () => { test('tracing in CJS works', async ({ lambdaClient }) => {