diff --git a/cli/src/__tests__/helpers/embedded-postgres.ts b/cli/src/__tests__/helpers/embedded-postgres.ts index 8249d98b..4318162a 100644 --- a/cli/src/__tests__/helpers/embedded-postgres.ts +++ b/cli/src/__tests__/helpers/embedded-postgres.ts @@ -1,148 +1,6 @@ -import fs from "node:fs"; -import net from "node:net"; -import os from "node:os"; -import path from "node:path"; -import { applyPendingMigrations, ensurePostgresDatabase } from "@paperclipai/db"; - -type EmbeddedPostgresInstance = { - initialise(): Promise; - start(): Promise; - stop(): Promise; -}; - -type EmbeddedPostgresCtor = new (opts: { - databaseDir: string; - user: string; - password: string; - port: number; - persistent: boolean; - initdbFlags?: string[]; - onLog?: (message: unknown) => void; - onError?: (message: unknown) => void; -}) => EmbeddedPostgresInstance; - -export type EmbeddedPostgresTestSupport = { - supported: boolean; - reason?: string; -}; - -export type EmbeddedPostgresTestDatabase = { - connectionString: string; - cleanup(): Promise; -}; - -let embeddedPostgresSupportPromise: Promise | null = null; - -async function getEmbeddedPostgresCtor(): Promise { - const mod = await import("embedded-postgres"); - return mod.default as EmbeddedPostgresCtor; -} - -async function getAvailablePort(): Promise { - return await new Promise((resolve, reject) => { - const server = net.createServer(); - server.unref(); - server.on("error", reject); - server.listen(0, "127.0.0.1", () => { - const address = server.address(); - if (!address || typeof address === "string") { - server.close(() => reject(new Error("Failed to allocate test port"))); - return; - } - const { port } = address; - server.close((error) => { - if (error) reject(error); - else resolve(port); - }); - }); - }); -} - -function formatEmbeddedPostgresError(error: unknown): string { - if (error instanceof Error && error.message.length > 0) return error.message; - if (typeof error === "string" && error.length > 0) return error; - return "embedded Postgres startup failed"; -} - -async function probeEmbeddedPostgresSupport(): Promise { - if (process.platform !== "darwin") { - return { supported: true }; - } - - const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "paperclip-embedded-postgres-probe-")); - const port = await getAvailablePort(); - const EmbeddedPostgres = await getEmbeddedPostgresCtor(); - const instance = new EmbeddedPostgres({ - databaseDir: dataDir, - user: "paperclip", - password: "paperclip", - port, - persistent: true, - initdbFlags: ["--encoding=UTF8", "--locale=C", "--lc-messages=C"], - onLog: () => {}, - onError: () => {}, - }); - - try { - await instance.initialise(); - await instance.start(); - return { supported: true }; - } catch (error) { - return { - supported: false, - reason: formatEmbeddedPostgresError(error), - }; - } finally { - await instance.stop().catch(() => {}); - fs.rmSync(dataDir, { recursive: true, force: true }); - } -} - -export async function getEmbeddedPostgresTestSupport(): Promise { - if (!embeddedPostgresSupportPromise) { - embeddedPostgresSupportPromise = probeEmbeddedPostgresSupport(); - } - return await embeddedPostgresSupportPromise; -} - -export async function startEmbeddedPostgresTestDatabase( - tempDirPrefix: string, -): Promise { - const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), tempDirPrefix)); - const port = await getAvailablePort(); - const EmbeddedPostgres = await getEmbeddedPostgresCtor(); - const instance = new EmbeddedPostgres({ - databaseDir: dataDir, - user: "paperclip", - password: "paperclip", - port, - persistent: true, - initdbFlags: ["--encoding=UTF8", "--locale=C", "--lc-messages=C"], - onLog: () => {}, - onError: () => {}, - }); - - try { - await instance.initialise(); - await instance.start(); - - const adminConnectionString = `postgres://paperclip:paperclip@127.0.0.1:${port}/postgres`; - await ensurePostgresDatabase(adminConnectionString, "paperclip"); - const connectionString = `postgres://paperclip:paperclip@127.0.0.1:${port}/paperclip`; - await applyPendingMigrations(connectionString); - - return { - connectionString, - cleanup: async () => { - await instance.stop().catch(() => {}); - fs.rmSync(dataDir, { recursive: true, force: true }); - }, - }; - } catch (error) { - await instance.stop().catch(() => {}); - fs.rmSync(dataDir, { recursive: true, force: true }); - throw new Error( - `Failed to start embedded PostgreSQL test database: ${formatEmbeddedPostgresError(error)}`, - ); - } -} +export { + getEmbeddedPostgresTestSupport, + startEmbeddedPostgresTestDatabase, + type EmbeddedPostgresTestDatabase, + type EmbeddedPostgresTestSupport, +} from "@paperclipai/db"; diff --git a/packages/db/src/test-embedded-postgres.ts b/packages/db/src/test-embedded-postgres.ts index 1a959ed5..04fa642d 100644 --- a/packages/db/src/test-embedded-postgres.ts +++ b/packages/db/src/test-embedded-postgres.ts @@ -65,10 +65,6 @@ function formatEmbeddedPostgresError(error: unknown): string { } async function probeEmbeddedPostgresSupport(): Promise { - if (process.platform !== "darwin") { - return { supported: true }; - } - const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "paperclip-embedded-postgres-probe-")); const port = await getAvailablePort(); const EmbeddedPostgres = await getEmbeddedPostgresCtor(); diff --git a/server/src/__tests__/helpers/embedded-postgres.ts b/server/src/__tests__/helpers/embedded-postgres.ts index 8249d98b..4318162a 100644 --- a/server/src/__tests__/helpers/embedded-postgres.ts +++ b/server/src/__tests__/helpers/embedded-postgres.ts @@ -1,148 +1,6 @@ -import fs from "node:fs"; -import net from "node:net"; -import os from "node:os"; -import path from "node:path"; -import { applyPendingMigrations, ensurePostgresDatabase } from "@paperclipai/db"; - -type EmbeddedPostgresInstance = { - initialise(): Promise; - start(): Promise; - stop(): Promise; -}; - -type EmbeddedPostgresCtor = new (opts: { - databaseDir: string; - user: string; - password: string; - port: number; - persistent: boolean; - initdbFlags?: string[]; - onLog?: (message: unknown) => void; - onError?: (message: unknown) => void; -}) => EmbeddedPostgresInstance; - -export type EmbeddedPostgresTestSupport = { - supported: boolean; - reason?: string; -}; - -export type EmbeddedPostgresTestDatabase = { - connectionString: string; - cleanup(): Promise; -}; - -let embeddedPostgresSupportPromise: Promise | null = null; - -async function getEmbeddedPostgresCtor(): Promise { - const mod = await import("embedded-postgres"); - return mod.default as EmbeddedPostgresCtor; -} - -async function getAvailablePort(): Promise { - return await new Promise((resolve, reject) => { - const server = net.createServer(); - server.unref(); - server.on("error", reject); - server.listen(0, "127.0.0.1", () => { - const address = server.address(); - if (!address || typeof address === "string") { - server.close(() => reject(new Error("Failed to allocate test port"))); - return; - } - const { port } = address; - server.close((error) => { - if (error) reject(error); - else resolve(port); - }); - }); - }); -} - -function formatEmbeddedPostgresError(error: unknown): string { - if (error instanceof Error && error.message.length > 0) return error.message; - if (typeof error === "string" && error.length > 0) return error; - return "embedded Postgres startup failed"; -} - -async function probeEmbeddedPostgresSupport(): Promise { - if (process.platform !== "darwin") { - return { supported: true }; - } - - const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "paperclip-embedded-postgres-probe-")); - const port = await getAvailablePort(); - const EmbeddedPostgres = await getEmbeddedPostgresCtor(); - const instance = new EmbeddedPostgres({ - databaseDir: dataDir, - user: "paperclip", - password: "paperclip", - port, - persistent: true, - initdbFlags: ["--encoding=UTF8", "--locale=C", "--lc-messages=C"], - onLog: () => {}, - onError: () => {}, - }); - - try { - await instance.initialise(); - await instance.start(); - return { supported: true }; - } catch (error) { - return { - supported: false, - reason: formatEmbeddedPostgresError(error), - }; - } finally { - await instance.stop().catch(() => {}); - fs.rmSync(dataDir, { recursive: true, force: true }); - } -} - -export async function getEmbeddedPostgresTestSupport(): Promise { - if (!embeddedPostgresSupportPromise) { - embeddedPostgresSupportPromise = probeEmbeddedPostgresSupport(); - } - return await embeddedPostgresSupportPromise; -} - -export async function startEmbeddedPostgresTestDatabase( - tempDirPrefix: string, -): Promise { - const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), tempDirPrefix)); - const port = await getAvailablePort(); - const EmbeddedPostgres = await getEmbeddedPostgresCtor(); - const instance = new EmbeddedPostgres({ - databaseDir: dataDir, - user: "paperclip", - password: "paperclip", - port, - persistent: true, - initdbFlags: ["--encoding=UTF8", "--locale=C", "--lc-messages=C"], - onLog: () => {}, - onError: () => {}, - }); - - try { - await instance.initialise(); - await instance.start(); - - const adminConnectionString = `postgres://paperclip:paperclip@127.0.0.1:${port}/postgres`; - await ensurePostgresDatabase(adminConnectionString, "paperclip"); - const connectionString = `postgres://paperclip:paperclip@127.0.0.1:${port}/paperclip`; - await applyPendingMigrations(connectionString); - - return { - connectionString, - cleanup: async () => { - await instance.stop().catch(() => {}); - fs.rmSync(dataDir, { recursive: true, force: true }); - }, - }; - } catch (error) { - await instance.stop().catch(() => {}); - fs.rmSync(dataDir, { recursive: true, force: true }); - throw new Error( - `Failed to start embedded PostgreSQL test database: ${formatEmbeddedPostgresError(error)}`, - ); - } -} +export { + getEmbeddedPostgresTestSupport, + startEmbeddedPostgresTestDatabase, + type EmbeddedPostgresTestDatabase, + type EmbeddedPostgresTestSupport, +} from "@paperclipai/db";