Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline (Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/* packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
116 lines
4.0 KiB
TypeScript
116 lines
4.0 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import {
|
|
createDb,
|
|
createPool,
|
|
isTooManyDatabaseConnections,
|
|
parsePositiveInteger,
|
|
retryOnTooManyConnections,
|
|
} from "./client.js";
|
|
|
|
const pools: Array<{ end: () => Promise<void> }> = [];
|
|
|
|
afterEach(async () => {
|
|
await Promise.all(pools.splice(0).map((pool) => pool.end()));
|
|
});
|
|
|
|
describe("createDb", () => {
|
|
it("caps the pg pool and swallows idle-client errors so they cannot crash the process", () => {
|
|
const { pool } = createDb("postgres://rakazo:rakazo@127.0.0.1:9/rakazo", {
|
|
poolMax: 3,
|
|
applicationName: "rakazo-test",
|
|
});
|
|
pools.push(pool);
|
|
|
|
expect(pool.options.max).toBe(3);
|
|
expect(pool.options.connectionTimeoutMillis).toBe(10_000);
|
|
expect(pool.options.idleTimeoutMillis).toBe(0);
|
|
expect(pool.options.application_name).toBe("rakazo-test");
|
|
expect(pool.listenerCount("error")).toBeGreaterThan(0);
|
|
expect(pool.listenerCount("connect")).toBeGreaterThan(0);
|
|
|
|
expect(() => pool.emit("error", new Error("idle client lost"))).not.toThrow();
|
|
});
|
|
|
|
it("defaults to a four-connection pool", () => {
|
|
const { pool } = createDb("postgres://rakazo:rakazo@127.0.0.1:9/rakazo");
|
|
pools.push(pool);
|
|
expect(pool.options.max).toBe(4);
|
|
});
|
|
});
|
|
|
|
describe("parsePositiveInteger", () => {
|
|
it("falls back when the value is missing, zero, or not an integer", () => {
|
|
expect(parsePositiveInteger(undefined, 8)).toBe(8);
|
|
expect(parsePositiveInteger("0", 8)).toBe(8);
|
|
expect(parsePositiveInteger("nope", 8)).toBe(8);
|
|
expect(parsePositiveInteger("6", 8)).toBe(6);
|
|
});
|
|
});
|
|
|
|
describe("isTooManyDatabaseConnections", () => {
|
|
it("recognises Prisma P2037 and Postgres 53300", () => {
|
|
expect(isTooManyDatabaseConnections({ code: "P2037" })).toBe(true);
|
|
expect(isTooManyDatabaseConnections({ code: "53300" })).toBe(true);
|
|
expect(
|
|
isTooManyDatabaseConnections(
|
|
new Error("Too many database connections opened: sorry, too many clients already"),
|
|
),
|
|
).toBe(true);
|
|
expect(isTooManyDatabaseConnections(new Error("relation does not exist"))).toBe(false);
|
|
expect(isTooManyDatabaseConnections(undefined)).toBe(false);
|
|
});
|
|
|
|
it("walks a nested cause, matching the pg-pool unhandledRejection shape", () => {
|
|
const root = Object.assign(new Error("sorry, too many clients already"), { code: "53300" });
|
|
expect(isTooManyDatabaseConnections(new Error("pool.connect failed", { cause: root }))).toBe(
|
|
true,
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("retryOnTooManyConnections", () => {
|
|
it("retries 53300 and returns the first success", async () => {
|
|
const sleep = vi.fn(async () => undefined);
|
|
let attempts = 0;
|
|
const result = await retryOnTooManyConnections(
|
|
async () => {
|
|
attempts += 1;
|
|
if (attempts < 3) {
|
|
throw Object.assign(new Error("sorry, too many clients already"), { code: "53300" });
|
|
}
|
|
return "ok";
|
|
},
|
|
{ sleep },
|
|
);
|
|
expect(result).toBe("ok");
|
|
expect(attempts).toBe(3);
|
|
expect(sleep).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it("does not retry unrelated failures", async () => {
|
|
const error = new Error("relation does not exist");
|
|
await expect(
|
|
retryOnTooManyConnections(async () => {
|
|
throw error;
|
|
}),
|
|
).rejects.toBe(error);
|
|
});
|
|
});
|
|
|
|
describe("createPool", () => {
|
|
it("builds a bounded pool with connect-retry and idle-error listeners", () => {
|
|
const pool = createPool("postgres://rakazo:rakazo@127.0.0.1:9/rakazo", {
|
|
poolMax: 2,
|
|
applicationName: "rakazo-pool-test",
|
|
});
|
|
pools.push(pool);
|
|
expect(pool.options.max).toBe(2);
|
|
expect(pool.options.connectionTimeoutMillis).toBe(10_000);
|
|
expect(pool.options.idleTimeoutMillis).toBe(0);
|
|
expect(pool.options.application_name).toBe("rakazo-pool-test");
|
|
expect(pool.listenerCount("error")).toBeGreaterThan(0);
|
|
expect(pool.listenerCount("connect")).toBeGreaterThan(0);
|
|
expect(() => pool.emit("error", new Error("idle client lost"))).not.toThrow();
|
|
});
|
|
});
|