Files
Jyotisha/frontend/tests/chart-view-engine.test.ts
T
jesse-ux 5a8328e591
Independent Staging Quality Gate / validate (push) Successful in 27m22s
Independent Staging Quality Gate / publish (push) Canceled after 47s
fix(web): paint the chart page shell before engine calls
BUG-715/716/717: typed engine failures with logs and split copy, natal-only open, on-demand tabs, 10s timeout, in-memory cache, no speed-promise eyebrow.
2026-09-15 22:51:15 +08:00

124 lines
4.1 KiB
TypeScript

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",
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);
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), []);
assert.deepEqual(parseChartViewLayers("western,varga,nope,qizheng"), ["varga", "western", "qizheng"]);
});