Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 62 additions & 2 deletions src/api/tests/integration/migrations.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe.skipIf(SKIP)("runMigrations (integration)", () => {
}
});

it("applies migrations to an empty database and second run is a no-op", async () => {
it("applies migrations to an empty database and second run preserves epoch state", async () => {
const cfg = loadTenantConfig({
TENANT_DATABASES: JSON.stringify({ default: testUrl }),
});
Expand All @@ -79,10 +79,70 @@ describe.skipIf(SKIP)("runMigrations (integration)", () => {
WHERE n.nspname = 'public' AND p.proname = 'fs_resolve'
`;
expect(Number(procs[0]?.n)).toBeGreaterThanOrEqual(1);

const versionColumn = await sql<{ data_type: string; is_nullable: string; column_default: string | null }[]>`
SELECT data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 'sandboxes' AND column_name = 'version'
`;
expect(versionColumn).toEqual([{ data_type: "bigint", is_nullable: "NO", column_default: "0" }]);

const epochColumns = await sql<
{ column_name: string; data_type: string; is_nullable: string; column_default: string | null }[]
>`
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 'sandbox_epochs'
ORDER BY ordinal_position
`;
expect(epochColumns).toEqual([
{ column_name: "sandbox_id", data_type: "text", is_nullable: "NO", column_default: null },
{ column_name: "epoch", data_type: "bigint", is_nullable: "NO", column_default: "0" },
{
column_name: "deleted_at",
data_type: "timestamp with time zone",
is_nullable: "NO",
column_default: "now()",
},
]);

const sandboxId = "migration-epoch-sandbox";
await sql`INSERT INTO sandboxes (id, root_inode) VALUES (${sandboxId}, NULL)`;
const initial = await sql<{ version: string }[]>`
SELECT version::text FROM sandboxes WHERE id = ${sandboxId}
`;
expect(initial[0]?.version).toBe("0");

await sql`
INSERT INTO sandbox_epochs (sandbox_id, epoch)
VALUES (${sandboxId}, 1)
`;
await sql`DELETE FROM sandboxes WHERE id = ${sandboxId}`;
const tombstone = await sql<{ sandbox_id: string; epoch: string }[]>`
SELECT sandbox_id, epoch::text FROM sandbox_epochs WHERE sandbox_id = ${sandboxId}
`;
expect(tombstone).toEqual([{ sandbox_id: sandboxId, epoch: "1" }]);
} finally {
await sql.end({ timeout: 5 });
}

await expect(runMigrations(cfg)).resolves.toBeUndefined();
});

const afterRerun = postgres(testUrl, { prepare: false, max: 1 });
try {
const versionColumn = await afterRerun<{ data_type: string; is_nullable: string }[]>`
SELECT data_type, is_nullable
FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 'sandboxes' AND column_name = 'version'
`;
expect(versionColumn).toEqual([{ data_type: "bigint", is_nullable: "NO" }]);

const tombstone = await afterRerun<{ epoch: string }[]>`
SELECT epoch::text FROM sandbox_epochs WHERE sandbox_id = 'migration-epoch-sandbox'
`;
expect(tombstone).toEqual([{ epoch: "1" }]);
} finally {
await afterRerun.end({ timeout: 5 });
}
}, 60_000);
});
Loading
Loading