Files
OmniRoute/tests/unit/db-migration-runner.test.mjs
diegosouzapw 592ca9b5c4 fix: remove hardcoded localhost default arg from GET /api/keys, unify coverage to single coverage/ dir, fix test to pass explicit Request
- Remove `new Request('http://localhost/api/keys')` default arg from GET handler in src/app/api/keys/route.ts (line 26)
- Fix api-key-reveal-route.test.mjs to pass explicit Request instead of calling GET() with no args
- Add --output-dir coverage to all c8 scripts in package.json
- Add coverage.reportsDirectory: 'coverage' to vitest.config.ts and vitest.mcp.config.ts
- Fix CHANGELOG.md structure (# Changelog + [Unreleased] to top)
- Remove 30+ stale coverage-* directories from project root
- Coverage: Statements 78.76% | Branches 72.75% | Functions 80.93% | Lines 78.76% (all thresholds passed)
2026-04-05 23:21:08 -03:00

349 lines
9.5 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import { pathToFileURL } from "node:url";
import Database from "better-sqlite3";
const serial = { concurrency: false };
async function importFresh(modulePath) {
const url = pathToFileURL(path.resolve(modulePath)).href;
return import(`${url}?test=${Date.now()}-${Math.random().toString(16).slice(2)}`);
}
function withMockedMigrationFs(files, fn) {
const originalExistsSync = fs.existsSync;
const originalReaddirSync = fs.readdirSync;
const originalReadFileSync = fs.readFileSync;
const isMigrationDir = (target) =>
String(target).replaceAll("\\", "/").endsWith("/src/lib/db/migrations") ||
String(target).replaceAll("\\", "/").endsWith("/migrations");
fs.existsSync = (target) => {
if (files === null && isMigrationDir(target)) return false;
if (files && isMigrationDir(target)) return true;
const fileName = path.basename(String(target));
if (files && Object.hasOwn(files, fileName)) return true;
return originalExistsSync(target);
};
fs.readdirSync = (target, options) => {
if (files && isMigrationDir(target)) {
return Object.keys(files);
}
return originalReaddirSync(target, options);
};
fs.readFileSync = (target, options) => {
const fileName = path.basename(String(target));
if (files && Object.hasOwn(files, fileName)) {
return files[fileName];
}
return originalReadFileSync(target, options);
};
try {
return fn();
} finally {
fs.existsSync = originalExistsSync;
fs.readdirSync = originalReaddirSync;
fs.readFileSync = originalReadFileSync;
}
}
function createDb() {
return new Database(":memory:");
}
test("runMigrations applies pending files sequentially in version order", serial, async () => {
const runner = await importFresh("src/lib/db/migrationRunner.ts");
const db = createDb();
try {
const appliedCount = withMockedMigrationFs(
{
"010_last.sql": "CREATE TABLE migration_last (id INTEGER);",
"002_middle.sql": "CREATE TABLE migration_middle (id INTEGER);",
"001_first.sql": "CREATE TABLE migration_first (id INTEGER);",
},
() => runner.runMigrations(db)
);
assert.equal(appliedCount, 3);
assert.deepEqual(
db.prepare("SELECT version FROM _omniroute_migrations ORDER BY version").all(),
[{ version: "001" }, { version: "002" }, { version: "010" }]
);
assert.ok(
db
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?")
.get("migration_first")
);
assert.ok(
db
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?")
.get("migration_last")
);
} finally {
db.close();
}
});
test("runMigrations skips versions that are already tracked as applied", serial, async () => {
const runner = await importFresh("src/lib/db/migrationRunner.ts");
const db = createDb();
try {
withMockedMigrationFs(
{
"001_first.sql": "CREATE TABLE skip_first (id INTEGER);",
"002_second.sql": "CREATE TABLE skip_second (id INTEGER);",
},
() => runner.runMigrations(db)
);
const secondRun = withMockedMigrationFs(
{
"001_first.sql": "CREATE TABLE skip_first (id INTEGER);",
"002_second.sql": "CREATE TABLE skip_second (id INTEGER);",
},
() => runner.runMigrations(db)
);
assert.equal(secondRun, 0);
assert.equal(
db.prepare("SELECT COUNT(*) AS count FROM _omniroute_migrations WHERE version = ?").get("001")
.count,
1
);
assert.equal(
db.prepare("SELECT COUNT(*) AS count FROM _omniroute_migrations WHERE version = ?").get("002")
.count,
1
);
} finally {
db.close();
}
});
test("getMigrationStatus reports applied and pending migrations", serial, async () => {
const runner = await importFresh("src/lib/db/migrationRunner.ts");
const db = createDb();
try {
db.exec(`
CREATE TABLE _omniroute_migrations (
version TEXT PRIMARY KEY,
name TEXT NOT NULL,
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
);
`);
db.prepare("INSERT INTO _omniroute_migrations (version, name) VALUES (?, ?)").run(
"001",
"first"
);
const status = withMockedMigrationFs(
{
"001_first.sql": "CREATE TABLE status_first (id INTEGER);",
"002_second.sql": "CREATE TABLE status_second (id INTEGER);",
"003_third.sql": "CREATE TABLE status_third (id INTEGER);",
},
() => runner.getMigrationStatus(db)
);
assert.deepEqual(
status.applied.map((row) => row.version),
["001"]
);
assert.deepEqual(
status.pending.map((row) => row.version),
["002", "003"]
);
} finally {
db.close();
}
});
test(
"failed migrations roll back their transaction and do not record the version",
serial,
async () => {
const runner = await importFresh("src/lib/db/migrationRunner.ts");
const db = createDb();
try {
assert.throws(
() =>
withMockedMigrationFs(
{
"001_ok.sql": "CREATE TABLE rollback_ok (id INTEGER);",
"002_broken.sql":
"CREATE TABLE rollback_broken (id INTEGER); INSERT INTO missing_table VALUES (1);",
},
() => runner.runMigrations(db)
),
/missing_table/i
);
assert.ok(
db
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?")
.get("rollback_ok")
);
assert.equal(
db
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?")
.get("rollback_broken"),
undefined
);
assert.equal(
db
.prepare("SELECT COUNT(*) AS count FROM _omniroute_migrations WHERE version = ?")
.get("002").count,
0
);
} finally {
db.close();
}
}
);
test("missing or empty migration directories are treated as a no-op", serial, async () => {
const runner = await importFresh("src/lib/db/migrationRunner.ts");
const missingDb = createDb();
const emptyDb = createDb();
try {
assert.equal(
withMockedMigrationFs(null, () => runner.runMigrations(missingDb)),
0
);
assert.equal(
withMockedMigrationFs({}, () => runner.runMigrations(emptyDb)),
0
);
assert.deepEqual(
withMockedMigrationFs({}, () => runner.getMigrationStatus(emptyDb)),
{
applied: [],
pending: [],
}
);
} finally {
missingDb.close();
emptyDb.close();
}
});
test("invalid file names are ignored while valid migrations still run", serial, async () => {
const runner = await importFresh("src/lib/db/migrationRunner.ts");
const db = createDb();
try {
const count = withMockedMigrationFs(
{
"README.md": "# ignored",
"not-a-migration.sql": "CREATE TABLE should_not_exist (id INTEGER);",
"003_valid.sql": "CREATE TABLE valid_migration (id INTEGER);",
},
() => runner.runMigrations(db)
);
assert.equal(count, 1);
assert.deepEqual(
db.prepare("SELECT version, name FROM _omniroute_migrations ORDER BY version").all(),
[{ version: "003", name: "valid" }]
);
assert.equal(
db
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?")
.get("should_not_exist"),
undefined
);
} finally {
db.close();
}
});
test(
"new migrations are detected on subsequent runs without replaying old ones",
serial,
async () => {
const runner = await importFresh("src/lib/db/migrationRunner.ts");
const db = createDb();
try {
withMockedMigrationFs(
{
"001_first.sql": "CREATE TABLE rerun_first (id INTEGER);",
"002_second.sql": "CREATE TABLE rerun_second (id INTEGER);",
},
() => runner.runMigrations(db)
);
const count = withMockedMigrationFs(
{
"001_first.sql": "CREATE TABLE rerun_first (id INTEGER);",
"002_second.sql": "CREATE TABLE rerun_second (id INTEGER);",
"003_third.sql": "CREATE TABLE rerun_third (id INTEGER);",
},
() => runner.runMigrations(db)
);
assert.equal(count, 1);
assert.deepEqual(
db.prepare("SELECT version FROM _omniroute_migrations ORDER BY version").all(),
[{ version: "001" }, { version: "002" }, { version: "003" }]
);
} finally {
db.close();
}
}
);
test(
"unknown rows in the migration table do not block pending real migrations",
serial,
async () => {
const runner = await importFresh("src/lib/db/migrationRunner.ts");
const db = createDb();
try {
db.exec(`
CREATE TABLE _omniroute_migrations (
version TEXT PRIMARY KEY,
name TEXT NOT NULL,
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
);
`);
db.prepare("INSERT INTO _omniroute_migrations (version, name) VALUES (?, ?)").run(
"999",
"ghost"
);
const count = withMockedMigrationFs(
{
"001_first.sql": "CREATE TABLE recover_first (id INTEGER);",
"002_second.sql": "CREATE TABLE recover_second (id INTEGER);",
},
() => runner.runMigrations(db)
);
assert.equal(count, 2);
assert.deepEqual(
db.prepare("SELECT version FROM _omniroute_migrations ORDER BY version").all(),
[{ version: "001" }, { version: "002" }, { version: "999" }]
);
} finally {
db.close();
}
}
);