test: own maintenance artifacts and honor PostgreSQL support

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-30 23:07:34 -05:00
1 parent 4de3d713d8
commit 7be7896b84
6 files changed
+246 -96

No files matched your search

@@ -0,0 +1,24 @@
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { getEmbeddedPostgresTestSupport, type EmbeddedPostgresTestSupport } from "../embedded-postgres.js";
export async function maintenanceDatabaseSupport(databaseUrl?: string): Promise<EmbeddedPostgresTestSupport> {
return databaseUrl ? { supported: true } : getEmbeddedPostgresTestSupport();
}
export async function createMaintenanceArtifacts(root = tmpdir()) {
const directory = await mkdtemp(path.join(root, "paperclip-maintenance-"));
const retainedDirectory = process.env.PAPERCLIP_MAINTENANCE_KEEP_ARTIFACTS === "1" ? directory : undefined;
let disposal: Promise<void> | undefined;
return {
directory,
retainedDirectory,
dispose() {
return disposal ??= (async () => {
if (retainedDirectory) process.stdout.write(`[maintenance-test] retained artifacts: ${retainedDirectory}\n`);
else await rm(directory, { recursive: true, force: true });
})();
},
};
}
@@ -1,13 +1,13 @@
import { fork } from "node:child_process";
import { randomBytes, randomUUID } from "node:crypto";
import { createWriteStream } from "node:fs";
import { mkdtemp, writeFile } from "node:fs/promises";
import { writeFile } from "node:fs/promises";
import { createServer } from "node:net";
import { tmpdir } from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { setTimeout as delay } from "node:timers/promises";
import { startMaintenancePostgresProxy } from "./postgres-proxy.js";
import { createMaintenanceArtifacts } from "./environment.js";
async function withDeadline<T>(promise: Promise<T>, ms: number, message: () => string): Promise<T> {
const controller = new AbortController();
@@ -18,102 +18,123 @@ async function withDeadline<T>(promise: Promise<T>, ms: number, message: () => s
type Snapshot = { idle: { active: number; generation: number }; startup: { phase: string }; outgoingHttpRequests: number; network: { nonDatabaseConnections: number; databaseConnections: number; nonDatabaseBytesWritten: number }; now: number };
export async function startMaintenanceServer(databaseUrl: string, options: { enabled?: boolean; clock?: boolean; suppressed?: boolean; githubOrigin?: string; connector?: { origin: string; signPrivateKey: string; sealPrivateKey: string }; cloud?: { jwks: string; s3Origin: string; standby: boolean } } = {}) {
const proxy = await startMaintenancePostgresProxy(databaseUrl);
const directory = await mkdtemp(path.join(tmpdir(), "paperclip-maintenance-"));
export async function startMaintenanceServer(databaseUrl: string, options: { artifactRoot?: string; enabled?: boolean; clock?: boolean; suppressed?: boolean; githubOrigin?: string; connector?: { origin: string; signPrivateKey: string; sealPrivateKey: string }; cloud?: { jwks: string; s3Origin: string; standby: boolean } } = {}) {
const artifacts = await createMaintenanceArtifacts(options.artifactRoot);
const { directory, retainedDirectory } = artifacts;
const reservation = createServer();
await new Promise<void>(resolve => reservation.listen(0, "127.0.0.1", resolve));
const address = reservation.address();
if (!address || typeof address === "string") throw new Error("Maintenance HTTP reservation failed");
const configPath = path.join(directory, "config.json");
await writeFile(configPath, JSON.stringify({
$meta: { version: 1, updatedAt: new Date().toISOString(), source: "onboard" },
database: { mode: "postgres", connectionString: proxy.url, backup: { enabled: false, dir: path.join(directory, "backups") } },
logging: { mode: "file", logDir: path.join(directory, "logs") },
server: { deploymentMode: "local_trusted", exposure: "private", bind: "loopback", host: "127.0.0.1", port: address.port, serveUi: false },
storage: options.cloud ? { provider: "s3", s3: { bucket: "maintenance-test", region: "us-east-1", endpoint: options.cloud.s3Origin, forcePathStyle: true } }
: { provider: "local_disk", localDisk: { baseDir: path.join(directory, "storage") } },
secrets: { provider: "local_encrypted", strictMode: false, localEncrypted: { keyFilePath: path.join(directory, "master.key") } },
telemetry: { enabled: false }, updates: { checkEnabled: false },
}), { mode: 0o600 });
await new Promise<void>(resolve => reservation.close(() => resolve()));
const child = fork(fileURLToPath(new URL("./server-child.ts", import.meta.url)), [], {
cwd: fileURLToPath(new URL("../../../../..", import.meta.url)),
execArgv: ["--import", fileURLToPath(new URL("../../../../node_modules/tsx/dist/loader.mjs", import.meta.url))],
env: {
PATH: process.env.PATH, TMPDIR: process.env.TMPDIR, NODE_ENV: "production",
PAPERCLIP_HOME: directory, PAPERCLIP_CONFIG: configPath, PAPERCLIP_INSTANCE_ID: "maintenance-test",
PAPERCLIP_RUNNER_STATE_DIR: path.join(directory, "durable-sessions"),
DATABASE_URL: proxy.url, DATABASE_POOL_MAX: "6", DATABASE_APPLICATION_NAME: "maintenance-integration-test",
PAPERCLIP_AGENT_JWT_SECRET: randomUUID(), PAPERCLIP_DECISION_SIGNING_SECRET: randomUUID(),
PAPERCLIP_TOOL_ACTION_SIGNING_SECRET: randomUUID(), BETTER_AUTH_SECRET: randomUUID(),
CODEX_HOME: path.join(directory, "codex"), PAPERCLIP_ANNOUNCEMENTS_ENABLED: "false",
PAPERCLIP_DB_BACKUP_ENABLED: "false", PAPERCLIP_UI_DEV_MIDDLEWARE: "false",
HEARTBEAT_SCHEDULER_ENABLED: String(options.enabled ?? true),
PAPERCLIP_MAINTENANCE_FAKE_CLOCK: String(options.clock ?? false),
...(options.suppressed ? { PAPERCLIP_IN_WORKTREE: "true" } : {}),
...(options.githubOrigin ? { PAPERCLIP_MAINTENANCE_GITHUB_ORIGIN: options.githubOrigin } : {}),
...(options.connector ? {
PAPERCLIP_CLOUD_CONNECTOR_INSTANCE_ID: "maintenance-connector", PAPERCLIP_CLOUD_CONNECTOR_ENVIRONMENT: "test",
PAPERCLIP_CLOUD_CONNECTOR_SIGN_PRIVATE_KEY: options.connector.signPrivateKey,
PAPERCLIP_CLOUD_CONNECTOR_SEAL_PRIVATE_KEY: options.connector.sealPrivateKey,
PAPERCLIP_CLOUD_CONNECTOR_BASE_URL: options.connector.origin,
} : {}),
...(options.cloud ? {
PAPERCLIP_MANAGED_CONFIG: JSON.stringify({ v: 1, mode: "cloud", catalogVersion: "2026.720.0", features: {}, plugins: { autoInstall: [] } }),
PAPERCLIP_SECRETS_MASTER_KEY: randomBytes(32).toString("hex"),
PAPERCLIP_CLOUD_TENANT_SERVER_TOKEN: "maintenance-tenant-token", PAPERCLIP_CLOUD_STACK_ID: "maintenance-stack",
PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS: options.cloud.jwks, PAPERCLIP_CLOUD_WARM_STANDBY: options.cloud.standby ? "1" : "0",
PAPERCLIP_PUBLIC_URL: "https://pool-maintenance.staging.paperclip.app", PAPERCLIP_PRIMARY_HOST: "pool-maintenance.staging.paperclip.app",
AWS_ACCESS_KEY_ID: "maintenance-fixture", AWS_SECRET_ACCESS_KEY: "maintenance-fixture", AWS_EC2_METADATA_DISABLED: "true",
} : {}),
PAPERCLIP_MIGRATION_PROMPT: "never", PAPERCLIP_MIGRATION_AUTO_APPLY: "true", PAPERCLIP_MANAGED_RUNTIME_HTTPS: "off",
},
stdio: ["ignore", "pipe", "pipe", "ipc"],
});
let output = "", requestId = 0;
const log = createWriteStream(path.join(directory, "server.log"));
child.stdout?.on("data", chunk => { output += String(chunk); log.write(chunk); });
child.stderr?.on("data", chunk => { output += String(chunk); log.write(chunk); });
const requests = new Map<number, { resolve: (value: any) => void; reject: (error: Error) => void }>();
let readyResolve: (port: number) => void, readyReject: (error: Error) => void;
const ready = new Promise<number>((resolve, reject) => { readyResolve = resolve; readyReject = reject; });
const exited = new Promise<void>(resolve => child.once("exit", () => resolve()));
child.on("message", (message: any) => {
if (message.kind === "ready") readyResolve(message.port);
else if (message.id) {
const request = requests.get(message.id); requests.delete(message.id);
if (message.kind === "error") request?.reject(new Error(message.error)); else request?.resolve(message);
} else if (message.kind === "error") readyReject(new Error(message.error));
});
child.once("error", error => readyReject(error));
child.once("exit", (code, signal) => {
const error = new Error(`Maintenance child exited (${code}/${signal}): ${output.slice(-12_000)}`);
readyReject(error);
for (const request of requests.values()) request.reject(error);
requests.clear();
});
async function request<T>(command: string, payload: { ms?: number; issueUpdate?: { id: string; status: "done" }; orphanLease?: { companyId: string }; publication?: { companyId: string; scopeDigest: string; backupRoot: string; pendingRoot: string } } = {}): Promise<T> {
const id = ++requestId;
const result = new Promise<T>((resolve, reject) => requests.set(id, { resolve, reject }));
child.send({ id, command, ...payload });
try { return await withDeadline(result, 30_000, () => `Maintenance child ${command} timed out: ${output.slice(-8_000)}`); }
finally { requests.delete(id); }
}
async function stop() {
try {
if (child.exitCode === null && child.signalCode === null && child.connected) await request("shutdown");
await withDeadline(exited, 30_000, () => "Maintenance child did not exit after shutdown");
} finally {
if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
await proxy.stop();
await new Promise<void>(resolve => log.end(resolve));
}
let stopProxy = async () => {};
let stopChild = async () => {};
let stopLog = async () => {};
let stopping: Promise<void> | undefined;
function stop() {
return stopping ??= (async () => {
const errors: unknown[] = [];
for (const cleanup of [stopChild, stopProxy,
async () => { if (reservation.listening) await new Promise<void>(resolve => reservation.close(() => resolve())); },
stopLog, artifacts.dispose]) {
try { await cleanup(); } catch (error) { errors.push(error); }
}
if (errors.length) throw new AggregateError(errors, "Maintenance cleanup failed");
})();
}
try {
const proxy = await startMaintenancePostgresProxy(databaseUrl);
stopProxy = proxy.stop;
await new Promise<void>((resolve, reject) => { reservation.once("error", reject); reservation.listen(0, "127.0.0.1", resolve); });
const address = reservation.address();
if (!address || typeof address === "string") throw new Error("Maintenance HTTP reservation failed");
const configPath = path.join(directory, "config.json");
await writeFile(configPath, JSON.stringify({
$meta: { version: 1, updatedAt: new Date().toISOString(), source: "onboard" },
database: { mode: "postgres", connectionString: proxy.url, backup: { enabled: false, dir: path.join(directory, "backups") } },
logging: { mode: "file", logDir: path.join(directory, "logs") },
server: { deploymentMode: "local_trusted", exposure: "private", bind: "loopback", host: "127.0.0.1", port: address.port, serveUi: false },
storage: options.cloud ? { provider: "s3", s3: { bucket: "maintenance-test", region: "us-east-1", endpoint: options.cloud.s3Origin, forcePathStyle: true } }
: { provider: "local_disk", localDisk: { baseDir: path.join(directory, "storage") } },
secrets: { provider: "local_encrypted", strictMode: false, localEncrypted: { keyFilePath: path.join(directory, "master.key") } },
telemetry: { enabled: false }, updates: { checkEnabled: false },
}), { mode: 0o600 });
await new Promise<void>(resolve => reservation.close(() => resolve()));
const child = fork(fileURLToPath(new URL("./server-child.ts", import.meta.url)), [], {
cwd: fileURLToPath(new URL("../../../../..", import.meta.url)),
execArgv: ["--import", fileURLToPath(new URL("../../../../node_modules/tsx/dist/loader.mjs", import.meta.url))],
env: {
PATH: process.env.PATH, TMPDIR: process.env.TMPDIR, NODE_ENV: "production",
PAPERCLIP_HOME: directory, PAPERCLIP_CONFIG: configPath, PAPERCLIP_INSTANCE_ID: "maintenance-test",
PAPERCLIP_RUNNER_STATE_DIR: path.join(directory, "durable-sessions"),
DATABASE_URL: proxy.url, DATABASE_POOL_MAX: "6", DATABASE_APPLICATION_NAME: "maintenance-integration-test",
PAPERCLIP_AGENT_JWT_SECRET: randomUUID(), PAPERCLIP_DECISION_SIGNING_SECRET: randomUUID(),
PAPERCLIP_TOOL_ACTION_SIGNING_SECRET: randomUUID(), BETTER_AUTH_SECRET: randomUUID(),
CODEX_HOME: path.join(directory, "codex"), PAPERCLIP_ANNOUNCEMENTS_ENABLED: "false",
PAPERCLIP_DB_BACKUP_ENABLED: "false", PAPERCLIP_UI_DEV_MIDDLEWARE: "false",
HEARTBEAT_SCHEDULER_ENABLED: String(options.enabled ?? true),
PAPERCLIP_MAINTENANCE_FAKE_CLOCK: String(options.clock ?? false),
...(options.suppressed ? { PAPERCLIP_IN_WORKTREE: "true" } : {}),
...(options.githubOrigin ? { PAPERCLIP_MAINTENANCE_GITHUB_ORIGIN: options.githubOrigin } : {}),
...(options.connector ? {
PAPERCLIP_CLOUD_CONNECTOR_INSTANCE_ID: "maintenance-connector", PAPERCLIP_CLOUD_CONNECTOR_ENVIRONMENT: "test",
PAPERCLIP_CLOUD_CONNECTOR_SIGN_PRIVATE_KEY: options.connector.signPrivateKey,
PAPERCLIP_CLOUD_CONNECTOR_SEAL_PRIVATE_KEY: options.connector.sealPrivateKey,
PAPERCLIP_CLOUD_CONNECTOR_BASE_URL: options.connector.origin,
} : {}),
...(options.cloud ? {
PAPERCLIP_MANAGED_CONFIG: JSON.stringify({ v: 1, mode: "cloud", catalogVersion: "2026.720.0", features: {}, plugins: { autoInstall: [] } }),
PAPERCLIP_SECRETS_MASTER_KEY: randomBytes(32).toString("hex"),
PAPERCLIP_CLOUD_TENANT_SERVER_TOKEN: "maintenance-tenant-token", PAPERCLIP_CLOUD_STACK_ID: "maintenance-stack",
PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS: options.cloud.jwks, PAPERCLIP_CLOUD_WARM_STANDBY: options.cloud.standby ? "1" : "0",
PAPERCLIP_PUBLIC_URL: "https://pool-maintenance.staging.paperclip.app", PAPERCLIP_PRIMARY_HOST: "pool-maintenance.staging.paperclip.app",
AWS_ACCESS_KEY_ID: "maintenance-fixture", AWS_SECRET_ACCESS_KEY: "maintenance-fixture", AWS_EC2_METADATA_DISABLED: "true",
} : {}),
PAPERCLIP_MIGRATION_PROMPT: "never", PAPERCLIP_MIGRATION_AUTO_APPLY: "true", PAPERCLIP_MANAGED_RUNTIME_HTTPS: "off",
},
stdio: ["ignore", "pipe", "pipe", "ipc"],
});
let output = "", requestId = 0;
const log = createWriteStream(path.join(directory, "server.log"));
stopLog = () => new Promise<void>(resolve => log.end(resolve));
let serverReady = false;
child.stdout?.on("data", chunk => { output += String(chunk); log.write(chunk); });
child.stderr?.on("data", chunk => { output += String(chunk); log.write(chunk); });
const requests = new Map<number, { resolve: (value: any) => void; reject: (error: Error) => void }>();
let readyResolve: (port: number) => void, readyReject: (error: Error) => void;
const ready = new Promise<number>((resolve, reject) => { readyResolve = resolve; readyReject = reject; });
const exited = new Promise<void>(resolve => child.once("exit", () => resolve()));
child.on("message", (message: any) => {
if (message.kind === "ready") { serverReady = true; readyResolve(message.port); }
else if (message.id) {
const request = requests.get(message.id); requests.delete(message.id);
if (message.kind === "error") request?.reject(new Error(message.error)); else request?.resolve(message);
} else if (message.kind === "error") readyReject(new Error(message.error));
});
child.once("error", error => readyReject(error));
child.once("exit", (code, signal) => {
const error = new Error(`Maintenance child exited (${code}/${signal}): ${output.slice(-12_000)}`);
readyReject(error);
for (const request of requests.values()) request.reject(error);
requests.clear();
});
async function request<T>(command: string, payload: { ms?: number; issueUpdate?: { id: string; status: "done" }; orphanLease?: { companyId: string }; publication?: { companyId: string; scopeDigest: string; backupRoot: string; pendingRoot: string } } = {}): Promise<T> {
const id = ++requestId;
const result = new Promise<T>((resolve, reject) => requests.set(id, { resolve, reject }));
child.send({ id, command, ...payload });
try { return await withDeadline(result, 30_000, () => `Maintenance child ${command} timed out: ${output.slice(-8_000)}`); }
finally { requests.delete(id); }
}
stopChild = async () => {
try {
if (!serverReady && child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
else if (child.exitCode === null && child.signalCode === null && child.connected) await request("shutdown");
await withDeadline(exited, 30_000, () => "Maintenance child did not exit after shutdown");
} finally {
if (child.exitCode === null && child.signalCode === null) {
child.kill("SIGKILL");
await withDeadline(exited, 5_000, () => "Maintenance child did not exit after cancellation");
}
}
};
const port = await withDeadline(ready, 90_000, () => `Maintenance boot timed out: ${output.slice(-12_000)}`);
return {
proxy, directory, port, stop,
proxy, directory, retainedDirectory, port, stop,
async api<T>(method: string, route: string, body?: unknown, headers: Record<string, string> = {}): Promise<T> {
if (options.clock) await request("realtime");
try {
@@ -153,5 +174,9 @@ export async function startMaintenanceServer(databaseUrl: string, options: { ena
throw new Error(`Maintenance did not settle: ${JSON.stringify({ queries: proxy.queries.length, pending: proxy.pending, snapshot: await request("snapshot") })}\n${output.slice(-12_000)}`);
},
};
} catch (error) { await stop().catch(() => {}); throw error; }
} catch (error) {
try { await stop(); }
catch (cleanupError) { throw new AggregateError([error, cleanupError], "Maintenance startup and cleanup failed"); }
throw error;
}
}
@@ -0,0 +1,74 @@
import { access, mkdtemp, readdir, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { createMaintenanceArtifacts, maintenanceDatabaseSupport } from "./helpers/maintenance/environment.js";
import { startMaintenanceServer } from "./helpers/maintenance/server-harness.js";
const embeddedSupport = vi.hoisted(() => vi.fn());
vi.mock("./helpers/embedded-postgres.js", () => ({ getEmbeddedPostgresTestSupport: embeddedSupport }));
let root: string | undefined;
beforeEach(() => { vi.clearAllMocks(); vi.stubEnv("PAPERCLIP_MAINTENANCE_KEEP_ARTIFACTS", ""); });
afterEach(async () => {
vi.unstubAllEnvs(); vi.restoreAllMocks();
if (root) await rm(root, { recursive: true, force: true });
root = undefined;
});
async function ownedRoot() {
return root = await mkdtemp(path.join(tmpdir(), "maintenance-environment-test-"));
}
describe("maintenance database support", () => {
it("does not probe or skip an explicitly configured acceptance database", async () => {
embeddedSupport.mockRejectedValue(new Error("Embedded PostgreSQL must not be launched"));
expect(await maintenanceDatabaseSupport("postgresql://fixture/owned_test")).toEqual({ supported: true });
expect(embeddedSupport).not.toHaveBeenCalled();
});
it("allows a supported default embedded environment", async () => {
embeddedSupport.mockResolvedValue({ supported: true });
expect(await maintenanceDatabaseSupport()).toEqual({ supported: true });
expect(embeddedSupport).toHaveBeenCalledOnce();
});
it("returns the standard unsupported reason to the suite gate", async () => {
embeddedSupport.mockResolvedValue({ supported: false, reason: "No usable initdb" });
expect(await maintenanceDatabaseSupport()).toEqual({ supported: false, reason: "No usable initdb" });
expect(embeddedSupport).toHaveBeenCalledOnce();
});
});
describe("owned maintenance artifacts", () => {
it("removes owned config and receipt files by default, including repeated disposal", async () => {
const artifacts = await createMaintenanceArtifacts(await ownedRoot());
await writeFile(path.join(artifacts.directory, "measurement.json"), "{}");
expect(artifacts.retainedDirectory).toBeUndefined();
await artifacts.dispose(); await artifacts.dispose();
await expect(access(artifacts.directory)).rejects.toMatchObject({ code: "ENOENT" });
expect(await readdir(root!)).toEqual([]);
});
it("retains receipts only with explicit opt-in and exposes their path once", async () => {
vi.stubEnv("PAPERCLIP_MAINTENANCE_KEEP_ARTIFACTS", "1");
const print = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
const artifacts = await createMaintenanceArtifacts(await ownedRoot());
const receipt = path.join(artifacts.directory, "measurement.json");
await writeFile(receipt, "{}");
await artifacts.dispose(); await artifacts.dispose();
await access(receipt);
expect(artifacts.retainedDirectory).toBe(artifacts.directory);
expect(print).toHaveBeenCalledExactlyOnceWith(`[maintenance-test] retained artifacts: ${artifacts.directory}\n`);
});
it("cleans the owned directory when setup fails before the child starts", async () => {
const parent = await ownedRoot();
await expect(startMaintenanceServer("not-a-database-url", { artifactRoot: parent })).rejects.toThrow("Invalid URL");
expect(await readdir(parent)).toEqual([]);
});
it("prints retained diagnostics when failed startup was explicitly opted in", async () => {
vi.stubEnv("PAPERCLIP_MAINTENANCE_KEEP_ARTIFACTS", "1");
const print = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
const parent = await ownedRoot();
await expect(startMaintenanceServer("not-a-database-url", { artifactRoot: parent })).rejects.toThrow("Invalid URL");
const [name] = await readdir(parent);
expect(name).toMatch(/^paperclip-maintenance-/);
expect(print).toHaveBeenCalledExactlyOnceWith(`[maintenance-test] retained artifacts: ${path.join(parent, name!)}\n`);
});
});
@@ -2,6 +2,7 @@ import { createDb } from "@paperclipai/db";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { startEmbeddedPostgresTestDatabase } from "./helpers/embedded-postgres.js";
import { resolveExternalTestDatabaseUrl } from "./helpers/external-test-database.js";
import { maintenanceDatabaseSupport } from "./helpers/maintenance/environment.js";
import { createPostgresProtocolObserver, startMaintenancePostgresProxy, type ObservedQuery } from "./helpers/maintenance/postgres-proxy.js";
const startup = Buffer.from([0, 0, 0, 8, 0, 3, 0, 0]);
@@ -44,7 +45,10 @@ describe("passive maintenance PostgreSQL observer", () => {
let databaseUrl = resolveExternalTestDatabaseUrl("PAPERCLIP_MAINTENANCE_TEST_DATABASE_URL");
let temporary: Awaited<ReturnType<typeof startEmbeddedPostgresTestDatabase>> | undefined;
describe("maintenance observer against PostgreSQL", () => {
const databaseSupport = await maintenanceDatabaseSupport(databaseUrl);
const describeDatabase = databaseSupport.supported ? describe : describe.skip;
if (!databaseSupport.supported) console.warn(`Skipping maintenance PostgreSQL tests: ${databaseSupport.reason ?? "unsupported environment"}`);
describeDatabase("maintenance observer against PostgreSQL", () => {
beforeAll(async () => {
if (!databaseUrl) { temporary = await startEmbeddedPostgresTestDatabase("maintenance-observer-"); databaseUrl = temporary.connectionString; }
}, 90_000);
@@ -7,12 +7,16 @@ import { setTimeout as delay } from "node:timers/promises";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { startEmbeddedPostgresTestDatabase } from "./helpers/embedded-postgres.js";
import { resolveExternalTestDatabaseUrl } from "./helpers/external-test-database.js";
import { maintenanceDatabaseSupport } from "./helpers/maintenance/environment.js";
import { CLOUD_RUNTIME_IDENTITY_AUDIENCE, CLOUD_RUNTIME_IDENTITY_ISSUER, CLOUD_RUNTIME_IDENTITY_JWS_TYPE } from "../services/cloud-runtime-identity.js";
import { prepareMaintenanceRuntime } from "./helpers/maintenance/runtime-build.js";
import { startMaintenanceServer } from "./helpers/maintenance/server-harness.js";
let databaseUrl = resolveExternalTestDatabaseUrl("PAPERCLIP_MAINTENANCE_BOOT_TEST_DATABASE_URL");
let temporary: Awaited<ReturnType<typeof startEmbeddedPostgresTestDatabase>> | undefined;
const databaseSupport = await maintenanceDatabaseSupport(databaseUrl);
const describeDatabase = databaseSupport.supported ? describe : describe.skip;
if (!databaseSupport.supported) console.warn(`Skipping maintenance PostgreSQL tests: ${databaseSupport.reason ?? "unsupported environment"}`);
async function clearDatabase() {
if (!temporary && !new URL(databaseUrl!).pathname.endsWith("_test")) throw new Error("Maintenance boot requires an owned disposable _test database");
const fixture = createDb(databaseUrl!, { maxConnections: 1 });
@@ -39,7 +43,7 @@ function receipt(queries: Array<{ protocol: string; sql: string; fingerprint: st
}
return [...groups.values()];
}
describe("assembled maintenance server production wiring", () => {
describeDatabase("assembled maintenance server production wiring", () => {
beforeAll(async () => {
await prepareMaintenanceRuntime();
if (!databaseUrl) { temporary = await startEmbeddedPostgresTestDatabase("maintenance-server-"); databaseUrl = temporary.connectionString; }
@@ -244,6 +248,8 @@ describe("assembled maintenance server production wiring", () => {
await fixture.$client.unsafe("drop function if exists maintenance_age_isolation_attention()").catch(() => {});
await server?.stop(); await fixture.$client.end();
}
if (server!.retainedDirectory) await access(server!.retainedDirectory);
else await expect(access(server!.directory)).rejects.toMatchObject({ code: "ENOENT" });
}, 150_000);
it("exposes readiness while real deferred housekeeping is held and settles its work after release", async () => {