72 lines
2.3 KiB
TypeScript
72 lines
2.3 KiB
TypeScript
|
|
#!/usr/bin/env bun
|
||
|
|
/**
|
||
|
|
* Drop ONE throwaway test database by name.
|
||
|
|
*
|
||
|
|
* Two modes:
|
||
|
|
* bun scripts/drop-test-db.ts <name> drop now (with retries)
|
||
|
|
* bun scripts/drop-test-db.ts <name> --wait-for-pid N watcher mode: poll until
|
||
|
|
* pid N is gone, then drop
|
||
|
|
*
|
||
|
|
* Watcher mode is spawned detached by the test harness AT MODULE LOAD
|
||
|
|
* (src/__tests__/test-db.ts): a process-exit-hook spawn proved unreliable —
|
||
|
|
* bun tears the process down before the child finishes exec'ing — so instead
|
||
|
|
* a watcher outlives the test process by design and fires on its death,
|
||
|
|
* covering normal exit, crashes, and kills alike. A hard lifetime cap keeps
|
||
|
|
* a watcher from living forever if pid polling misbehaves.
|
||
|
|
*/
|
||
|
|
|
||
|
|
import postgres from 'postgres';
|
||
|
|
|
||
|
|
const name = process.argv[2];
|
||
|
|
if (!name || !/^quinn_test_[0-9a-z_]+$/.test(name)) {
|
||
|
|
process.stderr.write(`refusing to drop ${JSON.stringify(name)} — not a quinn_test_* name\n`);
|
||
|
|
process.exit(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
const waitFlagIdx = process.argv.indexOf('--wait-for-pid');
|
||
|
|
const waitPid = waitFlagIdx !== -1 ? Number(process.argv[waitFlagIdx + 1]) : null;
|
||
|
|
if (waitFlagIdx !== -1 && (!Number.isInteger(waitPid) || waitPid! <= 0)) {
|
||
|
|
process.stderr.write(`--wait-for-pid needs a positive pid, got ${JSON.stringify(process.argv[waitFlagIdx + 1])}\n`);
|
||
|
|
process.exit(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
const MAX_WATCH_MS = 3 * 3600_000;
|
||
|
|
|
||
|
|
function pidAlive(pid: number): boolean {
|
||
|
|
try {
|
||
|
|
process.kill(pid, 0);
|
||
|
|
return true;
|
||
|
|
} catch {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (waitPid !== null) {
|
||
|
|
const startedAt = Date.now();
|
||
|
|
while (pidAlive(waitPid) && Date.now() - startedAt < MAX_WATCH_MS) {
|
||
|
|
await Bun.sleep(1000);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const adminUrl =
|
||
|
|
process.env['QUINN_TEST_ADMIN_DB_URL'] ?? 'postgresql://quinn:devpassword@black.lan:25435/postgres';
|
||
|
|
|
||
|
|
const admin = postgres(adminUrl, { max: 1, connect_timeout: 10, onnotice: () => { /* suppress */ } });
|
||
|
|
|
||
|
|
let lastErr: unknown;
|
||
|
|
for (let attempt = 0; attempt < 5; attempt += 1) {
|
||
|
|
await Bun.sleep(500 * (attempt + 1));
|
||
|
|
try {
|
||
|
|
await admin.unsafe(`DROP DATABASE IF EXISTS "${name}" WITH (FORCE)`);
|
||
|
|
lastErr = undefined;
|
||
|
|
break;
|
||
|
|
} catch (err) {
|
||
|
|
lastErr = err;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
await admin.end();
|
||
|
|
if (lastErr !== undefined) {
|
||
|
|
process.stderr.write(`drop-test-db: ${name}: ${String(lastErr)}\n`);
|
||
|
|
process.exit(1);
|
||
|
|
}
|