fix: harden DEV runtime migration transactions

This commit is contained in:
Code Queue Review
2026-05-23 00:14:13 +00:00
parent 63d8f7b5a6
commit 852ec3dbc7
2 changed files with 115 additions and 6 deletions
@@ -104,6 +104,55 @@ test("apply mode records ledger and verifies durable runtime readiness", async (
assert.equal(JSON.stringify(report).includes("redacted@example.invalid"), false);
});
test("live dry-run closes pg pool after readiness verification", async () => {
const backingClient = createFakeQueryClient({ migrationReady: true });
const pools = [];
const report = await buildDevRuntimeMigrationReport(
parseArgs(["--dry-run", "--allow-live-db-read", "--confirm-dev"]),
{
env: {
HWLAB_CLOUD_DB_URL: "postgresql://hwlab:redacted@example.invalid:5432/hwlab",
HWLAB_CLOUD_DB_SSL_MODE: "require"
},
pgModuleLoader: async () => createFakePgModule({ backingClient, pools }),
now: () => "2026-05-22T00:00:00.000Z"
}
);
assert.equal(report.conclusion.status, "ready");
assert.equal(pools.length, 1);
assert.ok(pools[0].calls.some((call) => call.target === "pool" && call.sql.includes("information_schema.columns")));
assert.ok(pools[0].calls.some((call) => call.type === "end"));
});
test("apply mode uses a dedicated pg client transaction", async () => {
const backingClient = createFakeQueryClient({ migrationReady: false });
const pools = [];
const report = await buildDevRuntimeMigrationReport(
parseArgs(["--apply", "--confirm-dev", "--confirmed-non-production"]),
{
env: {
HWLAB_CLOUD_DB_URL: "postgresql://hwlab:redacted@example.invalid:5432/hwlab",
HWLAB_CLOUD_DB_SSL_MODE: "require"
},
pgModuleLoader: async () => createFakePgModule({ backingClient, pools }),
now: () => "2026-05-22T00:00:00.000Z"
}
);
assert.equal(report.conclusion.status, "ready");
assert.equal(pools.length, 2);
assert.ok(pools[0].calls.some((call) => call.type === "connect"));
assert.ok(pools[0].calls.some((call) => call.target === "client" && call.sql === "BEGIN"));
assert.ok(pools[0].calls.some((call) => call.target === "client" && call.sql.includes("INSERT INTO hwlab_schema_migrations")));
assert.ok(pools[0].calls.some((call) => call.target === "client" && call.sql === "COMMIT"));
assert.equal(pools[0].calls.some((call) => call.target === "pool"), false);
assert.ok(pools[0].calls.some((call) => call.type === "release"));
assert.ok(pools[0].calls.some((call) => call.type === "end"));
assert.ok(pools[1].calls.some((call) => call.target === "pool" && call.sql.includes("information_schema.columns")));
assert.ok(pools[1].calls.some((call) => call.type === "end"));
});
function createFakeQueryClient({ migrationReady }) {
const state = {
migrationReady,
@@ -145,6 +194,40 @@ function createFakeQueryClient({ migrationReady }) {
};
}
function createFakePgModule({ backingClient, pools }) {
return {
Pool: class FakePool {
constructor(config) {
this.config = config;
this.calls = [];
pools.push(this);
}
async connect() {
this.calls.push({ type: "connect" });
return {
query: async (sql, params = []) => {
this.calls.push({ target: "client", sql, params });
return backingClient.query(sql, params);
},
release: () => {
this.calls.push({ type: "release" });
}
};
}
async query(sql, params = []) {
this.calls.push({ target: "pool", sql, params });
return backingClient.query(sql, params);
}
async end() {
this.calls.push({ type: "end" });
}
}
};
}
function schemaRows() {
return Object.entries(CLOUD_RUNTIME_DURABLE_TABLE_COLUMNS).flatMap(([table, columns]) =>
columns.map((column) => ({