import { describe, it, expect } from "vitest"; import { readFileSync } from "node:fs"; import { join } from "node:path"; import { assertJournalMonotonic, formatJournalViolations, type JournalEntry, } from "@cmbot/db/journal-check"; /** * CI guard against the recurring drizzle journal-skip bug. * * Drizzle's migrator orders entries by `when` (not `idx`) and only * applies entries whose `when` is greater than the latest applied * row's recorded `created_at`. We've shipped two breaking deploys * (0010/0011 and 0012/0013) where freshly-generated migrations had * `when` values older than a prior manually-bumped entry — `pnpm * migrate` printed "Migrations applied." while silently skipping * the new SQL, and production 500'd until we hand-fixed the journal. * * This test reads the committed _journal.json and fails if the * entries aren't strictly monotonically increasing by `when` in the * same order as `idx`. Catches a bad commit at PR time instead of * at the next deploy. */ describe("drizzle journal monotonicity (regression guard)", () => { const journalPath = join( __dirname, "..", "..", "..", "..", "packages", "db", "migrations", "meta", "_journal.json", ); const raw = JSON.parse(readFileSync(journalPath, "utf8")) as { entries: JournalEntry[]; }; it("loads at least one journal entry (sanity)", () => { expect(raw.entries.length).toBeGreaterThan(0); }); it("`when` timestamps are strictly increasing in `idx` order", () => { const result = assertJournalMonotonic(raw.entries); if (!result.ok) { // Print the same actionable message migrate.ts prints, so a // failed CI run reads exactly like a failed local migrate. // eslint-disable-next-line no-console console.error(formatJournalViolations(result)); } expect(result.violations).toEqual([]); expect(result.ok).toBe(true); }); });