import assert from "node:assert/strict"; import test from "node:test"; import { buildCommanderReadinessSummary, commitsMatch, evaluateArtifactRuntimeReadiness, normalizeCommit } from "./src/artifact-runtime-readiness-guard.mjs"; const targetSha = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; const staleSha = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; const digest = "sha256:1111111111111111111111111111111111111111111111111111111111111111"; function artifactReport(sourceCommitId = targetSha, overrides = {}) { return { commitId: sourceCommitId.slice(0, 7), artifactPublish: { status: "published", sourceCommitId, serviceCount: 2, requiredServiceCount: 2, publishedCount: 2, registryPrefix: "127.0.0.1:5000/hwlab", services: [ { serviceId: "hwlab-cloud-api", artifactRequired: true, status: "published", imageTag: sourceCommitId.slice(0, 7), digest }, { serviceId: "hwlab-cloud-web", artifactRequired: true, status: "published", imageTag: sourceCommitId.slice(0, 7), digest, runtimeKind: "cloud-web", distFreshness: { status: "pass" } } ], ...overrides } }; } function artifactCatalog(commitId = targetSha, { ciPublished = true, registryVerified = true, serviceDigest = digest, artifactState = "published" } = {}) { return { artifactState, commitId, publish: { ciPublished, registryVerified }, services: [ { serviceId: "hwlab-cloud-api", commitId, artifactRequired: true, publishState: serviceDigest === "not_published" ? "skeleton-only" : "published", imageTag: commitId.slice(0, 7), digest: serviceDigest }, { serviceId: "hwlab-cloud-web", commitId, artifactRequired: true, publishState: serviceDigest === "not_published" ? "skeleton-only" : "published", imageTag: commitId.slice(0, 7), digest: serviceDigest } ] }; } function desiredStatePlan(commitId = targetSha, state = "already_promoted", status = "pass") { return { status, summary: { desiredCommitId: commitId, desiredImageTag: commitId.slice(0, 7), diagnostics: status === "blocked" ? 1 : 0, blockers: status === "blocked" ? 1 : 0 }, target: { convergence: { state, comparableFields: 8, matchingTargetFields: state === "already_promoted" ? 8 : 0, pendingTargetFields: state === "already_promoted" ? 0 : 8 } }, services: [ { serviceId: "hwlab-cloud-web", catalog: { commitId } } ] }; } function liveRuntime(commitId = targetSha, { cloudWebCommitId = commitId, apiCommitId = commitId } = {}) { return { mode: "live-read-only-http", api: { status: "observed", source: "health-live", evidenceSource: "live-http", endpoint: "http://74.48.78.17:16667/health/live", serviceId: "hwlab-cloud-api", environment: "dev", healthStatus: "ok", commitId: apiCommitId, shortCommitId: apiCommitId.slice(0, 7), commitSource: "runtime-env", imageTag: apiCommitId.slice(0, 7), observedAt: "2026-05-22T00:00:00.000Z" }, cloudWeb: { status: "observed", source: "health-live", evidenceSource: "live-http", endpoint: "http://74.48.78.17:16666/health/live", serviceId: "hwlab-cloud-web", environment: "dev", healthStatus: "ok", artifactKind: "cloud-web", commitId: cloudWebCommitId, shortCommitId: cloudWebCommitId.slice(0, 7), commitSource: "runtime-env", imageTag: cloudWebCommitId.slice(0, 7), observedAt: "2026-05-22T00:00:00.000Z" } }; } function readiness(overrides = {}) { return evaluateArtifactRuntimeReadiness({ artifactReport: artifactReport(overrides.artifactSource ?? targetSha, overrides.artifactReportOverrides), artifactCatalog: artifactCatalog(overrides.catalogCommit ?? targetSha, overrides.catalogOptions ?? {}), desiredStatePlan: overrides.desiredPlan ?? desiredStatePlan(overrides.desiredCommit ?? targetSha, overrides.desiredState ?? "already_promoted", overrides.desiredStatus ?? "pass"), runtimeReport: null, liveRuntime: overrides.live ?? liveRuntime(targetSha), targetRef: "origin/main", targetCommitId: overrides.targetCommit ?? targetSha }); } test("commit comparison accepts full and short lowercase SHA forms only", () => { assert.equal(normalizeCommit("ABCDEF1"), "abcdef1"); assert.equal(normalizeCommit("not-a-sha"), "unknown"); assert.equal(commitsMatch(targetSha, targetSha.slice(0, 7)), true); assert.equal(commitsMatch(targetSha, "unknown"), false); }); test("readiness passes only when artifact, desired-state, API runtime, and Cloud Web runtime match target", () => { const result = readiness(); assert.equal(result.ready, true); assert.equal(result.canInferFromSourceLocalStatic, false); assert.deepEqual(result.blockers, []); }); test("stale local artifact report cannot imply latest-main readiness", () => { const result = readiness({ targetCommit: staleSha }); assert.equal(result.ready, false); assert.equal(result.canInferFromSourceLocalStatic, false); assert.deepEqual( result.blockers.map((item) => item.id), [ "artifact-report-target-match", "artifact-catalog-target-match", "desired-state-target-match", "api-runtime-target-match", "cloud-web-runtime-target-match" ] ); }); test("desired-state promotion pending blocks before apply", () => { const result = readiness({ desiredCommit: staleSha, desiredState: "promotion_pending", desiredStatus: "planned" }); assert.equal(result.ready, false); assert.equal(result.desiredState.targetConvergence.state, "promotion_pending"); assert.ok(result.blockers.some((item) => item.id === "desired-state-target-match")); }); test("unpublished catalog digests keep readiness blocked", () => { const result = readiness({ catalogOptions: { artifactState: "contract-skeleton", ciPublished: false, registryVerified: false, serviceDigest: "not_published" } }); assert.equal(result.ready, false); assert.equal(result.catalog.ciPublished, false); assert.equal(result.catalog.registryVerified, false); assert.deepEqual( result.blockers.map((item) => item.id), ["artifact-catalog-published"] ); }); test("Cloud Web served runtime identity must match artifact and target", () => { const result = readiness({ live: liveRuntime(targetSha, { cloudWebCommitId: staleSha }) }); assert.equal(result.ready, false); assert.ok(result.blockers.some((item) => item.id === "cloud-web-runtime-target-match")); assert.ok(result.blockers.some((item) => item.id === "cloud-web-runtime-artifact-match")); assert.equal(result.runtime.cloudWeb.effective.shortCommitId, staleSha.slice(0, 7)); }); test("Cloud Web publish report must retain build freshness evidence", () => { const result = readiness({ artifactReportOverrides: { services: [ { serviceId: "hwlab-cloud-api", artifactRequired: true, status: "published", imageTag: targetSha.slice(0, 7), digest }, { serviceId: "hwlab-cloud-web", artifactRequired: true, status: "published", imageTag: targetSha.slice(0, 7), digest, runtimeKind: "cloud-web" } ] } }); assert.equal(result.ready, false); assert.equal(result.artifact.cloudWeb.distFreshnessStatus, "missing"); assert.ok(result.blockers.some((item) => item.id === "cloud-web-published-build-freshness")); }); test("compact commander summary exposes canPublish/canApply and dry-run devLiveClaim false", () => { const result = readiness(); const summary = buildCommanderReadinessSummary({ readiness: result, targetRef: "origin/main", generatedFromCommit: targetSha, explicitLive: false, cloudWebDistFreshness: { status: "pass", mismatches: [] }, codeAgentTimeoutReadiness: { status: "pass", frontendTimeoutMs: 1800000, backendTimeoutMs: 1200000, requiredFrontendTimeoutMs: 35000, requiredBackendTimeoutMs: 1200000, transportWaitsForBackend: true, summary: "ready" }, runtimeReport: { runtimeIdentity: { ready: true, runtime: { durable: true, ready: true }, readiness: { ready: true, durability: { ready: true } } } } }); assert.equal(summary.kind, "hwlab-dev-rollout-readiness"); assert.equal(summary.canPublish, true); assert.equal(summary.canApply, true); assert.equal(summary.devLiveClaim, false); assert.deepEqual(summary.blockedReasons, []); assert.equal(summary.currentSourceCommit.shortCommitId, targetSha.slice(0, 7)); }); test("compact summary blocks publish on stale Cloud Web source dist", () => { const result = readiness(); const summary = buildCommanderReadinessSummary({ readiness: result, targetRef: "origin/main", generatedFromCommit: targetSha, explicitLive: false, cloudWebDistFreshness: { status: "blocked", mismatches: ["index.html"] }, codeAgentTimeoutReadiness: { status: "pass", frontendTimeoutMs: 1800000, backendTimeoutMs: 1200000, requiredFrontendTimeoutMs: 35000, requiredBackendTimeoutMs: 1200000, transportWaitsForBackend: true, summary: "ready" }, runtimeReport: { runtimeIdentity: { ready: true, runtime: { durable: true, ready: true }, readiness: { ready: true, durability: { ready: true } } } } }); assert.equal(summary.canPublish, false); assert.equal(summary.canApply, false); assert.equal(summary.cloudWebBuildFreshness.status, "blocked"); assert.ok(summary.blockedReasons.some((item) => item.id === "cloud-web-build-freshness")); assert.ok(summary.nextCommands.includes("bun run --cwd web/hwlab-cloud-web build")); }); test("compact summary separates Code Agent timeout and durable runtime blockers from apply readiness", () => { const result = readiness(); const summary = buildCommanderReadinessSummary({ readiness: result, targetRef: "origin/main", generatedFromCommit: targetSha, explicitLive: false, cloudWebDistFreshness: { status: "pass", mismatches: [] }, codeAgentTimeoutReadiness: { status: "blocked", frontendTimeoutMs: 4500, backendTimeoutMs: 1200000, requiredFrontendTimeoutMs: 35000, requiredBackendTimeoutMs: 1200000, transportWaitsForBackend: false, summary: "Code Agent browser timeout is too short." }, runtimeReport: { runtimeIdentity: { ready: false, runtime: { durable: false, ready: false, blocker: "runtime_durable_adapter_query_blocked", connection: { queryResult: "query_blocked" } }, readiness: { ready: false, durability: { ready: false, blocker: "runtime_durable_adapter_query_blocked", blockedLayer: "durability_query", queryResult: "query_blocked" } }, blockerCodes: ["runtime_durable_adapter_query_blocked"] } } }); assert.equal(summary.canPublish, false); assert.equal(summary.canApply, false); assert.equal(summary.codeAgentTimeoutReadiness.status, "blocked"); assert.equal(summary.runtimeDurableBlocker.classification, "runtime_durable_adapter_query_blocked"); assert.ok(summary.blockedReasons.some((item) => item.id === "code-agent-timeout-readiness")); assert.ok(summary.blockedReasons.some((item) => item.id === "runtime-durable:runtime_durable_adapter_query_blocked")); }); test("compact summary blocks publish when Code Agent transport timeout does not exceed backend timeout", () => { const result = readiness(); const summary = buildCommanderReadinessSummary({ readiness: result, targetRef: "origin/main", generatedFromCommit: targetSha, explicitLive: false, cloudWebDistFreshness: { status: "pass", mismatches: [] }, codeAgentTimeoutReadiness: { status: "blocked", frontendTimeoutMs: 1200000, backendTimeoutMs: 1200000, requiredFrontendTimeoutMs: 35000, requiredBackendTimeoutMs: 1200000, transportWaitsForBackend: false, summary: "Code Agent timeout layering is unsafe." }, runtimeReport: { runtimeIdentity: { ready: true, runtime: { durable: true, ready: true }, readiness: { ready: true, durability: { ready: true } } } } }); assert.equal(summary.canPublish, false); assert.equal(summary.canApply, false); assert.equal(summary.codeAgentTimeoutReadiness.transportWaitsForBackend, false); assert.ok(summary.blockedReasons.some((item) => item.id === "code-agent-timeout-readiness")); }); test("compact summary keeps provider-secret blockers out of publish but blocks apply", () => { const result = readiness(); const summary = buildCommanderReadinessSummary({ readiness: result, targetRef: "origin/main", generatedFromCommit: targetSha, explicitLive: false, cloudWebDistFreshness: { status: "pass", mismatches: [] }, codeAgentTimeoutReadiness: { status: "pass", frontendTimeoutMs: 1800000, backendTimeoutMs: 1200000, requiredFrontendTimeoutMs: 35000, requiredBackendTimeoutMs: 1200000, transportWaitsForBackend: true, summary: "ready" }, edgeReport: { blockers: [ { scope: "code-agent-provider-secret", classification: "provider_secret_missing", summary: "Code Agent provider Secret is missing." } ] }, runtimeReport: { runtimeIdentity: { ready: true, runtime: { durable: true, ready: true }, readiness: { ready: true, durability: { ready: true } } } } }); assert.equal(summary.canPublish, true); assert.equal(summary.canApply, false); assert.equal(summary.codeAgentProviderReadiness.status, "blocked"); assert.ok(summary.blockedReasons.some((item) => item.id === "code-agent-provider:code-agent-provider-secret")); });