import { createHash } from "node:crypto"; import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs"; import os from "node:os"; import path from "node:path"; import { describe, expect, it, vi } from "vitest"; import { BaselineAdoptionError, REPOSITORY_ROOT, adoptPrismaBaseline, classifyMigrationHistory, deployMissingLegacyMigrations, resolveBaselineMigration, verifyLegacyVariantCatalog, verifyPrismaSchemaParity, } from "./adopt-prisma-baseline.mjs"; const checksum = (value) => createHash("sha256").update(value).digest("hex"); const manifest = { version: 1, baselineMigration: "00000000000000_hivegrid_baseline", baselineChecksum: checksum("baseline"), legacyMigrationCount: 2, migrations: [ { name: "20260101000000_first", checksum: checksum("first") }, { name: "20260102000000_second", checksum: checksum("second") }, ], legacyChecksumVariants: [ { name: "20260101000000_first", checksum: checksum("first-historical"), sourceCommit: "a".repeat(40), }, ], }; const success = (name, rowChecksum) => ({ name, checksum: rowChecksum, finishedAt: new Date(), rolledBackAt: null }); const completeLegacyHistory = { tableExists: true, rows: manifest.migrations.map((migration) => success(migration.name, migration.checksum)), }; const baselineHistory = { tableExists: true, rows: [success(manifest.baselineMigration, manifest.baselineChecksum)], }; describe("classifyMigrationHistory", () => { it("keeps a database without migration history on the fresh baseline path", () => { expect(classifyMigrationHistory({ tableExists: false, rows: [] }, manifest).mode).toBe("fresh"); expect(classifyMigrationHistory({ tableExists: true, rows: [] }, manifest).mode).toBe("fresh"); }); it("recognizes the committed baseline and permits later migration rows", () => { const history = { tableExists: true, rows: [...baselineHistory.rows, success("20270101000000_later", checksum("later"))], }; expect(classifyMigrationHistory(history, manifest).mode).toBe("baseline"); }); it("accepts exactly one successful checksum-identical copy of every legacy migration", () => { expect(classifyMigrationHistory(completeLegacyHistory, manifest)).toEqual({ mode: "legacy", detail: "complete reviewed legacy chain", variantNames: [], }); }); it("accepts only an explicitly reviewed historical checksum and reports its migration", () => { const history = { tableExists: true, rows: [success(manifest.migrations[0].name, manifest.legacyChecksumVariants[0].checksum), completeLegacyHistory.rows[1]], }; expect(classifyMigrationHistory(history, manifest)).toEqual({ mode: "legacy", detail: "complete reviewed legacy chain with historical checksums", variantNames: [manifest.migrations[0].name], }); }); it("accepts a checksum-verified exact legacy prefix for catch-up", () => { expect(classifyMigrationHistory({ tableExists: true, rows: completeLegacyHistory.rows.slice(0, 1) }, manifest)).toEqual({ mode: "legacy-prefix", detail: "checksum-verified legacy prefix", variantNames: [], appliedMigrationCount: 1, missingMigrations: ["20260102000000_second"], }); }); it.each([ { label: "gapped history", history: { tableExists: true, rows: completeLegacyHistory.rows.slice(1) }, message: /not an exact legacy prefix/, }, { label: "duplicate successful history", history: { tableExists: true, rows: [completeLegacyHistory.rows[0], completeLegacyHistory.rows[0]] }, message: /not an exact legacy prefix/, }, { label: "changed checksum", history: { tableExists: true, rows: [success(manifest.migrations[0].name, checksum("changed")), completeLegacyHistory.rows[1]] }, message: /checksum differs/, }, { label: "foreign migration", history: { tableExists: true, rows: [...completeLegacyHistory.rows, success("foreign", checksum("foreign"))] }, message: /outside the reviewed legacy chain/, }, { label: "unresolved failure", history: { tableExists: true, rows: [...completeLegacyHistory.rows, { name: "failed", checksum: checksum("failed"), finishedAt: null, rolledBackAt: null }], }, message: /unresolved failed migration/, }, { label: "changed baseline", history: { tableExists: true, rows: [success(manifest.baselineMigration, checksum("changed"))] }, message: /baseline checksum/, }, ])("fails closed for $label", ({ history, message }) => { expect(() => classifyMigrationHistory(history, manifest)).toThrow(message); }); }); describe("adoptPrismaBaseline", () => { it("does not resolve the baseline on a fresh database", async () => { const resolve = vi.fn(); const stdout = { write: vi.fn() }; await expect(adoptPrismaBaseline({ manifest, inspect: async () => ({ tableExists: false, rows: [] }), resolve, stdout })).resolves.toMatchObject({ mode: "fresh", }); expect(resolve).not.toHaveBeenCalled(); }); it("resolves a complete legacy chain once and verifies the resulting baseline row", async () => { const inspect = vi.fn().mockResolvedValueOnce(completeLegacyHistory).mockResolvedValueOnce(baselineHistory); const resolve = vi.fn(); const stdout = { write: vi.fn() }; await expect(adoptPrismaBaseline({ manifest, inspect, resolve, stdout })).resolves.toEqual({ mode: "adopted", detail: "complete reviewed legacy chain", }); expect(resolve).toHaveBeenCalledOnce(); expect(resolve).toHaveBeenCalledWith({ manifest }); expect(inspect).toHaveBeenCalledTimes(2); }); it("catches up an exact legacy prefix before resolving the baseline", async () => { const partialLegacyHistory = { tableExists: true, rows: completeLegacyHistory.rows.slice(0, 1) }; const inspect = vi.fn().mockResolvedValueOnce(partialLegacyHistory).mockResolvedValueOnce(completeLegacyHistory).mockResolvedValueOnce(baselineHistory); const deployMissing = vi.fn(); const resolve = vi.fn(); const stdout = { write: vi.fn() }; await expect(adoptPrismaBaseline({ manifest, inspect, deployMissing, resolve, stdout })).resolves.toEqual({ mode: "adopted", detail: "complete reviewed legacy chain", }); expect(deployMissing).toHaveBeenCalledOnce(); expect(deployMissing).toHaveBeenCalledWith({ manifest, missingMigrations: ["20260102000000_second"], }); expect(deployMissing.mock.invocationCallOrder[0]).toBeLessThan(resolve.mock.invocationCallOrder[0]); expect(inspect).toHaveBeenCalledTimes(3); }); it("verifies security-sensitive catalog contracts before resolving a reviewed historical checksum", async () => { const variantHistory = { tableExists: true, rows: [success(manifest.migrations[0].name, manifest.legacyChecksumVariants[0].checksum), completeLegacyHistory.rows[1]], }; const inspect = vi.fn().mockResolvedValueOnce(variantHistory).mockResolvedValueOnce(baselineHistory); const verifyCatalog = vi.fn(); const resolve = vi.fn(); await expect(adoptPrismaBaseline({ manifest, inspect, verifyCatalog, resolve, stdout: { write: vi.fn() } })).resolves.toMatchObject({ mode: "adopted", }); expect(verifyCatalog).toHaveBeenCalledWith({ variantNames: [manifest.migrations[0].name] }); expect(verifyCatalog.mock.invocationCallOrder[0]).toBeLessThan(resolve.mock.invocationCallOrder[0]); }); it("is a no-op after the baseline has already been recorded", async () => { const resolve = vi.fn(); await expect(adoptPrismaBaseline({ manifest, inspect: async () => baselineHistory, resolve, stdout: { write: vi.fn() } })).resolves.toMatchObject({ mode: "baseline", }); expect(resolve).not.toHaveBeenCalled(); }); }); describe("deployMissingLegacyMigrations", () => { it("runs Prisma migrate deploy with a writable temporary config and an absolute Prisma config import", () => { const cleanup = vi.fn(); const prepareArchive = vi.fn(() => ({ migrationsPath: "/tmp/reviewed-legacy/prisma/migrations", cleanup, })); const tempRoot = mkdtempSync(path.join(os.tmpdir(), "hivegrid-baseline-test-")); let configPath; let configSource; const spawnImpl = vi.fn((_command, args) => { configPath = args[args.indexOf("--config") + 1]; configSource = readFileSync(configPath, "utf8"); return { status: 0 }; }); try { expect(() => deployMissingLegacyMigrations({ manifest, missingMigrations: ["20260102000000_second"], env: { DIRECT_URL: "postgresql://redacted" }, prepareArchive, spawnImpl, tempRoot, }) ).not.toThrow(); expect(prepareArchive).toHaveBeenCalledWith({ manifest }); expect(spawnImpl).toHaveBeenCalledWith( process.execPath, expect.arrayContaining(["migrate", "deploy", "--config"]), expect.objectContaining({ env: { DIRECT_URL: "postgresql://redacted" }, stdio: "inherit", }) ); expect(configPath).toMatch(new RegExp(`^${tempRoot.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/hivegrid-prisma-config-`)); expect(configPath).not.toContain(REPOSITORY_ROOT); expect(configSource).toContain(`file://${path.join(REPOSITORY_ROOT, "node_modules/prisma/config.js")}`); expect(existsSync(configPath)).toBe(false); expect(cleanup).toHaveBeenCalledOnce(); } finally { rmSync(tempRoot, { recursive: true, force: true }); } }); it("rejects anything other than the exact missing manifest suffix", () => { expect(() => deployMissingLegacyMigrations({ manifest, missingMigrations: ["20260101000000_first"], env: { DIRECT_URL: "postgresql://redacted" }, prepareArchive: vi.fn(), }) ).toThrow(/exact missing manifest suffix/); }); }); describe("resolveBaselineMigration", () => { it("runs the installed Prisma CLI without a package manager", () => { const spawnImpl = vi.fn(() => ({ status: 0 })); resolveBaselineMigration({ manifest, env: { DIRECT_URL: "postgresql://redacted" }, spawnImpl }); expect(spawnImpl).toHaveBeenCalledWith( process.execPath, expect.arrayContaining(["migrate", "resolve", "--applied", manifest.baselineMigration, "--config", "prisma.config.ts"]), expect.objectContaining({ stdio: "inherit" }) ); }); it("fails closed when Prisma resolve fails", () => { expect(() => resolveBaselineMigration({ manifest, spawnImpl: () => ({ status: 1 }) })).toThrow(BaselineAdoptionError); }); }); describe("verifyPrismaSchemaParity", () => { it("compares the configured live datasource with the committed Prisma schema", () => { const spawnImpl = vi.fn(() => ({ status: 0 })); verifyPrismaSchemaParity({ env: { DIRECT_URL: "postgresql://redacted" }, spawnImpl }); expect(spawnImpl).toHaveBeenCalledWith( process.execPath, expect.arrayContaining(["migrate", "diff", "--from-config-datasource", "--to-schema", "prisma/schema", "--exit-code"]), expect.objectContaining({ stdio: "inherit" }) ); }); it.each([ { status: 1, message: /Could not verify/ }, { status: 2, message: /differs from the reviewed current Prisma schema/ }, { status: 3, message: /unsupported exit status 3/ }, ])("fails closed for Prisma diff exit $status", ({ status, message }) => { expect(() => verifyPrismaSchemaParity({ spawnImpl: () => ({ status }) })).toThrow(message); }); }); describe("verifyLegacyVariantCatalog", () => { it("does not open a database connection for variants without an extra raw-SQL contract", async () => { const clientFactory = vi.fn(); await expect(verifyLegacyVariantCatalog({ variantNames: ["20260517200000_08_capacitor_device_tokens"], clientFactory })).resolves.toBeUndefined(); expect(clientFactory).not.toHaveBeenCalled(); }); it("fails closed when a security-sensitive historical variant does not match the live catalog", async () => { const client = { connect: vi.fn(), query: vi.fn().mockResolvedValue({ rows: [{ matches: false }] }), end: vi.fn().mockResolvedValue(undefined), }; await expect( verifyLegacyVariantCatalog({ variantNames: ["20260717090000_ak_p16b_booking_overlap_constraint"], env: { DIRECT_URL: "postgresql://redacted" }, clientFactory: () => client, }) ).rejects.toThrow(/tenant-scoped booking overlap constraint/); expect(client.end).toHaveBeenCalledOnce(); }); it("accepts a security-sensitive historical variant only after the catalog contract passes", async () => { const client = { connect: vi.fn(), query: vi.fn().mockResolvedValue({ rows: [{ matches: true }] }), end: vi.fn().mockResolvedValue(undefined), }; await expect( verifyLegacyVariantCatalog({ variantNames: ["20260721093000_ak_p21_support_retention_purge"], env: { DIRECT_URL: "postgresql://redacted" }, clientFactory: () => client, }) ).resolves.toBeUndefined(); expect(client.query).toHaveBeenCalledOnce(); }); });