Files
ekklesia 88c28d75c9
CI / verify (pull_request) Successful in 21s
CI / infrastructure-smoke (pull_request) Successful in 20s
feat: add module migration contracts and validation
2026-07-21 08:09:30 -05:00

102 lines
2.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
DRIZZLE_MIGRATION_ORIGIN_UUID,
moduleMigrationTagSchema,
parseDrizzleMigrationJournal,
parseDrizzleMigrationSnapshotHeader,
parseModuleMigrationPolicy,
} from "../src/index.js";
describe("module migration contracts", () => {
it("parses the supported stable Drizzle PostgreSQL journal format", () => {
const journal = parseDrizzleMigrationJournal({
version: "7",
dialect: "postgresql",
entries: [
{
idx: 0,
version: "7",
when: 1_750_000_000_000,
tag: "0000_initial",
breakpoints: true,
},
],
});
expect(journal.entries[0]?.tag).toBe("0000_initial");
});
it("rejects unsupported journal versions, dialects, and unknown metadata", () => {
const base = {
version: "7",
dialect: "postgresql",
entries: [
{
idx: 0,
version: "7",
when: 1,
tag: "0000_initial",
breakpoints: true,
},
],
};
expect(() => parseDrizzleMigrationJournal({ ...base, version: "8" })).toThrow();
expect(() => parseDrizzleMigrationJournal({ ...base, dialect: "sqlite" })).toThrow();
expect(() => parseDrizzleMigrationJournal({ ...base, extra: true })).toThrow();
});
it("parses snapshot identity metadata while allowing Drizzle's schema payload", () => {
const snapshot = parseDrizzleMigrationSnapshotHeader({
version: "7",
dialect: "postgresql",
id: "10000000-0000-4000-8000-000000000001",
prevId: DRIZZLE_MIGRATION_ORIGIN_UUID,
tables: {},
_meta: { tables: {}, schemas: {}, columns: {} },
});
expect(snapshot.prevId).toBe(DRIZZLE_MIGRATION_ORIGIN_UUID);
});
it("requires explicit non-destructive or approval-required policy declarations", () => {
expect(
parseModuleMigrationPolicy({
version: 1,
moduleId: "calendar",
entries: [
{ tag: "0000_initial", destructive: { mode: "none" } },
{
tag: "0001_remove_legacy",
destructive: { mode: "approval-required", reason: "Removes a legacy column." },
},
],
}).entries,
).toHaveLength(2);
expect(() =>
parseModuleMigrationPolicy({
version: 1,
moduleId: "calendar",
entries: [
{
tag: "0000_initial",
destructive: { mode: "approval-required", reason: "Drops data.", approved: true },
},
],
}),
).toThrow();
});
it.each([
"../outside",
"/absolute",
"0000_nested/name",
"0000_back\\slash",
"0000_already.sql",
"0000_Uppercase",
])("rejects unsafe migration tag %s", (tag) => {
expect(moduleMigrationTagSchema.safeParse(tag).success).toBe(false);
});
});