From c5c967ccc14ac420d4e63125a5084f355681a30b Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Tue, 21 Apr 2026 20:11:57 +0200 Subject: [PATCH 1/4] test(node): Use docker-compose healthchecks for service readiness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the stdout-scraping readyMatches approach in the node-integration-tests runner with Docker's native healthchecks + `docker compose up -d --wait`. The old approach matched log substrings like `'port 5432'` or `'port: 3306'`, which fire when the socket is bound but not when the server is actually accepting queries — producing intermittent CI flakes (e.g. #20418, #20335) under load. Each docker-compose.yml now defines a proper healthcheck (pg_isready, mysqladmin ping, redis-cli ping, rabbitmq-diagnostics, etc.), and the runner blocks on `--wait` until every service reports healthy. On failure, the runner surfaces `docker compose logs` for easier debugging. Also renames the redis-cache container to avoid collision with redis. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../suites/tracing/amqplib/docker-compose.yml | 6 ++ .../suites/tracing/amqplib/test.ts | 1 - .../suites/tracing/kafkajs/docker-compose.yml | 6 ++ .../suites/tracing/kafkajs/test.ts | 1 - .../tracing/knex/mysql2/docker-compose.yml | 6 ++ .../suites/tracing/knex/mysql2/test.ts | 2 +- .../suites/tracing/knex/pg/docker-compose.yml | 6 ++ .../suites/tracing/knex/pg/test.ts | 2 +- .../suites/tracing/mysql2/docker-compose.yml | 6 ++ .../suites/tracing/mysql2/test.ts | 2 +- .../tracing/postgres/docker-compose.yml | 6 ++ .../suites/tracing/postgres/test.ts | 3 - .../tracing/postgresjs/docker-compose.yml | 6 ++ .../suites/tracing/postgresjs/test.ts | 16 ++-- .../tracing/prisma-orm-v5/docker-compose.yml | 6 ++ .../suites/tracing/prisma-orm-v5/test.ts | 1 - .../tracing/prisma-orm-v6/docker-compose.yml | 6 ++ .../suites/tracing/prisma-orm-v6/test.ts | 1 - .../tracing/prisma-orm-v7/docker-compose.yml | 6 ++ .../suites/tracing/prisma-orm-v7/test.ts | 1 - .../tracing/redis-cache/docker-compose.yml | 8 +- .../suites/tracing/redis-cache/test.ts | 6 +- .../suites/tracing/redis/docker-compose.yml | 6 ++ .../suites/tracing/redis/test.ts | 2 +- .../suites/tracing/tedious/docker-compose.yml | 6 ++ .../suites/tracing/tedious/test.ts | 2 +- .../node-integration-tests/utils/runner.ts | 79 ++++++++----------- 27 files changed, 130 insertions(+), 69 deletions(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/amqplib/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/amqplib/docker-compose.yml index c1127f097dbf..6d1468d3e04c 100644 --- a/dev-packages/node-integration-tests/suites/tracing/amqplib/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/amqplib/docker-compose.yml @@ -10,6 +10,12 @@ services: ports: - '5672:5672' - '15672:15672' + healthcheck: + test: ['CMD-SHELL', 'rabbitmq-diagnostics -q ping'] + interval: 2s + timeout: 10s + retries: 30 + start_period: 15s networks: default: 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 0be272187f3f..62f2931daba5 100644 --- a/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts @@ -34,7 +34,6 @@ describe('amqplib auto-instrumentation', () => { await createTestRunner() .withDockerCompose({ workingDirectory: [__dirname], - readyMatches: ['Time to start RabbitMQ'], }) .expect({ transaction: (transaction: TransactionEvent) => { diff --git a/dev-packages/node-integration-tests/suites/tracing/kafkajs/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/kafkajs/docker-compose.yml index f744bfe6d50c..ab430a56ad00 100644 --- a/dev-packages/node-integration-tests/suites/tracing/kafkajs/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/kafkajs/docker-compose.yml @@ -5,3 +5,9 @@ services: container_name: integration-tests-kafka ports: - '9092:9092' + healthcheck: + test: ['CMD-SHELL', '/opt/kafka/bin/kafka-broker-api-versions.sh --bootstrap-server localhost:9092'] + interval: 2s + timeout: 5s + retries: 30 + start_period: 15s 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 84e8d4a5612e..b7a996bddc53 100644 --- a/dev-packages/node-integration-tests/suites/tracing/kafkajs/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/kafkajs/test.ts @@ -16,7 +16,6 @@ describe('kafkajs', () => { await createRunner() .withDockerCompose({ workingDirectory: [__dirname], - readyMatches: ['9092'], }) .expect({ transaction: (transaction: TransactionEvent) => { diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/docker-compose.yml index 788311e4e117..a57c8cb840f8 100644 --- a/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/knex/mysql2/docker-compose.yml @@ -10,3 +10,9 @@ services: environment: MYSQL_ROOT_PASSWORD: docker MYSQL_DATABASE: tests + healthcheck: + test: ['CMD-SHELL', 'mysqladmin ping -h 127.0.0.1 -uroot -pdocker'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 10s 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 e8116293de09..01db0bf6e88a 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 @@ -63,7 +63,7 @@ describe('knex auto instrumentation', () => { }; await createRunner() - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port: 3306'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/pg/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/knex/pg/docker-compose.yml index e3edcd1d8d7b..28a916737317 100644 --- a/dev-packages/node-integration-tests/suites/tracing/knex/pg/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/knex/pg/docker-compose.yml @@ -11,3 +11,9 @@ services: POSTGRES_USER: test POSTGRES_PASSWORD: test POSTGRES_DB: tests + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U test -d tests'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 5s 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 7c38381eb125..63af7b3628b5 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 @@ -61,7 +61,7 @@ describe('knex auto instrumentation', () => { }; await createRunner() - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); diff --git a/dev-packages/node-integration-tests/suites/tracing/mysql2/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/mysql2/docker-compose.yml index 71ea54ad7e70..598c394ace5e 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mysql2/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/mysql2/docker-compose.yml @@ -7,3 +7,9 @@ services: - '3306:3306' environment: MYSQL_ROOT_PASSWORD: password + healthcheck: + test: ['CMD-SHELL', 'mysqladmin ping -h 127.0.0.1 -uroot -ppassword'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 10s 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..4c3078b922f7 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] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); diff --git a/dev-packages/node-integration-tests/suites/tracing/postgres/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/postgres/docker-compose.yml index 51d9b86d028f..44425ae56188 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/postgres/docker-compose.yml @@ -11,3 +11,9 @@ services: POSTGRES_USER: test POSTGRES_PASSWORD: test POSTGRES_DB: tests + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U test -d tests'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 5s 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 1fd03e92d0e2..98c42976498a 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts @@ -49,7 +49,6 @@ describe('postgres auto instrumentation', () => { await createRunner(__dirname, 'scenario.js') .withDockerCompose({ workingDirectory: [__dirname], - readyMatches: ['port 5432'], setupCommand: 'yarn', }) .expect({ transaction: EXPECTED_TRANSACTION }) @@ -61,7 +60,6 @@ describe('postgres auto instrumentation', () => { await createRunner(__dirname, 'scenario-ignoreConnect.js') .withDockerCompose({ workingDirectory: [__dirname], - readyMatches: ['port 5432'], setupCommand: 'yarn', }) .expect({ @@ -152,7 +150,6 @@ describe('postgres auto instrumentation', () => { await createRunner(__dirname, 'scenario-native.js') .withDockerCompose({ workingDirectory: [__dirname], - readyMatches: ['port 5432'], setupCommand: 'yarn', }) .expect({ transaction: EXPECTED_TRANSACTION }) diff --git a/dev-packages/node-integration-tests/suites/tracing/postgresjs/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/postgresjs/docker-compose.yml index 301280106faa..f3afd85af9ab 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgresjs/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/postgresjs/docker-compose.yml @@ -11,3 +11,9 @@ services: POSTGRES_USER: test POSTGRES_PASSWORD: test POSTGRES_DB: test_db + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U test -d test_db'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 5s 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 2dfbc020966b..f5f06208812e 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgresjs/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/postgresjs/test.ts @@ -218,7 +218,7 @@ describe('postgresjs auto instrumentation', () => { }; await createRunner(__dirname, 'scenario.js') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .expect({ event: EXPECTED_ERROR_EVENT }) .start() @@ -438,7 +438,7 @@ describe('postgresjs auto instrumentation', () => { await createRunner(__dirname, 'scenario.mjs') .withFlags('--import', `${__dirname}/instrument.mjs`) - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .expect({ event: EXPECTED_ERROR_EVENT }) .start() @@ -532,7 +532,7 @@ describe('postgresjs auto instrumentation', () => { await createRunner(__dirname, 'scenario-requestHook.js') .withFlags('--require', `${__dirname}/instrument-requestHook.cjs`) - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); @@ -625,7 +625,7 @@ describe('postgresjs auto instrumentation', () => { await createRunner(__dirname, 'scenario-requestHook.mjs') .withFlags('--import', `${__dirname}/instrument-requestHook.mjs`) - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); @@ -706,7 +706,7 @@ describe('postgresjs auto instrumentation', () => { }; await createRunner(__dirname, 'scenario-url.cjs') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); @@ -787,7 +787,7 @@ describe('postgresjs auto instrumentation', () => { await createRunner(__dirname, 'scenario-url.mjs') .withFlags('--import', `${__dirname}/instrument.mjs`) - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); @@ -866,7 +866,7 @@ describe('postgresjs auto instrumentation', () => { }; await createRunner(__dirname, 'scenario-unsafe.cjs') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); @@ -946,7 +946,7 @@ describe('postgresjs auto instrumentation', () => { await createRunner(__dirname, 'scenario-unsafe.mjs') .withFlags('--import', `${__dirname}/instrument.mjs`) - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); 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 37d45547b537..24bc212cac77 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 @@ -11,3 +11,9 @@ services: POSTGRES_USER: prisma POSTGRES_PASSWORD: prisma POSTGRES_DB: tests + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U prisma -d tests'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 5s 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 74bdf4be4bd2..252ed938bf0d 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 @@ -15,7 +15,6 @@ describe('Prisma ORM v5 Tests', () => { await createRunner() .withDockerCompose({ workingDirectory: [cwd], - readyMatches: ['port 5432'], setupCommand: 'yarn prisma generate && yarn prisma migrate dev -n sentry-test', }) .expect({ 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 ddab7cb9c563..28f111fe3156 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 @@ -11,3 +11,9 @@ services: POSTGRES_USER: prisma POSTGRES_PASSWORD: prisma POSTGRES_DB: tests + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U prisma -d tests'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 5s 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 07405a496fd0..b804adb10f71 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 @@ -16,7 +16,6 @@ describe('Prisma ORM v6 Tests', () => { await createRunner() .withDockerCompose({ workingDirectory: [cwd], - readyMatches: ['port 5432'], setupCommand: `yarn prisma generate --schema ${cwd}/prisma/schema.prisma && yarn prisma migrate dev -n sentry-test --schema ${cwd}/prisma/schema.prisma`, }) .expect({ 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 a56fd51cf7d9..117b5ef2c901 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 @@ -11,3 +11,9 @@ services: POSTGRES_USER: prisma POSTGRES_PASSWORD: prisma POSTGRES_DB: tests + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U prisma -d tests'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 5s 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 5bb0158eee3c..f9fb22606772 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 @@ -17,7 +17,6 @@ conditionalTest({ min: 20 })('Prisma ORM v7 Tests', () => { await createRunner() .withDockerCompose({ workingDirectory: [cwd], - readyMatches: ['port 5432'], 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({ diff --git a/dev-packages/node-integration-tests/suites/tracing/redis-cache/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/redis-cache/docker-compose.yml index 164d5977e33d..ded08e8f62e5 100644 --- a/dev-packages/node-integration-tests/suites/tracing/redis-cache/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/redis-cache/docker-compose.yml @@ -4,6 +4,12 @@ services: db: image: redis:latest restart: always - container_name: integration-tests-redis + container_name: integration-tests-redis-cache ports: - '6379:6379' + healthcheck: + test: ['CMD-SHELL', 'redis-cli ping | grep -q PONG'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 5s 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..c27957c37b06 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] }) .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] }) .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] }) .expect({ transaction: EXPECTED_REDIS_CONNECT }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() diff --git a/dev-packages/node-integration-tests/suites/tracing/redis/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/redis/docker-compose.yml index 164d5977e33d..356302aa1f93 100644 --- a/dev-packages/node-integration-tests/suites/tracing/redis/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/redis/docker-compose.yml @@ -7,3 +7,9 @@ services: container_name: integration-tests-redis ports: - '6379:6379' + healthcheck: + test: ['CMD-SHELL', 'redis-cli ping | grep -q PONG'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 5s 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 ec9cc0d93e84..7add18746f3a 100644 --- a/dev-packages/node-integration-tests/suites/tracing/redis/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/redis/test.ts @@ -43,7 +43,7 @@ describe('redis auto instrumentation', () => { }; await createRunner(__dirname, 'scenario-ioredis.js') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port=6379'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .expect({ transaction: EXPECTED_TRANSACTION }) .start() .completed(); diff --git a/dev-packages/node-integration-tests/suites/tracing/tedious/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/tedious/docker-compose.yml index 8e3604dca209..b77d05e2be20 100644 --- a/dev-packages/node-integration-tests/suites/tracing/tedious/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/tedious/docker-compose.yml @@ -10,3 +10,9 @@ services: environment: ACCEPT_EULA: 'Y' MSSQL_SA_PASSWORD: 'TESTing123' + healthcheck: + test: ['CMD-SHELL', '/opt/mssql-tools18/bin/sqlcmd -S localhost -U SA -P "TESTing123" -C -Q "SELECT 1"'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 20s 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 4b64611ac8f2..a8d45ad43877 100644 --- a/dev-packages/node-integration-tests/suites/tracing/tedious/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/tedious/test.ts @@ -42,7 +42,7 @@ describe.skip('tedious auto instrumentation', { timeout: 75_000 }, () => { }; await createRunner(__dirname, 'scenario.js') - .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['1433'] }) + .withDockerCompose({ workingDirectory: [__dirname] }) .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 7690fa40ee8b..fd02e64ae555 100644 --- a/dev-packages/node-integration-tests/utils/runner.ts +++ b/dev-packages/node-integration-tests/utils/runner.ts @@ -68,10 +68,6 @@ 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 */ @@ -79,56 +75,51 @@ interface DockerOptions { } /** - * Runs docker compose up and waits for the readyMatches to appear in the output + * Runs `docker compose up -d --wait`, which blocks until every service reports + * its container `healthcheck` as `healthy` (see the `healthcheck:` stanza in + * each suite's `docker-compose.yml`). * * Returns a function that can be called to docker compose down */ async function runDockerCompose(options: DockerOptions): Promise { - return new Promise((resolve, reject) => { - const cwd = join(...options.workingDirectory); - const close = (): void => { - spawnSync('docker', ['compose', 'down', '--volumes'], { - cwd, - stdio: process.env.DEBUG ? 'inherit' : undefined, - }); - }; - - // ensure we're starting fresh - close(); - - const child = spawn('docker', ['compose', 'up'], { cwd }); + const cwd = join(...options.workingDirectory); + const close = (): void => { + spawnSync('docker', ['compose', 'down', '--volumes'], { + cwd, + stdio: process.env.DEBUG ? 'inherit' : undefined, + }); + }; - const timeout = setTimeout(() => { - close(); - reject(new Error('Timed out waiting for docker-compose')); - }, 75_000); + // ensure we're starting fresh + close(); - function newData(data: Buffer): void { - const text = data.toString('utf8'); + const result = spawnSync('docker', ['compose', 'up', '-d', '--wait'], { + cwd, + stdio: process.env.DEBUG ? 'inherit' : 'pipe', + }); - if (process.env.DEBUG) log(text); + 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', ['compose', 'logs'], { cwd }).stdout?.toString() ?? ''; + close(); + throw new Error( + `docker compose up --wait failed (exit ${result.status})\n${stderr}${stdout}\n--- container logs ---\n${logs}`, + ); + } - for (const match of options.readyMatches) { - if (text.includes(match)) { - child.stdout.removeAllListeners(); - clearTimeout(timeout); - if (options.setupCommand) { - try { - // Prepend local node_modules/.bin to PATH so additionalDependencies binaries take precedence - const env = { ...process.env, PATH: `${cwd}/node_modules/.bin:${process.env.PATH}` }; - execSync(options.setupCommand, { cwd, stdio: 'inherit', env }); - } catch (e) { - log('Error running docker setup command', e); - } - } - resolve(close); - } - } + if (options.setupCommand) { + try { + // Prepend local node_modules/.bin to PATH so additionalDependencies binaries take precedence + const env = { ...process.env, PATH: `${cwd}/node_modules/.bin:${process.env.PATH}` }; + execSync(options.setupCommand, { cwd, stdio: 'inherit', env }); + } catch (e) { + log('Error running docker setup command', e); } + } - child.stdout.on('data', newData); - child.stderr.on('data', newData); - }); + return close; } type ExpectedEvent = Partial | ((event: Event) => void); From 4dc102097f67fbda2979af796e96f3f437a24a20 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 22 Apr 2026 09:37:10 +0200 Subject: [PATCH 2/4] update comment --- dev-packages/node-integration-tests/utils/runner.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dev-packages/node-integration-tests/utils/runner.ts b/dev-packages/node-integration-tests/utils/runner.ts index fd02e64ae555..65280056174b 100644 --- a/dev-packages/node-integration-tests/utils/runner.ts +++ b/dev-packages/node-integration-tests/utils/runner.ts @@ -75,11 +75,11 @@ interface DockerOptions { } /** - * Runs `docker compose up -d --wait`, which blocks until every service reports - * its container `healthcheck` as `healthy` (see the `healthcheck:` stanza in - * each suite's `docker-compose.yml`). + * 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 + * Returns a function that can be called to docker compose down. */ async function runDockerCompose(options: DockerOptions): Promise { const cwd = join(...options.workingDirectory); From ba5f19d817841a0c4308b06a400fab71dd18d167 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 22 Apr 2026 09:43:55 +0200 Subject: [PATCH 3/4] . --- dev-packages/node-integration-tests/utils/runner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/node-integration-tests/utils/runner.ts b/dev-packages/node-integration-tests/utils/runner.ts index 65280056174b..89f96974c123 100644 --- a/dev-packages/node-integration-tests/utils/runner.ts +++ b/dev-packages/node-integration-tests/utils/runner.ts @@ -79,7 +79,7 @@ interface DockerOptions { * 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. + * Returns a function that can be called to docker compose down */ async function runDockerCompose(options: DockerOptions): Promise { const cwd = join(...options.workingDirectory); From d4279c67ad21480ae8367ac5cedb62ae8966510b Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 22 Apr 2026 10:47:46 +0200 Subject: [PATCH 4/4] bugbot doc --- .cursor/BUGBOT.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.cursor/BUGBOT.md b/.cursor/BUGBOT.md index f4b4bd287271..c1a7811e5971 100644 --- a/.cursor/BUGBOT.md +++ b/.cursor/BUGBOT.md @@ -63,6 +63,7 @@ Do not flag the issues below if they appear in tests. - Race conditions when waiting on multiple requests. Ensure that waiting checks are unique enough and don't depend on a hard order when there's a chance that telemetry can be sent in arbitrary order. - Timeouts or sleeps in tests. Instead suggest concrete events or other signals to wait on. - Flag usage of `getFirstEnvelope*`, `getMultipleEnvelope*` or related test helpers. These are NOT reliable anymore. Instead suggest helpers like `waitForTransaction`, `waitForError`, `waitForSpans`, etc. +- Flag any new or modified `docker-compose.yml` under `dev-packages/node-integration-tests/suites/` or `dev-packages/node-core-integration-tests/suites/` where a service does not define a `healthcheck:`. The runner uses `docker compose up --wait` and relies on healthchecks to know when services are actually ready; without one the test will race the service's startup. ## Platform-safe code