Files
leistungsbilanz-ts/tests/database-backup.test.ts
T

89 lines
2.8 KiB
TypeScript

import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { describe, it } from "node:test";
import Database from "better-sqlite3";
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
import {
createVerifiedDatabaseBackup,
verifyDatabaseFile,
} from "../src/db/database-backup.js";
import { createDatabaseContext } from "../src/db/database-context.js";
import { projects } from "../src/db/schema/projects.js";
describe("database backup and restore", () => {
it("backs up committed WAL data and restores it as an independent database", async () => {
const testDirectory = fs.mkdtempSync(
path.join(os.tmpdir(), "leistungsbilanz-db-backup-")
);
const sourcePath = path.join(testDirectory, "source.db");
const backupPath = path.join(testDirectory, "backup.db");
const restoredPath = path.join(testDirectory, "restored.db");
const source = createDatabaseContext(sourcePath);
try {
source.sqlite.pragma("journal_mode = WAL");
migrate(source.db, {
migrationsFolder: path.resolve("src", "db", "migrations"),
});
source.db
.insert(projects)
.values({ id: "project-in-backup", name: "Backup project" })
.run();
assert.equal(fs.existsSync(`${sourcePath}-wal`), true);
const backupVerification = await createVerifiedDatabaseBackup(
sourcePath,
backupPath
);
assert.deepEqual(backupVerification, {
integrityCheck: "ok",
foreignKeyViolations: 0,
});
source.db
.insert(projects)
.values({ id: "project-after-backup", name: "Later project" })
.run();
source.close();
await createVerifiedDatabaseBackup(backupPath, restoredPath);
const restored = new Database(restoredPath, {
readonly: true,
fileMustExist: true,
});
try {
const restoredProjects = restored
.prepare("SELECT id, name FROM projects ORDER BY id")
.all();
assert.deepEqual(restoredProjects, [
{ id: "project-in-backup", name: "Backup project" },
]);
} finally {
restored.close();
}
} finally {
if (source.sqlite.open) {
source.close();
}
fs.rmSync(testDirectory, { recursive: true, force: true });
}
});
it("rejects a corrupt database file", () => {
const testDirectory = fs.mkdtempSync(
path.join(os.tmpdir(), "leistungsbilanz-db-verify-")
);
const corruptPath = path.join(testDirectory, "corrupt.db");
try {
fs.writeFileSync(corruptPath, "not a sqlite database");
assert.throws(() => verifyDatabaseFile(corruptPath), /not a database/);
} finally {
fs.rmSync(testDirectory, { recursive: true, force: true });
}
});
});