Files
Jyotisha/frontend/tests/chart-view-engine.test.ts
T

127 lines
4.3 KiB
TypeScript
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.
import assert from "node:assert/strict";
import test from "node:test";
import {
CHART_VIEW_ENGINE_CACHE_TTL_MS,
CHART_VIEW_ENGINE_TIMEOUT_MS,
chartViewEngineCacheKey,
clearChartViewEngineCache,
engineCallFromHttp,
engineCallFromThrown,
logEngineCallFailure,
parseChartViewLayers,
readChartViewEngineCache,
writeChartViewEngineCache,
} from "../src/lib/chart-view-engine.ts";
function captureWarnings(run: () => void): unknown[] {
const warnings: unknown[] = [];
const original = console.warn;
console.warn = (...args: unknown[]) => {
warnings.push(args);
};
try {
run();
} finally {
console.warn = original;
}
return warnings;
}
const baseKey = {
userId: "user-1",
subjectId: "self",
date: "1990-04-09",
time: "12:00",
latitude: 39.9042,
longitude: 116.4074,
timezoneOffset: 8,
ayanamsa: "raman",
nodeMode: "mean",
path: "/api/chart",
};
test("engine HTTP outcomes classify 429, other errors, and bad JSON separately", () => {
assert.equal(engineCallFromHttp({
path: "/api/chart",
elapsedMs: 12,
httpStatus: 429,
payload: { error: "busy" },
}).status, "busy");
assert.equal(engineCallFromHttp({
path: "/api/chart",
elapsedMs: 12,
httpStatus: 500,
payload: { error: "boom" },
}).status, "http_error");
assert.equal(engineCallFromHttp({
path: "/api/chart",
elapsedMs: 12,
httpStatus: 200,
payload: "not-json-object",
}).status, "bad_payload");
assert.equal(engineCallFromHttp({
path: "/api/chart",
elapsedMs: 12,
httpStatus: 200,
payload: null,
}).status, "bad_payload");
});
test("timeout and abort errors classify as timeout; other throws as http_error", () => {
const timeout = engineCallFromThrown("/api/chart", 10_001, Object.assign(new Error("aborted"), { name: "TimeoutError" }));
assert.equal(timeout.status, "timeout");
const abort = engineCallFromThrown("/api/chart", 10_001, Object.assign(new Error("aborted"), { name: "AbortError" }));
assert.equal(abort.status, "timeout");
const network = engineCallFromThrown("/api/chart", 30, Object.assign(new Error("fetch failed"), { name: "TypeError" }));
assert.equal(network.status, "http_error");
assert.equal(network.errorName, "TypeError");
});
test("each non-ok engine result logs path, status, and elapsed ms without birth fields", () => {
const cases = [
{ status: "busy" as const, httpStatus: 429 },
{ status: "http_error" as const, httpStatus: 500 },
{ status: "timeout" as const, errorName: "TimeoutError" },
{ status: "bad_payload" as const, httpStatus: 200 },
];
const seen = new Set<string>();
for (const item of cases) {
const warnings = captureWarnings(() => {
logEngineCallFailure({
path: "/api/chart",
elapsedMs: 18,
...item,
});
});
assert.equal(warnings.length, 1);
const line = JSON.stringify(warnings[0]);
assert.match(line, /chart_view_engine_failed/);
assert.match(line, new RegExp(item.status));
assert.match(line, /"elapsedMs":18/);
assert.doesNotMatch(line, /1990|latitude|longitude|name|birth/);
seen.add(item.status);
}
assert.deepEqual([...seen], ["busy", "http_error", "timeout", "bad_payload"]);
});
test("engine cache keys include ayanamsa and node mode and expire", () => {
clearChartViewEngineCache();
const raman = chartViewEngineCacheKey(baseKey);
const lahiri = chartViewEngineCacheKey({ ...baseKey, ayanamsa: "lahiri" });
const trueNode = chartViewEngineCacheKey({ ...baseKey, nodeMode: "true" });
assert.notEqual(raman, lahiri);
assert.notEqual(raman, trueNode);
assert.notEqual(raman, chartViewEngineCacheKey({ ...baseKey, subjectId: "other-1" }));
writeChartViewEngineCache(raman, { success: true }, 1_000);
assert.deepEqual(readChartViewEngineCache(raman, 1_000), { success: true });
assert.equal(readChartViewEngineCache(raman, 1_000 + CHART_VIEW_ENGINE_CACHE_TTL_MS + 1), null);
assert.equal(CHART_VIEW_ENGINE_TIMEOUT_MS, 10_000);
});
test("layer query parsing keeps only known layers in stable order", () => {
assert.deepEqual(parseChartViewLayers(null), []);
// 原值: "qizheng" 被保留为已知层;新值: 与 "nope" 一样被丢弃;原因: 七政层已删(TASK-chart-types D1)
assert.deepEqual(parseChartViewLayers("western,varga,nope,qizheng"), ["varga", "western"]);
});