diff --git a/dev-packages/node-integration-tests/package.json b/dev-packages/node-integration-tests/package.json index 26f880284b99..c7490ec848e6 100644 --- a/dev-packages/node-integration-tests/package.json +++ b/dev-packages/node-integration-tests/package.json @@ -67,6 +67,7 @@ "reflect-metadata": "0.2.1", "rxjs": "^7.8.1", "tedious": "^18.6.1", + "wait-port": "^1.1.0", "yargs": "^16.2.0" }, "devDependencies": { 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 250e1e31c40c..1c6f6f11a54e 100644 --- a/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts @@ -33,7 +33,7 @@ describe('amqplib auto-instrumentation', () => { await createRunner(__dirname, 'scenario-message.ts') .withDockerCompose({ workingDirectory: [__dirname], - readyMatches: ['Time to start RabbitMQ'], + waitForPorts: [5672], }) .expect({ transaction: (transaction: TransactionEvent) => { 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 f1d6e46db743..d58d53432260 100644 --- a/dev-packages/node-integration-tests/suites/tracing/kafkajs/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/kafkajs/test.ts @@ -10,7 +10,7 @@ describe('kafkajs', () => { await createRunner(__dirname, 'scenario.js') .withDockerCompose({ workingDirectory: [__dirname], - readyMatches: ['9092'], + waitForPorts: [9092], }) .expect({ transaction: { diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/test.ts b/dev-packages/node-integration-tests/suites/tracing/knex/test.ts index 8fd445d51525..38d2213fc6df 100644 --- a/dev-packages/node-integration-tests/suites/tracing/knex/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/knex/test.ts @@ -59,7 +59,7 @@ describe('knex auto instrumentation', () => { }; await createRunner(__dirname, 'scenario-withPostgres.js') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname], waitForPorts: [5432] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); @@ -121,7 +121,7 @@ describe('knex auto instrumentation', () => { }; await createRunner(__dirname, 'scenario-withMysql2.js') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port: 3306'] }) + .withDockerCompose({ workingDirectory: [__dirname], waitForPorts: [3306] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); 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 c1d680b9a52e..98157752cc5b 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mysql2/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/mysql2/test.ts @@ -34,7 +34,7 @@ describe('mysql2 auto instrumentation', () => { }; await createRunner(__dirname, 'scenario.js') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port: 3306'] }) + .withDockerCompose({ workingDirectory: [__dirname], waitForPorts: [3306] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); 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 c34619e161ad..8f2a22387942 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts @@ -47,7 +47,7 @@ describe('postgres auto instrumentation', () => { }; await createRunner(__dirname, 'scenario.js') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname], waitForPorts: [5432] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); 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 b7788d58e372..ecee49de8122 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 @@ -10,7 +10,7 @@ describe('Prisma ORM v5 Tests', () => { await createRunner(__dirname, 'scenario.js') .withDockerCompose({ workingDirectory: [__dirname], - readyMatches: ['port 5432'], + waitForPorts: [5432], setupCommand: 'yarn && yarn setup', }) .expect({ 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 5e00a00966b7..e9841b7cc146 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 @@ -11,7 +11,7 @@ describe('Prisma ORM v6 Tests', () => { await createRunner(__dirname, 'scenario.js') .withDockerCompose({ workingDirectory: [__dirname], - readyMatches: ['port 5432'], + waitForPorts: [5432], setupCommand: 'yarn && yarn setup', }) .expect({ 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 e1aa0b9c1494..0b70623dfd44 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 @@ -38,7 +38,7 @@ describe('redis cache auto instrumentation', () => { }; await createRunner(__dirname, 'scenario-ioredis.js') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port=6379'] }) + .withDockerCompose({ workingDirectory: [__dirname], waitForPorts: [6379] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); @@ -137,7 +137,7 @@ describe('redis cache auto instrumentation', () => { }; await createRunner(__dirname, 'scenario-ioredis.js') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port=6379'] }) + .withDockerCompose({ workingDirectory: [__dirname], waitForPorts: [6379] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); @@ -228,7 +228,7 @@ describe('redis cache auto instrumentation', () => { }; await createRunner(__dirname, 'scenario-redis-4.js') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port=6379'] }) + .withDockerCompose({ workingDirectory: [__dirname], waitForPorts: [6379] }) .expect({ transaction: EXPECTED_REDIS_CONNECT }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() 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 0c9bb65facdc..83375a4b57d5 100644 --- a/dev-packages/node-integration-tests/suites/tracing/redis/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/redis/test.ts @@ -40,7 +40,7 @@ describe('redis auto instrumentation', () => { }; await createRunner(__dirname, 'scenario-ioredis.js') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port=6379'] }) + .withDockerCompose({ workingDirectory: [__dirname], waitForPorts: [6379] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); diff --git a/dev-packages/node-integration-tests/suites/tracing/tedious/scenario.js b/dev-packages/node-integration-tests/suites/tracing/tedious/scenario.js index 1a375cfb78e9..82b17402621c 100644 --- a/dev-packages/node-integration-tests/suites/tracing/tedious/scenario.js +++ b/dev-packages/node-integration-tests/suites/tracing/tedious/scenario.js @@ -10,7 +10,7 @@ Sentry.init({ const { Connection, Request } = require('tedious'); -const config = { +const connection = new Connection({ server: '127.0.0.1', authentication: { type: 'default', @@ -23,9 +23,7 @@ const config = { port: 1433, encrypt: false, }, -}; - -const connection = new Connection(config); +}); function executeAllStatements(span) { executeStatement('SELECT 1 + 1 AS solution', () => { 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 797ede718b39..286883778883 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, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; -describe('tedious auto instrumentation', {timeout: 75_000}, () => { +describe('tedious auto instrumentation', { timeout: 75_000 }, () => { afterAll(() => { cleanupChildProcesses(); }); @@ -42,7 +42,7 @@ describe('tedious auto instrumentation', {timeout: 75_000}, () => { }; await createRunner(__dirname, 'scenario.js') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['1433'] }) + .withDockerCompose({ workingDirectory: [__dirname], waitForPorts: [1433] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); diff --git a/dev-packages/node-integration-tests/utils/runner.ts b/dev-packages/node-integration-tests/utils/runner.ts index 1d77e80bf55c..e34c8dd89425 100644 --- a/dev-packages/node-integration-tests/utils/runner.ts +++ b/dev-packages/node-integration-tests/utils/runner.ts @@ -1,5 +1,5 @@ /* eslint-disable max-lines */ -import { execSync, spawn, spawnSync } from 'child_process'; +import { spawn, spawnSync } from 'child_process'; import { existsSync } from 'fs'; import { join } from 'path'; import { normalize } from '@sentry/core'; @@ -25,6 +25,7 @@ import { assertSentryTransaction, } from './assertions'; import { createBasicSentryServer } from './server'; +import waitPort from 'wait-port'; const CLEANUP_STEPS = new Set(); @@ -56,14 +57,14 @@ interface DockerOptions { * The working directory to run docker compose in */ workingDirectory: string[]; - /** - * The strings to look for in the output to know that the docker compose is ready for the test to be run - */ - readyMatches: string[]; /** * The command to run after docker compose is up */ setupCommand?: string; + /** + * Ports to watch until proceeding to know that the containers are ready for the tests to be run + */ + waitForPorts: number[]; } /** @@ -84,32 +85,37 @@ async function runDockerCompose(options: DockerOptions): Promise { // ensure we're starting fresh close(); - const child = spawn('docker', ['compose', 'up'], { cwd }); - - const timeout = setTimeout(() => { - close(); - reject(new Error('Timed out waiting for docker-compose')); - }, 75_000); - - function newData(data: Buffer): void { - const text = data.toString('utf8'); - - if (process.env.DEBUG) log(text); - - for (const match of options.readyMatches) { - if (text.includes(match)) { - child.stdout.removeAllListeners(); - clearTimeout(timeout); - if (options.setupCommand) { - execSync(options.setupCommand, { cwd, stdio: 'inherit' }); - } + spawn('docker', ['compose', 'up'], { cwd }); + + Promise.all( + (options.waitForPorts ?? []).map(async port => { + return { + port: port, + isReachable: ( + await waitPort({ + port, + host: 'localhost', + timeout: 75_000, + interval: 1_000, + output: 'silent', + }) + ).open, + }; + }), + ).then( + isReachableResults => { + if (isReachableResults.some(({ isReachable }) => !isReachable)) { + close(); + reject(`Timed out waiting for docker-compose ${JSON.stringify(isReachableResults)}`); + } else { resolve(close); } - } - } - - child.stdout.on('data', newData); - child.stderr.on('data', newData); + }, + e => { + close(); + reject(e); + }, + ); }); } diff --git a/yarn.lock b/yarn.lock index 549b2e4eba54..b8f22f51607d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8509,7 +8509,12 @@ dependencies: "@types/unist" "*" -"@types/history-4@npm:@types/history@4.7.8", "@types/history-5@npm:@types/history@4.7.8": +"@types/history-4@npm:@types/history@4.7.8": + version "4.7.8" + resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" + integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA== + +"@types/history-5@npm:@types/history@4.7.8": version "4.7.8" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA== @@ -10879,7 +10884,16 @@ aws-ssl-profiles@^1.1.1: resolved "https://registry.yarnpkg.com/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz#157dd77e9f19b1d123678e93f120e6f193022641" integrity sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g== -axios@1.7.7, axios@^1.0.0, axios@^1.7.7: +axios@1.8.2: + version "1.8.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.2.tgz#fabe06e241dfe83071d4edfbcaa7b1c3a40f7979" + integrity sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +axios@^1.0.0, axios@^1.7.7: version "1.7.7" resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== @@ -12883,7 +12897,7 @@ commander@^8.0.0, commander@^8.3.0: resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== -commander@^9.0.0: +commander@^9.0.0, commander@^9.3.0: version "9.5.0" resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== @@ -28343,7 +28357,16 @@ string-template@~0.2.1: resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" integrity sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0= -"string-width-cjs@npm:string-width@^4.2.0", string-width@4.2.3, "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -28446,7 +28469,7 @@ stringify-object@^3.2.1: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -28474,6 +28497,13 @@ strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -28609,7 +28639,7 @@ stylus@0.59.0, stylus@^0.59.0: sax "~1.2.4" source-map "^0.7.3" -sucrase@^3.27.0, sucrase@^3.35.0, sucrase@getsentry/sucrase#es2020-polyfills: +sucrase@^3.27.0, sucrase@^3.35.0: version "3.36.0" resolved "https://codeload.github.com/getsentry/sucrase/tar.gz/fd682f6129e507c00bb4e6319cc5d6b767e36061" dependencies: @@ -30713,6 +30743,15 @@ w3c-xmlserializer@^4.0.0: dependencies: xml-name-validator "^4.0.0" +wait-port@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/wait-port/-/wait-port-1.1.0.tgz#e5d64ee071118d985e2b658ae7ad32b2ce29b6b5" + integrity sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q== + dependencies: + chalk "^4.1.2" + commander "^9.3.0" + debug "^4.3.4" + walk-sync@^0.3.0, walk-sync@^0.3.1, walk-sync@^0.3.3: version "0.3.4" resolved "https://registry.yarnpkg.com/walk-sync/-/walk-sync-0.3.4.tgz#cf78486cc567d3a96b5b2237c6108017a5ffb9a4" @@ -31283,7 +31322,7 @@ wrangler@^3.67.1: optionalDependencies: fsevents "~2.3.2" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@7.0.0, wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -31301,6 +31340,15 @@ wrap-ansi@^6.0.1: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"