Refactor Study Desk application structure
This commit is contained in:
parent
faaccf8a7e
commit
089439ed90
145 changed files with 8087 additions and 3412 deletions
107
tests/helpers/testDatabase.ts
Normal file
107
tests/helpers/testDatabase.ts
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
import { createHash, randomUUID } from "node:crypto";
|
||||
import {
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
readFileSync,
|
||||
readdirSync,
|
||||
rmSync,
|
||||
} from "node:fs";
|
||||
import path from "node:path";
|
||||
import Database from "better-sqlite3";
|
||||
|
||||
export interface DisposableDatabase {
|
||||
databaseUrl: string;
|
||||
databasePath: string;
|
||||
directory: string;
|
||||
}
|
||||
|
||||
function pathFromDatabaseUrl(databaseUrl: string, cwd = process.cwd()) {
|
||||
if (!databaseUrl.startsWith("file:")) {
|
||||
throw new Error("Tests require a file: SQLite DATABASE_URL");
|
||||
}
|
||||
const withoutScheme = databaseUrl.slice("file:".length).split("?")[0];
|
||||
return path.resolve(cwd, withoutScheme);
|
||||
}
|
||||
|
||||
export function assertDisposableTestDatabase(databaseUrl: string) {
|
||||
const databasePath = pathFromDatabaseUrl(databaseUrl);
|
||||
const normalized = databasePath.replaceAll("\\", "/").toLowerCase();
|
||||
if (
|
||||
normalized.endsWith("/dev.db") ||
|
||||
normalized === "/app/data/study.db" ||
|
||||
existsSync(databasePath) ||
|
||||
!path.basename(databasePath).includes(".test.")
|
||||
) {
|
||||
throw new Error(`Refusing to use non-disposable database: ${databasePath}`);
|
||||
}
|
||||
return databasePath;
|
||||
}
|
||||
|
||||
export function createDisposableDatabase(): DisposableDatabase {
|
||||
const directory = path.join(
|
||||
process.cwd(),
|
||||
".test-databases",
|
||||
`study-desk-tests-${randomUUID()}`
|
||||
);
|
||||
mkdirSync(directory, { recursive: true });
|
||||
const databasePath = path.join(directory, `${randomUUID()}.test.db`);
|
||||
const relativePath = path.relative(process.cwd(), databasePath).replaceAll("\\", "/");
|
||||
const databaseUrl = `file:./${relativePath}`;
|
||||
assertDisposableTestDatabase(databaseUrl);
|
||||
return { databaseUrl, databasePath, directory };
|
||||
}
|
||||
|
||||
export function applyCommittedMigrations(
|
||||
database: DisposableDatabase,
|
||||
options: { exclude?: string[]; through?: string } = {}
|
||||
) {
|
||||
assertDisposableTestDatabase(database.databaseUrl);
|
||||
const migrationRoot = path.join(process.cwd(), "prisma", "migrations");
|
||||
const migrationNames = readdirSync(migrationRoot, { withFileTypes: true })
|
||||
.filter((entry) => entry.isDirectory())
|
||||
.map((entry) => entry.name)
|
||||
.filter((name) => !(options.exclude ?? []).includes(name))
|
||||
.filter((name) => !options.through || name <= options.through)
|
||||
.sort();
|
||||
const client = new Database(database.databasePath);
|
||||
try {
|
||||
client.pragma("foreign_keys = ON");
|
||||
client.exec(`
|
||||
CREATE TABLE "_prisma_migrations" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"checksum" TEXT NOT NULL,
|
||||
"finished_at" DATETIME,
|
||||
"migration_name" TEXT NOT NULL,
|
||||
"logs" TEXT,
|
||||
"rolled_back_at" DATETIME,
|
||||
"started_at" DATETIME NOT NULL DEFAULT current_timestamp,
|
||||
"applied_steps_count" INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
`);
|
||||
const recordMigration = client.prepare(`
|
||||
INSERT INTO "_prisma_migrations"
|
||||
("id", "checksum", "finished_at", "migration_name", "applied_steps_count")
|
||||
VALUES (?, ?, current_timestamp, ?, 1)
|
||||
`);
|
||||
for (const migrationName of migrationNames) {
|
||||
const sql = readFileSync(
|
||||
path.join(migrationRoot, migrationName, "migration.sql"),
|
||||
"utf8"
|
||||
);
|
||||
client.transaction(() => {
|
||||
client.exec(sql);
|
||||
recordMigration.run(
|
||||
randomUUID(),
|
||||
createHash("sha256").update(sql).digest("hex"),
|
||||
migrationName
|
||||
);
|
||||
})();
|
||||
}
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
export function disposeDisposableDatabase(database: DisposableDatabase) {
|
||||
rmSync(database.directory, { recursive: true, force: true });
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue