From 691f523833708cda70f158f657bb3f2d337ae361 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Thu, 9 Jul 2026 09:02:20 +0200 Subject: [PATCH 1/8] test: Avoid re-running docker compose for node-integration-tests --- .../suites/tracing/amqplib/test.ts | 13 +- .../suites/tracing/ioredis-dc/test.ts | 187 ++++++++-------- .../suites/tracing/kafkajs/test.ts | 12 +- .../suites/tracing/knex/mysql2/scenario.mjs | 1 + .../suites/tracing/knex/mysql2/test.ts | 10 +- .../suites/tracing/knex/pg/scenario.mjs | 1 + .../suites/tracing/knex/pg/test.ts | 10 +- .../tracing/mysql2-tracing-channel/test.ts | 94 ++++---- .../suites/tracing/mysql2/test.ts | 12 +- .../postgres-streamed/scenario-native.mjs | 14 +- .../tracing/postgres-streamed/scenario.mjs | 14 +- .../suites/tracing/postgres-streamed/test.ts | 19 +- .../tracing/postgres/scenario-native.mjs | 14 +- .../suites/tracing/postgres/scenario.mjs | 18 +- .../suites/tracing/postgres/test.ts | 56 +---- .../postgresjs/scenario-requestHook.mjs | 12 +- .../tracing/postgresjs/scenario-unsafe.mjs | 9 +- .../tracing/postgresjs/scenario-url.mjs | 19 +- .../suites/tracing/postgresjs/scenario.mjs | 19 +- .../suites/tracing/postgresjs/test.ts | 118 ++++------ .../tracing/postgresjs/wait-for-postgres.js | 22 -- .../suites/tracing/redis-cache/test.ts | 18 +- .../suites/tracing/redis-dc/test.ts | 203 +++++++++--------- .../suites/tracing/redis/test.ts | 12 +- .../suites/tracing/tedious/test.ts | 12 +- .../node-integration-tests/utils/index.ts | 10 +- .../utils/runner/createRunner.ts | 4 +- .../utils/runner/describeWithDockerCompose.ts | 58 +++++ .../utils/runner/index.ts | 1 + 29 files changed, 453 insertions(+), 539 deletions(-) delete mode 100644 dev-packages/node-integration-tests/suites/tracing/postgresjs/wait-for-postgres.js create mode 100644 dev-packages/node-integration-tests/utils/runner/describeWithDockerCompose.ts diff --git a/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts b/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts index d882b03f05b8..de30dd3f6bd7 100644 --- a/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts @@ -1,7 +1,7 @@ import type { TransactionEvent } from '@sentry/core'; import { afterAll, describe, expect } from 'vitest'; import { isOrchestrionEnabled } from '../../../utils'; -import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; +import { cleanupChildProcesses, createEsmAndCjsTests, describeWithDockerCompose } from '../../../utils/runner'; // The span origin depends on which instrumentation is active. These blocks drive the SDK's default // integrations, so when the generic orchestrion run is enabled (via INJECT_ORCHESTRION) the OTel @@ -73,7 +73,7 @@ const EXPECTED_MESSAGE_SPAN_CONSUMER = expect.objectContaining({ status: 'ok', }); -describe('amqplib auto-instrumentation', () => { +describeWithDockerCompose('amqplib auto-instrumentation', { workingDirectory: [__dirname] }, () => { afterAll(async () => { cleanupChildProcesses(); }); @@ -93,9 +93,6 @@ describe('amqplib auto-instrumentation', () => { const receivedTransactions: TransactionEvent[] = []; await createTestRunner() - .withDockerCompose({ - workingDirectory: [__dirname], - }) .expect({ transaction: (transaction: TransactionEvent) => { receivedTransactions.push(transaction); @@ -145,9 +142,6 @@ describe('amqplib auto-instrumentation', () => { const receivedTransactions: TransactionEvent[] = []; await createTestRunner() - .withDockerCompose({ - workingDirectory: [__dirname], - }) .expect({ transaction: (transaction: TransactionEvent) => { receivedTransactions.push(transaction); @@ -194,9 +188,6 @@ describe('amqplib auto-instrumentation', () => { { timeout: 60_000 }, async () => { await createTestRunner() - .withDockerCompose({ - workingDirectory: [__dirname], - }) .expect({ transaction: (transaction: TransactionEvent) => { expect(transaction.transaction).toBe('root span'); diff --git a/dev-packages/node-integration-tests/suites/tracing/ioredis-dc/test.ts b/dev-packages/node-integration-tests/suites/tracing/ioredis-dc/test.ts index 74abb6c7df41..61fe42a02738 100644 --- a/dev-packages/node-integration-tests/suites/tracing/ioredis-dc/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/ioredis-dc/test.ts @@ -1,104 +1,107 @@ -import { afterAll, describe, expect } from 'vitest'; -import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; +import { afterAll, expect } from 'vitest'; +import { cleanupChildProcesses, createEsmAndCjsTests, describeWithDockerCompose } from '../../../utils/runner'; -describe('ioredis v5.11 diagnostics_channel auto instrumentation', () => { - afterAll(() => { - cleanupChildProcesses(); - }); +describeWithDockerCompose( + 'ioredis v5.11 diagnostics_channel auto instrumentation', + { workingDirectory: [__dirname] }, + () => { + afterAll(() => { + cleanupChildProcesses(); + }); - const EXPECTED_TRANSACTION = { - transaction: 'Test Span IORedis 5.11 DC', - spans: expect.arrayContaining([ - expect.objectContaining({ - op: 'db.redis', - origin: 'auto.db.redis.diagnostic_channel', - data: expect.objectContaining({ - 'sentry.op': 'db.redis', - 'sentry.origin': 'auto.db.redis.diagnostic_channel', - 'db.system.name': 'redis', - 'db.query.text': 'set dc-test-key ?', + const EXPECTED_TRANSACTION = { + transaction: 'Test Span IORedis 5.11 DC', + spans: expect.arrayContaining([ + expect.objectContaining({ + op: 'db.redis', + origin: 'auto.db.redis.diagnostic_channel', + data: expect.objectContaining({ + 'sentry.op': 'db.redis', + 'sentry.origin': 'auto.db.redis.diagnostic_channel', + 'db.system.name': 'redis', + 'db.query.text': 'set dc-test-key ?', + }), }), - }), - expect.objectContaining({ - description: 'dc-cache:test-key', - op: 'cache.put', - origin: 'auto.db.redis.diagnostic_channel', - data: expect.objectContaining({ - 'sentry.origin': 'auto.db.redis.diagnostic_channel', - 'db.query.text': 'set dc-cache:test-key ?', - 'cache.key': ['dc-cache:test-key'], - 'cache.item_size': 2, + expect.objectContaining({ + description: 'dc-cache:test-key', + op: 'cache.put', + origin: 'auto.db.redis.diagnostic_channel', + data: expect.objectContaining({ + 'sentry.origin': 'auto.db.redis.diagnostic_channel', + 'db.query.text': 'set dc-cache:test-key ?', + 'cache.key': ['dc-cache:test-key'], + 'cache.item_size': 2, + }), }), - }), - expect.objectContaining({ - description: 'dc-cache:test-key-ex', - op: 'cache.put', - origin: 'auto.db.redis.diagnostic_channel', - data: expect.objectContaining({ - 'sentry.origin': 'auto.db.redis.diagnostic_channel', - 'db.query.text': 'set dc-cache:test-key-ex ? ? ?', - 'cache.key': ['dc-cache:test-key-ex'], - 'cache.item_size': 2, + expect.objectContaining({ + description: 'dc-cache:test-key-ex', + op: 'cache.put', + origin: 'auto.db.redis.diagnostic_channel', + data: expect.objectContaining({ + 'sentry.origin': 'auto.db.redis.diagnostic_channel', + 'db.query.text': 'set dc-cache:test-key-ex ? ? ?', + 'cache.key': ['dc-cache:test-key-ex'], + 'cache.item_size': 2, + }), }), - }), - expect.objectContaining({ - op: 'db.redis', - origin: 'auto.db.redis.diagnostic_channel', - data: expect.objectContaining({ - 'sentry.op': 'db.redis', - 'sentry.origin': 'auto.db.redis.diagnostic_channel', - 'db.system.name': 'redis', - 'db.query.text': 'get dc-test-key', + expect.objectContaining({ + op: 'db.redis', + origin: 'auto.db.redis.diagnostic_channel', + data: expect.objectContaining({ + 'sentry.op': 'db.redis', + 'sentry.origin': 'auto.db.redis.diagnostic_channel', + 'db.system.name': 'redis', + 'db.query.text': 'get dc-test-key', + }), }), - }), - expect.objectContaining({ - description: 'dc-cache:test-key', - op: 'cache.get', - origin: 'auto.db.redis.diagnostic_channel', - data: expect.objectContaining({ - 'sentry.origin': 'auto.db.redis.diagnostic_channel', - 'db.query.text': 'get dc-cache:test-key', - 'cache.hit': true, - 'cache.key': ['dc-cache:test-key'], - 'cache.item_size': 10, + expect.objectContaining({ + description: 'dc-cache:test-key', + op: 'cache.get', + origin: 'auto.db.redis.diagnostic_channel', + data: expect.objectContaining({ + 'sentry.origin': 'auto.db.redis.diagnostic_channel', + 'db.query.text': 'get dc-cache:test-key', + 'cache.hit': true, + 'cache.key': ['dc-cache:test-key'], + 'cache.item_size': 10, + }), }), - }), - expect.objectContaining({ - description: 'dc-cache:unavailable-data', - op: 'cache.get', - origin: 'auto.db.redis.diagnostic_channel', - data: expect.objectContaining({ - 'sentry.origin': 'auto.db.redis.diagnostic_channel', - 'db.query.text': 'get dc-cache:unavailable-data', - 'cache.hit': false, - 'cache.key': ['dc-cache:unavailable-data'], + expect.objectContaining({ + description: 'dc-cache:unavailable-data', + op: 'cache.get', + origin: 'auto.db.redis.diagnostic_channel', + data: expect.objectContaining({ + 'sentry.origin': 'auto.db.redis.diagnostic_channel', + 'db.query.text': 'get dc-cache:unavailable-data', + 'cache.hit': false, + 'cache.key': ['dc-cache:unavailable-data'], + }), }), - }), - expect.objectContaining({ - op: 'db.redis', - origin: 'auto.db.redis.diagnostic_channel', - data: expect.objectContaining({ - 'sentry.op': 'db.redis', - 'sentry.origin': 'auto.db.redis.diagnostic_channel', - 'db.system.name': 'redis', - 'db.query.text': 'mget ? ? ?', + expect.objectContaining({ + op: 'db.redis', + origin: 'auto.db.redis.diagnostic_channel', + data: expect.objectContaining({ + 'sentry.op': 'db.redis', + 'sentry.origin': 'auto.db.redis.diagnostic_channel', + 'db.system.name': 'redis', + 'db.query.text': 'mget ? ? ?', + }), }), - }), - ]), - }; + ]), + }; - const EXPECTED_CONNECT = { - transaction: 'redis-connect', - }; + const EXPECTED_CONNECT = { + transaction: 'redis-connect', + }; - createEsmAndCjsTests(__dirname, 'scenario-ioredis-5-11.mjs', 'instrument.mjs', (createTestRunner, test) => { - test('creates spans for ioredis v5.11 commands via diagnostics_channel', { timeout: 75_000 }, async () => { - await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) - .expect({ transaction: EXPECTED_CONNECT }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); + createEsmAndCjsTests(__dirname, 'scenario-ioredis-5-11.mjs', 'instrument.mjs', (createTestRunner, test) => { + test('creates spans for ioredis v5.11 commands via diagnostics_channel', { timeout: 75_000 }, async () => { + await createTestRunner() + .expect({ transaction: EXPECTED_CONNECT }) + .expect({ transaction: EXPECTED_TRANSACTION }) + .start() + .completed(); + }); }); - }); -}); + }, +); diff --git a/dev-packages/node-integration-tests/suites/tracing/kafkajs/test.ts b/dev-packages/node-integration-tests/suites/tracing/kafkajs/test.ts index 0d87e5e69ded..ee017195f1fc 100644 --- a/dev-packages/node-integration-tests/suites/tracing/kafkajs/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/kafkajs/test.ts @@ -1,8 +1,8 @@ import type { TransactionEvent } from '@sentry/core'; -import { afterAll, describe, expect } from 'vitest'; -import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; +import { afterAll, expect } from 'vitest'; +import { cleanupChildProcesses, createEsmAndCjsTests, describeWithDockerCompose } from '../../../utils/runner'; -describe('kafkajs', () => { +describeWithDockerCompose('kafkajs', { workingDirectory: [__dirname] }, () => { afterAll(() => { cleanupChildProcesses(); }); @@ -14,9 +14,6 @@ describe('kafkajs', () => { const receivedTransactions: TransactionEvent[] = []; await createRunner() - .withDockerCompose({ - workingDirectory: [__dirname], - }) .expect({ transaction: (transaction: TransactionEvent) => { receivedTransactions.push(transaction); @@ -87,9 +84,6 @@ describe('kafkajs', () => { createEsmAndCjsTests(__dirname, 'scenario-error.mjs', 'instrument.mjs', (createRunner, test) => { test('marks the producer span as errored when a send fails', { timeout: 90_000 }, async () => { await createRunner() - .withDockerCompose({ - workingDirectory: [__dirname], - }) .expect({ transaction: (transaction: TransactionEvent) => { expect(transaction.transaction).toBe('send invalid topic name'); diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/scenario.mjs b/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/scenario.mjs index 04fa38ebf0b9..5c6854735844 100644 --- a/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/scenario.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/scenario.mjs @@ -40,6 +40,7 @@ async function run() { await mysql2Client('User').insert({ name: 'jane', email: 'jane@domain.com' }); await mysql2Client('User').select('*'); + await mysql2Client.schema.dropTable('User'); } finally { await mysql2Client.destroy(); } diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/test.ts b/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/test.ts index 01db0bf6e88a..2d8c798e5e6f 100644 --- a/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/test.ts @@ -1,7 +1,7 @@ import { describe, expect } from 'vitest'; -import { createEsmAndCjsTests } from '../../../../utils/runner'; +import { createEsmAndCjsTests, describeWithDockerCompose } from '../../../../utils/runner'; -describe('knex auto instrumentation', () => { +describeWithDockerCompose('knex auto instrumentation', { workingDirectory: [__dirname] }, () => { // Update this if another knex version is installed const KNEX_VERSION = '2.5.1'; @@ -62,11 +62,7 @@ describe('knex auto instrumentation', () => { ]), }; - await createRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); + await createRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); }); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/pg/scenario.mjs b/dev-packages/node-integration-tests/suites/tracing/knex/pg/scenario.mjs index e8f21b377e13..0a75a99955e6 100644 --- a/dev-packages/node-integration-tests/suites/tracing/knex/pg/scenario.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/knex/pg/scenario.mjs @@ -34,6 +34,7 @@ async function run() { await pgClient('DoesNotExist') .select('*') .catch(() => {}); + await pgClient.schema.dropTable('User'); } finally { await pgClient.destroy(); } diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/pg/test.ts b/dev-packages/node-integration-tests/suites/tracing/knex/pg/test.ts index f2defdada93f..2a00286f8152 100644 --- a/dev-packages/node-integration-tests/suites/tracing/knex/pg/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/knex/pg/test.ts @@ -1,11 +1,11 @@ import { describe, expect } from 'vitest'; -import { createEsmAndCjsTests } from '../../../../utils/runner'; +import { createEsmAndCjsTests, describeWithDockerCompose } from '../../../../utils/runner'; describe('knex auto instrumentation', () => { // Update this if another knex version is installed const KNEX_VERSION = '2.5.1'; - describe('with `pg` client', () => { + describeWithDockerCompose('with `pg` client', { workingDirectory: [__dirname] }, () => { createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createRunner, test) => { test('should auto-instrument `knex` package', { timeout: 60_000 }, async () => { const EXPECTED_TRANSACTION = { @@ -76,11 +76,7 @@ describe('knex auto instrumentation', () => { ]), }; - await createRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); + await createRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); }); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/mysql2-tracing-channel/test.ts b/dev-packages/node-integration-tests/suites/tracing/mysql2-tracing-channel/test.ts index 627ded3164fe..7971974f9ca4 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mysql2-tracing-channel/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/mysql2-tracing-channel/test.ts @@ -1,6 +1,6 @@ import { afterAll, expect } from 'vitest'; import { conditionalTest } from '../../../utils'; -import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; +import { cleanupChildProcesses, createEsmAndCjsTests, describeWithDockerCompose } from '../../../utils/runner'; // mysql2 >= 3.20.0 publishes its operations via `node:diagnostics_channel`, so the SDK subscribes // to those channels (`subscribeMysql2DiagnosticChannels`) instead of monkey-patching. This suite @@ -45,55 +45,51 @@ conditionalTest({ min: 20 })('mysql2 tracing channel Test', () => { ]), }; - createEsmAndCjsTests( - __dirname, - 'scenario.mjs', - 'instrument.mjs', - (createTestRunner, test) => { - test('subscribes to mysql2 >= 3.20.0 diagnostics channels with stable semconv attributes', async () => { - await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); - }, 30_000); + describeWithDockerCompose('with pg docker compose', { workingDirectory: [__dirname] }, () => { + createEsmAndCjsTests( + __dirname, + 'scenario.mjs', + 'instrument.mjs', + (createTestRunner, test) => { + test('subscribes to mysql2 >= 3.20.0 diagnostics channels with stable semconv attributes', async () => { + await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); + }, 30_000); - test('does not double-instrument: the legacy IITM mysql2 patcher does not fire on 3.20.0+', async () => { - await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) - .expect({ - transaction: event => { - expect(event.transaction).toBe('Test Transaction'); - const spans = event.spans || []; - // The monkey-patch path (origin `auto.db.otel.mysql2`) must be inactive on 3.20.0+. - expect(spans.find(span => span.origin === 'auto.db.otel.mysql2')).toBeUndefined(); - // ...while the diagnostics-channel path is active. - expect(spans.find(span => span.origin === 'auto.db.mysql2.diagnostic_channel')).toBeDefined(); - }, - }) - .start() - .completed(); - }, 30_000); + test('does not double-instrument: the legacy IITM mysql2 patcher does not fire on 3.20.0+', async () => { + await createTestRunner() + .expect({ + transaction: event => { + expect(event.transaction).toBe('Test Transaction'); + const spans = event.spans || []; + // The monkey-patch path (origin `auto.db.otel.mysql2`) must be inactive on 3.20.0+. + expect(spans.find(span => span.origin === 'auto.db.otel.mysql2')).toBeUndefined(); + // ...while the diagnostics-channel path is active. + expect(spans.find(span => span.origin === 'auto.db.mysql2.diagnostic_channel')).toBeDefined(); + }, + }) + .start() + .completed(); + }, 30_000); - test('never leaks raw values into db.query.text', async () => { - await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) - .expect({ - transaction: event => { - expect(event.transaction).toBe('Test Transaction'); - const spans = event.spans || []; - for (const span of spans) { - const queryText = span.data?.['db.query.text']; - if (typeof queryText === 'string') { - expect(queryText).not.toContain('super-secret'); + test('never leaks raw values into db.query.text', async () => { + await createTestRunner() + .expect({ + transaction: event => { + expect(event.transaction).toBe('Test Transaction'); + const spans = event.spans || []; + for (const span of spans) { + const queryText = span.data?.['db.query.text']; + if (typeof queryText === 'string') { + expect(queryText).not.toContain('super-secret'); + } } - } - }, - }) - .start() - .completed(); - }, 30_000); - }, - { additionalDependencies: { mysql2: '^3.20.0' } }, - ); + }, + }) + .start() + .completed(); + }, 30_000); + }, + { additionalDependencies: { mysql2: '^3.20.0' } }, + ); + }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/mysql2/test.ts b/dev-packages/node-integration-tests/suites/tracing/mysql2/test.ts index 458b91ccdc52..eb63e1cf10b7 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mysql2/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/mysql2/test.ts @@ -1,7 +1,7 @@ -import { afterAll, describe, expect } from 'vitest'; -import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; +import { afterAll, expect } from 'vitest'; +import { cleanupChildProcesses, createEsmAndCjsTests, describeWithDockerCompose } from '../../../utils/runner'; -describe('mysql2 auto instrumentation', () => { +describeWithDockerCompose('mysql2 auto instrumentation', { workingDirectory: [__dirname] }, () => { afterAll(() => { cleanupChildProcesses(); }); @@ -59,11 +59,7 @@ describe('mysql2 auto instrumentation', () => { createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createTestRunner, test) => { test('should auto-instrument `mysql` package without connection.connect()', { timeout: 75_000 }, async () => { - await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); + await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); }); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/scenario-native.mjs b/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/scenario-native.mjs index 4c32b238b78b..9b540ed3fa3e 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/scenario-native.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/scenario-native.mjs @@ -28,17 +28,15 @@ async function run() { try { await client.connect(); - await client - .query( - 'CREATE TABLE "NativeUser" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id"));', - ) - .catch(() => { - // if this is not a fresh database, the table might already exist - }); + await client.query( + 'CREATE TABLE "NativeUser" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id"));', + ); - await client.query('INSERT INTO "NativeUser" ("email", "name") VALUES ($1, $2)', ['tim', 'tim@domain.com']); + const email = `${crypto.randomUUID()}@domain.com`; + await client.query('INSERT INTO "NativeUser" ("email", "name") VALUES ($1, $2)', [email, 'tim']); await client.query('SELECT * FROM "NativeUser"'); } finally { + await client.query('DROP TABLE "NativeUser"'); await client.end(); } }, diff --git a/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/scenario.mjs b/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/scenario.mjs index a5f96dcac5bd..46d6cfdf0958 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/scenario.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/scenario.mjs @@ -22,17 +22,15 @@ async function run() { try { await client.connect(); - await client - .query( - 'CREATE TABLE "User" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id"));', - ) - .catch(() => { - // if this is not a fresh database, the table might already exist - }); + await client.query( + 'CREATE TABLE "User" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id"));', + ); - await client.query('INSERT INTO "User" ("email", "name") VALUES ($1, $2)', ['tim', 'tim@domain.com']); + const email = `${crypto.randomUUID()}@domain.com`; + await client.query('INSERT INTO "User" ("email", "name") VALUES ($1, $2)', [email, 'tim']); await client.query('SELECT * FROM "User"'); } finally { + await client.query('DROP TABLE "User"'); await client.end(); } }, diff --git a/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/test.ts b/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/test.ts index 82496d3565c0..8813b129b134 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/test.ts @@ -3,7 +3,7 @@ import type { SerializedStreamedSpanContainer } from '@sentry/core'; import { SENTRY_TRACE_LIFECYCLE } from '@sentry/conventions/attributes'; import { afterAll, describe, expect } from 'vitest'; import { conditionalTest, isOrchestrionEnabled } from '../../../utils'; -import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; +import { cleanupChildProcesses, createEsmAndCjsTests, describeWithDockerCompose } from '../../../utils/runner'; // Query-span origin depends on which instrumentation is active. Blocks driving the SDK's default // integrations get the diagnostics-channel origin when the generic orchestrion run is enabled (via @@ -159,7 +159,7 @@ function getDbSpans(container: SerializedStreamedSpanContainer): SerializedStrea return container.items.filter(item => item.attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP]?.value === 'db'); } -describe('postgres auto instrumentation (streamed)', () => { +describeWithDockerCompose('postgres auto instrumentation (streamed)', { workingDirectory: [__dirname] }, () => { afterAll(() => { cleanupChildProcesses(); }); @@ -168,16 +168,13 @@ describe('postgres auto instrumentation (streamed)', () => { createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createTestRunner, test) => { test('should auto-instrument `pg` package with span streaming enabled', { timeout: 90_000 }, async () => { await createTestRunner() - .withDockerCompose({ - workingDirectory: [__dirname], - }) .expect({ span: container => { const segmentSpan = container.items.find(item => item.is_segment); expect(segmentSpan?.name).toBe('Test Span'); const dbSpans = getDbSpans(container); - expect(dbSpans.length).toBe(4); + expect(dbSpans.length).toBe(5); expect(dbSpans).toEqual([ expectedDbSpan({ name: 'pg.connect' }), @@ -187,6 +184,7 @@ describe('postgres auto instrumentation (streamed)', () => { statement: 'INSERT INTO "User" ("email", "name") VALUES ($1, $2)', }), expectedDbSpan({ name: 'SELECT * FROM "User"', statement: 'SELECT * FROM "User"' }), + expectedDbSpan({ name: 'DROP TABLE "User"', statement: 'DROP TABLE "User"' }), ]); }, }) @@ -200,14 +198,11 @@ describe('postgres auto instrumentation (streamed)', () => { createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument-ignoreConnect.mjs', (createTestRunner, test) => { test("doesn't emit connect spans if ignoreConnectSpans is true", { timeout: 90_000 }, async () => { await createTestRunner() - .withDockerCompose({ - workingDirectory: [__dirname], - }) .expect({ span: container => { const dbSpans = getDbSpans(container); expect(dbSpans.find(span => span.name.includes('connect'))).toBeUndefined(); - expect(dbSpans.length).toBe(3); + expect(dbSpans.length).toBe(4); // This block passes an explicit `postgresIntegration({ ignoreConnectSpans: true })`, which // survives the orchestrion swap, so query spans keep the OTel origin even under INJECT_ORCHESTRION. @@ -220,6 +215,7 @@ describe('postgres auto instrumentation (streamed)', () => { origin, }), expectedDbSpan({ name: 'SELECT * FROM "User"', statement: 'SELECT * FROM "User"', origin }), + expectedDbSpan({ name: 'DROP TABLE "User"', statement: 'DROP TABLE "User"' }), ]); }, }) @@ -240,9 +236,6 @@ describe('postgres auto instrumentation (streamed)', () => { { timeout: 120_000 }, async () => { await createTestRunner() - .withDockerCompose({ - workingDirectory: [__dirname], - }) .expect({ span: container => { const segmentSpan = container.items.find(item => item.is_segment); diff --git a/dev-packages/node-integration-tests/suites/tracing/postgres/scenario-native.mjs b/dev-packages/node-integration-tests/suites/tracing/postgres/scenario-native.mjs index eef378bb13fa..e9200bff0f35 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres/scenario-native.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/postgres/scenario-native.mjs @@ -28,17 +28,15 @@ async function run() { try { await client.connect(); - await client - .query( - 'CREATE TABLE "NativeUser" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id"));', - ) - .catch(() => { - // if this is not a fresh database, the table might already exist - }); + await client.query( + 'CREATE TABLE "NativeUser" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id"));', + ); - await client.query('INSERT INTO "NativeUser" ("email", "name") VALUES ($1, $2)', ['tim', 'tim@domain.com']); + const email = `${crypto.randomUUID()}@domain.com`; + await client.query('INSERT INTO "NativeUser" ("email", "name") VALUES ($1, $2)', [email, 'tim']); await client.query('SELECT * FROM "NativeUser"'); } finally { + await client.query('DROP TABLE "NativeUser"'); await client.end(); } }, diff --git a/dev-packages/node-integration-tests/suites/tracing/postgres/scenario.mjs b/dev-packages/node-integration-tests/suites/tracing/postgres/scenario.mjs index 29a152f015f2..46ffe62a3f64 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres/scenario.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/postgres/scenario.mjs @@ -22,22 +22,19 @@ async function run() { try { await client.connect(); - await client - .query( - 'CREATE TABLE "User" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id"));', - ) - .catch(() => { - // if this is not a fresh database, the table might already exist - }); - - await client.query('INSERT INTO "User" ("email", "name") VALUES ($1, $2)', ['tim', 'tim@domain.com']); + await client.query( + 'CREATE TABLE "User" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id"));', + ); + + const email = `${crypto.randomUUID()}@domain.com`; + await client.query('INSERT INTO "User" ("email", "name") VALUES ($1, $2)', [email, 'tim']); await client.query('SELECT * FROM "User"'); // A named (prepared) query records its name as the `db.postgresql.plan` attribute await client.query({ name: 'select-user-by-email', text: 'SELECT * FROM "User" WHERE "email" = $1', - values: ['tim'], + values: [email], }); // A failing query should still produce an errored span @@ -45,6 +42,7 @@ async function run() { // swallow: we only care about the span it produces }); } finally { + await client.query('DROP TABLE "User"'); await client.end(); } }, diff --git a/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts b/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts index a042c88c27ed..fdb1cf710501 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts @@ -1,8 +1,8 @@ import { afterAll, describe, expect } from 'vitest'; import { conditionalTest, isOrchestrionEnabled } from '../../../utils'; -import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; +import { cleanupChildProcesses, createEsmAndCjsTests, describeWithDockerCompose } from '../../../utils/runner'; -describe('postgres auto instrumentation', () => { +describeWithDockerCompose('postgres auto instrumentation', { workingDirectory: [__dirname] }, () => { afterAll(() => { cleanupChildProcesses(); }); @@ -86,13 +86,7 @@ describe('postgres auto instrumentation', () => { createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createTestRunner, test) => { test('should auto-instrument `pg` package', { timeout: 90_000 }, async () => { - await createTestRunner() - .withDockerCompose({ - workingDirectory: [__dirname], - }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); + await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); }); }); }); @@ -101,9 +95,6 @@ describe('postgres auto instrumentation', () => { createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument-ignoreConnect.mjs', (createTestRunner, test) => { test("doesn't emit connect spans if ignoreConnectSpans is true", { timeout: 90_000 }, async () => { await createTestRunner() - .withDockerCompose({ - workingDirectory: [__dirname], - }) .expect({ transaction: txn => { const spanNames = txn.spans?.map(span => span.description); @@ -187,13 +178,7 @@ describe('postgres auto instrumentation', () => { 'auto-instruments `pg.Pool`, masks connection-string credentials, and handles callback-style queries', { timeout: 90_000 }, async () => { - await createTestRunner() - .withDockerCompose({ - workingDirectory: [__dirname], - }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); + await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); }, ); }); @@ -251,13 +236,7 @@ describe('postgres auto instrumentation', () => { createEsmAndCjsTests(__dirname, 'scenario-connect-then.mjs', 'instrument.mjs', (createTestRunner, test) => { test('parents a query chained off connect() to the active transaction', { timeout: 90_000 }, async () => { - await createTestRunner() - .withDockerCompose({ - workingDirectory: [__dirname], - }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); + await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); }); }); }); @@ -266,9 +245,6 @@ describe('postgres auto instrumentation', () => { createEsmAndCjsTests(__dirname, 'scenario-no-parent.mjs', 'instrument.mjs', (createTestRunner, test) => { test('does not instrument queries or connects without an active parent span', { timeout: 90_000 }, async () => { await createTestRunner() - .withDockerCompose({ - workingDirectory: [__dirname], - }) .expect({ transaction: txn => { const descriptions = txn.spans?.map(span => span.description) ?? []; @@ -352,13 +328,7 @@ describe('postgres auto instrumentation', () => { 'instrument.mjs', (createTestRunner, test) => { test('should auto-instrument `pg-native` package', { timeout: 120_000 }, async () => { - await createTestRunner() - .withDockerCompose({ - workingDirectory: [__dirname], - }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); + await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); }); }, { additionalDependencies: { 'pg-native': '3.7.0', pg: '8.20.0' } }, @@ -436,11 +406,7 @@ describe('postgres auto instrumentation', () => { 'instrument-orchestrion.mjs', (createTestRunner, test) => { test('auto-instruments `pg` via diagnostics channels', { timeout: 90_000 }, async () => { - await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); + await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); }); }, // This block enables orchestrion itself via its instrument file, so opt out of the generic @@ -487,11 +453,7 @@ describe('postgres auto instrumentation', () => { 'instrument-orchestrion.mjs', (createTestRunner, test) => { test('auto-instruments `pg.Pool` and handles callback-style queries', { timeout: 90_000 }, async () => { - await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); + await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); }); }, // Enables orchestrion itself; opt out of the generic INJECT_ORCHESTRION auto-injection. @@ -542,7 +504,6 @@ describe('postgres auto instrumentation', () => { { timeout: 90_000 }, async () => { await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: txn => { const descriptions = txn.spans?.map(span => span.description) ?? []; @@ -588,7 +549,6 @@ describe('postgres auto instrumentation', () => { { timeout: 90_000 }, async () => { await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: txn => { const spanNames = txn.spans?.map(span => span.description); diff --git a/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-requestHook.mjs b/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-requestHook.mjs index 0914c2e0afcc..41dc1ae36844 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-requestHook.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-requestHook.mjs @@ -1,6 +1,6 @@ import * as Sentry from '@sentry/node'; import postgres from 'postgres'; -import { waitForPostgres } from './wait-for-postgres.js'; +import { waitForConnection } from '@sentry-internal/node-integration-tests'; const sql = postgres({ port: 5444, user: 'test', password: 'test', database: 'test_db' }); @@ -12,23 +12,23 @@ async function run() { }, async () => { try { - await waitForPostgres(sql); + await waitForConnection(() => sql`SELECT 1`); await sql` CREATE TABLE "User" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id")); `; + const email = `${crypto.randomUUID()}@domain.com`; await sql` - INSERT INTO "User" ("email", "name") VALUES ('Foo', 'bar@baz.com'); + INSERT INTO "User" ("email", "name") VALUES (${email}, 'tim'); `; await sql` - SELECT * FROM "User" WHERE "email" = 'bar@baz.com'; + SELECT * FROM "User" WHERE "email" = ${email}; `; - + } finally { await sql` DROP TABLE "User"; `; - } finally { await sql.end(); } }, diff --git a/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-unsafe.mjs b/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-unsafe.mjs index 3265f4ea22cf..bde864938130 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-unsafe.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-unsafe.mjs @@ -1,6 +1,6 @@ import * as Sentry from '@sentry/node'; import postgres from 'postgres'; -import { waitForPostgres } from './wait-for-postgres.js'; +import { waitForConnection } from '@sentry-internal/node-integration-tests'; // Test with plain object options const sql = postgres({ port: 5444, user: 'test', password: 'test', database: 'test_db' }); @@ -13,13 +13,14 @@ async function run() { }, async () => { try { - await waitForPostgres(sql); + await waitForConnection(() => sql`SELECT 1`); // Test sql.unsafe() - this was not being instrumented before the fix await sql.unsafe('CREATE TABLE "User" ("id" SERIAL NOT NULL, "email" TEXT NOT NULL, PRIMARY KEY ("id"))'); - await sql.unsafe('INSERT INTO "User" ("email") VALUES ($1)', ['test@example.com']); + const email = `${crypto.randomUUID()}@domain.com`; + await sql.unsafe('INSERT INTO "User" ("email") VALUES ($1)', [email]); - await sql.unsafe('SELECT * FROM "User" WHERE "email" = $1', ['test@example.com']); + await sql.unsafe('SELECT * FROM "User" WHERE "email" = $1', [email]); await sql.unsafe('DROP TABLE "User"'); diff --git a/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-url.mjs b/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-url.mjs index a21e6e41101b..18d28ccdd7ca 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-url.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-url.mjs @@ -1,6 +1,6 @@ import * as Sentry from '@sentry/node'; import postgres from 'postgres'; -import { waitForPostgres } from './wait-for-postgres.js'; +import { waitForConnection } from '@sentry-internal/node-integration-tests'; // Test URL-based initialization - this is the common pattern that was causing the regression const sql = postgres('postgres://test:test@localhost:5444/test_db'); @@ -13,36 +13,37 @@ async function run() { }, async () => { try { - await waitForPostgres(sql); + await waitForConnection(() => sql`SELECT 1`); await sql` CREATE TABLE "User" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id")); `; + const email = `${crypto.randomUUID()}@domain.com`; await sql` - INSERT INTO "User" ("email", "name") VALUES ('Foo', 'bar@baz.com'); + INSERT INTO "User" ("email", "name") VALUES (${email}, 'tim'); `; await sql` - UPDATE "User" SET "name" = 'Foo' WHERE "email" = 'bar@baz.com'; + UPDATE "User" SET "name" = 'Foo' WHERE "email" = ${email}; `; await sql` - SELECT * FROM "User" WHERE "email" = 'bar@baz.com'; + SELECT * FROM "User" WHERE "email" = ${email}; `; // Test parameterized queries await sql` - SELECT * FROM "User" WHERE "email" = ${'bar@baz.com'} AND "name" = ${'Foo'}; + SELECT * FROM "User" WHERE "email" = ${email} AND "name" = ${'Foo'}; `; // Test DELETE operation await sql` - DELETE FROM "User" WHERE "email" = 'bar@baz.com'; + DELETE FROM "User" WHERE "email" = ${email}; `; // Test INSERT with RETURNING await sql` - INSERT INTO "User" ("email", "name") VALUES ('test@example.com', 'Test User') RETURNING *; + INSERT INTO "User" ("email", "name") VALUES (${email}, 'Test User') RETURNING *; `; // Test cursor-based queries @@ -61,7 +62,7 @@ async function run() { // This will be captured as an error as the table no longer exists await sql` - SELECT * FROM "User" WHERE "email" = 'foo@baz.com'; + SELECT * FROM "User" WHERE "email" = ${email}; `; } finally { await sql.end(); diff --git a/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario.mjs b/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario.mjs index 2334ee36dcf1..ea8fbe921515 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario.mjs @@ -1,6 +1,6 @@ import * as Sentry from '@sentry/node'; import postgres from 'postgres'; -import { waitForPostgres } from './wait-for-postgres.js'; +import { waitForConnection } from '@sentry-internal/node-integration-tests'; const sql = postgres({ port: 5444, user: 'test', password: 'test', database: 'test_db' }); @@ -12,36 +12,37 @@ async function run() { }, async () => { try { - await waitForPostgres(sql); + await waitForConnection(() => sql`SELECT 1`); await sql` CREATE TABLE "User" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id")); `; + const email = `${crypto.randomUUID()}@domain.com`; await sql` - INSERT INTO "User" ("email", "name") VALUES ('Foo', 'bar@baz.com'); + INSERT INTO "User" ("email", "name") VALUES (${email}, 'tim'); `; await sql` - UPDATE "User" SET "name" = 'Foo' WHERE "email" = 'bar@baz.com'; + UPDATE "User" SET "name" = 'Foo' WHERE "email" = ${email}; `; await sql` - SELECT * FROM "User" WHERE "email" = 'bar@baz.com'; + SELECT * FROM "User" WHERE "email" = ${email}; `; // Test parameterized queries await sql` - SELECT * FROM "User" WHERE "email" = ${'bar@baz.com'} AND "name" = ${'Foo'}; + SELECT * FROM "User" WHERE "email" = ${email} AND "name" = ${'Foo'}; `; // Test DELETE operation await sql` - DELETE FROM "User" WHERE "email" = 'bar@baz.com'; + DELETE FROM "User" WHERE "email" = ${email}; `; // Test INSERT with RETURNING await sql` - INSERT INTO "User" ("email", "name") VALUES ('test@example.com', 'Test User') RETURNING *; + INSERT INTO "User" ("email", "name") VALUES (${email}, 'Test User') RETURNING *; `; // Test cursor-based queries @@ -60,7 +61,7 @@ async function run() { // This will be captured as an error as the table no longer exists await sql` - SELECT * FROM "User" WHERE "email" = 'foo@baz.com'; + SELECT * FROM "User" WHERE "email" = ${email}; `; } finally { await sql.end(); diff --git a/dev-packages/node-integration-tests/suites/tracing/postgresjs/test.ts b/dev-packages/node-integration-tests/suites/tracing/postgresjs/test.ts index 535135b2ddc4..f0da918c9b89 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgresjs/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/postgresjs/test.ts @@ -1,8 +1,8 @@ import { afterAll, describe, expect } from 'vitest'; import { isOrchestrionEnabled } from '../../../utils'; -import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; +import { cleanupChildProcesses, createEsmAndCjsTests, describeWithDockerCompose } from '../../../utils/runner'; -describe('postgresjs auto instrumentation', () => { +describeWithDockerCompose('postgresjs auto instrumentation', { workingDirectory: [__dirname] }, () => { afterAll(() => { cleanupChildProcesses(); }); @@ -44,13 +44,13 @@ describe('postgresjs auto instrumentation', () => { 'db.namespace': 'test_db', 'db.system.name': 'postgres', 'db.operation.name': 'INSERT', - 'db.query.text': 'INSERT INTO "User" ("email", "name") VALUES (?, ?)', + 'db.query.text': 'INSERT INTO "User" ("email", "name") VALUES ($1, ?)', 'sentry.origin': ORIGIN, 'sentry.op': 'db', 'server.address': 'localhost', 'server.port': 5444, }), - description: 'INSERT INTO "User" ("email", "name") VALUES (?, ?)', + description: 'INSERT INTO "User" ("email", "name") VALUES ($1, ?)', op: 'db', status: 'ok', origin: ORIGIN, @@ -65,13 +65,13 @@ describe('postgresjs auto instrumentation', () => { 'db.namespace': 'test_db', 'db.system.name': 'postgres', 'db.operation.name': 'UPDATE', - 'db.query.text': 'UPDATE "User" SET "name" = ? WHERE "email" = ?', + 'db.query.text': 'UPDATE "User" SET "name" = ? WHERE "email" = $1', 'sentry.op': 'db', 'sentry.origin': ORIGIN, 'server.address': 'localhost', 'server.port': 5444, }), - description: 'UPDATE "User" SET "name" = ? WHERE "email" = ?', + description: 'UPDATE "User" SET "name" = ? WHERE "email" = $1', op: 'db', status: 'ok', origin: ORIGIN, @@ -86,13 +86,13 @@ describe('postgresjs auto instrumentation', () => { 'db.namespace': 'test_db', 'db.system.name': 'postgres', 'db.operation.name': 'SELECT', - 'db.query.text': 'SELECT * FROM "User" WHERE "email" = ?', + 'db.query.text': 'SELECT * FROM "User" WHERE "email" = $1', 'sentry.op': 'db', 'sentry.origin': ORIGIN, 'server.address': 'localhost', 'server.port': 5444, }), - description: 'SELECT * FROM "User" WHERE "email" = ?', + description: 'SELECT * FROM "User" WHERE "email" = $1', op: 'db', status: 'ok', origin: ORIGIN, @@ -175,13 +175,13 @@ describe('postgresjs auto instrumentation', () => { 'db.operation.name': 'SELECT', 'db.response.status_code': '42P01', 'error.type': 'PostgresError', - 'db.query.text': 'SELECT * FROM "User" WHERE "email" = ?', + 'db.query.text': 'SELECT * FROM "User" WHERE "email" = $1', 'sentry.op': 'db', 'sentry.origin': ORIGIN, 'server.address': 'localhost', 'server.port': 5444, }), - description: 'SELECT * FROM "User" WHERE "email" = ?', + description: 'SELECT * FROM "User" WHERE "email" = $1', op: 'db', status: 'internal_error', origin: ORIGIN, @@ -224,25 +224,18 @@ describe('postgresjs auto instrumentation', () => { }, }; - createEsmAndCjsTests( - __dirname, - 'scenario.mjs', - 'instrument.mjs', - (createTestRunner, test) => { - test('should auto-instrument `postgres` package', { timeout: 60_000 }, async () => { - await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .expect({ event: EXPECTED_ERROR_EVENT }) - // The error event is captured via an unhandled rejection processed on a later tick than - // the transaction, so the two envelopes can reach the transport in either order. - .unordered() - .start() - .completed(); - }); - }, - { copyPaths: ['wait-for-postgres.js'] }, - ); + createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createTestRunner, test) => { + test('should auto-instrument `postgres` package', { timeout: 60_000 }, async () => { + await createTestRunner() + .expect({ transaction: EXPECTED_TRANSACTION }) + .expect({ event: EXPECTED_ERROR_EVENT }) + // The error event is captured via an unhandled rejection processed on a later tick than + // the transaction, so the two envelopes can reach the transport in either order. + .unordered() + .start() + .completed(); + }); + }); }); describe('requestHook', () => { @@ -273,14 +266,14 @@ describe('postgresjs auto instrumentation', () => { 'db.namespace': 'test_db', 'db.system.name': 'postgres', 'db.operation.name': 'INSERT', - 'db.query.text': 'INSERT INTO "User" ("email", "name") VALUES (?, ?)', + 'db.query.text': 'INSERT INTO "User" ("email", "name") VALUES ($1, ?)', 'custom.requestHook': 'called', 'sentry.op': 'db', 'sentry.origin': ORIGIN, 'server.address': 'localhost', 'server.port': 5444, }), - description: 'INSERT INTO "User" ("email", "name") VALUES (?, ?)', + description: 'INSERT INTO "User" ("email", "name") VALUES ($1, ?)', op: 'db', status: 'ok', origin: ORIGIN, @@ -290,14 +283,14 @@ describe('postgresjs auto instrumentation', () => { 'db.namespace': 'test_db', 'db.system.name': 'postgres', 'db.operation.name': 'SELECT', - 'db.query.text': 'SELECT * FROM "User" WHERE "email" = ?', + 'db.query.text': 'SELECT * FROM "User" WHERE "email" = $1', 'custom.requestHook': 'called', 'sentry.op': 'db', 'sentry.origin': ORIGIN, 'server.address': 'localhost', 'server.port': 5444, }), - description: 'SELECT * FROM "User" WHERE "email" = ?', + description: 'SELECT * FROM "User" WHERE "email" = $1', op: 'db', status: 'ok', origin: ORIGIN, @@ -336,14 +329,9 @@ describe('postgresjs auto instrumentation', () => { 'instrument-requestHook.mjs', (createTestRunner, test) => { test('should call requestHook when provided', { timeout: 60_000 }, async () => { - await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); + await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); }); }, - { copyPaths: ['wait-for-postgres.js'] }, ); }); @@ -374,13 +362,13 @@ describe('postgresjs auto instrumentation', () => { 'db.namespace': 'test_db', 'db.system.name': 'postgres', 'db.operation.name': 'INSERT', - 'db.query.text': 'INSERT INTO "User" ("email", "name") VALUES (?, ?)', + 'db.query.text': 'INSERT INTO "User" ("email", "name") VALUES ($1, ?)', 'sentry.op': 'db', 'sentry.origin': ORIGIN, 'server.address': 'localhost', 'server.port': 5444, }), - description: 'INSERT INTO "User" ("email", "name") VALUES (?, ?)', + description: 'INSERT INTO "User" ("email", "name") VALUES ($1, ?)', op: 'db', status: 'ok', origin: ORIGIN, @@ -390,13 +378,13 @@ describe('postgresjs auto instrumentation', () => { 'db.namespace': 'test_db', 'db.system.name': 'postgres', 'db.operation.name': 'SELECT', - 'db.query.text': 'SELECT * FROM "User" WHERE "email" = ?', + 'db.query.text': 'SELECT * FROM "User" WHERE "email" = $1', 'sentry.op': 'db', 'sentry.origin': ORIGIN, 'server.address': 'localhost', 'server.port': 5444, }), - description: 'SELECT * FROM "User" WHERE "email" = ?', + description: 'SELECT * FROM "User" WHERE "email" = $1', op: 'db', status: 'ok', origin: ORIGIN, @@ -406,13 +394,13 @@ describe('postgresjs auto instrumentation', () => { 'db.namespace': 'test_db', 'db.system.name': 'postgres', 'db.operation.name': 'DELETE', - 'db.query.text': 'DELETE FROM "User" WHERE "email" = ?', + 'db.query.text': 'DELETE FROM "User" WHERE "email" = $1', 'sentry.op': 'db', 'sentry.origin': ORIGIN, 'server.address': 'localhost', 'server.port': 5444, }), - description: 'DELETE FROM "User" WHERE "email" = ?', + description: 'DELETE FROM "User" WHERE "email" = $1', op: 'db', status: 'ok', origin: ORIGIN, @@ -420,21 +408,11 @@ describe('postgresjs auto instrumentation', () => { ]), }; - createEsmAndCjsTests( - __dirname, - 'scenario-url.mjs', - 'instrument.mjs', - (createTestRunner, test) => { - test('should instrument postgres package with URL initialization', { timeout: 90_000 }, async () => { - await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); - }); - }, - { copyPaths: ['wait-for-postgres.js'] }, - ); + createEsmAndCjsTests(__dirname, 'scenario-url.mjs', 'instrument.mjs', (createTestRunner, test) => { + test('should instrument postgres package with URL initialization', { timeout: 90_000 }, async () => { + await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); + }); + }); }); describe('sql.unsafe()', () => { @@ -509,20 +487,10 @@ describe('postgresjs auto instrumentation', () => { ]), }; - createEsmAndCjsTests( - __dirname, - 'scenario-unsafe.mjs', - 'instrument.mjs', - (createTestRunner, test) => { - test('should instrument sql.unsafe() queries', { timeout: 90_000 }, async () => { - await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); - }); - }, - { copyPaths: ['wait-for-postgres.js'] }, - ); + createEsmAndCjsTests(__dirname, 'scenario-unsafe.mjs', 'instrument.mjs', (createTestRunner, test) => { + test('should instrument sql.unsafe() queries', { timeout: 90_000 }, async () => { + await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); + }); + }); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/postgresjs/wait-for-postgres.js b/dev-packages/node-integration-tests/suites/tracing/postgresjs/wait-for-postgres.js deleted file mode 100644 index c8c10c6eeb80..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/postgresjs/wait-for-postgres.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -/** - * Retries until Postgres accepts connections. `docker compose up --wait` can report healthy - * before the port forward on the host is ready (flaky on busy CI). - */ -async function waitForPostgres(sql, maxWaitMs = 60_000) { - const deadline = Date.now() + maxWaitMs; - for (;;) { - try { - await sql`SELECT 1`; - return; - } catch { - if (Date.now() > deadline) { - throw new Error('Timed out waiting for Postgres to accept connections'); - } - await new Promise(r => setTimeout(r, 250)); - } - } -} - -module.exports = { waitForPostgres }; diff --git a/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts b/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts index 6a1ebe80d601..8012fd9062f6 100644 --- a/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts @@ -1,8 +1,8 @@ import { afterAll, describe, expect } from 'vitest'; import { isOrchestrionEnabled } from '../../../utils'; -import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; +import { cleanupChildProcesses, createEsmAndCjsTests, describeWithDockerCompose } from '../../../utils/runner'; -describe('redis cache auto instrumentation', () => { +describeWithDockerCompose('redis cache auto instrumentation', { workingDirectory: [__dirname] }, () => { afterAll(() => { cleanupChildProcesses(); }); @@ -42,11 +42,7 @@ describe('redis cache auto instrumentation', () => { createEsmAndCjsTests(__dirname, 'scenario-ioredis.mjs', 'instrument-ioredis.mjs', (createTestRunner, test) => { test('should not add cache spans when key is not prefixed', { timeout: 60_000 }, async () => { - await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); + await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); }); }); }); @@ -145,11 +141,7 @@ describe('redis cache auto instrumentation', () => { createEsmAndCjsTests(__dirname, 'scenario-ioredis.mjs', 'instrument-ioredis.mjs', (createTestRunner, test) => { test('should create cache spans for prefixed keys (ioredis)', { timeout: 60_000 }, async () => { - await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); + await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); }); }); }); @@ -285,7 +277,6 @@ describe('redis cache auto instrumentation', () => { createEsmAndCjsTests(__dirname, 'scenario-redis-4.mjs', 'instrument-redis-4.mjs', (createTestRunner, test) => { test('should create cache spans for prefixed keys (redis-4)', async () => { await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_REDIS_CONNECT }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() @@ -428,7 +419,6 @@ describe('redis cache auto instrumentation', () => { createEsmAndCjsTests(__dirname, 'scenario-redis-5.mjs', 'instrument-redis-5.mjs', (createTestRunner, test) => { test('should create cache spans for prefixed keys (redis-5)', async () => { await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_REDIS_CONNECT }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() diff --git a/dev-packages/node-integration-tests/suites/tracing/redis-dc/test.ts b/dev-packages/node-integration-tests/suites/tracing/redis-dc/test.ts index c78bc92c6293..5f5336d2185b 100644 --- a/dev-packages/node-integration-tests/suites/tracing/redis-dc/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/redis-dc/test.ts @@ -1,112 +1,115 @@ -import { afterAll, describe, expect } from 'vitest'; -import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; +import { afterAll, expect } from 'vitest'; +import { cleanupChildProcesses, createEsmAndCjsTests, describeWithDockerCompose } from '../../../utils/runner'; -describe('redis v5 diagnostics_channel auto instrumentation', () => { - afterAll(() => { - cleanupChildProcesses(); - }); +describeWithDockerCompose( + 'redis v5 diagnostics_channel auto instrumentation', + { workingDirectory: [__dirname] }, + () => { + afterAll(() => { + cleanupChildProcesses(); + }); - const EXPECTED_TRANSACTION = { - transaction: 'Test Span Redis 5 DC', - spans: expect.arrayContaining([ - expect.objectContaining({ - op: 'db.redis', - origin: 'auto.db.redis.diagnostic_channel', - data: expect.objectContaining({ - 'sentry.op': 'db.redis', - 'sentry.origin': 'auto.db.redis.diagnostic_channel', - 'db.system.name': 'redis', - 'db.query.text': 'SET dc-test-key ?', + const EXPECTED_TRANSACTION = { + transaction: 'Test Span Redis 5 DC', + spans: expect.arrayContaining([ + expect.objectContaining({ + op: 'db.redis', + origin: 'auto.db.redis.diagnostic_channel', + data: expect.objectContaining({ + 'sentry.op': 'db.redis', + 'sentry.origin': 'auto.db.redis.diagnostic_channel', + 'db.system.name': 'redis', + 'db.query.text': 'SET dc-test-key ?', + }), }), - }), - // cache SET: span name updated to key by cacheResponseHook - expect.objectContaining({ - description: 'dc-cache:test-key', - op: 'cache.put', - origin: 'auto.db.redis.diagnostic_channel', - data: expect.objectContaining({ - 'sentry.origin': 'auto.db.redis.diagnostic_channel', - 'db.query.text': 'SET dc-cache:test-key ?', - 'cache.key': ['dc-cache:test-key'], - 'cache.item_size': 2, + // cache SET: span name updated to key by cacheResponseHook + expect.objectContaining({ + description: 'dc-cache:test-key', + op: 'cache.put', + origin: 'auto.db.redis.diagnostic_channel', + data: expect.objectContaining({ + 'sentry.origin': 'auto.db.redis.diagnostic_channel', + 'db.query.text': 'SET dc-cache:test-key ?', + 'cache.key': ['dc-cache:test-key'], + 'cache.item_size': 2, + }), }), - }), - // cache SET with EX option: redis v5 sends SET key value EX 10 as the command - expect.objectContaining({ - description: 'dc-cache:test-key-ex', - op: 'cache.put', - origin: 'auto.db.redis.diagnostic_channel', - data: expect.objectContaining({ - 'sentry.origin': 'auto.db.redis.diagnostic_channel', - 'db.query.text': 'SET dc-cache:test-key-ex ? ? ?', - 'cache.key': ['dc-cache:test-key-ex'], - 'cache.item_size': 2, + // cache SET with EX option: redis v5 sends SET key value EX 10 as the command + expect.objectContaining({ + description: 'dc-cache:test-key-ex', + op: 'cache.put', + origin: 'auto.db.redis.diagnostic_channel', + data: expect.objectContaining({ + 'sentry.origin': 'auto.db.redis.diagnostic_channel', + 'db.query.text': 'SET dc-cache:test-key-ex ? ? ?', + 'cache.key': ['dc-cache:test-key-ex'], + 'cache.item_size': 2, + }), }), - }), - expect.objectContaining({ - op: 'db.redis', - origin: 'auto.db.redis.diagnostic_channel', - data: expect.objectContaining({ - 'sentry.op': 'db.redis', - 'sentry.origin': 'auto.db.redis.diagnostic_channel', - 'db.system.name': 'redis', - 'db.query.text': 'GET dc-test-key', + expect.objectContaining({ + op: 'db.redis', + origin: 'auto.db.redis.diagnostic_channel', + data: expect.objectContaining({ + 'sentry.op': 'db.redis', + 'sentry.origin': 'auto.db.redis.diagnostic_channel', + 'db.system.name': 'redis', + 'db.query.text': 'GET dc-test-key', + }), }), - }), - // cache GET (hit) - expect.objectContaining({ - description: 'dc-cache:test-key', - op: 'cache.get', - origin: 'auto.db.redis.diagnostic_channel', - data: expect.objectContaining({ - 'sentry.origin': 'auto.db.redis.diagnostic_channel', - 'db.query.text': 'GET dc-cache:test-key', - 'cache.hit': true, - 'cache.key': ['dc-cache:test-key'], - 'cache.item_size': 10, + // cache GET (hit) + expect.objectContaining({ + description: 'dc-cache:test-key', + op: 'cache.get', + origin: 'auto.db.redis.diagnostic_channel', + data: expect.objectContaining({ + 'sentry.origin': 'auto.db.redis.diagnostic_channel', + 'db.query.text': 'GET dc-cache:test-key', + 'cache.hit': true, + 'cache.key': ['dc-cache:test-key'], + 'cache.item_size': 10, + }), }), - }), - // cache GET (miss) - expect.objectContaining({ - description: 'dc-cache:unavailable-data', - op: 'cache.get', - origin: 'auto.db.redis.diagnostic_channel', - data: expect.objectContaining({ - 'sentry.origin': 'auto.db.redis.diagnostic_channel', - 'db.query.text': 'GET dc-cache:unavailable-data', - 'cache.hit': false, - 'cache.key': ['dc-cache:unavailable-data'], + // cache GET (miss) + expect.objectContaining({ + description: 'dc-cache:unavailable-data', + op: 'cache.get', + origin: 'auto.db.redis.diagnostic_channel', + data: expect.objectContaining({ + 'sentry.origin': 'auto.db.redis.diagnostic_channel', + 'db.query.text': 'GET dc-cache:unavailable-data', + 'cache.hit': false, + 'cache.key': ['dc-cache:unavailable-data'], + }), }), - }), - // MGET: node-redis sanitizes args for diagnostics_channel (keys become '?'), - // so cache detection cannot match prefixes — remains a plain db.redis span. - expect.objectContaining({ - op: 'db.redis', - origin: 'auto.db.redis.diagnostic_channel', - data: expect.objectContaining({ - 'sentry.op': 'db.redis', - 'sentry.origin': 'auto.db.redis.diagnostic_channel', - 'db.system.name': 'redis', - 'db.query.text': 'MGET ? ? ?', + // MGET: node-redis sanitizes args for diagnostics_channel (keys become '?'), + // so cache detection cannot match prefixes — remains a plain db.redis span. + expect.objectContaining({ + op: 'db.redis', + origin: 'auto.db.redis.diagnostic_channel', + data: expect.objectContaining({ + 'sentry.op': 'db.redis', + 'sentry.origin': 'auto.db.redis.diagnostic_channel', + 'db.system.name': 'redis', + 'db.query.text': 'MGET ? ? ?', + }), }), - }), - ]), - }; + ]), + }; - // node-redis emits a node-redis:connect DC event for the initial connection. - // That fires before startSpan so it arrives as the first envelope. - const EXPECTED_CONNECT = { - transaction: 'redis-connect', - }; + // node-redis emits a node-redis:connect DC event for the initial connection. + // That fires before startSpan so it arrives as the first envelope. + const EXPECTED_CONNECT = { + transaction: 'redis-connect', + }; - createEsmAndCjsTests(__dirname, 'scenario-redis-5-tracing.mjs', 'instrument.mjs', (createTestRunner, test) => { - test('should create spans for redis v5 commands via diagnostics_channel', { timeout: 60_000 }, async () => { - await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) - .expect({ transaction: EXPECTED_CONNECT }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); + createEsmAndCjsTests(__dirname, 'scenario-redis-5-tracing.mjs', 'instrument.mjs', (createTestRunner, test) => { + test('should create spans for redis v5 commands via diagnostics_channel', { timeout: 60_000 }, async () => { + await createTestRunner() + .expect({ transaction: EXPECTED_CONNECT }) + .expect({ transaction: EXPECTED_TRANSACTION }) + .start() + .completed(); + }); }); - }); -}); + }, +); diff --git a/dev-packages/node-integration-tests/suites/tracing/redis/test.ts b/dev-packages/node-integration-tests/suites/tracing/redis/test.ts index 936dbe26ad0a..20c41136d4d5 100644 --- a/dev-packages/node-integration-tests/suites/tracing/redis/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/redis/test.ts @@ -1,8 +1,8 @@ -import { afterAll, describe, expect } from 'vitest'; +import { afterAll, expect } from 'vitest'; import { isOrchestrionEnabled } from '../../../utils'; -import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; +import { cleanupChildProcesses, createEsmAndCjsTests, describeWithDockerCompose } from '../../../utils/runner'; -describe('redis auto instrumentation', () => { +describeWithDockerCompose('redis auto instrumentation', { workingDirectory: [__dirname] }, () => { afterAll(() => { cleanupChildProcesses(); }); @@ -64,11 +64,7 @@ describe('redis auto instrumentation', () => { 'should auto-instrument `ioredis` package when using redis.set() and redis.get()', { timeout: 75_000 }, async () => { - await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); + await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); }, ); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/tedious/test.ts b/dev-packages/node-integration-tests/suites/tracing/tedious/test.ts index 6442ce6fbd13..9315adc85cf3 100644 --- a/dev-packages/node-integration-tests/suites/tracing/tedious/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/tedious/test.ts @@ -1,7 +1,7 @@ -import { afterAll, describe, expect } from 'vitest'; -import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; +import { afterAll, expect } from 'vitest'; +import { cleanupChildProcesses, createEsmAndCjsTests, describeWithDockerCompose } from '../../../utils/runner'; -describe('tedious auto instrumentation', { timeout: 90_000 }, () => { +describeWithDockerCompose('tedious auto instrumentation', { workingDirectory: [__dirname] }, () => { afterAll(() => { cleanupChildProcesses(); }); @@ -42,11 +42,7 @@ describe('tedious auto instrumentation', { timeout: 90_000 }, () => { createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createTestRunner, test) => { test('should auto-instrument `tedious` package', async () => { - await createTestRunner() - .withDockerCompose({ workingDirectory: [__dirname] }) - .expect({ transaction: EXPECTED_TRANSACTION }) - .start() - .completed(); + await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); }); }); }); diff --git a/dev-packages/node-integration-tests/utils/index.ts b/dev-packages/node-integration-tests/utils/index.ts index 705921cd1b6e..5c67842b6977 100644 --- a/dev-packages/node-integration-tests/utils/index.ts +++ b/dev-packages/node-integration-tests/utils/index.ts @@ -37,13 +37,15 @@ export function conditionalTest(allowedVersion: { min?: number; max?: number; }): typeof describe | typeof describe.skip { + return describe.skipIf(() => !matchesNodeVersion(allowedVersion)); +} + +function matchesNodeVersion({ min, max }: { min?: number; max?: number }): boolean { if (!NODE_VERSION) { - return describe.skip; + return false; } - return NODE_VERSION < (allowedVersion.min || -Infinity) || NODE_VERSION > (allowedVersion.max || Infinity) - ? describe.skip - : describe; + return !(NODE_VERSION < (min || -Infinity) || NODE_VERSION > (max || Infinity)); } /** diff --git a/dev-packages/node-integration-tests/utils/runner/createRunner.ts b/dev-packages/node-integration-tests/utils/runner/createRunner.ts index 488212aae15a..587a0cda6fe6 100644 --- a/dev-packages/node-integration-tests/utils/runner/createRunner.ts +++ b/dev-packages/node-integration-tests/utils/runner/createRunner.ts @@ -35,7 +35,7 @@ import { assertSpanEnvelopeHeader, } from './../assertions'; -interface DockerOptions { +export interface DockerOptions { /** * The working directory to run docker compose in */ @@ -646,7 +646,7 @@ export function createRunner(...paths: string[]) { * * Returns a function that can be called to docker compose down */ -async function runDockerCompose(options: DockerOptions): Promise { +export async function runDockerCompose(options: DockerOptions): Promise { const cwd = join(...options.workingDirectory); // Docker Compose derives the project name from the compose file's directory diff --git a/dev-packages/node-integration-tests/utils/runner/describeWithDockerCompose.ts b/dev-packages/node-integration-tests/utils/runner/describeWithDockerCompose.ts new file mode 100644 index 000000000000..39b9dc0f601b --- /dev/null +++ b/dev-packages/node-integration-tests/utils/runner/describeWithDockerCompose.ts @@ -0,0 +1,58 @@ +import type { TestOptions } from 'vitest'; +import { afterAll, beforeAll, describe } from 'vitest'; +import { type DockerOptions, runDockerCompose } from './createRunner'; + +type DefineTests = () => void; + +/** + * Wraps a group of `createEsmAndCjsTests` (or `createCjsTests`/`createEsmTests`) calls that all + * talk to the same Docker Compose service, bringing the container up **once** for the whole group + * and tearing it down once at the end. + * + * Previously each test brought its own container up and down via `runner.withDockerCompose(...)`, + * so a suite with N tests paid N × (`docker compose down` + `up --wait` healthcheck) — the + * dominant cost of the Docker-backed suites. With this wrapper the container is started in a single + * `beforeAll` and reused across every test in the group; the inner tests must NOT call + * `withDockerCompose()` themselves. + * + * @example + * describeWithDockerCompose('postgres', { workingDirectory: [__dirname] }, () => { + * createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createRunner, test) => { + * test('...', async () => { + * await createRunner().expect({ transaction: EXPECTED }).start().completed(); + * }); + * }); + * }); + */ +export function describeWithDockerCompose( + name: string, + dockerOptions: DockerOptions, + options: TestOptions, + defineTests: DefineTests, +): void; +export function describeWithDockerCompose(name: string, dockerOptions: DockerOptions, defineTests: DefineTests): void; +export function describeWithDockerCompose( + name: string, + dockerOptions: DockerOptions, + defineTestsOrOptions: DefineTests | TestOptions, + defineTests?: DefineTests, +): void { + const options = typeof defineTestsOrOptions === 'function' ? { timeout: 120_000 } : defineTestsOrOptions; + const callback = typeof defineTestsOrOptions === 'function' ? defineTestsOrOptions : defineTests!; + + describe(name, options, () => { + let dockerDown: (() => void) | undefined; + + // Bring the container up once for the whole group. The generous timeout covers image pulls and + // slow healthchecks on the first run. + beforeAll(async () => { + dockerDown = await runDockerCompose(dockerOptions); + }, 120_000); + + afterAll(() => { + dockerDown?.(); + }, 60_000); + + callback(); + }); +} diff --git a/dev-packages/node-integration-tests/utils/runner/index.ts b/dev-packages/node-integration-tests/utils/runner/index.ts index 82ef9bd9bd04..8f6432ad5de2 100644 --- a/dev-packages/node-integration-tests/utils/runner/index.ts +++ b/dev-packages/node-integration-tests/utils/runner/index.ts @@ -2,6 +2,7 @@ import { cleanupChildProcesses } from './createRunner'; export { createRunner } from './createRunner'; export { createEsmAndCjsTests, createEsmTests, createCjsTests } from './createEsmAndCjsTests'; +export { describeWithDockerCompose } from './describeWithDockerCompose'; export { cleanupChildProcesses }; // Backup call to cleanup everything leftover on process exit From 0cfa72837572b0837f2d82270479b46cc8cb32f0 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Thu, 9 Jul 2026 09:59:26 +0200 Subject: [PATCH 2/8] streamline --- .../tracing/prisma-orm-v5/docker-compose.yml | 3 + .../suites/tracing/prisma-orm-v5/test.ts | 88 +++++----- .../tracing/prisma-orm-v6/docker-compose.yml | 3 + .../suites/tracing/prisma-orm-v6/test.ts | 15 +- .../tracing/prisma-orm-v7/docker-compose.yml | 3 + .../suites/tracing/prisma-orm-v7/test.ts | 155 +++++++++--------- .../node-integration-tests/utils/index.ts | 2 +- .../utils/runner/createEsmAndCjsTests.ts | 9 + .../utils/runner/createRunner.ts | 16 +- 9 files changed, 143 insertions(+), 151 deletions(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/docker-compose.yml index dffb4cd81ba0..b5b82025ba5b 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/docker-compose.yml @@ -10,6 +10,9 @@ services: POSTGRES_USER: prisma POSTGRES_PASSWORD: prisma POSTGRES_DB: tests + # Create the schema on first init instead of running `prisma migrate` at test time. + volumes: + - ./prisma/migrations/sentry_test/migration.sql:/docker-entrypoint-initdb.d/init.sql:ro healthcheck: test: ['CMD-SHELL', 'pg_isready -U prisma -d tests'] interval: 2s diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/test.ts b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/test.ts index 1b2783fc05fb..94afb45905d4 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/test.ts @@ -1,6 +1,6 @@ import type { TransactionEvent } from '@sentry/core'; import { afterAll, describe, expect } from 'vitest'; -import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; +import { cleanupChildProcesses, createEsmAndCjsTests, describeWithDockerCompose } from '../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); @@ -112,52 +112,44 @@ function expectPrismaV5Spans(transaction: TransactionEvent): void { ); } -describe('Prisma ORM v5 Tests', () => { - createEsmAndCjsTests( - __dirname, - 'scenario.mjs', - 'instrument.mjs', - (createRunner, test, _mode, cwd) => { - test('should instrument PostgreSQL queries from Prisma ORM', { timeout: 75_000 }, async () => { - await createRunner() - .withDockerCompose({ - workingDirectory: [cwd], - setupCommand: 'yarn prisma generate && yarn prisma migrate dev -n sentry-test', - }) - .expect({ transaction: expectPrismaV5Spans }) - .start() - .completed(); - }); - }, - { - additionalDependencies: ADDITIONAL_DEPENDENCIES, - copyPaths: ['prisma', 'docker-compose.yml'], - }, - ); -}); +const AFTER_SETUP_COMMAND = 'prisma generate --schema prisma/schema.prisma'; -// The BasicTracerProvider path is opt-in via `openTelemetryBasicTracerProvider: true`; it must produce -// the same Prisma v5 span tree as the default SentryTracerProvider. -describe('Prisma ORM v5 Tests (BasicTracerProvider)', () => { - createEsmAndCjsTests( - __dirname, - 'scenario.mjs', - 'instrument-basic-tracer-provider.mjs', - (createRunner, test, _mode, cwd) => { - test('should instrument PostgreSQL queries from Prisma ORM', { timeout: 75_000 }, async () => { - await createRunner() - .withDockerCompose({ - workingDirectory: [cwd], - setupCommand: 'yarn prisma generate && yarn prisma migrate dev -n sentry-test', - }) - .expect({ transaction: expectPrismaV5Spans }) - .start() - .completed(); - }); - }, - { - additionalDependencies: ADDITIONAL_DEPENDENCIES, - copyPaths: ['prisma', 'docker-compose.yml'], - }, - ); +describeWithDockerCompose('Prisma ORM v5', { workingDirectory: [__dirname] }, () => { + describe('Prisma ORM v5 Tests', () => { + createEsmAndCjsTests( + __dirname, + 'scenario.mjs', + 'instrument.mjs', + (createRunner, test) => { + test('should instrument PostgreSQL queries from Prisma ORM', { timeout: 75_000 }, async () => { + await createRunner().expect({ transaction: expectPrismaV5Spans }).start().completed(); + }); + }, + { + additionalDependencies: ADDITIONAL_DEPENDENCIES, + afterSetupCommand: AFTER_SETUP_COMMAND, + copyPaths: ['prisma'], + }, + ); + }); + + // The BasicTracerProvider path is opt-in via `openTelemetryBasicTracerProvider: true`; it must produce + // the same Prisma v5 span tree as the default SentryTracerProvider. + describe('Prisma ORM v5 Tests (BasicTracerProvider)', () => { + createEsmAndCjsTests( + __dirname, + 'scenario.mjs', + 'instrument-basic-tracer-provider.mjs', + (createRunner, test) => { + test('should instrument PostgreSQL queries from Prisma ORM', { timeout: 75_000 }, async () => { + await createRunner().expect({ transaction: expectPrismaV5Spans }).start().completed(); + }); + }, + { + additionalDependencies: ADDITIONAL_DEPENDENCIES, + afterSetupCommand: AFTER_SETUP_COMMAND, + copyPaths: ['prisma'], + }, + ); + }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/docker-compose.yml index a8fc44deae58..a2351ffcb843 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/docker-compose.yml @@ -10,6 +10,9 @@ services: POSTGRES_USER: prisma POSTGRES_PASSWORD: prisma POSTGRES_DB: tests + # Create the schema on first init instead of running `prisma migrate` at test time. + volumes: + - ./prisma/migrations/sentry_test/migration.sql:/docker-entrypoint-initdb.d/init.sql:ro healthcheck: test: ['CMD-SHELL', 'pg_isready -U prisma -d tests'] interval: 2s diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/test.ts b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/test.ts index 6783e8885253..4516c5627508 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/test.ts @@ -1,23 +1,19 @@ import type { SpanJSON } from '@sentry/core'; -import { afterAll, describe, expect } from 'vitest'; -import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; +import { afterAll, expect } from 'vitest'; +import { cleanupChildProcesses, createEsmAndCjsTests, describeWithDockerCompose } from '../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -describe('Prisma ORM v6 Tests', () => { +describeWithDockerCompose('Prisma ORM v6 Tests', { workingDirectory: [__dirname] }, () => { createEsmAndCjsTests( __dirname, 'scenario.mjs', 'instrument.mjs', - (createRunner, test, _mode, cwd) => { + (createRunner, test) => { test('should instrument PostgreSQL queries from Prisma ORM', { timeout: 75_000 }, async () => { await createRunner() - .withDockerCompose({ - workingDirectory: [cwd], - setupCommand: `yarn prisma generate --schema ${cwd}/prisma/schema.prisma && yarn prisma migrate dev -n sentry-test --schema ${cwd}/prisma/schema.prisma`, - }) .expect({ transaction: transaction => { expect(transaction.transaction).toBe('Test Transaction'); @@ -119,7 +115,8 @@ describe('Prisma ORM v6 Tests', () => { }); }, { - copyPaths: ['prisma', 'docker-compose.yml'], + afterSetupCommand: 'prisma generate --schema prisma/schema.prisma', + copyPaths: ['prisma'], }, ); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v7/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v7/docker-compose.yml index d3b885122a3c..83e24502804b 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v7/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v7/docker-compose.yml @@ -10,6 +10,9 @@ services: POSTGRES_USER: prisma POSTGRES_PASSWORD: prisma POSTGRES_DB: tests + # Create the schema on first init instead of running `prisma migrate` at test time. + volumes: + - ./prisma/migrations/sentry_test/migration.sql:/docker-entrypoint-initdb.d/init.sql:ro healthcheck: test: ['CMD-SHELL', 'pg_isready -U prisma -d tests'] interval: 2s diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v7/test.ts b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v7/test.ts index 84142a9cc20b..2947fda3c045 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v7/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v7/test.ts @@ -1,6 +1,6 @@ import { afterAll, expect } from 'vitest'; import { conditionalTest } from '../../../utils'; -import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; +import { cleanupChildProcesses, createEsmAndCjsTests, describeWithDockerCompose } from '../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); @@ -8,91 +8,90 @@ afterAll(() => { // Prisma 7 requires Node.js 20.19+ conditionalTest({ min: 20 })('Prisma ORM v7 Tests', () => { - createEsmAndCjsTests( - __dirname, - 'scenario.mjs', - 'instrument.mjs', - (createRunner, test, _mode, cwd) => { - test('should instrument PostgreSQL queries from Prisma ORM', { timeout: 75_000 }, async () => { - await createRunner() - .withDockerCompose({ - workingDirectory: [cwd], - setupCommand: `yarn prisma generate --schema ${cwd}/prisma/schema.prisma && tsc -p ${cwd}/prisma/tsconfig.json && yarn prisma migrate dev -n sentry-test --schema ${cwd}/prisma/schema.prisma`, - }) - .expect({ - transaction: transaction => { - expect(transaction.transaction).toBe('Test Transaction'); + describeWithDockerCompose('Prisma ORM v7', { workingDirectory: [__dirname] }, () => { + createEsmAndCjsTests( + __dirname, + 'scenario.mjs', + 'instrument.mjs', + (createRunner, test) => { + test('should instrument PostgreSQL queries from Prisma ORM', { timeout: 75_000 }, async () => { + await createRunner() + .expect({ + transaction: transaction => { + expect(transaction.transaction).toBe('Test Transaction'); - const spans = transaction.spans || []; - expect(spans.length).toBeGreaterThanOrEqual(5); + const spans = transaction.spans || []; + expect(spans.length).toBeGreaterThanOrEqual(5); - // Each operation span is a direct child of the transaction; the db query span is a child of its operation span. - const rootSpanId = transaction.contexts?.trace?.span_id; + // Each operation span is a direct child of the transaction; the db query span is a child of its operation span. + const rootSpanId = transaction.contexts?.trace?.span_id; - const operationSpans = spans.filter(s => s.description === 'prisma:client:operation'); - expect(operationSpans.length).toBeGreaterThanOrEqual(1); - operationSpans.forEach(operation => { - expect(operation.parent_span_id).toBe(rootSpanId); - }); + const operationSpans = spans.filter(s => s.description === 'prisma:client:operation'); + expect(operationSpans.length).toBeGreaterThanOrEqual(1); + operationSpans.forEach(operation => { + expect(operation.parent_span_id).toBe(rootSpanId); + }); - const prismaDbQuerySpan = spans.find( - s => s.data?.['sentry.origin'] === 'auto.db.otel.prisma' && s.data?.['db.query.text'], - ); - expect(prismaDbQuerySpan).toBeDefined(); - const dbQueryParent = spans.find(s => s.span_id === prismaDbQuerySpan?.parent_span_id); - expect(dbQueryParent?.description).toBe('prisma:client:operation'); + const prismaDbQuerySpan = spans.find( + s => s.data?.['sentry.origin'] === 'auto.db.otel.prisma' && s.data?.['db.query.text'], + ); + expect(prismaDbQuerySpan).toBeDefined(); + const dbQueryParent = spans.find(s => s.span_id === prismaDbQuerySpan?.parent_span_id); + expect(dbQueryParent?.description).toBe('prisma:client:operation'); - // Verify Prisma spans have the correct origin - const prismaSpans = spans.filter( - span => span.data && span.data['sentry.origin'] === 'auto.db.otel.prisma', - ); - expect(prismaSpans.length).toBeGreaterThanOrEqual(5); + // Verify Prisma spans have the correct origin + const prismaSpans = spans.filter( + span => span.data && span.data['sentry.origin'] === 'auto.db.otel.prisma', + ); + expect(prismaSpans.length).toBeGreaterThanOrEqual(5); - // Check for key Prisma span descriptions - const spanDescriptions = prismaSpans.map(span => span.description); - expect(spanDescriptions).toContain('prisma:client:operation'); - expect(spanDescriptions).toContain('prisma:client:serialize'); - expect(spanDescriptions).toContain('prisma:client:connect'); + // Check for key Prisma span descriptions + const spanDescriptions = prismaSpans.map(span => span.description); + expect(spanDescriptions).toContain('prisma:client:operation'); + expect(spanDescriptions).toContain('prisma:client:serialize'); + expect(spanDescriptions).toContain('prisma:client:connect'); - // Verify the create operation has correct metadata - const createSpan = prismaSpans.find( - span => - span.description === 'prisma:client:operation' && - span.data?.['method'] === 'create' && - span.data?.['model'] === 'User', - ); - expect(createSpan).toBeDefined(); + // Verify the create operation has correct metadata + const createSpan = prismaSpans.find( + span => + span.description === 'prisma:client:operation' && + span.data?.['method'] === 'create' && + span.data?.['model'] === 'User', + ); + expect(createSpan).toBeDefined(); - // Verify db_query span has system info and correct op (v7 uses db.system.name). - // The SDK should rewrite the span name to the actual SQL text (same as v5/v6 - // `prisma:engine:db_query`), so we find it via op/origin rather than description. - const dbQuerySpan = prismaSpans.find( - span => span.data?.['sentry.op'] === 'db' && span.data?.['db.query.text'], - ); - expect(dbQuerySpan).toBeDefined(); - expect(dbQuerySpan?.data?.['db.system.name']).toBe('postgresql'); - expect(dbQuerySpan?.op).toBe('db'); - expect(dbQuerySpan?.description).toBe(dbQuerySpan?.data?.['db.query.text']); - expect(dbQuerySpan?.description).not.toBe('prisma:client:db_query'); + // Verify db_query span has system info and correct op (v7 uses db.system.name). + // The SDK should rewrite the span name to the actual SQL text (same as v5/v6 + // `prisma:engine:db_query`), so we find it via op/origin rather than description. + const dbQuerySpan = prismaSpans.find( + span => span.data?.['sentry.op'] === 'db' && span.data?.['db.query.text'], + ); + expect(dbQuerySpan).toBeDefined(); + expect(dbQuerySpan?.data?.['db.system.name']).toBe('postgresql'); + expect(dbQuerySpan?.op).toBe('db'); + expect(dbQuerySpan?.description).toBe(dbQuerySpan?.data?.['db.query.text']); + expect(dbQuerySpan?.description).not.toBe('prisma:client:db_query'); - // The db query span name must always be rewritten to the SQL text; the raw client span - // name should never leak through. - expect(spans.find(span => span.description === 'prisma:client:db_query')).toBeUndefined(); - }, - }) - .start() - .completed(); - }); - }, - { - additionalDependencies: { - '@prisma/adapter-pg': '7.2.0', - '@prisma/client': '7.2.0', - pg: '^8.11.0', - prisma: '7.2.0', - typescript: '^5.9.0', + // The db query span name must always be rewritten to the SQL text; the raw client span + // name should never leak through. + expect(spans.find(span => span.description === 'prisma:client:db_query')).toBeUndefined(); + }, + }) + .start() + .completed(); + }); }, - copyPaths: ['prisma', 'prisma.config.ts', 'docker-compose.yml'], - }, - ); + { + additionalDependencies: { + '@prisma/adapter-pg': '7.2.0', + '@prisma/client': '7.2.0', + pg: '^8.11.0', + prisma: '7.2.0', + typescript: '^5.9.0', + }, + afterSetupCommand: 'prisma generate --schema prisma/schema.prisma && tsc -p prisma/tsconfig.json', + copyPaths: ['prisma', 'prisma.config.ts'], + }, + ); + }); }); diff --git a/dev-packages/node-integration-tests/utils/index.ts b/dev-packages/node-integration-tests/utils/index.ts index 5c67842b6977..145b9cacdc15 100644 --- a/dev-packages/node-integration-tests/utils/index.ts +++ b/dev-packages/node-integration-tests/utils/index.ts @@ -37,7 +37,7 @@ export function conditionalTest(allowedVersion: { min?: number; max?: number; }): typeof describe | typeof describe.skip { - return describe.skipIf(() => !matchesNodeVersion(allowedVersion)); + return describe.skipIf(!matchesNodeVersion(allowedVersion)); } function matchesNodeVersion({ min, max }: { min?: number; max?: number }): boolean { diff --git a/dev-packages/node-integration-tests/utils/runner/createEsmAndCjsTests.ts b/dev-packages/node-integration-tests/utils/runner/createEsmAndCjsTests.ts index 91de78c7c5ab..e480bbc1b68f 100644 --- a/dev-packages/node-integration-tests/utils/runner/createEsmAndCjsTests.ts +++ b/dev-packages/node-integration-tests/utils/runner/createEsmAndCjsTests.ts @@ -25,6 +25,10 @@ interface CommonTestOptions { * `additionalDependencies` to install in the tmp dir. */ additionalDependencies?: Record; + /** + * A command to run once in the tmp dir after `additionalDependencies` are installed. + */ + afterSetupCommand?: string; /** Copy these files/dirs into the tmp dir. */ copyPaths?: string[]; /** If orchestrion should be injected before any instrument file. */ @@ -304,6 +308,11 @@ experimentalUseDiagnosticsChannelInjection();`, await npmInstallWithRetry(tmpDirPath, deps); } } + + if (options?.afterSetupCommand) { + const env = { ...process.env, PATH: `${join(tmpDirPath, 'node_modules', '.bin')}:${process.env.PATH}` }; + await execPromise(options.afterSetupCommand, { cwd: tmpDirPath, env }); + } } const paths: ScenarioPaths = { diff --git a/dev-packages/node-integration-tests/utils/runner/createRunner.ts b/dev-packages/node-integration-tests/utils/runner/createRunner.ts index 587a0cda6fe6..08d4c1885991 100644 --- a/dev-packages/node-integration-tests/utils/runner/createRunner.ts +++ b/dev-packages/node-integration-tests/utils/runner/createRunner.ts @@ -14,7 +14,7 @@ import type { } from '@sentry/core'; import { normalize } from '@sentry/core'; import { createBasicSentryServer } from '@sentry-internal/test-utils'; -import { execSync, spawn, spawnSync } from 'child_process'; +import { spawn, spawnSync } from 'child_process'; import { createHash } from 'crypto'; import { existsSync } from 'fs'; import { tmpdir } from 'os'; @@ -40,10 +40,6 @@ export interface DockerOptions { * The working directory to run docker compose in */ workingDirectory: string[]; - /** - * The command to run after docker compose is up - */ - setupCommand?: string; } type VoidFunction = () => void; @@ -698,16 +694,6 @@ export async function runDockerCompose(options: DockerOptions): Promise Date: Thu, 9 Jul 2026 10:03:33 +0200 Subject: [PATCH 3/8] remove withDockerCompose --- .../utils/runner/createRunner.ts | 92 +------------------ .../utils/runner/describeWithDockerCompose.ts | 73 ++++++++++++++- 2 files changed, 75 insertions(+), 90 deletions(-) diff --git a/dev-packages/node-integration-tests/utils/runner/createRunner.ts b/dev-packages/node-integration-tests/utils/runner/createRunner.ts index 08d4c1885991..ba6dc025ea46 100644 --- a/dev-packages/node-integration-tests/utils/runner/createRunner.ts +++ b/dev-packages/node-integration-tests/utils/runner/createRunner.ts @@ -14,8 +14,7 @@ import type { } from '@sentry/core'; import { normalize } from '@sentry/core'; import { createBasicSentryServer } from '@sentry-internal/test-utils'; -import { spawn, spawnSync } from 'child_process'; -import { createHash } from 'crypto'; +import { spawn } from 'child_process'; import { existsSync } from 'fs'; import { tmpdir } from 'os'; import { join } from 'path'; @@ -35,13 +34,6 @@ import { assertSpanEnvelopeHeader, } from './../assertions'; -export interface DockerOptions { - /** - * The working directory to run docker compose in - */ - workingDirectory: string[]; -} - type VoidFunction = () => void; type ExpectedEvent = Partial | ((event: Event) => void); @@ -142,7 +134,6 @@ export function createRunner(...paths: string[]) { let unordered = false; let withEnv: Record = {}; let withSentryServer = false; - let dockerOptions: DockerOptions | undefined; let ensureNoErrorOutput = false; // When set, the test using this runner expects `completed()` to reject (e.g. `test.fails` variants // created via `createEsmAndCjsTests` with `failsOnEsm`/`failsOnCjs`). We suppress the captured-log @@ -231,10 +222,6 @@ export function createRunner(...paths: string[]) { unordered = true; return this; }, - withDockerCompose: function (options: DockerOptions) { - dockerOptions = options; - return this; - }, ensureNoErrorOutput: function () { if (expectedEnvelopes.length > 0) { throw new Error('You should not use `ensureNoErrorOutput` when using `expect`!'); @@ -397,30 +384,19 @@ export function createRunner(...paths: string[]) { // We need to properly define & pass these types around for TS 3.8, // which otherwise fails to infer these correctly :( type ServerStartup = [number | undefined, (() => void) | undefined]; - type DockerStartup = VoidFunction | undefined; const serverStartup: Promise = withSentryServer ? createBasicSentryServer(newEnvelope) : Promise.resolve([undefined, undefined]); - const dockerStartup: Promise = dockerOptions - ? runDockerCompose(dockerOptions) - : Promise.resolve(undefined); - - const startup = Promise.all([dockerStartup, serverStartup]); - - startup - .then(([dockerChild, [mockServerPort, mockServerClose]]) => { + serverStartup + .then(([mockServerPort, mockServerClose]) => { if (mockServerClose) { registerCleanupStep(() => { mockServerClose(); }); } - if (dockerChild) { - registerCleanupStep(dockerChild); - } - const env = mockServerPort ? { ...COMPILE_CACHE_ENV, @@ -635,68 +611,6 @@ export function createRunner(...paths: string[]) { }; } -/** - * Runs `docker compose up -d --wait`, which blocks until every service's - * healthcheck reports healthy. Each suite defines its healthcheck in its - * own docker-compose.yml. - * - * Returns a function that can be called to docker compose down - */ -export async function runDockerCompose(options: DockerOptions): Promise { - const cwd = join(...options.workingDirectory); - - // Docker Compose derives the project name from the compose file's directory - // basename by default. Several suites live in directories that share a - // basename (e.g. `tracing/mysql2` and `tracing/knex/mysql2`), so they collide - // on the same project + network when running in parallel: one suite's - // teardown removes the shared `_default` network while a sibling is - // still starting, producing "network _default not found". Deriving a - // unique, stable project name from the full working directory isolates every - // suite from each other. - const projectName = `sentry-it-${createHash('sha1').update(cwd).digest('hex').slice(0, 12)}`; - const composeArgs = (...args: string[]): string[] => ['compose', '-p', projectName, ...args]; - - const close = (): void => { - spawnSync('docker', composeArgs('down', '--volumes'), { - cwd, - stdio: process.env.DEBUG ? 'inherit' : undefined, - }); - }; - - // ensure we're starting fresh - close(); - - const composeUp = (): ReturnType => - spawnSync('docker', composeArgs('up', '-d', '--wait'), { - cwd, - stdio: process.env.DEBUG ? 'inherit' : 'pipe', - }); - - // `docker compose up` occasionally fails on CI with transient daemon races - // (e.g. "failed to set up container networking: network _default not - // found" right after the network was created). A clean teardown plus retry - // clears these, while genuine healthcheck failures stay red on every attempt. - const maxAttempts = 3; - let result = composeUp(); - for (let attempt = 1; attempt < maxAttempts && result.status !== 0; attempt++) { - close(); - result = composeUp(); - } - - if (result.status !== 0) { - const stderr = result.stderr?.toString() ?? ''; - const stdout = result.stdout?.toString() ?? ''; - // Surface container logs to make healthcheck failures easier to diagnose in CI - const logs = spawnSync('docker', composeArgs('logs'), { cwd }).stdout?.toString() ?? ''; - close(); - throw new Error( - `docker compose up --wait failed (exit ${result.status})\n${stderr}${stdout}\n--- container logs ---\n${logs}`, - ); - } - - return close; -} - interface Deferred { promise: Promise; resolve: () => void; diff --git a/dev-packages/node-integration-tests/utils/runner/describeWithDockerCompose.ts b/dev-packages/node-integration-tests/utils/runner/describeWithDockerCompose.ts index 39b9dc0f601b..07a19489ee1a 100644 --- a/dev-packages/node-integration-tests/utils/runner/describeWithDockerCompose.ts +++ b/dev-packages/node-integration-tests/utils/runner/describeWithDockerCompose.ts @@ -1,9 +1,18 @@ +import { spawnSync } from 'child_process'; +import { createHash } from 'crypto'; +import { join } from 'path'; import type { TestOptions } from 'vitest'; import { afterAll, beforeAll, describe } from 'vitest'; -import { type DockerOptions, runDockerCompose } from './createRunner'; type DefineTests = () => void; +interface DockerOptions { + /** + * The working directory to run docker compose in + */ + workingDirectory: string[]; +} + /** * Wraps a group of `createEsmAndCjsTests` (or `createCjsTests`/`createEsmTests`) calls that all * talk to the same Docker Compose service, bringing the container up **once** for the whole group @@ -56,3 +65,65 @@ export function describeWithDockerCompose( callback(); }); } + +/** + * Runs `docker compose up -d --wait`, which blocks until every service's + * healthcheck reports healthy. Each suite defines its healthcheck in its + * own docker-compose.yml. + * + * Returns a function that can be called to docker compose down + */ +async function runDockerCompose(options: DockerOptions): Promise { + const cwd = join(...options.workingDirectory); + + // Docker Compose derives the project name from the compose file's directory + // basename by default. Several suites live in directories that share a + // basename (e.g. `tracing/mysql2` and `tracing/knex/mysql2`), so they collide + // on the same project + network when running in parallel: one suite's + // teardown removes the shared `_default` network while a sibling is + // still starting, producing "network _default not found". Deriving a + // unique, stable project name from the full working directory isolates every + // suite from each other. + const projectName = `sentry-it-${createHash('sha1').update(cwd).digest('hex').slice(0, 12)}`; + const composeArgs = (...args: string[]): string[] => ['compose', '-p', projectName, ...args]; + + const close = (): void => { + spawnSync('docker', composeArgs('down', '--volumes'), { + cwd, + stdio: process.env.DEBUG ? 'inherit' : undefined, + }); + }; + + // ensure we're starting fresh + close(); + + const composeUp = (): ReturnType => + spawnSync('docker', composeArgs('up', '-d', '--wait'), { + cwd, + stdio: process.env.DEBUG ? 'inherit' : 'pipe', + }); + + // `docker compose up` occasionally fails on CI with transient daemon races + // (e.g. "failed to set up container networking: network _default not + // found" right after the network was created). A clean teardown plus retry + // clears these, while genuine healthcheck failures stay red on every attempt. + const maxAttempts = 3; + let result = composeUp(); + for (let attempt = 1; attempt < maxAttempts && result.status !== 0; attempt++) { + close(); + result = composeUp(); + } + + if (result.status !== 0) { + const stderr = result.stderr?.toString() ?? ''; + const stdout = result.stdout?.toString() ?? ''; + // Surface container logs to make healthcheck failures easier to diagnose in CI + const logs = spawnSync('docker', composeArgs('logs'), { cwd }).stdout?.toString() ?? ''; + close(); + throw new Error( + `docker compose up --wait failed (exit ${result.status})\n${stderr}${stdout}\n--- container logs ---\n${logs}`, + ); + } + + return close; +} From f897b92328b31dcacb484c880a8aea1e4d4d7e4a Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Thu, 9 Jul 2026 10:31:51 +0200 Subject: [PATCH 4/8] some fixes --- .../suites/tracing/postgres/test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts b/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts index fdb1cf710501..088ee66828ec 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts @@ -107,26 +107,26 @@ describeWithDockerCompose('postgres auto instrumentation', { workingDirectory: [ 'db.system': 'postgresql', 'db.name': 'tests', 'db.statement': 'INSERT INTO "User" ("email", "name") VALUES ($1, $2)', - 'sentry.origin': 'auto.db.otel.postgres', + 'sentry.origin': QUERY_ORIGIN, 'sentry.op': 'db', }), description: 'INSERT INTO "User" ("email", "name") VALUES ($1, $2)', op: 'db', status: 'ok', - origin: 'auto.db.otel.postgres', + origin: QUERY_ORIGIN, }), expect.objectContaining({ data: expect.objectContaining({ 'db.system': 'postgresql', 'db.name': 'tests', 'db.statement': 'SELECT * FROM "User"', - 'sentry.origin': 'auto.db.otel.postgres', + 'sentry.origin': QUERY_ORIGIN, 'sentry.op': 'db', }), description: 'SELECT * FROM "User"', op: 'db', status: 'ok', - origin: 'auto.db.otel.postgres', + origin: QUERY_ORIGIN, }), ]), }); From 812b877482a4b5e6d8b6aad41b110af1c996533e Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Thu, 9 Jul 2026 10:37:23 +0200 Subject: [PATCH 5/8] fix test --- .../suites/tracing/postgres-streamed/test.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/test.ts b/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/test.ts index 8813b129b134..0afe4aa6a823 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/test.ts @@ -242,7 +242,7 @@ describeWithDockerCompose('postgres auto instrumentation (streamed)', { workingD expect(segmentSpan?.name).toBe('Test Span'); const dbSpans = getDbSpans(container); - expect(dbSpans.length).toBe(4); + expect(dbSpans.length).toBe(5); expect(dbSpans).toEqual([ expectedDbSpan({ name: 'pg.connect', host: '127.0.0.1' }), @@ -261,6 +261,11 @@ describeWithDockerCompose('postgres auto instrumentation (streamed)', { workingD statement: 'SELECT * FROM "NativeUser"', host: '127.0.0.1', }), + expectedDbSpan({ + name: 'DROP TABLE "NativeUser"', + statement: 'DROP TABLE "NativeUser"', + host: '127.0.0.1', + }), ]); }, }) From c07692267cd6ec7380017b9012550d7eff84601a Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Thu, 9 Jul 2026 10:40:50 +0200 Subject: [PATCH 6/8] Revert "some fixes" This reverts commit 5a918d978994fe1684102d5ec999bd059358119b. --- .../suites/tracing/postgres/test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts b/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts index 088ee66828ec..fdb1cf710501 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts @@ -107,26 +107,26 @@ describeWithDockerCompose('postgres auto instrumentation', { workingDirectory: [ 'db.system': 'postgresql', 'db.name': 'tests', 'db.statement': 'INSERT INTO "User" ("email", "name") VALUES ($1, $2)', - 'sentry.origin': QUERY_ORIGIN, + 'sentry.origin': 'auto.db.otel.postgres', 'sentry.op': 'db', }), description: 'INSERT INTO "User" ("email", "name") VALUES ($1, $2)', op: 'db', status: 'ok', - origin: QUERY_ORIGIN, + origin: 'auto.db.otel.postgres', }), expect.objectContaining({ data: expect.objectContaining({ 'db.system': 'postgresql', 'db.name': 'tests', 'db.statement': 'SELECT * FROM "User"', - 'sentry.origin': QUERY_ORIGIN, + 'sentry.origin': 'auto.db.otel.postgres', 'sentry.op': 'db', }), description: 'SELECT * FROM "User"', op: 'db', status: 'ok', - origin: QUERY_ORIGIN, + origin: 'auto.db.otel.postgres', }), ]), }); From 33f33a26018bddc1e49867b4ba16f67c8412a851 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Thu, 9 Jul 2026 10:42:20 +0200 Subject: [PATCH 7/8] really fix it --- .../suites/tracing/postgres-streamed/test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/test.ts b/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/test.ts index 0afe4aa6a823..6db43ab26971 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/test.ts @@ -215,7 +215,7 @@ describeWithDockerCompose('postgres auto instrumentation (streamed)', { workingD origin, }), expectedDbSpan({ name: 'SELECT * FROM "User"', statement: 'SELECT * FROM "User"', origin }), - expectedDbSpan({ name: 'DROP TABLE "User"', statement: 'DROP TABLE "User"' }), + expectedDbSpan({ name: 'DROP TABLE "User"', statement: 'DROP TABLE "User"', origin }), ]); }, }) From f8b991207b3dd9653b1927969b85c062d3a04991 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 10 Jul 2026 10:50:44 +0200 Subject: [PATCH 8/8] fix for node 18 --- .../suites/tracing/postgres-streamed/scenario-native.mjs | 3 ++- .../suites/tracing/postgres-streamed/scenario.mjs | 3 ++- .../suites/tracing/postgres/scenario-native.mjs | 3 ++- .../suites/tracing/postgres/scenario.mjs | 3 ++- .../suites/tracing/postgresjs/scenario-requestHook.mjs | 3 ++- .../suites/tracing/postgresjs/scenario-unsafe.mjs | 3 ++- .../suites/tracing/postgresjs/scenario-url.mjs | 3 ++- .../suites/tracing/postgresjs/scenario.mjs | 3 ++- 8 files changed, 16 insertions(+), 8 deletions(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/scenario-native.mjs b/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/scenario-native.mjs index 9b540ed3fa3e..3456d84bea8d 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/scenario-native.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/scenario-native.mjs @@ -1,4 +1,5 @@ import * as Sentry from '@sentry/node'; +import { uuid4 } from '@sentry/core/server'; import { waitForConnection } from '@sentry-internal/node-integration-tests'; import pg from 'pg'; @@ -32,7 +33,7 @@ async function run() { 'CREATE TABLE "NativeUser" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id"));', ); - const email = `${crypto.randomUUID()}@domain.com`; + const email = `${uuid4()}@domain.com`; await client.query('INSERT INTO "NativeUser" ("email", "name") VALUES ($1, $2)', [email, 'tim']); await client.query('SELECT * FROM "NativeUser"'); } finally { diff --git a/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/scenario.mjs b/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/scenario.mjs index 46d6cfdf0958..5f13c25b5c07 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/scenario.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/postgres-streamed/scenario.mjs @@ -1,4 +1,5 @@ import * as Sentry from '@sentry/node'; +import { uuid4 } from '@sentry/core/server'; import { waitForConnection } from '@sentry-internal/node-integration-tests'; import { Client } from 'pg'; @@ -26,7 +27,7 @@ async function run() { 'CREATE TABLE "User" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id"));', ); - const email = `${crypto.randomUUID()}@domain.com`; + const email = `${uuid4()}@domain.com`; await client.query('INSERT INTO "User" ("email", "name") VALUES ($1, $2)', [email, 'tim']); await client.query('SELECT * FROM "User"'); } finally { diff --git a/dev-packages/node-integration-tests/suites/tracing/postgres/scenario-native.mjs b/dev-packages/node-integration-tests/suites/tracing/postgres/scenario-native.mjs index e9200bff0f35..1cea5bdc1261 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres/scenario-native.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/postgres/scenario-native.mjs @@ -1,4 +1,5 @@ import * as Sentry from '@sentry/node'; +import { uuid4 } from '@sentry/core/server'; import { waitForConnection } from '@sentry-internal/node-integration-tests'; import pg from 'pg'; @@ -32,7 +33,7 @@ async function run() { 'CREATE TABLE "NativeUser" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id"));', ); - const email = `${crypto.randomUUID()}@domain.com`; + const email = `${uuid4()}@domain.com`; await client.query('INSERT INTO "NativeUser" ("email", "name") VALUES ($1, $2)', [email, 'tim']); await client.query('SELECT * FROM "NativeUser"'); } finally { diff --git a/dev-packages/node-integration-tests/suites/tracing/postgres/scenario.mjs b/dev-packages/node-integration-tests/suites/tracing/postgres/scenario.mjs index 46ffe62a3f64..30dec0ded24e 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres/scenario.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/postgres/scenario.mjs @@ -1,4 +1,5 @@ import * as Sentry from '@sentry/node'; +import { uuid4 } from '@sentry/core/server'; import { waitForConnection } from '@sentry-internal/node-integration-tests'; import { Client } from 'pg'; @@ -26,7 +27,7 @@ async function run() { 'CREATE TABLE "User" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id"));', ); - const email = `${crypto.randomUUID()}@domain.com`; + const email = `${uuid4()}@domain.com`; await client.query('INSERT INTO "User" ("email", "name") VALUES ($1, $2)', [email, 'tim']); await client.query('SELECT * FROM "User"'); diff --git a/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-requestHook.mjs b/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-requestHook.mjs index 41dc1ae36844..7435146e2312 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-requestHook.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-requestHook.mjs @@ -1,4 +1,5 @@ import * as Sentry from '@sentry/node'; +import { uuid4 } from '@sentry/core/server'; import postgres from 'postgres'; import { waitForConnection } from '@sentry-internal/node-integration-tests'; @@ -17,7 +18,7 @@ async function run() { CREATE TABLE "User" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id")); `; - const email = `${crypto.randomUUID()}@domain.com`; + const email = `${uuid4()}@domain.com`; await sql` INSERT INTO "User" ("email", "name") VALUES (${email}, 'tim'); `; diff --git a/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-unsafe.mjs b/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-unsafe.mjs index bde864938130..fe6d5126a3c6 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-unsafe.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-unsafe.mjs @@ -1,4 +1,5 @@ import * as Sentry from '@sentry/node'; +import { uuid4 } from '@sentry/core/server'; import postgres from 'postgres'; import { waitForConnection } from '@sentry-internal/node-integration-tests'; @@ -17,7 +18,7 @@ async function run() { // Test sql.unsafe() - this was not being instrumented before the fix await sql.unsafe('CREATE TABLE "User" ("id" SERIAL NOT NULL, "email" TEXT NOT NULL, PRIMARY KEY ("id"))'); - const email = `${crypto.randomUUID()}@domain.com`; + const email = `${uuid4()}@domain.com`; await sql.unsafe('INSERT INTO "User" ("email") VALUES ($1)', [email]); await sql.unsafe('SELECT * FROM "User" WHERE "email" = $1', [email]); diff --git a/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-url.mjs b/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-url.mjs index 18d28ccdd7ca..227daada3be1 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-url.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-url.mjs @@ -1,4 +1,5 @@ import * as Sentry from '@sentry/node'; +import { uuid4 } from '@sentry/core/server'; import postgres from 'postgres'; import { waitForConnection } from '@sentry-internal/node-integration-tests'; @@ -18,7 +19,7 @@ async function run() { CREATE TABLE "User" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id")); `; - const email = `${crypto.randomUUID()}@domain.com`; + const email = `${uuid4()}@domain.com`; await sql` INSERT INTO "User" ("email", "name") VALUES (${email}, 'tim'); `; diff --git a/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario.mjs b/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario.mjs index ea8fbe921515..a975bc518272 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario.mjs @@ -1,4 +1,5 @@ import * as Sentry from '@sentry/node'; +import { uuid4 } from '@sentry/core/server'; import postgres from 'postgres'; import { waitForConnection } from '@sentry-internal/node-integration-tests'; @@ -17,7 +18,7 @@ async function run() { CREATE TABLE "User" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id")); `; - const email = `${crypto.randomUUID()}@domain.com`; + const email = `${uuid4()}@domain.com`; await sql` INSERT INTO "User" ("email", "name") VALUES (${email}, 'tim'); `;