feat(core): export bundle, restore, backups

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Sonnet 5.5 committed 2026-10-30 20:17:25 +01:00
1 parent 33631aef3c
commit a047020d54
5 files changed
+626

No files matched your search

+44
View File
@@ -0,0 +1,44 @@
import { afterEach, beforeEach, expect, test } from "bun:test";
import { mkdtemp, readdir, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { forEachDialect } from "@sttracker/db/testing";
import { backupHandler, runBackup } from "../src/backup.ts";
import { makeUser } from "./user-layer-fixtures.ts";
forEachDialect("backup", (getDb) => {
let dir: string;
beforeEach(async () => {
dir = await mkdtemp(join(tmpdir(), "sttracker-bak-"));
});
afterEach(async () => {
await rm(dir, { recursive: true, force: true });
});
test("backup writes a bundle per user (and the db on sqlite)", async () => {
const db = getDb();
await makeUser(db, "ada");
await makeUser(db, "bob");
const out = await runBackup(db, dir, { keep: 14 });
expect(out.startsWith(join(dir, "backups"))).toBe(true);
const files = (await readdir(out)).sort();
expect(files).toEqual(db.dialect === "sqlite" ? ["ada.zip", "bob.zip", "sttracker.db"] : ["ada.zip", "bob.zip"]);
});
test("backup prunes to keep", async () => {
const db = getDb();
await makeUser(db, "ada");
const made: string[] = [];
for (let i = 0; i < 4; i++) made.push(await runBackup(db, dir, { keep: 2 }));
const left = (await readdir(join(dir, "backups"))).sort();
expect(left).toHaveLength(2);
expect(left.map((n) => join(dir, "backups", n))).toEqual(made.slice(2));
});
test("backupHandler runs a backup", async () => {
const db = getDb();
await makeUser(db, "ada");
await backupHandler({ db, dataDir: dir, keep: 3 })();
expect(await readdir(join(dir, "backups"))).toHaveLength(1);
});
});