181 lines
8.0 KiB
JavaScript
181 lines
8.0 KiB
JavaScript
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();
|
|
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.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);
|
|
|
|
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";
|
|
process.env.HWLAB_CLOUD_DB_PROBE_DISABLED = "1";
|
|
|
|
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, false);
|
|
assert.equal(payload.db.connectionResult, "not_attempted");
|
|
assert.equal(payload.db.evidence, "env_presence_only_probe_disabled");
|
|
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()));
|
|
});
|
|
}
|
|
});
|