diff --git a/dev-packages/e2e-tests/test-applications/node-express-v5/.gitignore b/dev-packages/e2e-tests/test-applications/node-express-v5/.gitignore new file mode 100644 index 000000000000..1521c8b7652b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-express-v5/.gitignore @@ -0,0 +1 @@ +dist diff --git a/dev-packages/e2e-tests/test-applications/node-express-v5/.npmrc b/dev-packages/e2e-tests/test-applications/node-express-v5/.npmrc new file mode 100644 index 000000000000..070f80f05092 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-express-v5/.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/node-express-v5/package.json b/dev-packages/e2e-tests/test-applications/node-express-v5/package.json new file mode 100644 index 000000000000..b7caf4610712 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-express-v5/package.json @@ -0,0 +1,35 @@ +{ + "name": "node-express-v5-app", + "version": "1.0.0", + "private": true, + "scripts": { + "build": "tsc", + "start": "node dist/app.js", + "test": "playwright test", + "clean": "npx rimraf node_modules pnpm-lock.yaml", + "test:build": "pnpm install && pnpm build", + "test:assert": "pnpm test" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.10.2", + "@sentry/node": "latest || *", + "@trpc/server": "10.45.2", + "@trpc/client": "10.45.2", + "@types/express": "^4.17.21", + "@types/node": "^18.19.1", + "express": "^5.1.0", + "typescript": "~5.0.0", + "zod": "~3.24.3" + }, + "devDependencies": { + "@playwright/test": "~1.53.2", + "@sentry-internal/test-utils": "link:../../../test-utils", + "@sentry/core": "latest || *" + }, + "resolutions": { + "@types/qs": "6.9.17" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/dev-packages/e2e-tests/test-applications/node-express-v5/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/node-express-v5/playwright.config.mjs new file mode 100644 index 000000000000..31f2b913b58b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-express-v5/playwright.config.mjs @@ -0,0 +1,7 @@ +import { getPlaywrightConfig } from '@sentry-internal/test-utils'; + +const config = getPlaywrightConfig({ + startCommand: `pnpm start`, +}); + +export default config; diff --git a/dev-packages/e2e-tests/test-applications/node-express-v5/src/app.ts b/dev-packages/e2e-tests/test-applications/node-express-v5/src/app.ts new file mode 100644 index 000000000000..20dfa5bf84c5 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-express-v5/src/app.ts @@ -0,0 +1,152 @@ +import * as Sentry from '@sentry/node'; + +declare global { + namespace globalThis { + var transactionIds: string[]; + } +} + +Sentry.init({ + environment: 'qa', // dynamic sampling bias to keep transactions + dsn: process.env.E2E_TEST_DSN, + includeLocalVariables: true, + debug: !!process.env.DEBUG, + tunnel: `http://localhost:3031/`, // proxy server + tracesSampleRate: 1, + enableLogs: true, +}); + +import { TRPCError, initTRPC } from '@trpc/server'; +import * as trpcExpress from '@trpc/server/adapters/express'; +import express from 'express'; +import { z } from 'zod'; +import { mcpRouter } from './mcp'; + +const app = express(); +const port = 3030; + +app.use(mcpRouter); + +app.get('/crash-in-with-monitor/:id', async (req, res) => { + try { + await Sentry.withMonitor('express-crash', async () => { + throw new Error(`This is an exception withMonitor: ${req.params.id}`); + }); + res.sendStatus(200); + } catch (error: any) { + res.status(500); + res.send({ message: error.message, pid: process.pid }); + } +}); + +app.get('/test-success', function (req, res) { + res.send({ version: 'v1' }); +}); + +app.get('/test-log', function (req, res) { + Sentry.logger.debug('Accessed /test-log route'); + res.send({ message: 'Log sent' }); +}); + +app.get('/test-param/:param', function (req, res) { + res.send({ paramWas: req.params.param }); +}); + +app.get('/test-transaction', function (_req, res) { + Sentry.startSpan({ name: 'test-span' }, () => undefined); + + res.send({ status: 'ok' }); +}); + +app.get('/test-error', async function (req, res) { + const exceptionId = Sentry.captureException(new Error('This is an error')); + + await Sentry.flush(2000); + + res.send({ exceptionId }); +}); + +app.get('/test-exception/:id', function (req, _res) { + throw new Error(`This is an exception with id ${req.params.id}`); +}); + +app.get('/test-local-variables-uncaught', function (req, res) { + const randomVariableToRecord = Math.random(); + throw new Error(`Uncaught Local Variable Error - ${JSON.stringify({ randomVariableToRecord })}`); +}); + +app.get('/test-local-variables-caught', function (req, res) { + const randomVariableToRecord = Math.random(); + + let exceptionId: string; + try { + throw new Error('Local Variable Error'); + } catch (e) { + exceptionId = Sentry.captureException(e); + } + + res.send({ exceptionId, randomVariableToRecord }); +}); + +Sentry.setupExpressErrorHandler(app); + +// @ts-ignore +app.use(function onError(err, req, res, next) { + // The error id is attached to `res.sentry` to be returned + // and optionally displayed to the user for support. + res.statusCode = 500; + res.end(res.sentry + '\n'); +}); + +app.listen(port, () => { + console.log(`Example app listening on port ${port}`); +}); + +Sentry.addEventProcessor(event => { + global.transactionIds = global.transactionIds || []; + + if (event.type === 'transaction') { + const eventId = event.event_id; + + if (eventId) { + global.transactionIds.push(eventId); + } + } + + return event; +}); + +export const t = initTRPC.context().create(); + +const procedure = t.procedure.use(Sentry.trpcMiddleware({ attachRpcInput: true })); + +export const appRouter = t.router({ + getSomething: procedure.input(z.string()).query(opts => { + return { id: opts.input, name: 'Bilbo' }; + }), + createSomething: procedure.mutation(async () => { + await new Promise(resolve => setTimeout(resolve, 400)); + return { success: true }; + }), + crashSomething: procedure + .input(z.object({ nested: z.object({ nested: z.object({ nested: z.string() }) }) })) + .mutation(() => { + throw new Error('I crashed in a trpc handler'); + }), + badRequest: procedure.mutation(() => { + throw new TRPCError({ code: 'BAD_REQUEST', cause: new Error('Bad Request') }); + }), +}); + +export type AppRouter = typeof appRouter; + +const createContext = () => ({ someStaticValue: 'asdf' }); +type Context = Awaited>; + +app.use( + '/trpc', + trpcExpress.createExpressMiddleware({ + router: appRouter, + createContext, + }), +); diff --git a/dev-packages/e2e-tests/test-applications/node-express-v5/src/mcp.ts b/dev-packages/e2e-tests/test-applications/node-express-v5/src/mcp.ts new file mode 100644 index 000000000000..c5f2c24c61b8 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-express-v5/src/mcp.ts @@ -0,0 +1,64 @@ +import express from 'express'; +import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'; +import { z } from 'zod'; +import { wrapMcpServerWithSentry } from '@sentry/node'; + +const mcpRouter = express.Router(); + +const server = wrapMcpServerWithSentry( + new McpServer({ + name: 'Echo', + version: '1.0.0', + }), +); + +server.resource('echo', new ResourceTemplate('echo://{message}', { list: undefined }), async (uri, { message }) => ({ + contents: [ + { + uri: uri.href, + text: `Resource echo: ${message}`, + }, + ], +})); + +server.tool('echo', { message: z.string() }, async ({ message }, rest) => { + return { + content: [{ type: 'text', text: `Tool echo: ${message}` }], + }; +}); + +server.prompt('echo', { message: z.string() }, ({ message }, extra) => ({ + messages: [ + { + role: 'user', + content: { + type: 'text', + text: `Please process this message: ${message}`, + }, + }, + ], +})); + +const transports: Record = {}; + +mcpRouter.get('/sse', async (_, res) => { + const transport = new SSEServerTransport('/messages', res); + transports[transport.sessionId] = transport; + res.on('close', () => { + delete transports[transport.sessionId]; + }); + await server.connect(transport); +}); + +mcpRouter.post('/messages', async (req, res) => { + const sessionId = req.query.sessionId; + const transport = transports[sessionId as string]; + if (transport) { + await transport.handlePostMessage(req, res); + } else { + res.status(400).send('No transport found for sessionId'); + } +}); + +export { mcpRouter }; diff --git a/dev-packages/e2e-tests/test-applications/node-express-v5/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/node-express-v5/start-event-proxy.mjs new file mode 100644 index 000000000000..9bc400437556 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-express-v5/start-event-proxy.mjs @@ -0,0 +1,6 @@ +import { startEventProxyServer } from '@sentry-internal/test-utils'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'node-express-v5', +}); diff --git a/dev-packages/e2e-tests/test-applications/node-express-v5/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/node-express-v5/tests/errors.test.ts new file mode 100644 index 000000000000..2b810615039a --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-express-v5/tests/errors.test.ts @@ -0,0 +1,53 @@ +import { expect, test } from '@playwright/test'; +import { waitForError } from '@sentry-internal/test-utils'; + +test('Sends correct error event', async ({ baseURL }) => { + const errorEventPromise = waitForError('node-express-v5', event => { + return !event.type && event.exception?.values?.[0]?.value === 'This is an exception with id 123'; + }); + + await fetch(`${baseURL}/test-exception/123`); + + const errorEvent = await errorEventPromise; + + expect(errorEvent.exception?.values).toHaveLength(1); + expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an exception with id 123'); + + expect(errorEvent.request).toEqual({ + method: 'GET', + cookies: {}, + headers: expect.any(Object), + url: 'http://localhost:3030/test-exception/123', + }); + + expect(errorEvent.transaction).toEqual('GET /test-exception/:id'); + + expect(errorEvent.contexts?.trace).toEqual({ + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + span_id: expect.stringMatching(/[a-f0-9]{16}/), + }); +}); + +test('Should record caught exceptions with local variable', async ({ baseURL }) => { + const errorEventPromise = waitForError('node-express-v5', event => { + return event.transaction === 'GET /test-local-variables-caught'; + }); + + await fetch(`${baseURL}/test-local-variables-caught`); + + const errorEvent = await errorEventPromise; + + const frames = errorEvent.exception?.values?.[0]?.stacktrace?.frames; + expect(frames?.[frames.length - 1]?.vars?.randomVariableToRecord).toBeDefined(); +}); + +test('To not crash app from withMonitor', async ({ baseURL }) => { + const doRequest = async (id: number) => { + const response = await fetch(`${baseURL}/crash-in-with-monitor/${id}`) + return response.json(); + } + const [response1, response2] = await Promise.all([doRequest(1), doRequest(2)]) + expect(response1.message).toBe('This is an exception withMonitor: 1') + expect(response2.message).toBe('This is an exception withMonitor: 2') + expect(response1.pid).toBe(response2.pid) //Just to double-check, TBS +}); diff --git a/dev-packages/e2e-tests/test-applications/node-express-v5/tests/logs.test.ts b/dev-packages/e2e-tests/test-applications/node-express-v5/tests/logs.test.ts new file mode 100644 index 000000000000..34bce0a0b978 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-express-v5/tests/logs.test.ts @@ -0,0 +1,16 @@ +import { expect, test } from '@playwright/test'; +import { waitForEnvelopeItem } from '@sentry-internal/test-utils'; +import type { SerializedLogContainer } from '@sentry/core'; + +test('should send logs', async ({ baseURL }) => { + const logEnvelopePromise = waitForEnvelopeItem('node-express-v5', envelope => { + return envelope[0].type === 'log' && (envelope[1] as SerializedLogContainer).items[0]?.level === 'debug'; + }); + + await fetch(`${baseURL}/test-log`); + + const logEnvelope = await logEnvelopePromise; + const log = (logEnvelope[1] as SerializedLogContainer).items[0]; + expect(log?.level).toBe('debug'); + expect(log?.body).toBe('Accessed /test-log route'); +}); diff --git a/dev-packages/e2e-tests/test-applications/node-express-v5/tests/mcp.test.ts b/dev-packages/e2e-tests/test-applications/node-express-v5/tests/mcp.test.ts new file mode 100644 index 000000000000..abf6ab4fbe4e --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-express-v5/tests/mcp.test.ts @@ -0,0 +1,109 @@ +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/test-utils'; +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'; + +test('Should record transactions for mcp handlers', async ({ baseURL }) => { + const transport = new SSEClientTransport(new URL(`${baseURL}/sse`)); + + const client = new Client({ + name: 'test-client', + version: '1.0.0', + }); + + await client.connect(transport); + + await test.step('tool handler', async () => { + const postTransactionPromise = waitForTransaction('node-express-v5', transactionEvent => { + return transactionEvent.transaction === 'POST /messages'; + }); + const toolTransactionPromise = waitForTransaction('node-express-v5', transactionEvent => { + return transactionEvent.transaction === 'mcp-server/tool:echo'; + }); + + const toolResult = await client.callTool({ + name: 'echo', + arguments: { + message: 'foobar', + }, + }); + + expect(toolResult).toMatchObject({ + content: [ + { + text: 'Tool echo: foobar', + type: 'text', + }, + ], + }); + + const postTransaction = await postTransactionPromise; + expect(postTransaction).toBeDefined(); + + const toolTransaction = await toolTransactionPromise; + expect(toolTransaction).toBeDefined(); + + // TODO: When https://github.com/modelcontextprotocol/typescript-sdk/pull/358 is released check for trace id equality between the post transaction and the handler transaction + }); + + await test.step('resource handler', async () => { + const postTransactionPromise = waitForTransaction('node-express-v5', transactionEvent => { + return transactionEvent.transaction === 'POST /messages'; + }); + const resourceTransactionPromise = waitForTransaction('node-express-v5', transactionEvent => { + return transactionEvent.transaction === 'mcp-server/resource:echo'; + }); + + const resourceResult = await client.readResource({ + uri: 'echo://foobar', + }); + + expect(resourceResult).toMatchObject({ + contents: [{ text: 'Resource echo: foobar', uri: 'echo://foobar' }], + }); + + const postTransaction = await postTransactionPromise; + expect(postTransaction).toBeDefined(); + + const resourceTransaction = await resourceTransactionPromise; + expect(resourceTransaction).toBeDefined(); + + // TODO: When https://github.com/modelcontextprotocol/typescript-sdk/pull/358 is released check for trace id equality between the post transaction and the handler transaction + }); + + await test.step('prompt handler', async () => { + const postTransactionPromise = waitForTransaction('node-express-v5', transactionEvent => { + return transactionEvent.transaction === 'POST /messages'; + }); + const promptTransactionPromise = waitForTransaction('node-express-v5', transactionEvent => { + return transactionEvent.transaction === 'mcp-server/prompt:echo'; + }); + + const promptResult = await client.getPrompt({ + name: 'echo', + arguments: { + message: 'foobar', + }, + }); + + expect(promptResult).toMatchObject({ + messages: [ + { + content: { + text: 'Please process this message: foobar', + type: 'text', + }, + role: 'user', + }, + ], + }); + + const postTransaction = await postTransactionPromise; + expect(postTransaction).toBeDefined(); + + const promptTransaction = await promptTransactionPromise; + expect(promptTransaction).toBeDefined(); + + // TODO: When https://github.com/modelcontextprotocol/typescript-sdk/pull/358 is released check for trace id equality between the post transaction and the handler transaction + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/node-express-v5/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/node-express-v5/tests/transactions.test.ts new file mode 100644 index 000000000000..86fdffd3b452 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-express-v5/tests/transactions.test.ts @@ -0,0 +1,116 @@ +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/test-utils'; + +test('Sends an API route transaction', async ({ baseURL }) => { + const pageloadTransactionEventPromise = waitForTransaction('node-express-v5', transactionEvent => { + return ( + transactionEvent?.contexts?.trace?.op === 'http.server' && + transactionEvent?.transaction === 'GET /test-transaction' + ); + }); + + await fetch(`${baseURL}/test-transaction`); + + const transactionEvent = await pageloadTransactionEventPromise; + + expect(transactionEvent.contexts?.trace).toEqual({ + data: { + 'sentry.source': 'route', + 'sentry.origin': 'auto.http.otel.http', + 'sentry.op': 'http.server', + 'sentry.sample_rate': 1, + url: 'http://localhost:3030/test-transaction', + 'otel.kind': 'SERVER', + 'http.response.status_code': 200, + 'http.url': 'http://localhost:3030/test-transaction', + 'http.host': 'localhost:3030', + 'net.host.name': 'localhost', + 'http.method': 'GET', + 'http.scheme': 'http', + 'http.target': '/test-transaction', + 'http.user_agent': 'node', + 'http.flavor': '1.1', + 'net.transport': 'ip_tcp', + 'net.host.ip': expect.any(String), + 'net.host.port': expect.any(Number), + 'net.peer.ip': expect.any(String), + 'net.peer.port': expect.any(Number), + 'http.status_code': 200, + 'http.status_text': 'OK', + 'http.route': '/test-transaction', + }, + op: 'http.server', + span_id: expect.stringMatching(/[a-f0-9]{16}/), + status: 'ok', + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + origin: 'auto.http.otel.http', + }); + + expect(transactionEvent.contexts?.response).toEqual({ + status_code: 200, + }); + + expect(transactionEvent).toEqual( + expect.objectContaining({ + transaction: 'GET /test-transaction', + type: 'transaction', + transaction_info: { + source: 'route', + }, + }), + ); + + const spans = transactionEvent.spans || []; + + // Manually started span + expect(spans).toContainEqual({ + data: { 'sentry.origin': 'manual' }, + description: 'test-span', + origin: 'manual', + parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), + span_id: expect.stringMatching(/[a-f0-9]{16}/), + start_timestamp: expect.any(Number), + status: 'ok', + timestamp: expect.any(Number), + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + }); + + // auto instrumented span + expect(spans).toContainEqual({ + data: { + 'sentry.origin': 'auto.http.otel.express', + 'sentry.op': 'request_handler.express', + 'http.route': '/test-transaction', + 'express.name': '/test-transaction', + 'express.type': 'request_handler', + }, + description: '/test-transaction', + op: 'request_handler.express', + origin: 'auto.http.otel.express', + parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), + span_id: expect.stringMatching(/[a-f0-9]{16}/), + start_timestamp: expect.any(Number), + status: 'ok', + timestamp: expect.any(Number), + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + }); +}); + +test('Sends an API route transaction for an errored route', async ({ baseURL }) => { + const transactionEventPromise = waitForTransaction('node-express-v5', transactionEvent => { + return ( + transactionEvent.contexts?.trace?.op === 'http.server' && + transactionEvent.transaction === 'GET /test-exception/:id' && + transactionEvent.request?.url === 'http://localhost:3030/test-exception/777' + ); + }); + + await fetch(`${baseURL}/test-exception/777`); + + const transactionEvent = await transactionEventPromise; + + expect(transactionEvent.contexts?.trace?.op).toEqual('http.server'); + expect(transactionEvent.transaction).toEqual('GET /test-exception/:id'); + expect(transactionEvent.contexts?.trace?.status).toEqual('internal_error'); + expect(transactionEvent.contexts?.trace?.data?.['http.status_code']).toEqual(500); +}); diff --git a/dev-packages/e2e-tests/test-applications/node-express-v5/tests/trpc.test.ts b/dev-packages/e2e-tests/test-applications/node-express-v5/tests/trpc.test.ts new file mode 100644 index 000000000000..1618313ff444 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-express-v5/tests/trpc.test.ts @@ -0,0 +1,132 @@ +import { expect, test } from '@playwright/test'; +import { waitForError, waitForTransaction } from '@sentry-internal/test-utils'; +import { createTRPCProxyClient, httpBatchLink } from '@trpc/client'; +import type { AppRouter } from '../src/app'; + +test('Should record span for trpc query', async ({ baseURL }) => { + const transactionEventPromise = waitForTransaction('node-express-v5', transactionEvent => { + return ( + transactionEvent.transaction === 'GET /trpc' && + !!transactionEvent.spans?.find(span => span.description === 'trpc/getSomething') + ); + }); + + const trpcClient = createTRPCProxyClient({ + links: [ + httpBatchLink({ + url: `${baseURL}/trpc`, + }), + ], + }); + + await trpcClient.getSomething.query('foobar'); + + await expect(transactionEventPromise).resolves.toBeDefined(); + const transaction = await transactionEventPromise; + + expect(transaction.spans).toContainEqual( + expect.objectContaining({ + data: expect.objectContaining({ + 'sentry.op': 'rpc.server', + 'sentry.origin': 'auto.rpc.trpc', + }), + description: `trpc/getSomething`, + }), + ); +}); + +test('Should record transaction for trpc mutation', async ({ baseURL }) => { + const transactionEventPromise = waitForTransaction('node-express-v5', transactionEvent => { + return ( + transactionEvent.transaction === 'POST /trpc' && + !!transactionEvent.spans?.find(span => span.description === 'trpc/createSomething') + ); + }); + + const trpcClient = createTRPCProxyClient({ + links: [ + httpBatchLink({ + url: `${baseURL}/trpc`, + }), + ], + }); + + await trpcClient.createSomething.mutate(); + + await expect(transactionEventPromise).resolves.toBeDefined(); + const transaction = await transactionEventPromise; + + expect(transaction.spans).toContainEqual( + expect.objectContaining({ + data: expect.objectContaining({ + 'sentry.op': 'rpc.server', + 'sentry.origin': 'auto.rpc.trpc', + }), + description: `trpc/createSomething`, + }), + ); +}); + +test('Should record transaction and error for a crashing trpc handler', async ({ baseURL }) => { + const transactionEventPromise = waitForTransaction('node-express-v5', transactionEvent => { + return ( + transactionEvent.transaction === 'POST /trpc' && + !!transactionEvent.spans?.find(span => span.description === 'trpc/crashSomething') + ); + }); + + const errorEventPromise = waitForError('node-express-v5', errorEvent => { + return !!errorEvent?.exception?.values?.some(exception => exception.value?.includes('I crashed in a trpc handler')); + }); + + const trpcClient = createTRPCProxyClient({ + links: [ + httpBatchLink({ + url: `${baseURL}/trpc`, + }), + ], + }); + + await expect(trpcClient.crashSomething.mutate({ nested: { nested: { nested: 'foobar' } } })).rejects.toBeDefined(); + + await expect(transactionEventPromise).resolves.toBeDefined(); + await expect(errorEventPromise).resolves.toBeDefined(); + + expect((await errorEventPromise).contexts?.trpc?.['procedure_type']).toBe('mutation'); + expect((await errorEventPromise).contexts?.trpc?.['procedure_path']).toBe('crashSomething'); + + // Should record nested context + expect((await errorEventPromise).contexts?.trpc?.['input']).toEqual({ + nested: { + nested: { + nested: 'foobar', + }, + }, + }); +}); + +test('Should record transaction and error for a trpc handler that returns a status code', async ({ baseURL }) => { + const transactionEventPromise = waitForTransaction('node-express-v5', transactionEvent => { + return ( + transactionEvent.transaction === 'POST /trpc' && + !!transactionEvent.spans?.find(span => span.description === 'trpc/badRequest') + ); + }); + + const errorEventPromise = waitForError('node-express-v5', errorEvent => { + return !!errorEvent?.exception?.values?.some(exception => exception.value?.includes('Bad Request')); + }); + + const trpcClient = createTRPCProxyClient({ + links: [ + httpBatchLink({ + url: `${baseURL}/trpc`, + }), + ], + }); + + await expect(trpcClient.badRequest.mutate()).rejects.toBeDefined(); + + await expect(transactionEventPromise).resolves.toBeDefined(); + await expect(errorEventPromise).resolves.toBeDefined(); +}); diff --git a/dev-packages/e2e-tests/test-applications/node-express-v5/tsconfig.json b/dev-packages/e2e-tests/test-applications/node-express-v5/tsconfig.json new file mode 100644 index 000000000000..0060abd94682 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-express-v5/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "types": ["node"], + "esModuleInterop": true, + "lib": ["es2020"], + "strict": true, + "outDir": "dist", + "skipLibCheck": true + }, + "include": ["src/**/*.ts"] +} diff --git a/dev-packages/e2e-tests/test-applications/node-express/src/app.ts b/dev-packages/e2e-tests/test-applications/node-express/src/app.ts index 0ac843b5409e..e5ca5e594605 100644 --- a/dev-packages/e2e-tests/test-applications/node-express/src/app.ts +++ b/dev-packages/e2e-tests/test-applications/node-express/src/app.ts @@ -52,20 +52,11 @@ app.get('/test-param/:param', function (req, res) { res.send({ paramWas: req.params.param }); }); -app.get('/test-transaction', function (req, res) { - Sentry.withActiveSpan(null, async () => { - Sentry.startSpan({ name: 'test-transaction', op: 'e2e-test' }, () => { - Sentry.startSpan({ name: 'test-span' }, () => undefined); - }); - - await Sentry.flush(); +app.get('/test-transaction', function (_req, res) { + Sentry.startSpan({ name: 'test-span' }, () => undefined); - res.send({ - transactionIds: global.transactionIds || [], - }); - }); + res.send({ status: 'ok' }); }); - app.get('/test-error', async function (req, res) { const exceptionId = Sentry.captureException(new Error('This is an error')); diff --git a/dev-packages/e2e-tests/test-applications/node-express/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/node-express/tests/transactions.test.ts index 4d3ee36c9778..b47feebcd728 100644 --- a/dev-packages/e2e-tests/test-applications/node-express/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/node-express/tests/transactions.test.ts @@ -62,6 +62,20 @@ test('Sends an API route transaction', async ({ baseURL }) => { const spans = transactionEvent.spans || []; + // Manually started span + expect(spans).toContainEqual({ + data: { 'sentry.origin': 'manual' }, + description: 'test-span', + origin: 'manual', + parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), + span_id: expect.stringMatching(/[a-f0-9]{16}/), + start_timestamp: expect.any(Number), + status: 'ok', + timestamp: expect.any(Number), + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + }); + + // auto instrumented spans expect(spans).toContainEqual({ data: { 'sentry.origin': 'auto.http.otel.express', diff --git a/dev-packages/node-integration-tests/package.json b/dev-packages/node-integration-tests/package.json index bac4cce0e1b9..f9497afcacb0 100644 --- a/dev-packages/node-integration-tests/package.json +++ b/dev-packages/node-integration-tests/package.json @@ -16,11 +16,9 @@ "build:types": "tsc -p tsconfig.types.json", "clean": "rimraf -g **/node_modules && run-p clean:script", "clean:script": "node scripts/clean.js", - "express-v5-install": "cd suites/express-v5 && yarn --no-lockfile", "lint": "eslint . --format stylish", "fix": "eslint . --format stylish --fix", "type-check": "tsc", - "pretest": "yarn express-v5-install", "test": "vitest run", "test:watch": "yarn test --watch" }, diff --git a/dev-packages/node-integration-tests/suites/express-v5/handle-error-scope-data-loss/server.ts b/dev-packages/node-integration-tests/suites/express-v5/handle-error-scope-data-loss/server.ts deleted file mode 100644 index 8f594e449162..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/handle-error-scope-data-loss/server.ts +++ /dev/null @@ -1,37 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - transport: loggingTransport, -}); - -import express from 'express'; - -const app = express(); - -Sentry.setTag('global', 'tag'); - -app.get('/test/withScope', () => { - Sentry.withScope(scope => { - scope.setTag('local', 'tag'); - throw new Error('test_error'); - }); -}); - -app.get('/test/isolationScope', () => { - Sentry.getIsolationScope().setTag('isolation-scope', 'tag'); - throw new Error('isolation_test_error'); -}); - -app.get('/test/withIsolationScope', () => { - Sentry.withIsolationScope(iScope => { - iScope.setTag('with-isolation-scope', 'tag'); - throw new Error('with_isolation_scope_test_error'); - }); -}); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/handle-error-scope-data-loss/test.ts b/dev-packages/node-integration-tests/suites/express-v5/handle-error-scope-data-loss/test.ts deleted file mode 100644 index 306449b09569..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/handle-error-scope-data-loss/test.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { afterAll, expect, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -/** - * Why does this test exist? - * - * We recently discovered that errors caught by global handlers will potentially loose scope data from the active scope - * where the error was originally thrown in. The simple example in this test (see subject.ts) demonstrates this behavior - * (in a Node environment but the same behavior applies to the browser; see the test there). - * - * This test nevertheless covers the behavior so that we're aware. - */ -test('withScope scope is NOT applied to thrown error caught by global handler', async () => { - const runner = createRunner(__dirname, 'server.ts') - .expect({ - event: { - exception: { - values: [ - { - mechanism: { - type: 'middleware', - handled: false, - }, - type: 'Error', - value: 'test_error', - stacktrace: { - frames: expect.arrayContaining([ - expect.objectContaining({ - function: expect.any(String), - lineno: expect.any(Number), - colno: expect.any(Number), - }), - ]), - }, - }, - ], - }, - // 'local' tag is not applied to the event - tags: expect.not.objectContaining({ local: expect.anything() }), - }, - }) - .start(); - runner.makeRequest('get', '/test/withScope', { expectError: true }); - await runner.completed(); -}); - -/** - * This test shows that the isolation scope set tags are applied correctly to the error. - */ -test('isolation scope is applied to thrown error caught by global handler', async () => { - const runner = createRunner(__dirname, 'server.ts') - .expect({ - event: { - exception: { - values: [ - { - mechanism: { - type: 'middleware', - handled: false, - }, - type: 'Error', - value: 'isolation_test_error', - stacktrace: { - frames: expect.arrayContaining([ - expect.objectContaining({ - function: expect.any(String), - lineno: expect.any(Number), - colno: expect.any(Number), - }), - ]), - }, - }, - ], - }, - tags: { - global: 'tag', - 'isolation-scope': 'tag', - }, - }, - }) - .start(); - runner.makeRequest('get', '/test/isolationScope', { expectError: true }); - await runner.completed(); -}); - -/** - * This test shows that an inner isolation scope, created via `withIsolationScope`, is not applied to the error. - * - * This behaviour occurs because, just like in the test above where we use `getIsolationScope().setTag`, - * this isolation scope again is only valid as long as we're in the callback. - * - * So why _does_ the http isolation scope get applied then? Because express' error handler applies on - * a per-request basis, meaning, it's called while we're inside the isolation scope of the http request, - * created from our `httpIntegration`. - */ -test('withIsolationScope scope is NOT applied to thrown error caught by global handler', async () => { - const runner = createRunner(__dirname, 'server.ts') - .expect({ - event: { - exception: { - values: [ - { - mechanism: { - type: 'middleware', - handled: false, - }, - type: 'Error', - value: 'with_isolation_scope_test_error', - stacktrace: { - frames: expect.arrayContaining([ - expect.objectContaining({ - function: expect.any(String), - lineno: expect.any(Number), - colno: expect.any(Number), - }), - ]), - }, - }, - ], - }, - // 'with-isolation-scope' tag is not applied to the event - tags: expect.not.objectContaining({ 'with-isolation-scope': expect.anything() }), - }, - }) - .start(); - - runner.makeRequest('get', '/test/withIsolationScope', { expectError: true }); - - await runner.completed(); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-0/server.ts b/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-0/server.ts deleted file mode 100644 index 323093ce38e0..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-0/server.ts +++ /dev/null @@ -1,21 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - transport: loggingTransport, - tracesSampleRate: 1, -}); - -import express from 'express'; - -const app = express(); - -app.get('/test/express/:id', req => { - throw new Error(`test_error with id ${req.params.id}`); -}); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-0/test.ts b/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-0/test.ts deleted file mode 100644 index b6bc5de97cdb..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-0/test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { afterAll, expect, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -test('should capture and send Express controller error with txn name if tracesSampleRate is 0', async () => { - const runner = createRunner(__dirname, 'server.ts') - .ignore('transaction') - .expect({ - event: { - exception: { - values: [ - { - mechanism: { - type: 'middleware', - handled: false, - }, - type: 'Error', - value: 'test_error with id 123', - stacktrace: { - frames: expect.arrayContaining([ - expect.objectContaining({ - function: expect.any(String), - lineno: expect.any(Number), - colno: expect.any(Number), - }), - ]), - }, - }, - ], - }, - transaction: 'GET /test/express/:id', - }, - }) - .start(); - runner.makeRequest('get', '/test/express/123', { expectError: true }); - await runner.completed(); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-unset/server.ts b/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-unset/server.ts deleted file mode 100644 index eb7044faee97..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-unset/server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - transport: loggingTransport, -}); - -import express from 'express'; - -const app = express(); - -app.get('/test/express/:id', req => { - throw new Error(`test_error with id ${req.params.id}`); -}); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-unset/test.ts b/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-unset/test.ts deleted file mode 100644 index 899a611cdfe0..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-unset/test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { afterAll, expect, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -test('should capture and send Express controller error if tracesSampleRate is not set.', async () => { - const runner = createRunner(__dirname, 'server.ts') - .ignore('transaction') - .expect({ - event: { - exception: { - values: [ - { - mechanism: { - type: 'middleware', - handled: false, - }, - type: 'Error', - value: 'test_error with id 123', - stacktrace: { - frames: expect.arrayContaining([ - expect.objectContaining({ - function: expect.any(String), - lineno: expect.any(Number), - colno: expect.any(Number), - }), - ]), - }, - }, - ], - }, - }, - }) - .start(); - runner.makeRequest('get', '/test/express/123', { expectError: true }); - await runner.completed(); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-init/server.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-init/server.ts deleted file mode 100644 index f9952ce43a9f..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-init/server.ts +++ /dev/null @@ -1,73 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -Sentry.init({ - // No dsn, means client is disabled - // dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - transport: loggingTransport, -}); - -// We add http integration to ensure request isolation etc. works -const initialClient = Sentry.getClient(); -initialClient?.addIntegration(Sentry.httpIntegration()); - -// Store this so we can update the client later -const initialCurrentScope = Sentry.getCurrentScope(); - -import express from 'express'; - -const app = express(); - -Sentry.setTag('global', 'tag'); - -app.get('/test/no-init', (_req, res) => { - Sentry.addBreadcrumb({ message: 'no init breadcrumb' }); - Sentry.setTag('no-init', 'tag'); - - res.send({}); -}); - -app.get('/test/init', (_req, res) => { - // Call init again, but with DSN - Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - transport: loggingTransport, - }); - // Set this on initial scope, to ensure it can be inherited - initialCurrentScope.setClient(Sentry.getClient()!); - - Sentry.addBreadcrumb({ message: 'init breadcrumb' }); - Sentry.setTag('init', 'tag'); - - res.send({}); -}); - -app.get('/test/error/:id', (req, res) => { - const id = req.params.id; - Sentry.addBreadcrumb({ message: `error breadcrumb ${id}` }); - Sentry.setTag('error', id); - - Sentry.captureException(new Error(`This is an exception ${id}`)); - - setTimeout(() => { - // We flush to ensure we are sending exceptions in a certain order - Sentry.flush(1000).then( - () => { - // We send this so we can wait for this, to know the test is ended & server can be closed - if (id === '3') { - Sentry.captureException(new Error('Final exception was captured')); - } - res.send({}); - }, - () => { - res.send({}); - }, - ); - }, 1); -}); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-init/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-init/test.ts deleted file mode 100644 index 04d45fe557ef..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-init/test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { afterAll, expect, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -test('allows to call init multiple times', async () => { - const runner = createRunner(__dirname, 'server.ts') - .expect({ - event: { - exception: { - values: [ - { - value: 'This is an exception 2', - }, - ], - }, - breadcrumbs: [ - { - message: 'error breadcrumb 2', - timestamp: expect.any(Number), - }, - ], - tags: { - global: 'tag', - error: '2', - }, - }, - }) - .expect({ - event: { - exception: { - values: [ - { - value: 'This is an exception 3', - }, - ], - }, - breadcrumbs: [ - { - message: 'error breadcrumb 3', - timestamp: expect.any(Number), - }, - ], - tags: { - global: 'tag', - error: '3', - }, - }, - }) - .expect({ - event: { - exception: { - values: [ - { - value: 'Final exception was captured', - }, - ], - }, - }, - }) - .start(); - - runner - .makeRequest('get', '/test/no-init') - .then(() => runner.makeRequest('get', '/test/error/1')) - .then(() => runner.makeRequest('get', '/test/init')) - .then(() => runner.makeRequest('get', '/test/error/2')) - .then(() => runner.makeRequest('get', '/test/error/3')); - await runner.completed(); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix-parameterized/server.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix-parameterized/server.ts deleted file mode 100644 index 6cbe4a330463..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix-parameterized/server.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - tracesSampleRate: 1.0, - transport: loggingTransport, -}); - -import cors from 'cors'; -import express from 'express'; - -const app = express(); - -app.use(cors()); - -const APIv1 = express.Router(); - -APIv1.get('/user/:userId', function (_req, res) { - Sentry.captureMessage('Custom Message'); - res.send('Success'); -}); - -const root = express.Router(); - -app.use('/api2/v1', root); -app.use('/api/v1', APIv1); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix-parameterized/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix-parameterized/test.ts deleted file mode 100644 index 85c3d0a2c353..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix-parameterized/test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { afterAll, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -test('should construct correct url with common infixes with multiple parameterized routers.', async () => { - const runner = createRunner(__dirname, 'server.ts') - .ignore('transaction') - .expect({ event: { message: 'Custom Message', transaction: 'GET /api/v1/user/:userId' } }) - .start(); - runner.makeRequest('get', '/api/v1/user/3212'); - await runner.completed(); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix/server.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix/server.ts deleted file mode 100644 index b07226733bc6..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix/server.ts +++ /dev/null @@ -1,33 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -Sentry.init({ - debug: true, - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - tracesSampleRate: 1.0, - transport: loggingTransport, -}); - -import cors from 'cors'; -import express from 'express'; - -const app = express(); - -app.use(cors()); - -const APIv1 = express.Router(); - -APIv1.get('/test', function (_req, res) { - Sentry.captureMessage('Custom Message'); - res.send('Success'); -}); - -const root = express.Router(); - -app.use('/api/v1', root); -app.use('/api2/v1', APIv1); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix/test.ts deleted file mode 100644 index f6a29574e254..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix/test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { afterAll, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -test('should construct correct url with common infixes with multiple routers.', async () => { - const runner = createRunner(__dirname, 'server.ts') - .ignore('transaction') - .expect({ event: { message: 'Custom Message', transaction: 'GET /api2/v1/test' } }) - .start(); - runner.makeRequest('get', '/api2/v1/test'); - await runner.completed(); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized-reverse/server.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized-reverse/server.ts deleted file mode 100644 index 99792fe8185c..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized-reverse/server.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - tracesSampleRate: 1.0, - transport: loggingTransport, -}); - -import cors from 'cors'; -import express from 'express'; - -const app = express(); - -app.use(cors()); - -const APIv1 = express.Router(); - -APIv1.get('/user/:userId', function (_req, res) { - Sentry.captureMessage('Custom Message'); - res.send('Success'); -}); - -const root = express.Router(); - -app.use('/api/v1', APIv1); -app.use('/api', root); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized-reverse/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized-reverse/test.ts deleted file mode 100644 index b2b5baabd103..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized-reverse/test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { afterAll, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -test('should construct correct urls with multiple parameterized routers (use order reversed).', async () => { - const runner = createRunner(__dirname, 'server.ts') - .ignore('transaction') - .expect({ event: { message: 'Custom Message', transaction: 'GET /api/v1/user/:userId' } }) - .start(); - runner.makeRequest('get', '/api/v1/user/1234/'); - await runner.completed(); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized/server.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized/server.ts deleted file mode 100644 index e405a5f17d27..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized/server.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - tracesSampleRate: 1.0, - transport: loggingTransport, -}); - -import cors from 'cors'; -import express from 'express'; - -const app = express(); - -app.use(cors()); - -const APIv1 = express.Router(); - -APIv1.get('/user/:userId', function (_req, res) { - Sentry.captureMessage('Custom Message'); - res.send('Success'); -}); - -const root = express.Router(); - -app.use('/api', root); -app.use('/api/v1', APIv1); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized/test.ts deleted file mode 100644 index f362d49ddd03..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized/test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { afterAll, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -test('should construct correct urls with multiple parameterized routers.', async () => { - const runner = createRunner(__dirname, 'server.ts') - .ignore('transaction') - .expect({ event: { message: 'Custom Message', transaction: 'GET /api/v1/user/:userId' } }) - .start(); - runner.makeRequest('get', '/api/v1/user/1234/'); - await runner.completed(); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized copy/server.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized copy/server.ts deleted file mode 100644 index 0ef9dbee681e..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized copy/server.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - tracesSampleRate: 1.0, - transport: loggingTransport, -}); - -import cors from 'cors'; -import express from 'express'; - -const app = express(); - -app.use(cors()); - -const APIv1 = express.Router(); - -APIv1.get('/:userId', function (_req, res) { - Sentry.captureMessage('Custom Message'); - res.send('Success'); -}); - -const root = express.Router(); - -app.use('/api/v1', APIv1); -app.use('/api', root); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized copy/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized copy/test.ts deleted file mode 100644 index 146e7d0625c5..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized copy/test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { afterAll, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -test('should construct correct url with multiple parameterized routers of the same length (use order reversed).', async () => { - const runner = createRunner(__dirname, 'server.ts') - .ignore('transaction') - .expect({ event: { message: 'Custom Message', transaction: 'GET /api/v1/:userId' } }) - .start(); - runner.makeRequest('get', '/api/v1/1234/'); - await runner.completed(); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized/server.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized/server.ts deleted file mode 100644 index a5272e134c48..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized/server.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - tracesSampleRate: 1.0, - transport: loggingTransport, -}); - -import cors from 'cors'; -import express from 'express'; - -const app = express(); - -app.use(cors()); - -const APIv1 = express.Router(); - -APIv1.get('/:userId', function (_req, res) { - Sentry.captureMessage('Custom Message'); - res.send('Success'); -}); - -const root = express.Router(); - -app.use('/api', root); -app.use('/api/v1', APIv1); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized/test.ts deleted file mode 100644 index 3209cde3ea23..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized/test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { afterAll, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -test('should construct correct url with multiple parameterized routers of the same length.', async () => { - const runner = createRunner(__dirname, 'server.ts') - .ignore('transaction') - .expect({ event: { message: 'Custom Message', transaction: 'GET /api/v1/:userId' } }) - .start(); - runner.makeRequest('get', '/api/v1/1234/'); - await runner.completed(); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix/server.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix/server.ts deleted file mode 100644 index 52cce3594b68..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix/server.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - tracesSampleRate: 1.0, - transport: loggingTransport, -}); - -import cors from 'cors'; -import express from 'express'; - -const app = express(); - -app.use(cors()); - -const APIv1 = express.Router(); - -APIv1.get('/test', function (_req, res) { - Sentry.captureMessage('Custom Message'); - res.send('Success'); -}); - -const root = express.Router(); - -app.use('/api', root); -app.use('/api/v1', APIv1); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix/test.ts deleted file mode 100644 index 12c6584b2d3f..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix/test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { afterAll, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -test('should construct correct urls with multiple routers.', async () => { - const runner = createRunner(__dirname, 'server.ts') - .ignore('transaction') - .expect({ event: { message: 'Custom Message', transaction: 'GET /api/v1/test' } }) - .start(); - runner.makeRequest('get', '/api/v1/test'); - await runner.completed(); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/complex-router/server.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/complex-router/server.ts deleted file mode 100644 index 7d875b47f13b..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/complex-router/server.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - tracesSampleRate: 1.0, - transport: loggingTransport, -}); - -import express from 'express'; - -const app = express(); - -const APIv1 = express.Router(); - -APIv1.use( - '/users/:userId', - APIv1.get('/posts/:postId', (_req, res) => { - Sentry.captureMessage('Custom Message'); - return res.send('Success'); - }), -); - -const router = express.Router(); - -app.use('/api', router); -app.use('/api/api/v1', APIv1.use('/sub-router', APIv1)); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/complex-router/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/complex-router/test.ts deleted file mode 100644 index fbb97cb6b1df..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/complex-router/test.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { afterAll, describe, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -describe('complex-router', () => { - test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route and express used multiple middlewares with route', async () => { - const EXPECTED_TRANSACTION = { - transaction: 'GET /api/api/v1/sub-router/users/:userId/posts/:postId', - transaction_info: { - source: 'route', - }, - }; - - const runner = createRunner(__dirname, 'server.ts') - .ignore('event') - .expect({ transaction: EXPECTED_TRANSACTION as any }) - .start(); - runner.makeRequest('get', '/api/api/v1/sub-router/users/123/posts/456'); - await runner.completed(); - }); - - test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route and express used multiple middlewares with route and original url has query params', async () => { - const EXPECTED_TRANSACTION = { - transaction: 'GET /api/api/v1/sub-router/users/:userId/posts/:postId', - transaction_info: { - source: 'route', - }, - }; - - const runner = createRunner(__dirname, 'server.ts') - .ignore('event') - .expect({ transaction: EXPECTED_TRANSACTION as any }) - .start(); - runner.makeRequest('get', '/api/api/v1/sub-router/users/123/posts/456?param=1'); - await runner.completed(); - }); - - test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route and express used multiple middlewares with route and original url ends with trailing slash and has query params', async () => { - const EXPECTED_TRANSACTION = { - transaction: 'GET /api/api/v1/sub-router/users/:userId/posts/:postId', - transaction_info: { - source: 'route', - }, - }; - - const runner = createRunner(__dirname, 'server.ts') - .ignore('event') - .expect({ transaction: EXPECTED_TRANSACTION as any }) - .start(); - runner.makeRequest('get', '/api/api/v1/sub-router/users/123/posts/456/?param=1'); - await runner.completed(); - }); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/middle-layer-parameterized/server.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/middle-layer-parameterized/server.ts deleted file mode 100644 index 793a84924001..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/middle-layer-parameterized/server.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - tracesSampleRate: 1.0, - transport: loggingTransport, -}); - -import express from 'express'; - -const app = express(); - -const APIv1 = express.Router(); - -APIv1.use( - '/users/:userId', - APIv1.get('/posts/:postId', (_req, res) => { - Sentry.captureMessage('Custom Message'); - return res.send('Success'); - }), -); - -const root = express.Router(); - -app.use('/api/v1', APIv1); -app.use('/api', root); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/middle-layer-parameterized/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/middle-layer-parameterized/test.ts deleted file mode 100644 index 6f24e03bac59..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/middle-layer-parameterized/test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { afterAll, describe, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -// Before Node 16, parametrization is not working properly here -describe('middle-layer-parameterized', () => { - test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route', async () => { - const EXPECTED_TRANSACTION = { - transaction: 'GET /api/v1/users/:userId/posts/:postId', - transaction_info: { - source: 'route', - }, - }; - - const runner = createRunner(__dirname, 'server.ts') - .ignore('event') - .expect({ transaction: EXPECTED_TRANSACTION as any }) - .start(); - runner.makeRequest('get', '/api/v1/users/123/posts/456'); - await runner.completed(); - }); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/package.json b/dev-packages/node-integration-tests/suites/express-v5/package.json deleted file mode 100644 index b3855635c556..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "express-v5", - "dependencies": { - "express": "^5.0.0" - } -} diff --git a/dev-packages/node-integration-tests/suites/express-v5/requestUser/server.js b/dev-packages/node-integration-tests/suites/express-v5/requestUser/server.js deleted file mode 100644 index d93d22905506..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/requestUser/server.js +++ /dev/null @@ -1,49 +0,0 @@ -const { loggingTransport } = require('@sentry-internal/node-integration-tests'); -const Sentry = require('@sentry/node'); - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - transport: loggingTransport, - debug: true, -}); - -// express must be required after Sentry is initialized -const express = require('express'); -const cors = require('cors'); -const { startExpressServerAndSendPortToRunner } = require('@sentry-internal/node-integration-tests'); - -const app = express(); - -app.use(cors()); - -app.use((req, _res, next) => { - // We simulate this, which would in other cases be done by some middleware - req.user = { - id: '1', - email: 'test@sentry.io', - }; - - next(); -}); - -app.get('/test1', (_req, _res) => { - throw new Error('error_1'); -}); - -app.use((_req, _res, next) => { - Sentry.setUser({ - id: '2', - email: 'test2@sentry.io', - }); - - next(); -}); - -app.get('/test2', (_req, _res) => { - throw new Error('error_2'); -}); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/requestUser/test.ts b/dev-packages/node-integration-tests/suites/express-v5/requestUser/test.ts deleted file mode 100644 index 2605b7ed5127..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/requestUser/test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { afterAll, describe, expect, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; - -describe('express user handling', () => { - afterAll(() => { - cleanupChildProcesses(); - }); - - test('ignores user from request', async () => { - expect.assertions(2); - - const runner = createRunner(__dirname, 'server.js') - .expect({ - event: event => { - expect(event.user).toBeUndefined(); - expect(event.exception?.values?.[0]?.value).toBe('error_1'); - }, - }) - .start(); - runner.makeRequest('get', '/test1', { expectError: true }); - await runner.completed(); - }); - - test('using setUser in middleware works', async () => { - const runner = createRunner(__dirname, 'server.js') - .expect({ - event: { - user: { - id: '2', - email: 'test2@sentry.io', - }, - exception: { - values: [ - { - value: 'error_2', - }, - ], - }, - }, - }) - .start(); - runner.makeRequest('get', '/test2', { expectError: true }); - await runner.completed(); - }); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-header-assign/test.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-header-assign/test.ts deleted file mode 100644 index 8b8d648513e4..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-header-assign/test.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { parseBaggageHeader } from '@sentry/core'; -import { afterAll, expect, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; -import type { TestAPIResponse } from '../server'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -test('Should overwrite baggage if the incoming request already has Sentry baggage data but no sentry-trace', async () => { - const runner = createRunner(__dirname, '..', 'server.ts').start(); - - const response = await runner.makeRequest('get', '/test/express', { - headers: { - baggage: 'sentry-release=2.0.0,sentry-environment=myEnv', - }, - }); - - expect(response).toBeDefined(); - expect(response).not.toMatchObject({ - test_data: { - host: 'somewhere.not.sentry', - baggage: 'sentry-release=2.0.0,sentry-environment=myEnv', - }, - }); -}); - -test('Should propagate sentry trace baggage data from an incoming to an outgoing request.', async () => { - const runner = createRunner(__dirname, '..', 'server.ts').start(); - - const response = await runner.makeRequest('get', '/test/express', { - headers: { - 'sentry-trace': '12312012123120121231201212312012-1121201211212012-1', - baggage: 'sentry-release=2.0.0,sentry-environment=myEnv,dogs=great,sentry-sample_rand=0.42', - }, - }); - - expect(response).toBeDefined(); - expect(response).toMatchObject({ - test_data: { - host: 'somewhere.not.sentry', - baggage: 'sentry-release=2.0.0,sentry-environment=myEnv,sentry-sample_rand=0.42', - }, - }); -}); - -test('Should not propagate baggage data from an incoming to an outgoing request if sentry-trace is faulty.', async () => { - const runner = createRunner(__dirname, '..', 'server.ts').start(); - - const response = await runner.makeRequest('get', '/test/express', { - headers: { - 'sentry-trace': '', - baggage: 'sentry-release=2.0.0,sentry-environment=myEnv,dogs=great', - }, - }); - - expect(response).toBeDefined(); - expect(response).not.toMatchObject({ - test_data: { - host: 'somewhere.not.sentry', - baggage: 'sentry-release=2.0.0,sentry-environment=myEnv', - }, - }); -}); - -test('Should not propagate baggage if sentry-trace header is present in incoming request but no baggage header', async () => { - const runner = createRunner(__dirname, '..', 'server.ts').start(); - - const response = await runner.makeRequest('get', '/test/express', { - headers: { - 'sentry-trace': '12312012123120121231201212312012-1121201211212012-1', - }, - }); - - expect(response).toBeDefined(); - expect(response).toMatchObject({ - test_data: { - host: 'somewhere.not.sentry', - }, - }); -}); - -test('Should not propagate baggage and ignore original 3rd party baggage entries if sentry-trace header is present', async () => { - const runner = createRunner(__dirname, '..', 'server.ts').start(); - - const response = await runner.makeRequest('get', '/test/express', { - headers: { - 'sentry-trace': '12312012123120121231201212312012-1121201211212012-1', - baggage: 'foo=bar', - }, - }); - - expect(response).toBeDefined(); - expect(response).toMatchObject({ - test_data: { - host: 'somewhere.not.sentry', - }, - }); -}); - -test('Should populate and propagate sentry baggage if sentry-trace header does not exist', async () => { - const runner = createRunner(__dirname, '..', 'server.ts').start(); - - const response = await runner.makeRequest('get', '/test/express'); - - expect(response).toBeDefined(); - - const parsedBaggage = parseBaggageHeader(response?.test_data.baggage); - - expect(response?.test_data.host).toBe('somewhere.not.sentry'); - expect(parsedBaggage).toStrictEqual({ - 'sentry-environment': 'prod', - 'sentry-release': '1.0', - 'sentry-public_key': 'public', - // TraceId changes, hence we only expect that the string contains the traceid key - 'sentry-trace_id': expect.stringMatching(/[\S]*/), - 'sentry-sample_rand': expect.stringMatching(/[\S]*/), - 'sentry-sample_rate': '1', - 'sentry-sampled': 'true', - 'sentry-transaction': 'GET /test/express', - }); -}); - -test('Should populate Sentry and ignore 3rd party content if sentry-trace header does not exist', async () => { - const runner = createRunner(__dirname, '..', 'server.ts').start(); - - const response = await runner.makeRequest('get', '/test/express', { - headers: { - baggage: 'foo=bar,bar=baz', - }, - }); - - expect(response).toBeDefined(); - expect(response?.test_data.host).toBe('somewhere.not.sentry'); - - const parsedBaggage = parseBaggageHeader(response?.test_data.baggage); - expect(parsedBaggage).toStrictEqual({ - 'sentry-environment': 'prod', - 'sentry-release': '1.0', - 'sentry-public_key': 'public', - // TraceId changes, hence we only expect that the string contains the traceid key - 'sentry-trace_id': expect.stringMatching(/[\S]*/), - 'sentry-sample_rand': expect.stringMatching(/[\S]*/), - 'sentry-sample_rate': '1', - 'sentry-sampled': 'true', - 'sentry-transaction': 'GET /test/express', - }); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-header-out/server.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-header-out/server.ts deleted file mode 100644 index a66661c50bd2..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-header-out/server.ts +++ /dev/null @@ -1,38 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -export type TestAPIResponse = { test_data: { host: string; 'sentry-trace': string; baggage: string } }; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - environment: 'prod', - tracePropagationTargets: [/^(?!.*express).*$/], - tracesSampleRate: 1.0, - transport: loggingTransport, -}); - -import cors from 'cors'; -import express from 'express'; -import http from 'http'; - -const app = express(); - -Sentry.setUser({ id: 'user123' }); - -app.use(cors()); - -app.get('/test/express', (_req, res) => { - const span = Sentry.getActiveSpan(); - const traceId = span?.spanContext().traceId; - const headers = http.get('http://somewhere.not.sentry/').getHeaders(); - if (traceId) { - headers['baggage'] = (headers['baggage'] as string).replace(traceId, '__SENTRY_TRACE_ID__'); - } - // Responding with the headers outgoing request headers back to the assertions. - res.send({ test_data: headers }); -}); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-header-out/test.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-header-out/test.ts deleted file mode 100644 index 913fcd5e2038..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-header-out/test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { afterAll, expect, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; -import type { TestAPIResponse } from './server'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -test('should attach a baggage header to an outgoing request.', async () => { - const runner = createRunner(__dirname, 'server.ts').start(); - - const response = await runner.makeRequest('get', '/test/express'); - - expect(response).toBeDefined(); - - const baggage = response?.test_data.baggage?.split(','); - - [ - 'sentry-environment=prod', - 'sentry-public_key=public', - 'sentry-release=1.0', - 'sentry-sample_rate=1', - 'sentry-sampled=true', - 'sentry-trace_id=__SENTRY_TRACE_ID__', - 'sentry-transaction=GET%20%2Ftest%2Fexpress', - expect.stringMatching(/sentry-sample_rand=0\.[0-9]+/), - ].forEach(item => { - expect(baggage).toContainEqual(item); - }); - - expect(response).toMatchObject({ - test_data: { - host: 'somewhere.not.sentry', - }, - }); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors-with-sentry-entries/server.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors-with-sentry-entries/server.ts deleted file mode 100644 index 372d413a158e..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors-with-sentry-entries/server.ts +++ /dev/null @@ -1,42 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -export type TestAPIResponse = { test_data: { host: string; 'sentry-trace': string; baggage: string } }; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - environment: 'prod', - // disable requests to /express - tracePropagationTargets: [/^(?!.*express).*$/], - tracesSampleRate: 1.0, - transport: loggingTransport, -}); - -import cors from 'cors'; -import express from 'express'; -import * as http from 'http'; - -const app = express(); - -app.use(cors()); - -app.get('/test/express', (_req, res) => { - // simulate setting a "third party" baggage header which the Sentry SDK should merge with Sentry DSC entries - const headers = http - .get({ - hostname: 'somewhere.not.sentry', - headers: { - baggage: - 'other=vendor,foo=bar,third=party,sentry-release=9.9.9,sentry-environment=staging,sentry-sample_rate=0.54,last=item', - }, - }) - .getHeaders(); - - // Responding with the headers outgoing request headers back to the assertions. - res.send({ test_data: headers }); -}); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors-with-sentry-entries/test.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors-with-sentry-entries/test.ts deleted file mode 100644 index 2d2074be773c..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors-with-sentry-entries/test.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { afterAll, expect, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; -import type { TestAPIResponse } from '../server'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -test('should ignore sentry-values in `baggage` header of a third party vendor and overwrite them with incoming DSC', async () => { - const runner = createRunner(__dirname, 'server.ts').start(); - - const response = await runner.makeRequest('get', '/test/express', { - headers: { - 'sentry-trace': '12312012123120121231201212312012-1121201211212012-1', - baggage: 'sentry-release=2.1.0,sentry-environment=myEnv', - }, - }); - - expect(response).toBeDefined(); - - const baggage = response?.test_data.baggage?.split(',').sort(); - - expect(response).toMatchObject({ - test_data: { - host: 'somewhere.not.sentry', - }, - }); - - expect(baggage).toEqual([ - 'foo=bar', - 'last=item', - 'other=vendor', - 'sentry-environment=myEnv', - 'sentry-release=2.1.0', - expect.stringMatching(/sentry-sample_rand=[0-9]+/), - 'sentry-sample_rate=0.54', - 'third=party', - ]); -}); - -test('should ignore sentry-values in `baggage` header of a third party vendor and overwrite them with new DSC', async () => { - const runner = createRunner(__dirname, 'server.ts').start(); - - const response = await runner.makeRequest('get', '/test/express'); - - expect(response).toBeDefined(); - - const baggage = response?.test_data.baggage?.split(',').sort(); - - expect(response).toMatchObject({ - test_data: { - host: 'somewhere.not.sentry', - }, - }); - - expect(baggage).toEqual([ - 'foo=bar', - 'last=item', - 'other=vendor', - 'sentry-environment=prod', - 'sentry-public_key=public', - 'sentry-release=1.0', - expect.stringMatching(/sentry-sample_rand=[0-9]+/), - 'sentry-sample_rate=1', - 'sentry-sampled=true', - expect.stringMatching(/sentry-trace_id=[0-9a-f]{32}/), - 'sentry-transaction=GET%20%2Ftest%2Fexpress', - 'third=party', - ]); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors/server.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors/server.ts deleted file mode 100644 index 7aea36941e99..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors/server.ts +++ /dev/null @@ -1,36 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -export type TestAPIResponse = { test_data: { host: string; 'sentry-trace': string; baggage: string } }; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - environment: 'prod', - // disable requests to /express - tracePropagationTargets: [/^(?!.*express).*$/], - tracesSampleRate: 1.0, - transport: loggingTransport, -}); - -import cors from 'cors'; -import express from 'express'; -import http from 'http'; - -const app = express(); - -app.use(cors()); - -app.get('/test/express', (_req, res) => { - // simulate setting a "third party" baggage header which the Sentry SDK should merge with Sentry DSC entries - const headers = http - .get({ hostname: 'somewhere.not.sentry', headers: { baggage: 'other=vendor,foo=bar,third=party' } }) - .getHeaders(); - - // Responding with the headers outgoing request headers back to the assertions. - res.send({ test_data: headers }); -}); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors/test.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors/test.ts deleted file mode 100644 index beb118944408..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors/test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { afterAll, expect, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; -import type { TestAPIResponse } from './server'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -test('should merge `baggage` header of a third party vendor with the Sentry DSC baggage items', async () => { - const runner = createRunner(__dirname, 'server.ts').start(); - - const response = await runner.makeRequest('get', '/test/express', { - headers: { - 'sentry-trace': '12312012123120121231201212312012-1121201211212012-1', - baggage: 'sentry-release=2.0.0,sentry-environment=myEnv,sentry-sample_rand=0.42', - }, - }); - - expect(response).toBeDefined(); - expect(response).toMatchObject({ - test_data: { - host: 'somewhere.not.sentry', - baggage: 'other=vendor,foo=bar,third=party,sentry-release=2.0.0,sentry-environment=myEnv,sentry-sample_rand=0.42', - }, - }); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-transaction-name/server.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-transaction-name/server.ts deleted file mode 100644 index ef455fb9ec52..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-transaction-name/server.ts +++ /dev/null @@ -1,36 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -export type TestAPIResponse = { test_data: { host: string; 'sentry-trace': string; baggage: string } }; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - environment: 'prod', - // disable requests to /express - tracePropagationTargets: [/^(?!.*express).*$/], - tracesSampleRate: 1.0, - // TODO: We're rethinking the mechanism for including Pii data in DSC, hence commenting out sendDefaultPii for now - // sendDefaultPii: true, - transport: loggingTransport, -}); - -import cors from 'cors'; -import express from 'express'; -import http from 'http'; - -const app = express(); - -Sentry.setUser({ id: 'user123' }); - -app.use(cors()); - -app.get('/test/express', (_req, res) => { - const headers = http.get('http://somewhere.not.sentry/').getHeaders(); - // Responding with the headers outgoing request headers back to the assertions. - res.send({ test_data: headers }); -}); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-transaction-name/test.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-transaction-name/test.ts deleted file mode 100644 index 436a8ea9b4da..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-transaction-name/test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { afterAll, expect, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; -import type { TestAPIResponse } from '../server'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -test('Includes transaction in baggage if the transaction name is parameterized', async () => { - const runner = createRunner(__dirname, 'server.ts').start(); - - const response = await runner.makeRequest('get', '/test/express'); - - expect(response).toBeDefined(); - expect(response).toMatchObject({ - test_data: { - host: 'somewhere.not.sentry', - baggage: expect.stringContaining('sentry-transaction=GET%20%2Ftest%2Fexpress'), - }, - }); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/server.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/server.ts deleted file mode 100644 index cc8d5657dc7f..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/server.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -export type TestAPIResponse = { test_data: { host: string; 'sentry-trace': string; baggage: string } }; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - environment: 'prod', - tracePropagationTargets: [/^(?!.*express).*$/], - tracesSampleRate: 1.0, - transport: loggingTransport, -}); - -import cors from 'cors'; -import express from 'express'; -import http from 'http'; - -const app = express(); - -app.use(cors()); - -app.get('/test/express', (_req, res) => { - const headers = http.get('http://somewhere.not.sentry/').getHeaders(); - - // Responding with the headers outgoing request headers back to the assertions. - res.send({ test_data: headers }); -}); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/trace-header-assign/server.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/trace-header-assign/server.ts deleted file mode 100644 index 8aa9ed85d3be..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/trace-header-assign/server.ts +++ /dev/null @@ -1,31 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -export type TestAPIResponse = { test_data: { host: string; 'sentry-trace': string; baggage: string } }; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - environment: 'prod', - tracesSampleRate: 1.0, - transport: loggingTransport, -}); - -import cors from 'cors'; -import express from 'express'; -import http from 'http'; - -const app = express(); - -app.use(cors()); - -app.get('/test/express', (_req, res) => { - const headers = http.get('http://somewhere.not.sentry/').getHeaders(); - - // Responding with the headers outgoing request headers back to the assertions. - res.send({ test_data: headers }); -}); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/trace-header-assign/test.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/trace-header-assign/test.ts deleted file mode 100644 index 7d0a729dc4ff..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/trace-header-assign/test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { TRACEPARENT_REGEXP } from '@sentry/core'; -import { afterAll, expect, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; -import type { TestAPIResponse } from '../server'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -test('Should assign `sentry-trace` header which sets parent trace id of an outgoing request.', async () => { - const runner = createRunner(__dirname, 'server.ts').start(); - - const response = await runner.makeRequest('get', '/test/express', { - headers: { - 'sentry-trace': '12312012123120121231201212312012-1121201211212012-0', - }, - }); - - expect(response).toBeDefined(); - expect(response).toMatchObject({ - test_data: { - host: 'somewhere.not.sentry', - 'sentry-trace': expect.stringContaining('12312012123120121231201212312012-'), - }, - }); - - expect(TRACEPARENT_REGEXP.test(response?.test_data['sentry-trace'] || '')).toBe(true); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/trace-header-out/test.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/trace-header-out/test.ts deleted file mode 100644 index 8ed4d08bba55..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/trace-header-out/test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { TRACEPARENT_REGEXP } from '@sentry/core'; -import { afterAll, expect, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; -import type { TestAPIResponse } from '../server'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -test('should attach a `sentry-trace` header to an outgoing request.', async () => { - const runner = createRunner(__dirname, '..', 'server.ts').start(); - - const response = await runner.makeRequest('get', '/test/express'); - - expect(response).toBeDefined(); - expect(response).toMatchObject({ - test_data: { - host: 'somewhere.not.sentry', - 'sentry-trace': expect.any(String), - }, - }); - - expect(TRACEPARENT_REGEXP.test(response?.test_data['sentry-trace'] || '')).toBe(true); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/setupExpressErrorHandler/server.js b/dev-packages/node-integration-tests/suites/express-v5/setupExpressErrorHandler/server.js deleted file mode 100644 index 0e73923cf88a..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/setupExpressErrorHandler/server.js +++ /dev/null @@ -1,33 +0,0 @@ -const { loggingTransport } = require('@sentry-internal/node-integration-tests'); -const Sentry = require('@sentry/node'); - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - transport: loggingTransport, -}); - -// express must be required after Sentry is initialized -const express = require('express'); -const cors = require('cors'); -const { startExpressServerAndSendPortToRunner } = require('@sentry-internal/node-integration-tests'); - -const app = express(); - -app.use(cors()); - -app.get('/test1', (_req, _res) => { - throw new Error('error_1'); -}); - -app.get('/test2', (_req, _res) => { - throw new Error('error_2'); -}); - -Sentry.setupExpressErrorHandler(app, { - shouldHandleError: error => { - return error.message === 'error_2'; - }, -}); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/setupExpressErrorHandler/test.ts b/dev-packages/node-integration-tests/suites/express-v5/setupExpressErrorHandler/test.ts deleted file mode 100644 index 571ffc52e224..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/setupExpressErrorHandler/test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { afterAll, describe, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; - -describe('express setupExpressErrorHandler', () => { - afterAll(() => { - cleanupChildProcesses(); - }); - - describe('CJS', () => { - test('allows to pass options to setupExpressErrorHandler', async () => { - const runner = createRunner(__dirname, 'server.js') - .expect({ - event: { - exception: { - values: [ - { - value: 'error_2', - }, - ], - }, - }, - }) - .start(); - - // this error is filtered & ignored - runner.makeRequest('get', '/test1', { expectError: true }); - // this error is actually captured - runner.makeRequest('get', '/test2', { expectError: true }); - await runner.completed(); - }); - }); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/span-isolationScope/server.ts b/dev-packages/node-integration-tests/suites/express-v5/span-isolationScope/server.ts deleted file mode 100644 index 3e4f9d0de62b..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/span-isolationScope/server.ts +++ /dev/null @@ -1,28 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - tracesSampleRate: 1.0, - transport: loggingTransport, -}); - -import express from 'express'; - -const app = express(); - -Sentry.setTag('global', 'tag'); - -app.get('/test/isolationScope', (_req, res) => { - // eslint-disable-next-line no-console - console.log('This is a test log.'); - Sentry.addBreadcrumb({ message: 'manual breadcrumb' }); - Sentry.setTag('isolation-scope', 'tag'); - - res.send({}); -}); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/span-isolationScope/test.ts b/dev-packages/node-integration-tests/suites/express-v5/span-isolationScope/test.ts deleted file mode 100644 index f8c7c11378d5..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/span-isolationScope/test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { afterAll, expect, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -test('correctly applies isolation scope to span', async () => { - const runner = createRunner(__dirname, 'server.ts') - .expect({ - transaction: { - transaction: 'GET /test/isolationScope', - breadcrumbs: [ - { - category: 'console', - level: 'log', - message: expect.stringMatching(/\{"port":(\d+)\}/), - timestamp: expect.any(Number), - }, - { - category: 'console', - level: 'log', - message: 'This is a test log.', - timestamp: expect.any(Number), - }, - { - message: 'manual breadcrumb', - timestamp: expect.any(Number), - }, - ], - tags: { - global: 'tag', - 'isolation-scope': 'tag', - }, - }, - }) - .start(); - runner.makeRequest('get', '/test/isolationScope'); - await runner.completed(); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/tracing/instrument-filterStatusCode.mjs b/dev-packages/node-integration-tests/suites/express-v5/tracing/instrument-filterStatusCode.mjs deleted file mode 100644 index 31473a90df73..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/tracing/instrument-filterStatusCode.mjs +++ /dev/null @@ -1,14 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport } from '@sentry-internal/node-integration-tests'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - tracesSampleRate: 1.0, - transport: loggingTransport, - integrations: [ - Sentry.httpIntegration({ - dropSpansForIncomingRequestStatusCodes: [499, [300, 399]], - }), - ], -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/tracing/instrument.mjs b/dev-packages/node-integration-tests/suites/express-v5/tracing/instrument.mjs deleted file mode 100644 index 5cade6bb7ba1..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/tracing/instrument.mjs +++ /dev/null @@ -1,11 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport } from '@sentry-internal/node-integration-tests'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - // disable attaching headers to /test/* endpoints - tracePropagationTargets: [/^(?!.*test).*$/], - tracesSampleRate: 1.0, - transport: loggingTransport, -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/tracing/scenario-filterStatusCode.mjs b/dev-packages/node-integration-tests/suites/express-v5/tracing/scenario-filterStatusCode.mjs deleted file mode 100644 index c53a72951970..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/tracing/scenario-filterStatusCode.mjs +++ /dev/null @@ -1,37 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; -import express from 'express'; - -const app = express(); - -app.get('/', (_req, res) => { - res.send({ response: 'response 0' }); -}); - -app.get('/401', (_req, res) => { - res.status(401).send({ response: 'response 401' }); -}); - -app.get('/402', (_req, res) => { - res.status(402).send({ response: 'response 402' }); -}); - -app.get('/403', (_req, res) => { - res.status(403).send({ response: 'response 403' }); -}); - -app.get('/499', (_req, res) => { - res.status(499).send({ response: 'response 499' }); -}); - -app.get('/300', (_req, res) => { - res.status(300).send({ response: 'response 300' }); -}); - -app.get('/399', (_req, res) => { - res.status(399).send({ response: 'response 399' }); -}); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/tracing/scenario.mjs b/dev-packages/node-integration-tests/suites/express-v5/tracing/scenario.mjs deleted file mode 100644 index b0aebcbe8a79..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/tracing/scenario.mjs +++ /dev/null @@ -1,52 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; -import bodyParser from 'body-parser'; -import cors from 'cors'; -import express from 'express'; - -const app = express(); - -app.use(cors()); -app.use(bodyParser.json()); -app.use(bodyParser.text()); -app.use(bodyParser.raw()); - -app.get('/', (_req, res) => { - res.send({ response: 'response 0' }); -}); - -app.get('/401', (_req, res) => { - res.status(401).send({ response: 'response 401' }); -}); - -app.get('/402', (_req, res) => { - res.status(402).send({ response: 'response 402' }); -}); - -app.get('/403', (_req, res) => { - res.status(403).send({ response: 'response 403' }); -}); - -app.get('/test/express', (_req, res) => { - res.send({ response: 'response 1' }); -}); - -app.get(/\/test\/regex/, (_req, res) => { - res.send({ response: 'response 2' }); -}); - -app.get(['/test/array1', /\/test\/array[2-9]/], (_req, res) => { - res.send({ response: 'response 3' }); -}); - -app.get(['/test/arr/:id', /\/test\/arr[0-9]*\/required(path)?(\/optionalPath)?\/(lastParam)?/], (_req, res) => { - res.send({ response: 'response 4' }); -}); - -app.post('/test-post', function (req, res) { - res.send({ status: 'ok', body: req.body }); -}); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/tracing/test.ts b/dev-packages/node-integration-tests/suites/express-v5/tracing/test.ts deleted file mode 100644 index b8be6a003fda..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/tracing/test.ts +++ /dev/null @@ -1,356 +0,0 @@ -import { afterAll, describe, expect } from 'vitest'; -import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; - -describe('express v5 tracing', () => { - afterAll(() => { - cleanupChildProcesses(); - }); - - createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createRunner, test) => { - test('should create and send transactions for Express routes and spans for middlewares.', async () => { - const runner = createRunner() - .expect({ - transaction: { - contexts: { - trace: { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: { - url: expect.stringMatching(/\/test\/express$/), - 'http.response.status_code': 200, - }, - op: 'http.server', - status: 'ok', - }, - }, - spans: expect.arrayContaining([ - expect.objectContaining({ - data: expect.objectContaining({ - 'express.name': 'corsMiddleware', - 'express.type': 'middleware', - }), - description: 'corsMiddleware', - op: 'middleware.express', - origin: 'auto.http.otel.express', - }), - expect.objectContaining({ - data: expect.objectContaining({ - 'express.name': '/test/express', - 'express.type': 'request_handler', - }), - description: '/test/express', - op: 'request_handler.express', - origin: 'auto.http.otel.express', - }), - ]), - }, - }) - .start(); - runner.makeRequest('get', '/test/express'); - await runner.completed(); - }); - - test('should set a correct transaction name for routes specified in RegEx', async () => { - const runner = createRunner() - .expect({ - transaction: { - transaction: 'GET /\\/test\\/regex/', - transaction_info: { - source: 'route', - }, - contexts: { - trace: { - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - span_id: expect.stringMatching(/[a-f0-9]{16}/), - data: { - url: expect.stringMatching(/\/test\/regex$/), - 'http.response.status_code': 200, - }, - op: 'http.server', - status: 'ok', - }, - }, - }, - }) - .start(); - runner.makeRequest('get', '/test/regex'); - await runner.completed(); - }); - - test('handles root route correctly', async () => { - const runner = createRunner() - .expect({ - transaction: { - transaction: 'GET /', - contexts: { - trace: { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: { - 'http.response.status_code': 200, - url: expect.stringMatching(/\/$/), - 'http.method': 'GET', - 'http.url': expect.stringMatching(/\/$/), - 'http.route': '/', - 'http.target': '/', - }, - op: 'http.server', - status: 'ok', - }, - }, - }, - }) - .start(); - runner.makeRequest('get', '/'); - await runner.completed(); - }); - - test.each(['/401', '/402', '/403', '/does-not-exist'])('ignores %s route by default', async (url: string) => { - const runner = createRunner() - .expect({ - // No transaction is sent for the 401, 402, 403, 404 routes - transaction: { - transaction: 'GET /', - }, - }) - .start(); - runner.makeRequest('get', url, { expectError: true }); - runner.makeRequest('get', '/'); - await runner.completed(); - }); - - test.each([['array1'], ['array5']])( - 'should set a correct transaction name for routes consisting of arrays of routes for %p', - async (segment: string) => { - const runner = await createRunner() - .expect({ - transaction: { - transaction: 'GET /test/array1,/\\/test\\/array[2-9]/', - transaction_info: { - source: 'route', - }, - contexts: { - trace: { - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - span_id: expect.stringMatching(/[a-f0-9]{16}/), - data: { - url: expect.stringMatching(`/test/${segment}$`), - 'http.response.status_code': 200, - }, - op: 'http.server', - status: 'ok', - }, - }, - }, - }) - .start(); - await runner.makeRequest('get', `/test/${segment}`); - await runner.completed(); - }, - ); - - test.each([ - ['arr/545'], - ['arr/required'], - ['arr/required'], - ['arr/requiredPath'], - ['arr/required/lastParam'], - ['arr55/required/lastParam'], - ])('should handle more complex regexes in route arrays correctly for %p', async (segment: string) => { - const runner = await createRunner() - .expect({ - transaction: { - transaction: 'GET /test/arr/:id,/\\/test\\/arr[0-9]*\\/required(path)?(\\/optionalPath)?\\/(lastParam)?/', - transaction_info: { - source: 'route', - }, - contexts: { - trace: { - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - span_id: expect.stringMatching(/[a-f0-9]{16}/), - data: { - url: expect.stringMatching(`/test/${segment}$`), - 'http.response.status_code': 200, - }, - op: 'http.server', - status: 'ok', - }, - }, - }, - }) - .start(); - await runner.makeRequest('get', `/test/${segment}`); - await runner.completed(); - }); - - describe('request data', () => { - test('correctly captures JSON request data', async () => { - const runner = createRunner() - .expect({ - transaction: { - transaction: 'POST /test-post', - request: { - url: expect.stringMatching(/^http:\/\/localhost:(\d+)\/test-post$/), - method: 'POST', - headers: { - 'user-agent': expect.stringContaining(''), - 'content-type': 'application/json', - }, - data: JSON.stringify({ - foo: 'bar', - other: 1, - }), - }, - }, - }) - .start(); - - runner.makeRequest('post', '/test-post', { - data: JSON.stringify({ foo: 'bar', other: 1 }), - headers: { - 'Content-Type': 'application/json', - }, - }); - await runner.completed(); - }); - - test('correctly captures plain text request data', async () => { - const runner = createRunner() - .expect({ - transaction: { - transaction: 'POST /test-post', - request: { - url: expect.stringMatching(/^http:\/\/localhost:(\d+)\/test-post$/), - method: 'POST', - headers: { - 'user-agent': expect.stringContaining(''), - 'content-type': 'text/plain', - }, - data: 'some plain text', - }, - }, - }) - .start(); - - runner.makeRequest('post', '/test-post', { - headers: { 'Content-Type': 'text/plain' }, - data: 'some plain text', - }); - await runner.completed(); - }); - - test('correctly captures text buffer request data', async () => { - const runner = createRunner() - .expect({ - transaction: { - transaction: 'POST /test-post', - request: { - url: expect.stringMatching(/^http:\/\/localhost:(\d+)\/test-post$/), - method: 'POST', - headers: { - 'user-agent': expect.stringContaining(''), - 'content-type': 'application/octet-stream', - }, - data: 'some plain text in buffer', - }, - }, - }) - .start(); - - runner.makeRequest('post', '/test-post', { - headers: { 'Content-Type': 'application/octet-stream' }, - data: Buffer.from('some plain text in buffer'), - }); - await runner.completed(); - }); - - test('correctly captures non-text buffer request data', async () => { - const runner = createRunner() - .expect({ - transaction: { - transaction: 'POST /test-post', - request: { - url: expect.stringMatching(/^http:\/\/localhost:(\d+)\/test-post$/), - method: 'POST', - headers: { - 'user-agent': expect.stringContaining(''), - 'content-type': 'application/octet-stream', - }, - // This is some non-ascii string representation - data: expect.any(String), - }, - }, - }) - .start(); - - const body = new Uint8Array([1, 2, 3, 4, 5]).buffer; - - runner.makeRequest('post', '/test-post', { - headers: { 'Content-Type': 'application/octet-stream' }, - data: body, - }); - await runner.completed(); - }); - }); - }); - - describe('filter status codes', () => { - createEsmAndCjsTests( - __dirname, - 'scenario-filterStatusCode.mjs', - 'instrument-filterStatusCode.mjs', - (createRunner, test) => { - // We opt-out of the default [401, 404] fitering in order to test how these spans are handled - test.each([ - { status_code: 401, url: '/401', status: 'unauthenticated' }, - { status_code: 402, url: '/402', status: 'invalid_argument' }, - { status_code: 403, url: '/403', status: 'permission_denied' }, - { status_code: 404, url: '/does-not-exist', status: 'not_found' }, - ])( - 'handles %s route correctly', - async ({ status_code, url, status }: { status_code: number; url: string; status: string }) => { - const runner = createRunner() - .expect({ - transaction: { - transaction: `GET ${url}`, - contexts: { - trace: { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: { - 'http.response.status_code': status_code, - url: expect.stringMatching(url), - 'http.method': 'GET', - 'http.url': expect.stringMatching(url), - 'http.target': url, - }, - op: 'http.server', - status, - }, - }, - }, - }) - .start(); - runner.makeRequest('get', url, { expectError: true }); - await runner.completed(); - }, - ); - - test('filters defined status codes', async () => { - const runner = createRunner() - .expect({ - transaction: { - transaction: 'GET /', - }, - }) - .start(); - await runner.makeRequest('get', '/499', { expectError: true }); - await runner.makeRequest('get', '/300', { expectError: true }); - await runner.makeRequest('get', '/399', { expectError: true }); - await runner.makeRequest('get', '/'); - await runner.completed(); - }); - }, - ); - }); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/tracing/tracesSampler/scenario-normalizedRequest.js b/dev-packages/node-integration-tests/suites/express-v5/tracing/tracesSampler/scenario-normalizedRequest.js deleted file mode 100644 index da31780f2c5f..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/tracing/tracesSampler/scenario-normalizedRequest.js +++ /dev/null @@ -1,34 +0,0 @@ -const { loggingTransport } = require('@sentry-internal/node-integration-tests'); -const Sentry = require('@sentry/node'); - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - transport: loggingTransport, - tracesSampler: samplingContext => { - // The sampling decision is based on whether the data in `normalizedRequest` is available --> this is what we want to test for - return ( - samplingContext.normalizedRequest.url.includes('/test-normalized-request?query=123') && - samplingContext.normalizedRequest.method && - samplingContext.normalizedRequest.query_string === 'query=123' && - !!samplingContext.normalizedRequest.headers - ); - }, -}); - -// express must be required after Sentry is initialized -const express = require('express'); -const cors = require('cors'); -const { startExpressServerAndSendPortToRunner } = require('@sentry-internal/node-integration-tests'); - -const app = express(); - -app.use(cors()); - -app.get('/test-normalized-request', (_req, res) => { - res.send('Success'); -}); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/tracing/tracesSampler/server.js b/dev-packages/node-integration-tests/suites/express-v5/tracing/tracesSampler/server.js deleted file mode 100644 index b60ea07b636f..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/tracing/tracesSampler/server.js +++ /dev/null @@ -1,39 +0,0 @@ -const { loggingTransport } = require('@sentry-internal/node-integration-tests'); -const Sentry = require('@sentry/node'); - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - transport: loggingTransport, - tracesSampler: samplingContext => { - // The name we get here is inferred at span creation time - // At this point, we sadly do not have a http.route attribute yet, - // so we infer the name from the unparameterized route instead - return ( - samplingContext.name === 'GET /test/123' && - samplingContext.attributes['sentry.op'] === 'http.server' && - samplingContext.attributes['http.method'] === 'GET' - ); - }, -}); - -// express must be required after Sentry is initialized -const express = require('express'); -const cors = require('cors'); -const { startExpressServerAndSendPortToRunner } = require('@sentry-internal/node-integration-tests'); - -const app = express(); - -app.use(cors()); - -app.get('/test/:id', (_req, res) => { - res.send('Success'); -}); - -app.get('/test2', (_req, res) => { - res.send('Success'); -}); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/tracing/tracesSampler/test.ts b/dev-packages/node-integration-tests/suites/express-v5/tracing/tracesSampler/test.ts deleted file mode 100644 index 1b644ada387a..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/tracing/tracesSampler/test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { afterAll, describe, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; - -describe('express tracesSampler', () => { - afterAll(() => { - cleanupChildProcesses(); - }); - - describe('CJS', () => { - test('correctly samples & passes data to tracesSampler', async () => { - const runner = createRunner(__dirname, 'server.js') - .expect({ - transaction: { - transaction: 'GET /test/:id', - }, - }) - .start(); - - // This is not sampled - runner.makeRequest('get', '/test2?q=1'); - // This is sampled - runner.makeRequest('get', '/test/123?q=1'); - await runner.completed(); - }); - }); -}); - -describe('express tracesSampler includes normalizedRequest data', () => { - afterAll(() => { - cleanupChildProcesses(); - }); - - describe('CJS', () => { - test('correctly samples & passes data to tracesSampler', async () => { - const runner = createRunner(__dirname, 'scenario-normalizedRequest.js') - .expect({ - transaction: { - transaction: 'GET /test-normalized-request', - }, - }) - .start(); - - runner.makeRequest('get', '/test-normalized-request?query=123'); - await runner.completed(); - }); - }); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/tracing/updateName/server.js b/dev-packages/node-integration-tests/suites/express-v5/tracing/updateName/server.js deleted file mode 100644 index c98e17276d92..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/tracing/updateName/server.js +++ /dev/null @@ -1,58 +0,0 @@ -const { loggingTransport } = require('@sentry-internal/node-integration-tests'); -const Sentry = require('@sentry/node'); - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - // disable attaching headers to /test/* endpoints - tracePropagationTargets: [/^(?!.*test).*$/], - tracesSampleRate: 1.0, - transport: loggingTransport, -}); - -// express must be required after Sentry is initialized -const express = require('express'); -const cors = require('cors'); -const bodyParser = require('body-parser'); -const { startExpressServerAndSendPortToRunner } = require('@sentry-internal/node-integration-tests'); - -const app = express(); - -app.use(cors()); -app.use(bodyParser.json()); -app.use(bodyParser.text()); -app.use(bodyParser.raw()); - -app.get('/test/:id/span-updateName', (_req, res) => { - const span = Sentry.getActiveSpan(); - const rootSpan = Sentry.getRootSpan(span); - rootSpan.updateName('new-name'); - res.send({ response: 'response 1' }); -}); - -app.get('/test/:id/span-updateName-source', (_req, res) => { - const span = Sentry.getActiveSpan(); - const rootSpan = Sentry.getRootSpan(span); - rootSpan.updateName('new-name'); - rootSpan.setAttribute(Sentry.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'custom'); - res.send({ response: 'response 2' }); -}); - -app.get('/test/:id/updateSpanName', (_req, res) => { - const span = Sentry.getActiveSpan(); - const rootSpan = Sentry.getRootSpan(span); - Sentry.updateSpanName(rootSpan, 'new-name'); - res.send({ response: 'response 3' }); -}); - -app.get('/test/:id/updateSpanNameAndSource', (_req, res) => { - const span = Sentry.getActiveSpan(); - const rootSpan = Sentry.getRootSpan(span); - Sentry.updateSpanName(rootSpan, 'new-name'); - rootSpan.setAttribute(Sentry.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'component'); - res.send({ response: 'response 4' }); -}); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/tracing/updateName/test.ts b/dev-packages/node-integration-tests/suites/express-v5/tracing/updateName/test.ts deleted file mode 100644 index f8cbf3c2bd57..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/tracing/updateName/test.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME } from '@sentry/core'; -import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/node'; -import { afterAll, describe, expect, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; - -describe('express v5 tracing', () => { - afterAll(() => { - cleanupChildProcesses(); - }); - - describe('CJS', () => { - // This test documents the unfortunate behaviour of using `span.updateName` on the server-side. - // For http.server root spans (which is the root span on the server 99% of the time), Otel's http instrumentation - // calls `span.updateName` and overwrites whatever the name was set to before (by us or by users). - test("calling just `span.updateName` doesn't update the final name in express (missing source)", async () => { - const runner = createRunner(__dirname, 'server.js') - .expect({ - transaction: { - transaction: 'GET /test/:id/span-updateName', - transaction_info: { - source: 'route', - }, - }, - }) - .start(); - runner.makeRequest('get', '/test/123/span-updateName'); - await runner.completed(); - }); - - // Also calling `updateName` AND setting a source doesn't change anything - Otel has no concept of source, this is sentry-internal. - // Therefore, only the source is updated but the name is still overwritten by Otel. - test("calling `span.updateName` and setting attribute source doesn't update the final name in express but it updates the source", async () => { - const runner = createRunner(__dirname, 'server.js') - .expect({ - transaction: { - transaction: 'GET /test/:id/span-updateName-source', - transaction_info: { - source: 'custom', - }, - }, - }) - .start(); - runner.makeRequest('get', '/test/123/span-updateName-source'); - await runner.completed(); - }); - - // This test documents the correct way to update the span name (and implicitly the source) in Node: - test('calling `Sentry.updateSpanName` updates the final name and source in express', async () => { - const runner = createRunner(__dirname, 'server.js') - .expect({ - transaction: txnEvent => { - expect(txnEvent).toMatchObject({ - transaction: 'new-name', - transaction_info: { - source: 'custom', - }, - contexts: { - trace: { - op: 'http.server', - data: { [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'custom' }, - }, - }, - }); - // ensure we delete the internal attribute once we're done with it - expect(txnEvent.contexts?.trace?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME]).toBeUndefined(); - }, - }) - .start(); - runner.makeRequest('get', '/test/123/updateSpanName'); - await runner.completed(); - }); - }); - - // This test documents the correct way to update the span name (and implicitly the source) in Node: - test('calling `Sentry.updateSpanName` and setting source subsequently updates the final name and sets correct source', async () => { - const runner = createRunner(__dirname, 'server.js') - .expect({ - transaction: txnEvent => { - expect(txnEvent).toMatchObject({ - transaction: 'new-name', - transaction_info: { - source: 'component', - }, - contexts: { - trace: { - op: 'http.server', - data: { [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component' }, - }, - }, - }); - // ensure we delete the internal attribute once we're done with it - expect(txnEvent.contexts?.trace?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME]).toBeUndefined(); - }, - }) - .start(); - runner.makeRequest('get', '/test/123/updateSpanNameAndSource'); - await runner.completed(); - }); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/tracing/withError/server.js b/dev-packages/node-integration-tests/suites/express-v5/tracing/withError/server.js deleted file mode 100644 index d9ccc80fb7ad..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/tracing/withError/server.js +++ /dev/null @@ -1,30 +0,0 @@ -const { loggingTransport } = require('@sentry-internal/node-integration-tests'); -const Sentry = require('@sentry/node'); - -Sentry.init({ - debug: true, - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - // disable attaching headers to /test/* endpoints - tracePropagationTargets: [/^(?!.*test).*$/], - tracesSampleRate: 1.0, - transport: loggingTransport, -}); - -// express must be required after Sentry is initialized -const express = require('express'); -const cors = require('cors'); -const { startExpressServerAndSendPortToRunner } = require('@sentry-internal/node-integration-tests'); - -const app = express(); - -app.use(cors()); - -app.get('/test/:id1/:id2', (_req, res) => { - Sentry.captureException(new Error('error_1')); - res.send('Success'); -}); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/tracing/withError/test.ts b/dev-packages/node-integration-tests/suites/express-v5/tracing/withError/test.ts deleted file mode 100644 index 34d8cd515ec3..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/tracing/withError/test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { afterAll, describe, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; - -describe('express v5 tracing', () => { - afterAll(() => { - cleanupChildProcesses(); - }); - - describe('CJS', () => { - test('should apply the scope transactionName to error events', async () => { - const runner = createRunner(__dirname, 'server.js') - .ignore('transaction') - .expect({ - event: { - exception: { - values: [ - { - value: 'error_1', - }, - ], - }, - transaction: 'GET /test/:id1/:id2', - }, - }) - .start(); - runner.makeRequest('get', '/test/123/abc?q=1'); - await runner.completed(); - }); - }); -}); diff --git a/dev-packages/node-integration-tests/suites/express-v5/tsconfig.test.json b/dev-packages/node-integration-tests/suites/express-v5/tsconfig.test.json deleted file mode 100644 index 3c43903cfdd1..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/tsconfig.test.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../tsconfig.json" -} diff --git a/dev-packages/node-integration-tests/suites/express-v5/without-tracing/server.ts b/dev-packages/node-integration-tests/suites/express-v5/without-tracing/server.ts deleted file mode 100644 index 222566bc945b..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/without-tracing/server.ts +++ /dev/null @@ -1,39 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - transport: loggingTransport, -}); - -import bodyParser from 'body-parser'; -import express from 'express'; - -const app = express(); - -app.use(bodyParser.json()); -app.use(bodyParser.text()); -app.use(bodyParser.raw()); - -Sentry.setTag('global', 'tag'); - -app.get('/test/isolationScope/:id', (req, res) => { - const id = req.params.id; - Sentry.setTag('isolation-scope', 'tag'); - Sentry.setTag(`isolation-scope-${id}`, id); - - Sentry.captureException(new Error('This is an exception')); - - res.send({}); -}); - -app.post('/test-post', function (req, res) { - Sentry.captureException(new Error('This is an exception')); - - res.send({ status: 'ok', body: req.body }); -}); - -Sentry.setupExpressErrorHandler(app); - -startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express-v5/without-tracing/test.ts b/dev-packages/node-integration-tests/suites/express-v5/without-tracing/test.ts deleted file mode 100644 index 5286ab8d2953..000000000000 --- a/dev-packages/node-integration-tests/suites/express-v5/without-tracing/test.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { afterAll, describe, expect, test } from 'vitest'; -import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; - -afterAll(() => { - cleanupChildProcesses(); -}); - -describe('express without tracing', () => { - test('correctly applies isolation scope even without tracing', async () => { - const runner = createRunner(__dirname, 'server.ts') - .expect({ - event: { - transaction: 'GET /test/isolationScope/1', - tags: { - global: 'tag', - 'isolation-scope': 'tag', - 'isolation-scope-1': '1', - }, - // Request is correctly set - request: { - url: expect.stringMatching(/^http:\/\/localhost:(\d+)\/test\/isolationScope\/1$/), - method: 'GET', - headers: { - 'user-agent': expect.stringContaining(''), - }, - }, - }, - }) - .start(); - - runner.makeRequest('get', '/test/isolationScope/1'); - await runner.completed(); - }); - - describe('request data', () => { - test('correctly captures JSON request data', async () => { - const runner = createRunner(__dirname, 'server.ts') - .expect({ - event: { - transaction: 'POST /test-post', - request: { - url: expect.stringMatching(/^http:\/\/localhost:(\d+)\/test-post$/), - method: 'POST', - headers: { - 'user-agent': expect.stringContaining(''), - 'content-type': 'application/json', - }, - data: JSON.stringify({ - foo: 'bar', - other: 1, - }), - }, - }, - }) - .start(); - - runner.makeRequest('post', '/test-post', { - headers: { - 'Content-Type': 'application/json', - }, - data: JSON.stringify({ foo: 'bar', other: 1 }), - }); - await runner.completed(); - }); - - test('correctly captures plain text request data', async () => { - const runner = createRunner(__dirname, 'server.ts') - .expect({ - event: { - transaction: 'POST /test-post', - request: { - url: expect.stringMatching(/^http:\/\/localhost:(\d+)\/test-post$/), - method: 'POST', - headers: { - 'user-agent': expect.stringContaining(''), - 'content-type': 'text/plain', - }, - data: 'some plain text', - }, - }, - }) - .start(); - - runner.makeRequest('post', '/test-post', { - headers: { - 'Content-Type': 'text/plain', - }, - data: 'some plain text', - }); - await runner.completed(); - }); - - test('correctly captures text buffer request data', async () => { - const runner = createRunner(__dirname, 'server.ts') - .expect({ - event: { - transaction: 'POST /test-post', - request: { - url: expect.stringMatching(/^http:\/\/localhost:(\d+)\/test-post$/), - method: 'POST', - headers: { - 'user-agent': expect.stringContaining(''), - 'content-type': 'application/octet-stream', - }, - data: 'some plain text in buffer', - }, - }, - }) - .start(); - - runner.makeRequest('post', '/test-post', { - headers: { 'Content-Type': 'application/octet-stream' }, - data: Buffer.from('some plain text in buffer'), - }); - await runner.completed(); - }); - - test('correctly captures non-text buffer request data', async () => { - const runner = createRunner(__dirname, 'server.ts') - .expect({ - event: { - transaction: 'POST /test-post', - request: { - url: expect.stringMatching(/^http:\/\/localhost:(\d+)\/test-post$/), - method: 'POST', - headers: { - 'user-agent': expect.stringContaining(''), - 'content-type': 'application/octet-stream', - }, - // This is some non-ascii string representation - data: expect.any(String), - }, - }, - }) - .start(); - - const body = new Uint8Array([1, 2, 3, 4, 5]).buffer; - - runner.makeRequest('post', '/test-post', { headers: { 'Content-Type': 'application/octet-stream' }, data: body }); - await runner.completed(); - }); - }); -});