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.
124 lines
4.1 KiB
TypeScript
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"]);
|
|
});
|