feat: add module migration contracts and validation
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user