MessageWeave/cloudflare-worker/test/health.test.js

110 lines
3.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 单元测试:健康聚合(MOD-HEALTH-AGG / C-NO-DB / C-REDIS-ONLY-STATE)。
* 运行:cd cloudflare-worker && node --test
*/
import test from "node:test";
import assert from "node:assert/strict";
import { aggregateHealth, healthResponse } from "../src/health.js";
function mockCtx(plan, opts = {}) {
const calls = [];
const cache = opts.cache ?? new Map();
let time = 1_000_000;
const fetch = async (url) => {
calls.push(url);
const step = plan[url] ?? { status: 500 };
if (step.error) throw step.error;
return new Response(step.body ?? "ok", { status: step.status });
};
return {
fetch,
timeoutMs: 1000,
ttlMs: opts.ttlMs ?? 30_000,
now: () => time,
advance: (ms) => {
time += ms;
},
setTimeout: () => 0,
clearTimeout: () => {},
cache,
calls,
};
}
test("aggregateHealth: all up => 200 ok summary", async () => {
const ctx = mockCtx({
"https://a.example/healthz": { status: 200 },
"https://b.example/healthz": { status: 200 },
});
const res = await aggregateHealth(["https://a.example", "https://b.example"], ctx);
assert.equal(res.available, 2);
assert.equal(res.total, 2);
const resp = healthResponse(res);
assert.equal(resp.status, 200);
const body = await resp.json();
assert.equal(body.status, "ok");
assert.equal(body.available, 2);
assert.equal(body.total, 2);
});
test("healthResponse: version 回显;未传时字段整个消失", async () => {
const summary = { available: 1, total: 1, backends: [] };
const withVersion = await healthResponse(summary, "2026.10.1").json();
assert.equal(withVersion.version, "2026.10.1");
assert.equal(withVersion.status, "ok");
// 未传 version 时字段必须整体消失(而非 "undefined"),保持向后兼容。
const without = await healthResponse(summary).json();
assert.equal("version" in without, false);
});
test("aggregateHealth: partial down => still reports status (partial ok)", async () => {
const ctx = mockCtx({
"https://a.example/healthz": { status: 200 },
"https://b.example/healthz": { error: new Error("conn refused") },
});
const res = await aggregateHealth(["https://a.example", "https://b.example"], ctx);
assert.equal(res.available, 1);
assert.equal(res.total, 2);
const resp = healthResponse(res);
assert.equal(resp.status, 200, "at least one up => 200 ok");
const body = await resp.json();
assert.equal(body.status, "ok");
assert.equal(body.available, 1);
});
test("aggregateHealth: all down => 503", async () => {
const ctx = mockCtx({
"https://a.example/healthz": { status: 503 },
"https://b.example/healthz": { error: new Error("conn refused") },
});
const res = await aggregateHealth(["https://a.example", "https://b.example"], ctx);
assert.equal(res.available, 0);
const resp = healthResponse(res);
assert.equal(resp.status, 503);
const body = await resp.json();
assert.equal(body.status, "down");
});
test("aggregateHealth: respects cache TTL to avoid probe storm", async () => {
const ctx = mockCtx(
{
"https://a.example/healthz": { status: 200 },
},
{ ttlMs: 10_000 },
);
await aggregateHealth(["https://a.example"], ctx);
assert.equal(ctx.calls.length, 1);
// 未过 TTL:命中缓存,不再发起 fetch。
ctx.advance(5_000);
await aggregateHealth(["https://a.example"], ctx);
assert.equal(ctx.calls.length, 1);
// 超过 TTL:发起新 fetch。
ctx.advance(6_000);
await aggregateHealth(["https://a.example"], ctx);
assert.equal(ctx.calls.length, 2);
});