113 lines
3.9 KiB
TypeScript
113 lines
3.9 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
|
|
const clientConstructor = vi.hoisted(() => vi.fn());
|
|
|
|
vi.mock("pg", () => ({ Client: clientConstructor }));
|
|
|
|
import {
|
|
createPostgresMigrationDatabase,
|
|
PostgresMigrationDatabaseError,
|
|
} from "../src/migrations/postgres-database.js";
|
|
|
|
describe("PostgreSQL migration database composition", () => {
|
|
it("does not construct a client or execute SQL merely because the adapter is imported", () => {
|
|
expect(clientConstructor).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it.each([
|
|
undefined,
|
|
"",
|
|
"not-a-database-url",
|
|
"https://admin:[email protected]/database",
|
|
])("rejects missing or invalid configuration without exposing it (%s)", (databaseUrl) => {
|
|
let captured: unknown;
|
|
try {
|
|
createPostgresMigrationDatabase(databaseUrl);
|
|
} catch (error) {
|
|
captured = error;
|
|
}
|
|
|
|
expect(captured).toBeInstanceOf(PostgresMigrationDatabaseError);
|
|
expect(captured).toMatchObject({ code: "POSTGRES_MIGRATION_CONFIGURATION_INVALID" });
|
|
const publicText = `${String(captured)}\n${
|
|
captured instanceof Error ? (captured.stack ?? "") : ""
|
|
}\n${JSON.stringify(captured)}`;
|
|
if (databaseUrl) {
|
|
expect(publicText).not.toContain(databaseUrl);
|
|
}
|
|
expect(clientConstructor).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("creates a lazy database composition without opening a connection", () => {
|
|
const database = createPostgresMigrationDatabase(
|
|
"postgresql://operator:[email protected]/ekklesia",
|
|
);
|
|
|
|
expect(Object.isFrozen(database)).toBe(true);
|
|
expect(clientConstructor).not.toHaveBeenCalled();
|
|
expect(JSON.stringify(database)).not.toContain("operator");
|
|
expect(JSON.stringify(database)).not.toContain("do-not-log");
|
|
expect(JSON.stringify(database)).not.toContain("example.test");
|
|
});
|
|
|
|
it.each([
|
|
["connectionTimeoutMs", 0],
|
|
["connectionTimeoutMs", -1],
|
|
["connectionTimeoutMs", 1.5],
|
|
["connectionTimeoutMs", Number.NaN],
|
|
["connectionTimeoutMs", Number.POSITIVE_INFINITY],
|
|
["connectionTimeoutMs", 2_147_483_648],
|
|
["operationTimeoutMs", 0],
|
|
["operationTimeoutMs", -1],
|
|
["operationTimeoutMs", 1.5],
|
|
["operationTimeoutMs", Number.NaN],
|
|
["operationTimeoutMs", Number.POSITIVE_INFINITY],
|
|
["operationTimeoutMs", 2_147_483_648],
|
|
] as const)("rejects an invalid %s value without constructing a client", (name, value) => {
|
|
let captured: unknown;
|
|
try {
|
|
createPostgresMigrationDatabase("postgresql://example.test/ekklesia", {
|
|
[name]: value,
|
|
});
|
|
} catch (error) {
|
|
captured = error;
|
|
}
|
|
|
|
expect(captured).toBeInstanceOf(PostgresMigrationDatabaseError);
|
|
expect(captured).toMatchObject({ code: "POSTGRES_MIGRATION_CONFIGURATION_INVALID" });
|
|
expect(clientConstructor).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("passes bounded connection and operation timeouts to the PostgreSQL client", async () => {
|
|
const client = {
|
|
connect: vi.fn().mockResolvedValue(undefined),
|
|
end: vi.fn().mockResolvedValue(undefined),
|
|
on: vi.fn(),
|
|
};
|
|
const MockClient = class {
|
|
readonly connect = client.connect;
|
|
readonly end = client.end;
|
|
readonly on = client.on;
|
|
};
|
|
clientConstructor.mockImplementation(MockClient as unknown as () => typeof client);
|
|
|
|
const database = createPostgresMigrationDatabase("postgresql://example.test/ekklesia", {
|
|
connectionTimeoutMs: 10_000,
|
|
operationTimeoutMs: 12_000,
|
|
});
|
|
const session = await database.openSession();
|
|
|
|
expect(clientConstructor).toHaveBeenCalledWith({
|
|
connectionString: "postgresql://example.test/ekklesia",
|
|
connectionTimeoutMillis: 10_000,
|
|
query_timeout: 12_000,
|
|
statement_timeout: 12_000,
|
|
});
|
|
expect(client.on).toHaveBeenCalledWith("error", expect.any(Function));
|
|
expect(client.connect).toHaveBeenCalledOnce();
|
|
|
|
await session.close();
|
|
expect(client.end).toHaveBeenCalledOnce();
|
|
});
|
|
});
|