fix: harden durable runtime DEV gates
This commit is contained in:
@@ -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}`);
|
||||
|
||||
Reference in New Issue
Block a user