import assert from "node:assert/strict"; import test from "node:test"; import { classifyCodeAgentBrowserJourney, classifyLiveDeploymentIdentity, classifyLiveWebAssetIdentity, sanitizeAgentChatBody } from "./src/dev-cloud-workbench-smoke-lib.mjs"; import { classifyCodeAgentChatReadiness } from "./src/code-agent-response-contract.mjs"; const sourceIdentity = Object.freeze({ status: "observed", commitId: "ff4b3f928f76a0b46cd7998a2a865f3e1caf02c8", shortCommitId: "ff4b3f928f76", reportCommitId: "ff4b3f928f76", worktreeState: "clean", dirty: false }); const expectedRuntimeIdentity = Object.freeze({ status: "observed", serviceId: "hwlab-cloud-api", source: "deploy/artifact-catalog.dev.json+deploy/deploy.json", commitId: "7de6edd", imageTag: "7de6edd", image: "127.0.0.1:5000/hwlab/hwlab-cloud-api:7de6edd" }); test("live workbench identity passes only when runtime commit or image tag matches current source", () => { assert.equal( classifyLiveDeploymentIdentity(sourceIdentity, { status: "observed", commitId: "7de6edd2c41f", imageTag: "c7de474" }, expectedRuntimeIdentity).status, "pass" ); assert.equal( classifyLiveDeploymentIdentity(sourceIdentity, { status: "observed", commitId: "unknown", imageTag: "7de6edd" }, expectedRuntimeIdentity).status, "pass" ); const stale = classifyLiveDeploymentIdentity(sourceIdentity, { status: "observed", commitId: "c7de474", imageTag: "c7de474" }, expectedRuntimeIdentity); assert.equal(stale.status, "blocked"); assert.match(stale.summary, /Deployment drift/u); assert.match(stale.reason, /does not match current source desired runtime identity/u); }); test("live workbench identity blocks when source identity is dirty or runtime identity is absent", () => { const dirty = classifyLiveDeploymentIdentity( { ...sourceIdentity, worktreeState: "dirty", dirty: true, reportCommitId: "unknown" }, { status: "observed", commitId: "7de6edd", imageTag: "7de6edd" }, expectedRuntimeIdentity ); assert.equal(dirty.status, "blocked"); assert.match(dirty.reason, /not cleanly attributable/u); const missingRuntime = classifyLiveDeploymentIdentity(sourceIdentity, { status: "not_observed", commitId: "not_observed", imageTag: "not_observed" }, expectedRuntimeIdentity); assert.equal(missingRuntime.status, "blocked"); assert.match(missingRuntime.reason, /not observed/u); }); test("live workbench identity uses deploy desired runtime identity instead of git HEAD", () => { const deployed = classifyLiveDeploymentIdentity(sourceIdentity, { status: "observed", commitId: "7de6edd", imageTag: "7de6edd" }, expectedRuntimeIdentity); assert.equal(deployed.status, "pass"); assert.equal(deployed.expectedCommit, "7de6edd"); assert.equal(deployed.expectedReportCommitId, "ff4b3f928f76"); assert.equal(deployed.expectedSource, "deploy/artifact-catalog.dev.json+deploy/deploy.json"); }); test("live web asset identity blocks stale deployed primary assets", () => { const pass = classifyLiveWebAssetIdentity([ { path: "index.html", status: "match" }, { path: "styles.css", status: "match" }, { path: "app.mjs", status: "match" } ]); assert.equal(pass.status, "pass"); const stale = classifyLiveWebAssetIdentity([ { path: "index.html", status: "match" }, { path: "styles.css", status: "mismatch" }, { path: "app.mjs", status: "mismatch" } ]); assert.equal(stale.status, "blocked"); assert.deepEqual(stale.mismatches, ["styles.css", "app.mjs"]); assert.match(stale.summary, /Deployment drift/u); }); test("Code Agent browser classifier blocks provider 502 response shapes", () => { const summary = sanitizeAgentChatBody({ status: "completed", provider: "openai-responses", model: "gpt-5.5", backend: "hwlab-cloud-api/openai-responses", traceId: "trc_provider_502", reply: { content: "must not count" }, error: { code: "provider_unavailable", message: "OpenAI Responses returned HTTP 502: request rejected", providerStatus: 502, missingEnv: [] } }, { httpStatus: 502 }); assert.equal(summary.status, "failed"); assert.equal(summary.hasReply, false); assert.equal(summary.error.code, "provider_unavailable"); assert.equal(summary.error.providerStatus, 502); const classification = classifyCodeAgentBrowserJourney({ responseSummary: summary, httpOk: false, httpStatus: 502, ui: { agentChatStatus: "DEV-LIVE 回复", completedMessageVisible: true, failedMessageVisible: false } }); assert.equal(classification.status, "blocked"); assert.equal(classification.blocker, "provider-upstream"); }); test("Code Agent readiness classifier blocks completed payloads over any non-2xx HTTP status", () => { const completedPayload = { conversationId: "cnv_completed_non_2xx", sessionId: "cnv_completed_non_2xx", messageId: "msg_completed_non_2xx", status: "completed", provider: "openai-responses", model: "gpt-5.5", backend: "hwlab-cloud-api/openai-responses", traceId: "trc_completed_non_2xx", reply: { content: "must only count over HTTP 2xx" } }; const accepted = classifyCodeAgentChatReadiness(completedPayload, { realDevLive: true, httpStatus: 200 }); assert.equal(accepted.status, "pass"); assert.equal(accepted.devLiveReplyPass, true); for (const httpStatus of [400, 401, 403, 429, 500, 502, 503, 504]) { const blocked = classifyCodeAgentChatReadiness(completedPayload, { realDevLive: true, httpStatus }); assert.equal(blocked.status, "blocked", `HTTP ${httpStatus} must block`); assert.equal(blocked.blocker, "provider-upstream", `HTTP ${httpStatus} blocker`); assert.equal(blocked.devLiveReplyPass, false, `HTTP ${httpStatus} must not pass DEV-LIVE`); assert.equal(blocked.providerStatus, httpStatus, `HTTP ${httpStatus} providerStatus`); assert.match(blocked.reason, /HTTP non-2xx|upstream response/u, `HTTP ${httpStatus} reason`); } }); test("Code Agent browser classifier accepts only completed HTTP success plus completed UI and real provider evidence", () => { const summary = sanitizeAgentChatBody({ status: "completed", provider: "openai-responses", model: "gpt-5.5", backend: "hwlab-cloud-api/openai-responses", traceId: "trc_completed", reply: { content: "ok" } }, { httpStatus: 200 }); const pass = classifyCodeAgentBrowserJourney({ responseSummary: summary, httpOk: true, httpStatus: 200, ui: { agentChatStatus: "DEV-LIVE 回复", completedMessageVisible: true, failedMessageVisible: false } }); assert.equal(pass.status, "pass"); const blocked = classifyCodeAgentBrowserJourney({ responseSummary: summary, httpOk: false, httpStatus: 500, ui: { agentChatStatus: "DEV-LIVE 回复", completedMessageVisible: true, failedMessageVisible: false } }); assert.equal(blocked.status, "blocked"); assert.equal(blocked.blocker, "provider-upstream"); }); test("Code Agent browser classifier blocks completed payloads without backend evidence", () => { const summary = sanitizeAgentChatBody({ status: "completed", provider: "openai-responses", model: "gpt-5.5", traceId: "trc_missing_backend", reply: { content: "must not count" } }, { httpStatus: 200 }); const classification = classifyCodeAgentBrowserJourney({ responseSummary: summary, httpOk: true, httpStatus: 200, ui: { agentChatStatus: "DEV-LIVE 回复", completedMessageVisible: true, failedMessageVisible: false } }); assert.equal(classification.status, "blocked"); assert.equal(classification.blocker, "untrusted-completion"); });