#!/usr/bin/env node // Measure testcontainers boot/teardown vs test time for a single test file. // // Usage (from any package dir, or pass --cwd): // node /measure-test-timing.mjs [--cwd ] [--runs N] // // Relies on the TESTCONTAINERS_TIMING log gate in src/logs.ts and runs vitest with // --disableConsoleIntercept so the JSON timing lines reach stdout. import { spawn } from "node:child_process"; const args = process.argv.slice(2); const testFile = args.find((a) => !a.startsWith("--")); const cwd = valueOf("--cwd") ?? process.cwd(); const runs = Number(valueOf("--runs") ?? "1"); function valueOf(flag) { const i = args.indexOf(flag); return i >= 0 ? args[i + 1] : undefined; } if (!testFile) { console.error("usage: measure-test-timing.mjs [--cwd dir] [--runs N]"); process.exit(1); } function runOnce() { return new Promise((resolve) => { const child = spawn("pnpm", ["exec", "vitest", "run", testFile, "--disableConsoleIntercept"], { cwd, env: { ...process.env, TESTCONTAINERS_TIMING: "1" }, }); let out = ""; const collect = (buf) => (out += buf.toString()); child.stdout.on("data", collect); child.stderr.on("data", collect); child.on("close", () => { const cleanups = []; let duration = null; for (const line of out.split("\n")) { const trimmed = line.trim(); if (trimmed.startsWith("{")) { try { const ev = JSON.parse(trimmed); if (ev.type === "cleanup") cleanups.push(ev); } catch {} } const m = trimmed.match(/Duration\s+([\d.]+)s/); if (m) duration = Number(m[1]); } resolve({ cleanups, duration, passed: /Tests\s+\d+ passed/.test(out) }); }); }); } for (let i = 0; i < runs; i++) { const { cleanups, duration, passed } = await runOnce(); const byResource = {}; for (const c of cleanups) { const key = c.resource.split(":")[0]; byResource[key] ??= { totalMs: 0, count: 0 }; byResource[key].totalMs += c.durationMs ?? 0; byResource[key].count += 1; } const teardownMs = Object.values(byResource).reduce((a, r) => a + r.totalMs, 0); console.log( `\nrun ${i + 1}/${runs} passed=${passed} wall=${duration}s teardown=${( teardownMs / 1000 ).toFixed(2)}s` ); for (const [res, r] of Object.entries(byResource)) { console.log(` teardown ${res}: ${(r.totalMs / 1000).toFixed(2)}s over ${r.count}`); } }