fix: harden durable runtime DEV gates

This commit is contained in:
Code Queue Review
2026-05-23 12:41:41 +00:00
parent 85e4c1c4ad
commit 30967566b7
7 changed files with 355 additions and 19 deletions
+62 -1
View File
@@ -179,6 +179,62 @@ test("live dry-run classifies pg_hba no-encryption rejection as SSL with ssl dis
assert.equal(JSON.stringify(report).includes("pg_hba.conf"), false);
});
test("live dry-run classifies missing runtime schema before migration ledger", async () => {
const report = await buildDevRuntimeMigrationReport(
parseArgs(["--dry-run", "--allow-live-db-read", "--confirm-dev"]),
{
env: {
HWLAB_CLOUD_DB_SSL_MODE: "disable"
},
queryClient: {
async query(sql) {
assert.match(sql, /information_schema\.columns/u);
return { rows: [] };
}
},
now: () => "2026-05-23T00:00:00.000Z"
}
);
assert.equal(report.conclusion.status, "blocked");
assert.equal(report.actions.liveDbReadAttempted, true);
assert.equal(report.actions.liveDbWriteAttempted, false);
assert.equal(report.gates.auth.status, "ready");
assert.equal(report.gates.schema.status, "blocked");
assert.equal(report.gates.migration.status, "not_checked");
assert.equal(report.gates.readiness.status, "not_checked");
assert.equal(report.blockers[0].scope, "runtime-migration-schema");
assert.equal(report.blockers[0].evidence.blocker, "runtime_durable_adapter_schema_blocked");
assert.equal(report.runtime.schema.ready, false);
assert.ok(report.runtime.schema.missingTables.includes("gateway_sessions"));
});
test("live dry-run classifies durability read query blocker after schema and ledger are ready", async () => {
const report = await buildDevRuntimeMigrationReport(
parseArgs(["--dry-run", "--allow-live-db-read", "--confirm-dev"]),
{
env: {
HWLAB_CLOUD_DB_SSL_MODE: "disable"
},
queryClient: createFakeQueryClient({ migrationReady: true, countErrorCode: "57014" }),
now: () => "2026-05-23T00:00:00.000Z"
}
);
assert.equal(report.conclusion.status, "blocked");
assert.equal(report.actions.liveDbReadAttempted, true);
assert.equal(report.actions.liveDbWriteAttempted, false);
assert.equal(report.gates.auth.status, "ready");
assert.equal(report.gates.schema.status, "ready");
assert.equal(report.gates.migration.status, "ready");
assert.equal(report.gates.readiness.status, "blocked");
assert.equal(report.blockers[0].scope, "runtime-migration-readiness");
assert.equal(report.blockers[0].evidence.blocker, "runtime_durable_adapter_query_blocked");
assert.equal(report.blockers[0].evidence.queryResult, "query_blocked");
assert.equal(report.runtime.migration.ready, true);
assert.equal(report.runtime.connection.queryResult, "query_blocked");
});
test("apply mode records ledger and verifies durable runtime readiness", async () => {
const queryClient = createFakeQueryClient({ migrationReady: false });
const report = await buildDevRuntimeMigrationReport(
@@ -283,7 +339,7 @@ test("apply mode uses a dedicated pg client transaction", async () => {
assert.ok(pools[1].calls.some((call) => call.type === "end"));
});
function createFakeQueryClient({ migrationReady }) {
function createFakeQueryClient({ migrationReady, countErrorCode = null }) {
const state = {
migrationReady,
calls: []
@@ -317,6 +373,11 @@ function createFakeQueryClient({ migrationReady }) {
};
}
if (sql.startsWith("SELECT COUNT(*)::int AS count FROM ")) {
if (countErrorCode) {
const error = new Error("durability count blocked");
error.code = countErrorCode;
throw error;
}
return { rows: [{ count: 0 }] };
}
throw new Error(`unexpected query: ${sql}`);