Files
Termix/src/backend/tests/database/db/bootstrap-matches-schema-columns.test.ts
T

75 lines
2.7 KiB
TypeScript
Raw Normal View History

+17
2026-09-20 14:56:39 -05:00
import fs from "fs";
import os from "os";
import path from "path";
import { getTableColumns, getTableName, is, Table } from "drizzle-orm";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import * as schema from "../../../database/db/schema.js";
/**
* One level up from bootstrap-matches-schema.test.ts, which checks that every
* TABLE exists. This checks every COLUMN.
*
* SQLite never runs the drizzle migrations at all -- runRemoteMigrations
* throws for sqlite, and the live schema comes from hand-written DDL in
* db/index.ts (a CREATE TABLE plus addColumnIfNotExists calls). So adding a
* column to schema.ts and running `schema:migrations` type-checks, produces a
* migration file, passes every repository test (their fixtures are built from
* the schema), and still fails at runtime on the default dialect with "no such
* column" -- breaking every write to that table.
*
* A grep- or token-based guard cannot catch this: db/index.ts refers to
* columns as snake_case string arguments, never by the camelCase names the
* schema uses. Only booting a real database and reading PRAGMA table_info
* compares the two things that actually have to agree.
*/
describe("bootstrap creates every column in the drizzle schema", () => {
let dataDir: string;
beforeEach(() => {
dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "termix-columns-"));
vi.resetModules();
process.env.DATA_DIR = dataDir;
process.env.DB_FILE_ENCRYPTION = "false";
process.env.ALLOW_EMPTY_DATA_DIR = "true";
});
afterEach(() => {
delete process.env.DATA_DIR;
delete process.env.DB_FILE_ENCRYPTION;
delete process.env.ALLOW_EMPTY_DATA_DIR;
fs.rmSync(dataDir, { recursive: true, force: true });
});
it("leaves no schema column missing from a fresh database", async () => {
const db = await import("../../../database/db/index.js");
await db.initializeDatabase();
const sqlite = db.getSqlite();
const missing: string[] = [];
for (const value of Object.values(schema)) {
if (!is(value, Table)) continue;
const tableName = getTableName(value as Table);
const present = new Set(
(
sqlite
.prepare(`PRAGMA table_info(${JSON.stringify(tableName)})`)
.all() as Array<{ name: string }>
).map((row) => row.name),
);
// A table that does not exist at all is the sibling test's finding, not
// this one's -- reporting it here too would just duplicate the failure.
if (present.size === 0) continue;
for (const column of Object.values(getTableColumns(value as Table))) {
if (!present.has(column.name)) {
missing.push(`${tableName}.${column.name}`);
}
}
}
expect(missing.sort()).toEqual([]);
});
});