Files
PaperClipAI/tests/runner-e2e/external-controller-database.ts
T

48 lines
2.5 KiB
TypeScript

/** Launcher-owned disposable database. The admin credential never enters the
* server/browser environment. Unknown remote cleanup keeps this database alive. */
import { randomUUID } from "node:crypto";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { createDb } from "../../packages/db/src/index.js";
import { paperclipConfigSchema } from "../../packages/shared/src/config-schema.js";
import { recordControllerDatabaseOwnership } from "./instance-isolation.js";
import { prepareRunnerE2EServerConfig } from "./server-config.js";
export async function createExternalControllerDatabase(input: {
adminUrl: string; temporaryRoot: string; configPath: string; serverPort: number;
}) {
const url = new URL(input.adminUrl);
if (!["postgres:", "postgresql:"].includes(url.protocol)
|| !["127.0.0.1", "localhost", "[::1]"].includes(url.hostname)) {
throw new Error("E2E Postgres administrator must use an explicit loopback test server");
}
await (await prepareRunnerE2EServerConfig(input))?.close();
const config = paperclipConfigSchema.parse(JSON.parse(await readFile(input.configPath, "utf8")));
if (config.database.mode !== "embedded-postgres") throw new Error("E2E database fixture already configured");
const databaseName = `runner_e2e_${randomUUID().replaceAll("-", "")}`;
const admin = createDb(input.adminUrl, { maxConnections: 1 });
let created = false;
const cleanup = async () => {
if (!created) return;
const connection = createDb(input.adminUrl, { maxConnections: 1 });
try {
// No FORCE: a remaining controller keeps its evidence and fails cleanup.
await connection.$client.unsafe(`drop database "${databaseName}"`);
created = false;
} finally { await connection.$client.end({ timeout: 1 }); }
};
try {
await admin.$client.unsafe(`create database "${databaseName}"`);
created = true;
url.pathname = `/${databaseName}`;
config.database = { ...config.database, mode: "postgres", connectionString: url.toString() };
await mkdir(config.database.embeddedPostgresDataDir, { recursive: true, mode: 0o700 });
await recordControllerDatabaseOwnership({ ...input, dataDirectory: config.database.embeddedPostgresDataDir,
connectionString: url.toString(), externalDatabaseName: databaseName });
await writeFile(input.configPath, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
return { cleanup };
} catch (error) {
await cleanup();
throw error;
} finally { await admin.$client.end({ timeout: 1 }); }
}