From 3a5fe8ec9954d6227280327fbe2f321328505f6b Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 1 Jul 2026 19:09:57 +0200 Subject: [PATCH] test(node): Attempt to unflake docker-based node integration tests --- .../node-integration-tests/scripts/clean.js | 8 +++++++- .../utils/runner/createRunner.ts | 19 ++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/dev-packages/node-integration-tests/scripts/clean.js b/dev-packages/node-integration-tests/scripts/clean.js index e6fdb4c4f6e8..6d4510cd4568 100644 --- a/dev-packages/node-integration-tests/scripts/clean.js +++ b/dev-packages/node-integration-tests/scripts/clean.js @@ -1,10 +1,16 @@ const { execSync } = require('child_process'); +const { createHash } = require('crypto'); const globby = require('globby'); const { dirname, join } = require('path'); const cwd = join(__dirname, '..'); const paths = globby.sync(['suites/**/docker-compose.yml'], { cwd }).map(path => join(cwd, dirname(path))); +// Must stay in sync with `runDockerCompose` in utils/runner/createRunner.ts: +// the runner starts each suite under a unique, path-derived project name, so we +// have to target the same name here or the teardown misses the containers. +const projectNameFor = suiteDir => `sentry-it-${createHash('sha1').update(suiteDir).digest('hex').slice(0, 12)}`; + // eslint-disable-next-line no-console console.log('Cleaning up docker containers and volumes...'); @@ -12,7 +18,7 @@ for (const path of paths) { try { // eslint-disable-next-line no-console console.log(`docker compose down @ ${path}`); - execSync('docker compose down --volumes', { stdio: 'inherit', cwd: path }); + execSync(`docker compose -p ${projectNameFor(path)} down --volumes`, { stdio: 'inherit', cwd: path }); } catch { // } diff --git a/dev-packages/node-integration-tests/utils/runner/createRunner.ts b/dev-packages/node-integration-tests/utils/runner/createRunner.ts index 3b3f57ba35c3..6f7eb8a02597 100644 --- a/dev-packages/node-integration-tests/utils/runner/createRunner.ts +++ b/dev-packages/node-integration-tests/utils/runner/createRunner.ts @@ -15,6 +15,7 @@ import type { import { normalize } from '@sentry/core'; import { createBasicSentryServer } from '@sentry-internal/test-utils'; import { execSync, spawn, spawnSync } from 'child_process'; +import { createHash } from 'crypto'; import { existsSync } from 'fs'; import { join } from 'path'; import { inspect } from 'util'; @@ -601,8 +602,20 @@ export function createRunner(...paths: string[]) { */ 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', ['compose', 'down', '--volumes'], { + spawnSync('docker', composeArgs('down', '--volumes'), { cwd, stdio: process.env.DEBUG ? 'inherit' : undefined, }); @@ -612,7 +625,7 @@ async function runDockerCompose(options: DockerOptions): Promise { close(); const composeUp = (): ReturnType => - spawnSync('docker', ['compose', 'up', '-d', '--wait'], { + spawnSync('docker', composeArgs('up', '-d', '--wait'), { cwd, stdio: process.env.DEBUG ? 'inherit' : 'pipe', }); @@ -632,7 +645,7 @@ async function runDockerCompose(options: DockerOptions): Promise { 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() ?? ''; + 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}`,