import assert from "node:assert/strict"; import { createServer as createTcpServer } from "node:net"; import test from "node:test"; import { createCloudApiServer } from "./server.mjs"; test("cloud api exposes /health, /health/live, and /live probes", async () => { const originalUrl = process.env.HWLAB_CLOUD_DB_URL; const originalSslMode = process.env.HWLAB_CLOUD_DB_SSL_MODE; delete process.env.HWLAB_CLOUD_DB_URL; delete process.env.HWLAB_CLOUD_DB_SSL_MODE; const server = createCloudApiServer({ env: { PATH: "", HWLAB_CODE_AGENT_PROVIDER: "codex-cli", HWLAB_CODE_AGENT_MODEL: "gpt-test" } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const health = await fetch(`http://127.0.0.1:${port}/health`); assert.equal(health.status, 200); const healthPayload = await health.json(); assert.equal(healthPayload.serviceId, "hwlab-cloud-api"); assert.equal(healthPayload.environment, "dev"); assert.equal(healthPayload.status, "degraded"); assert.equal(healthPayload.commit.id.length > 0, true); assert.equal(healthPayload.image.reference.length > 0, true); assert.equal(healthPayload.service.id, "hwlab-cloud-api"); assert.equal(healthPayload.db.status, "blocked"); assert.equal(healthPayload.db.connected, false); assert.equal(healthPayload.db.liveConnected, false); assert.equal(healthPayload.db.liveDbEvidence, false); assert.equal(healthPayload.db.connectionAttempted, false); assert.equal(healthPayload.db.connectionResult, "not_attempted_missing_env"); assert.equal(healthPayload.db.redaction.valuesRedacted, true); assert.equal(healthPayload.db.redaction.secretMaterialRead, false); assert.equal(healthPayload.db.safety.liveDbEvidence, false); assert.equal(healthPayload.runtime.durable, false); assert.equal(healthPayload.runtime.status, "degraded"); assert.equal(healthPayload.codeAgent.status, "blocked"); assert.equal(healthPayload.codeAgent.blocker, "凭证缺口"); assert.equal(healthPayload.codeAgent.reason, "provider_unavailable"); assert.deepEqual(healthPayload.codeAgent.missingEnv, ["OPENAI_API_KEY"]); assert.equal(healthPayload.codeAgent.secretRefs[0].secretName, "hwlab-code-agent-provider"); assert.equal(healthPayload.codeAgent.secretRefs[0].secretKey, "openai-api-key"); assert.equal(healthPayload.codeAgent.secretRefs[0].redacted, true); assert.equal(healthPayload.codeAgent.egress.directPublicOpenAi, false); assert.equal(healthPayload.codeAgent.safety.secretMaterialRead, false); assert.equal(JSON.stringify(healthPayload.codeAgent).includes("sk-"), false); assert.deepEqual(healthPayload.db.missingEnv, ["HWLAB_CLOUD_DB_URL", "HWLAB_CLOUD_DB_SSL_MODE"]); assert.equal(healthPayload.db.secretRefs[0].secretName, "hwlab-cloud-api-dev-db"); assert.equal(healthPayload.db.secretRefs[0].redacted, true); const healthLive = await fetch(`http://127.0.0.1:${port}/health/live`); assert.equal(healthLive.status, 200); const healthLivePayload = await healthLive.json(); assert.equal(healthLivePayload.serviceId, "hwlab-cloud-api"); assert.equal(healthLivePayload.status, "degraded"); assert.equal(healthLivePayload.commit.id.length > 0, true); assert.equal(healthLivePayload.db.ready, false); assert.equal(healthLivePayload.codeAgent.status, "blocked"); assert.equal(healthLivePayload.codeAgent.ready, false); const readiness = await fetch(`http://127.0.0.1:${port}/health/live`); assert.equal(readiness.status, 200); assert.equal((await readiness.json()).status, "degraded"); const live = await fetch(`http://127.0.0.1:${port}/live`); assert.equal(live.status, 200); assert.equal((await live.json()).status, "live"); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); if (originalUrl === undefined) { delete process.env.HWLAB_CLOUD_DB_URL; } else { process.env.HWLAB_CLOUD_DB_URL = originalUrl; } if (originalSslMode === undefined) { delete process.env.HWLAB_CLOUD_DB_SSL_MODE; } else { process.env.HWLAB_CLOUD_DB_SSL_MODE = originalSslMode; } } }); test("cloud api health reports DB env presence and live connection classification without leaking values", async () => { const originalUrl = process.env.HWLAB_CLOUD_DB_URL; const originalSslMode = process.env.HWLAB_CLOUD_DB_SSL_MODE; const fakeDb = createTcpServer((socket) => socket.end()); await new Promise((resolve) => fakeDb.listen(0, "127.0.0.1", resolve)); const dbPort = fakeDb.address().port; process.env.HWLAB_CLOUD_DB_URL = `postgres://user:password@127.0.0.1:${dbPort}/hwlab`; process.env.HWLAB_CLOUD_DB_SSL_MODE = "require"; const server = createCloudApiServer(); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const response = await fetch(`http://127.0.0.1:${port}/health`); const payload = await response.json(); assert.equal(payload.status, "degraded"); assert.equal(payload.db.status, "ready"); assert.equal(payload.db.connected, true); assert.equal(payload.db.liveConnected, true); assert.equal(payload.db.liveDbEvidence, true); assert.equal(payload.db.connectionChecked, true); assert.equal(payload.db.connectionAttempted, true); assert.equal(payload.db.connectionResult, "connected"); assert.equal(payload.db.configReady, true); assert.equal(payload.db.ready, true); assert.equal(payload.db.evidence, "live_db_tcp_connection_ready"); assert.equal(payload.db.safety.liveDbEvidence, true); assert.equal(payload.db.safety.liveDbConnectedEvidence, true); assert.equal(payload.db.redaction.valuesRedacted, true); assert.equal(payload.db.redaction.endpointRedacted, true); assert.equal(payload.db.redaction.secretMaterialRead, false); assert.deepEqual(payload.db.missingEnv, []); assert.equal(JSON.stringify(payload.db).includes("password"), false); assert.equal(JSON.stringify(payload.db).includes("127.0.0.1"), false); assert.equal(JSON.stringify(payload.db).includes(String(dbPort)), false); assert.equal(payload.db.fields.find((field) => field.name === "HWLAB_CLOUD_DB_URL").redacted, true); assert.equal(payload.db.connection.endpointRedacted, true); } finally { if (originalUrl === undefined) { delete process.env.HWLAB_CLOUD_DB_URL; } else { process.env.HWLAB_CLOUD_DB_URL = originalUrl; } if (originalSslMode === undefined) { delete process.env.HWLAB_CLOUD_DB_SSL_MODE; } else { process.env.HWLAB_CLOUD_DB_SSL_MODE = originalSslMode; } await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); await new Promise((resolve, reject) => { fakeDb.close((error) => (error ? reject(error) : resolve())); }); } }); test("cloud api health does not treat DB env presence-only as live readiness", async () => { const originalUrl = process.env.HWLAB_CLOUD_DB_URL; const originalSslMode = process.env.HWLAB_CLOUD_DB_SSL_MODE; const originalProbeDisabled = process.env.HWLAB_CLOUD_DB_PROBE_DISABLED; process.env.HWLAB_CLOUD_DB_URL = "postgres://user:password@db.example.invalid:5432/hwlab"; process.env.HWLAB_CLOUD_DB_SSL_MODE = "require"; delete process.env.HWLAB_CLOUD_DB_PROBE_DISABLED; const server = createCloudApiServer(); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const response = await fetch(`http://127.0.0.1:${port}/health/live`); const payload = await response.json(); assert.equal(response.status, 200); assert.equal(payload.db.configReady, true); assert.equal(payload.db.connected, false); assert.equal(payload.db.liveConnected, false); assert.equal(payload.db.liveDbEvidence, false); assert.equal(payload.db.safety.liveDbEvidence, false); assert.equal(payload.db.ready, false); assert.equal(payload.db.connectionAttempted, true); assert.equal(payload.db.connectionResult, "forbidden_runtime_host"); assert.equal(payload.db.evidence, "live_db_tcp_connection_blocked"); assert.equal(payload.db.connection.networkAttempted, false); assert.equal(payload.db.connection.classification, "db_url_forbidden_invalid_host"); assert.equal(payload.db.readinessLayers.dns.status, "not_proven"); assert.equal(JSON.stringify(payload.db).includes("password"), false); assert.equal(JSON.stringify(payload.db).includes("db.example.invalid"), false); } finally { if (originalUrl === undefined) { delete process.env.HWLAB_CLOUD_DB_URL; } else { process.env.HWLAB_CLOUD_DB_URL = originalUrl; } if (originalSslMode === undefined) { delete process.env.HWLAB_CLOUD_DB_SSL_MODE; } else { process.env.HWLAB_CLOUD_DB_SSL_MODE = originalSslMode; } if (originalProbeDisabled === undefined) { delete process.env.HWLAB_CLOUD_DB_PROBE_DISABLED; } else { process.env.HWLAB_CLOUD_DB_PROBE_DISABLED = originalProbeDisabled; } await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); } }); test("cloud api /v1 describes Code Agent provider blocker without leaking secret values", async () => { const server = createCloudApiServer({ env: { PATH: "", HWLAB_CODE_AGENT_PROVIDER: "openai", HWLAB_CODE_AGENT_MODEL: "gpt-test" } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const response = await fetch(`http://127.0.0.1:${port}/v1`); assert.equal(response.status, 200); const payload = await response.json(); assert.equal(payload.codeAgent.endpoint, "POST /v1/agent/chat"); assert.equal(payload.codeAgent.provider, "openai-responses"); assert.equal(payload.codeAgent.model, "gpt-test"); assert.equal(payload.codeAgent.backend, "hwlab-cloud-api/openai-responses"); assert.equal(payload.codeAgent.mode, "openai"); assert.equal(payload.codeAgent.status, "blocked"); assert.match(payload.codeAgent.blocker, /DEV Code Agent OpenAI base URL is missing/u); assert.equal(payload.codeAgent.reason, "provider_unavailable"); assert.deepEqual(payload.codeAgent.missingEnv, ["OPENAI_API_KEY", "HWLAB_CODE_AGENT_OPENAI_BASE_URL"]); assert.equal(payload.codeAgent.secretRefs[0].env, "OPENAI_API_KEY"); assert.equal(payload.codeAgent.secretRefs[0].secretName, "hwlab-code-agent-provider"); assert.equal(payload.codeAgent.secretRefs[0].secretKey, "openai-api-key"); assert.equal(payload.codeAgent.secretRefs[0].redacted, true); assert.equal(payload.codeAgent.ready, false); assert.equal(payload.codeAgent.egress.present, false); assert.equal(payload.codeAgent.egress.valueRedacted, true); assert.equal(payload.codeAgent.safety.secretMaterialRead, false); assert.match(payload.codeAgent.summary, /真实后端已接入/u); assert.match(payload.codeAgent.summary, /hwlab-code-agent-provider\/openai-api-key/u); assert.equal(JSON.stringify(payload).includes("sk-"), false); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); } }); test("cloud api /v1 describes Code Agent egress blocker without leaking API key", async () => { const server = createCloudApiServer({ env: { OPENAI_API_KEY: "test-openai-key-material", HWLAB_CODE_AGENT_PROVIDER: "openai", HWLAB_CODE_AGENT_MODEL: "gpt-test", HWLAB_CODE_AGENT_OPENAI_BASE_URL: "https://api.openai.com/v1/responses" } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const response = await fetch(`http://127.0.0.1:${port}/v1`); assert.equal(response.status, 200); const payload = await response.json(); assert.equal(payload.codeAgent.status, "blocked"); assert.equal(payload.codeAgent.egress.directPublicOpenAi, true); assert.match(payload.codeAgent.blocker, /public api\.openai\.com/u); assert.equal(payload.codeAgent.safety.secretMaterialRead, false); assert.equal(JSON.stringify(payload).includes("test-openai-key-material"), false); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); } }); test("cloud api /v1/agent/chat returns structured completed Code Agent payload", async () => { const server = createCloudApiServer({ callCodeAgentProvider: async ({ providerPlan, message, traceId }) => ({ provider: providerPlan.provider, model: providerPlan.model, backend: providerPlan.backend, content: `真实 provider stub: ${message} / ${traceId}`, usage: null, providerTrace: { source: "test-provider" } }) }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const response = await fetch(`http://127.0.0.1:${port}/v1/agent/chat`, { method: "POST", headers: { "content-type": "application/json", "x-trace-id": "trc_server-test-agent-chat" }, body: JSON.stringify({ conversationId: "cnv_server-test-agent-chat", message: "请用一句话说明当前 HWLAB 工作台可以做什么。" }) }); assert.equal(response.status, 200); const payload = await response.json(); assert.equal(payload.conversationId, "cnv_server-test-agent-chat"); assert.equal(payload.sessionId, "cnv_server-test-agent-chat"); assert.match(payload.messageId, /^msg_/); assert.equal(payload.status, "completed"); assert.equal(payload.traceId, "trc_server-test-agent-chat"); assert.equal(payload.provider.length > 0, true); assert.equal(payload.model.length > 0, true); assert.equal(payload.backend.length > 0, true); assert.equal(Number.isNaN(Date.parse(payload.createdAt)), false); assert.equal(Number.isNaN(Date.parse(payload.updatedAt)), false); assert.match(payload.reply.content, /HWLAB 工作台/); assert.equal(Object.hasOwn(payload, "error"), false); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); } }); test("cloud api /v1/agent/chat reports provider gaps without faking a reply", async () => { const server = createCloudApiServer({ env: { PATH: "", HWLAB_CODE_AGENT_PROVIDER: "codex-cli", HWLAB_CODE_AGENT_MODEL: "gpt-test" } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const response = await fetch(`http://127.0.0.1:${port}/v1/agent/chat`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ message: "你好" }) }); assert.equal(response.status, 200); const payload = await response.json(); assert.match(payload.conversationId, /^cnv_/); assert.match(payload.messageId, /^msg_/); assert.equal(payload.status, "failed"); assert.equal(payload.provider, "codex-cli"); assert.equal(payload.model, "gpt-test"); assert.equal(payload.backend, "hwlab-cloud-api/codex-cli"); assert.equal(Number.isNaN(Date.parse(payload.createdAt)), false); assert.equal(Number.isNaN(Date.parse(payload.updatedAt)), false); assert.equal(payload.error.code, "provider_unavailable"); assert.match(payload.error.message, /Codex CLI command is not available/); assert.deepEqual(payload.error.missingCommands, ["codex"]); assert.ok(payload.error.missingEnv.includes("OPENAI_API_KEY")); assert.equal(payload.availability.status, "blocked"); assert.equal(payload.availability.blocker, "凭证缺口"); assert.equal(payload.availability.reason, "provider_unavailable"); assert.equal(payload.availability.secretRefs[0].secretName, "hwlab-code-agent-provider"); assert.equal(payload.availability.secretRefs[0].secretKey, "openai-api-key"); assert.equal(payload.availability.secretRefs[0].redacted, true); assert.match(payload.availability.summary, /真实后端已接入/u); assert.match(payload.availability.summary, /hwlab-code-agent-provider\/openai-api-key/u); assert.equal(JSON.stringify(payload).includes("sk-"), false); assert.equal(Object.hasOwn(payload, "reply"), false); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); } }); test("cloud api /v1/agent/chat does not complete on empty provider text", async () => { const server = createCloudApiServer({ callCodeAgentProvider: async ({ providerPlan }) => ({ provider: providerPlan.provider, model: providerPlan.model, backend: providerPlan.backend, content: " ", usage: null, providerTrace: { source: "empty-test-provider" } }) }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const response = await fetch(`http://127.0.0.1:${port}/v1/agent/chat`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ conversationId: "cnv_empty-provider-text", message: "你好" }) }); assert.equal(response.status, 200); const payload = await response.json(); assert.equal(payload.conversationId, "cnv_empty-provider-text"); assert.equal(payload.status, "failed"); assert.equal(payload.error.code, "provider_unavailable"); assert.match(payload.error.message, /no assistant text/); assert.equal(Object.hasOwn(payload, "reply"), false); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); } });