38 lines
1.7 KiB
TypeScript
38 lines
1.7 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
import { fileURLToPath } from "node:url";
|
|
import { dirname, join } from "node:path";
|
|
|
|
// Regression guard for #5406: the database-import route deleted the live
|
|
// storage.sqlite + WAL/-shm/-journal sidecars with a plain synchronous
|
|
// `fs.unlinkSync` and no retry. On Windows the OS releases the SQLite file
|
|
// handle asynchronously after `db.close()` (mmap / antivirus), so the immediate
|
|
// unlink races and throws EBUSY. The restore path already solved this with
|
|
// `unlinkFileWithRetry` (EBUSY/EPERM backoff); the import path must use the
|
|
// same helper instead of raw `fs.unlinkSync`.
|
|
|
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
const repoRoot = join(here, "..", "..");
|
|
const importRoute = join(repoRoot, "src/app/api/db-backups/import/route.ts");
|
|
|
|
test("#5406: import route uses unlinkFileWithRetry (EBUSY-safe on Windows)", () => {
|
|
const src = readFileSync(importRoute, "utf8");
|
|
assert.match(
|
|
src,
|
|
/unlinkFileWithRetry/,
|
|
"import route must delete the sqlite files via unlinkFileWithRetry (EBUSY retry)"
|
|
);
|
|
});
|
|
|
|
test("#5406: import route does not raw-unlink the live sqlite files (EBUSY race)", () => {
|
|
const src = readFileSync(importRoute, "utf8");
|
|
// The buggy code deleted the sqlite + WAL sidecars with `fs.unlinkSync(filePath)`
|
|
// inside the sqliteFilesToReplace loop. Only that path races to EBUSY; the
|
|
// temp-upload cleanup (`fs.unlinkSync(tmpPath)`) is a different, unlocked file.
|
|
assert.ok(
|
|
!/fs\.unlinkSync\s*\(\s*filePath\b/.test(src),
|
|
"the sqlite-replace loop must use unlinkFileWithRetry, not raw fs.unlinkSync(filePath)"
|
|
);
|
|
});
|