Files
PaperClipAI/packages/db/src/backup-lib-binary.test.ts
T
2026-11-03 10:47:19 -06:00

68 lines
3.8 KiB
TypeScript

import { mkdtemp, readFile, rm } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { gunzipSync } from "node:zlib";
import { describe, expect, it, vi } from "vitest";
import { runDatabaseBackup } from "./backup-lib.js";
const transport = vi.hoisted(() => ({ rows: [] as unknown[][], projection: "", end: vi.fn() }));
vi.mock("postgres", () => ({
default: () => {
const columns = [
{ column_name: "bytes", data_type: "bytea", udt_name: "bytea", udt_schema: "pg_catalog" },
{ column_name: "payloads", data_type: "ARRAY", udt_name: "_bytea", udt_schema: "pg_catalog" },
{ column_name: "metadata", data_type: "jsonb", udt_name: "jsonb", udt_schema: "pg_catalog" },
{ column_name: "redacted", data_type: "ARRAY", udt_name: "_bytea", udt_schema: "pg_catalog" },
];
const sql = async (parts: TemplateStringsArray) => {
const query = parts.join("?");
if (query.includes("FROM information_schema.tables")) return [{ schema_name: "public", tablename: "binary_fixture" }];
if (query.includes("FROM information_schema.columns")) return columns.map(column => ({ ...column, udt_schema: "pg_catalog", is_nullable: "YES", is_identity: "NO", column_default: null }));
return [];
};
return Object.assign(sql, {
end: transport.end,
unsafe: (query: string) => query.startsWith("SELECT count(*)") ? Promise.resolve([{ n: transport.rows.length }])
: query.startsWith("SELECT ") && query.includes('FROM "public"."binary_fixture"')
? { values: () => ({ cursor: async function* () { transport.projection = query; yield transport.rows; } }) }
: query,
});
},
}));
describe("JavaScript backup binary SQL", () => {
it("writes exact binary scalars and nested arrays without changing JSON or redaction", async () => {
const directory = await mkdtemp(path.join(os.tmpdir(), "backup-binary-sql-"));
const binary = Buffer.from([0, 255, ...Buffer.from("雪")]);
const metadata = { bytes: binary, values: [null, "雪"] };
const hex = binary.toString("hex");
const payloads = `{"\\\\x${hex}","\\\\x",NULL}`;
const boundedPayloads = `[0:1][3:4]={{"\\\\x${hex}",NULL},{"\\\\x","\\\\x00ff"}}`;
transport.rows = [
[binary, payloads, metadata, payloads],
[new Uint8Array(), "{}", [], payloads],
[null, null, null, payloads],
[new Uint8Array([0, 255]), boundedPayloads, {}, boundedPayloads],
];
transport.end.mockClear();
try {
const archive = await runDatabaseBackup({ connectionString: "postgres://unused/binary_test", backupDir: directory,
backupEngine: "javascript", retention: { dailyDays: 1, weeklyWeeks: 1, monthlyMonths: 1 },
nullifyColumns: { binary_fixture: ["redacted"] } });
const body = gunzipSync(await readFile(archive.backupFile)).toString("utf8");
const inserts = body.split("\n").filter(line => line.startsWith("INSERT INTO"));
const literal = (value: string) => `$paperclip$${value}$paperclip$`;
const array = (value: string) => literal(value);
const prefix = 'INSERT INTO "public"."binary_fixture" ("bytes", "payloads", "metadata", "redacted") VALUES ';
expect(inserts).toEqual([
`${prefix}(decode('${hex}', 'hex'), ${array(`{"\\\\x${hex}","\\\\x",NULL}`)}, ${literal(JSON.stringify(metadata))}, NULL);`,
`${prefix}(decode('', 'hex'), ${array("{}")}, ${literal("[]")}, NULL);`,
`${prefix}(NULL, NULL, NULL, NULL);`,
`${prefix}(decode('00ff', 'hex'), ${array(boundedPayloads)}, ${literal("{}")}, NULL);`,
]);
expect(transport.projection).toBe('SELECT "bytes", "payloads"::text AS "payloads", "metadata", "redacted"::text AS "redacted" FROM "public"."binary_fixture"');
expect(transport.end).toHaveBeenCalledTimes(1);
} finally { await rm(directory, { recursive: true, force: true }); }
});
});