feat: add transactional module migration runner
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
formatMigrationApplyReport,
|
||||
formatMigrationPlanReport,
|
||||
} from "../src/cli/migration-report.js";
|
||||
import type { MigrationApplyResult, MigrationPlanResult } from "../src/migrations/index.js";
|
||||
|
||||
describe("migration command reports", () => {
|
||||
it("reports a deterministic safe plan and its stale-plan warning", () => {
|
||||
const plan: MigrationPlanResult = Object.freeze({
|
||||
appliedCount: 1,
|
||||
approvalRequiredCount: 1,
|
||||
entries: Object.freeze([
|
||||
Object.freeze({
|
||||
approvalRequired: false,
|
||||
checksum: "a".repeat(64),
|
||||
identity: "people:0000_initial",
|
||||
index: 0,
|
||||
moduleId: "people",
|
||||
status: "applied",
|
||||
tag: "0000_initial",
|
||||
}),
|
||||
Object.freeze({
|
||||
approvalRequired: false,
|
||||
checksum: "b".repeat(64),
|
||||
identity: "people:0001_add_email",
|
||||
index: 1,
|
||||
moduleId: "people",
|
||||
status: "pending",
|
||||
tag: "0001_add_email",
|
||||
}),
|
||||
Object.freeze({
|
||||
approvalRequired: true,
|
||||
checksum: "c".repeat(64),
|
||||
identity: "calendar:0000_replace_events",
|
||||
index: 0,
|
||||
moduleId: "calendar",
|
||||
status: "approval-blocked",
|
||||
tag: "0000_replace_events",
|
||||
}),
|
||||
]),
|
||||
pendingCount: 2,
|
||||
});
|
||||
|
||||
expect(formatMigrationPlanReport(plan)).toEqual([
|
||||
"Migration plan (1 applied, 1 pending, 1 approval-blocked):",
|
||||
`- [applied] people:0000_initial sha256=${"a".repeat(64)}`,
|
||||
`- [pending] people:0001_add_email sha256=${"b".repeat(64)}`,
|
||||
`- [approval-blocked] calendar:0000_replace_events sha256=${"c".repeat(64)}`,
|
||||
"Informational only: this plan is read-only and may become stale; apply revalidates history under the migration lock.",
|
||||
]);
|
||||
});
|
||||
|
||||
it("reports applied identities without SQL and handles a genuine no-op", () => {
|
||||
const applied: MigrationApplyResult = Object.freeze({
|
||||
applied: Object.freeze([
|
||||
Object.freeze({
|
||||
checksum: "d".repeat(64),
|
||||
identity: "people:0000_initial",
|
||||
index: 0,
|
||||
moduleId: "people",
|
||||
tag: "0000_initial",
|
||||
}),
|
||||
]),
|
||||
appliedCount: 1,
|
||||
noOp: false,
|
||||
});
|
||||
|
||||
expect(formatMigrationApplyReport(applied)).toEqual([
|
||||
"Migration apply completed (1 migration applied):",
|
||||
`- people:0000_initial sha256=${"d".repeat(64)}`,
|
||||
]);
|
||||
expect(
|
||||
formatMigrationApplyReport(
|
||||
Object.freeze({ applied: Object.freeze([]), appliedCount: 0, noOp: true }),
|
||||
),
|
||||
).toEqual(["Migration apply completed: database is already current (no changes)."]);
|
||||
});
|
||||
|
||||
it("renders an empty installed inventory explicitly", () => {
|
||||
expect(
|
||||
formatMigrationPlanReport(
|
||||
Object.freeze({
|
||||
appliedCount: 0,
|
||||
approvalRequiredCount: 0,
|
||||
entries: Object.freeze([]),
|
||||
pendingCount: 0,
|
||||
}),
|
||||
),
|
||||
).toEqual([
|
||||
"Migration plan (0 applied, 0 pending, 0 approval-blocked):",
|
||||
"- No installed module migrations.",
|
||||
"Informational only: this plan is read-only and may become stale; apply revalidates history under the migration lock.",
|
||||
]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user