113 lines
3.9 KiB
TypeScript
113 lines
3.9 KiB
TypeScript
import { beforeEach, describe, expect, it } from "@suaveplan/testing/runner";
|
|
import { BatchMigrator } from "../src/batch/batch-migrator/batch-migrator.js";
|
|
import { MemoryMigrationLock } from "../src/lock/memory-lock/memory-lock.js";
|
|
import { MigrationRunner } from "../src/runner/migration-runner/migration-runner.js";
|
|
import { MigrationStatusDashboard } from "../src/status/migration-status/migration-status.js";
|
|
import { MemoryMigrationStore } from "../src/store/memory-store/memory-store.js";
|
|
import type { Migration } from "../src/types/migration.types.js";
|
|
|
|
const migrations: Migration[] = [
|
|
{
|
|
id: "001_create_users",
|
|
name: "Create users table",
|
|
async up(_ctx) {},
|
|
async down(_ctx) {},
|
|
},
|
|
{
|
|
id: "002_add_email",
|
|
name: "Add email column",
|
|
async up(_ctx) {},
|
|
async down(_ctx) {},
|
|
},
|
|
{
|
|
id: "003_create_posts",
|
|
name: "Create posts table",
|
|
async up(_ctx) {},
|
|
async down(_ctx) {},
|
|
},
|
|
];
|
|
|
|
describe("MigrationRunner integration", () => {
|
|
let store: MemoryMigrationStore;
|
|
|
|
beforeEach(() => {
|
|
store = new MemoryMigrationStore();
|
|
});
|
|
|
|
it("full up/down cycle", { timeout: 30000 }, async () => {
|
|
const runner = new MigrationRunner({ migrations, store });
|
|
|
|
const upResult = await runner.up();
|
|
expect(upResult.applied).toHaveLength(3);
|
|
expect(upResult.direction).toBe("up");
|
|
|
|
const statusAfterUp = await runner.status();
|
|
expect(statusAfterUp.every((s) => s.status === "applied")).toBe(true);
|
|
|
|
const downResult = await runner.down({ steps: 1 });
|
|
expect(downResult.applied).toHaveLength(1);
|
|
expect(downResult.applied[0]).toBe("003_create_posts");
|
|
|
|
// Up again applies only the rolled-back one
|
|
const upResult2 = await runner.up();
|
|
expect(upResult2.applied).toHaveLength(1);
|
|
});
|
|
|
|
it("idempotent up", { timeout: 30000 }, async () => {
|
|
const runner = new MigrationRunner({ migrations, store });
|
|
await runner.up();
|
|
const second = await runner.up();
|
|
expect(second.applied).toHaveLength(0);
|
|
});
|
|
|
|
it("with lock — runner acquires and releases", {
|
|
timeout: 30000,
|
|
}, async () => {
|
|
const lock = new MemoryMigrationLock();
|
|
const runner = new MigrationRunner({ migrations, store, lock });
|
|
const result = await runner.up();
|
|
expect(result.applied).toHaveLength(3);
|
|
expect(await lock.isLocked()).toBe(false);
|
|
});
|
|
|
|
it("dry-run does not write records", { timeout: 30000 }, async () => {
|
|
const runner = new MigrationRunner({ migrations, store, dryRun: true });
|
|
const result = await runner.up();
|
|
expect(result.dryRun).toBe(true);
|
|
expect(await store.getApplied()).toHaveLength(0);
|
|
});
|
|
|
|
it("status dashboard reflects runner results", {
|
|
timeout: 30000,
|
|
}, async () => {
|
|
const runner = new MigrationRunner({ migrations, store });
|
|
await runner.up();
|
|
|
|
const dashboard = new MigrationStatusDashboard({ store, migrations });
|
|
const health = await dashboard.healthCheck();
|
|
expect(health.healthy).toBe(true);
|
|
});
|
|
|
|
it("batch migrator runs multiple targets", { timeout: 30000 }, async () => {
|
|
const store2 = new MemoryMigrationStore();
|
|
const runner1 = new MigrationRunner({
|
|
migrations: migrations.slice(0, 2),
|
|
store,
|
|
});
|
|
const runner2 = new MigrationRunner({
|
|
migrations: migrations.slice(0, 2),
|
|
store: store2,
|
|
});
|
|
|
|
const batch = new BatchMigrator({
|
|
targets: [
|
|
{ name: "db1", runner: runner1 },
|
|
{ name: "db2", runner: runner2 },
|
|
],
|
|
});
|
|
const result = await batch.run();
|
|
expect(result.succeeded).toEqual(["db1", "db2"]);
|
|
expect(result.failed).toHaveLength(0);
|
|
});
|
|
});
|