Run tests against a throwaway MariaDB Docker container

- Add scripts/test.ts: starts a temporary MariaDB container on a random
  localhost port, waits for TCP readiness, runs all test files, and
  always removes the container (including on Ctrl+C)
- `bun run test` uses the script; `bun run test:unit` runs without a DB
- BUN_MARIADB_TEST_IMAGE selects the image; an existing
  BUN_MARIADB_TEST_DB skips Docker
- Document in README

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-14 02:55:32 +01:00
co-authored by Claude Opus 5.5
parent ddf431e866
commit 1b8e591787
3 changed files with 166 additions and 5 deletions
+157
View File
@@ -0,0 +1,157 @@
#!/usr/bin/env bun
/**
* Run the full test suite against a throwaway MariaDB Docker container.
*
* bun run test # start container, run all tests, remove container
* bun run test tests/db # extra args are passed to `bun test`
*
* Env:
* BUN_MARIADB_TEST_IMAGE Docker image (default: mariadb:11.8, needs 11.7+ for VECTOR)
* BUN_MARIADB_TEST_DB If already set, skip Docker and use the configured
* BUN_MARIADB_SERVER_* connection instead
*/
import { SQL } from "bun";
const IMAGE = process.env.BUN_MARIADB_TEST_IMAGE || "mariadb:11.8";
const DB_NAME = "bun_mariadb_test";
const PASSWORD = "bun_mariadb_test_pw";
const CONTAINER = `bun-mariadb-test-${process.pid}`;
const READY_TIMEOUT_MS = 120_000;
const testArgs = process.argv.slice(2);
async function run(cmd: string[], opts: { quiet?: boolean } = {}) {
const proc = Bun.spawn(cmd, {
stdout: opts.quiet ? "pipe" : "inherit",
stderr: opts.quiet ? "pipe" : "inherit",
});
const stdout = opts.quiet ? await new Response(proc.stdout).text() : "";
const exitCode = await proc.exited;
return { exitCode, stdout: stdout.trim() };
}
async function runTests(env: Record<string, string>): Promise<number> {
const proc = Bun.spawn(["bun", "test", ...testArgs], {
stdout: "inherit",
stderr: "inherit",
env: { ...process.env, ...env },
});
return proc.exited;
}
// Already pointed at a database: just run the tests
if (process.env.BUN_MARIADB_TEST_DB) {
process.exit(await runTests({}));
}
if ((await run(["docker", "info"], { quiet: true })).exitCode !== 0) {
console.error(
"Docker is not available. Start Docker, or set BUN_MARIADB_TEST_DB and BUN_MARIADB_SERVER_* to use an existing database.",
);
process.exit(1);
}
let removed = false;
async function removeContainer() {
if (removed) return;
removed = true;
await run(["docker", "rm", "-f", CONTAINER], { quiet: true });
}
for (const signal of ["SIGINT", "SIGTERM"] as const) {
process.on(signal, async () => {
await removeContainer();
process.exit(130);
});
}
let exitCode = 1;
try {
console.log(`Starting ${IMAGE} (${CONTAINER}) ...`);
const started = await run(
[
"docker",
"run",
"-d",
"--rm",
"--name",
CONTAINER,
"-e",
`MARIADB_ROOT_PASSWORD=${PASSWORD}`,
"-e",
`MARIADB_DATABASE=${DB_NAME}`,
// Random free host port, bound to localhost only
"-p",
"127.0.0.1::3306",
IMAGE,
],
{ quiet: true },
);
if (started.exitCode !== 0) {
throw new Error(`docker run failed (exit ${started.exitCode})`);
}
const portOut = await run(["docker", "port", CONTAINER, "3306/tcp"], {
quiet: true,
});
const port = portOut.stdout.split("\n")[0]?.split(":").pop();
if (!port) {
throw new Error(`Could not read the container's mapped port`);
}
/**
* Wait for a TCP connection. The image's init phase runs a temporary
* server with networking disabled, so a successful TCP query means the
* real server is up.
*/
const deadline = Date.now() + READY_TIMEOUT_MS;
let ready = false;
while (Date.now() < deadline) {
const client = new SQL({
adapter: "mariadb",
hostname: "127.0.0.1",
port: Number(port),
username: "root",
password: PASSWORD,
database: DB_NAME,
connectionTimeout: 2,
});
try {
await client.unsafe("SELECT 1");
ready = true;
} catch {
await Bun.sleep(1000);
} finally {
await client.close().catch(() => {});
}
if (ready) break;
}
if (!ready) {
throw new Error(`MariaDB did not become ready within ${READY_TIMEOUT_MS / 1000}s`);
}
console.log(`MariaDB ready on 127.0.0.1:${port}\n`);
exitCode = await runTests({
BUN_MARIADB_TEST_DB: DB_NAME,
BUN_MARIADB_SERVER_HOST: "127.0.0.1",
BUN_MARIADB_SERVER_PORT: port,
BUN_MARIADB_SERVER_USERNAME: "root",
BUN_MARIADB_SERVER_PASSWORD: PASSWORD,
});
} catch (error: any) {
console.error(`Test setup failed: ${error.message}`);
exitCode = 1;
} finally {
await removeContainer();
}
process.exit(exitCode);