Files
Jyotisha/frontend/tests/lagna-precheck-chart.test.ts
T
jesse-uxandGrok (agent loop) ccfe49e78f Attach the ascendant precheck to the chart request.
The D1 segment scan runs beside the natal chart with a 3 second timeout. A failed scan omits the field and still returns the chart. Ran tests/lagna-precheck-chart.test.ts: 3 pass, and tests/chart-view-route.test.ts passed.

Co-Authored-By: Grok (agent loop)
2026-10-27 03:52:12 +08:00

151 lines
5.3 KiB
TypeScript

/**
* Chart view keeps a D1 ascendant precheck beside the natal chart.
* A failed scan leaves the field off and the chart at 200. Fictional fixture only.
*/
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { assembleChartView, type ChartViewEnginePost } from "../src/lib/chart-view-load.ts";
import { fetchLagnaPrecheck } from "../src/lib/lagna-precheck-fetch.ts";
import { mapLagnaPrecheck, type ChartViewProfileInput } from "../src/lib/chart-view-mapper.ts";
import type { LagnaPrecheckWindow } from "../src/lib/lagna-precheck.ts";
const golden = JSON.parse(
readFileSync(new URL("./fixtures/chart-view-golden.json", import.meta.url), "utf8"),
) as { chart: Record<string, unknown> };
const profile: ChartViewProfileInput = {
name: "示例",
date: "1990-06-15",
time: "12:00",
placeLabel: "北京",
latitude: 39.9,
longitude: 116.4,
timezoneOffset: 8,
ayanamsa: "raman",
};
const birthWindow: LagnaPrecheckWindow = {
start_at: "1990-06-15T11:58",
end_at: "1990-06-15T12:02",
minutes: 5,
source: "hospital_record",
precisionKind: "minutes",
beforeMinutes: 2,
afterMinutes: 2,
};
function postEngine(fail = false): ChartViewEnginePost {
return async (path) => {
if (fail || path !== "/api/chart") return { status: "http_error", path, elapsedMs: 1, httpStatus: 500 };
return { status: "ok", payload: golden.chart };
};
}
test("one D1 segment is fixed and two segments name the change clock", () => {
const fixed = mapLagnaPrecheck(birthWindow, {
segments: { D1: [{ sign: 0, start_at: "1990-06-15T11:58", end_at: "1990-06-15T12:02" }] },
});
assert.equal(fixed?.state, "fixed");
assert.deepEqual(fixed?.signs, ["白羊"]);
assert.equal(fixed?.changeAt, undefined);
const changed = mapLagnaPrecheck(birthWindow, {
segments: { D1: [
{ sign: 0, start_at: "1990-06-15T11:58", end_at: "1990-06-15T12:00" },
{ sign: 1, start_at: "1990-06-15 12:01", end_at: "1990-06-15T12:02" },
] },
});
assert.equal(changed?.state, "may_change");
assert.deepEqual(changed?.signs, ["白羊", "金牛"]);
assert.deepEqual(changed?.changeAt, ["12:01"]);
const unknown = mapLagnaPrecheck({ ...birthWindow, source: "unknown", precisionKind: "unknown" }, {
segments: { D1: [{ sign: 2, start_at: "1990-06-15T00:00", end_at: "1990-06-15T23:59" }] },
});
assert.equal(unknown?.state, "unknown_time");
assert.equal(mapLagnaPrecheck(birthWindow, { segments: { D1: [] } }), null);
assert.equal(mapLagnaPrecheck(birthWindow, { segments: { D1: [{ sign: 12, start_at: "1990-06-15T11:58" }] } }), null);
});
test("a thrown or non-ok segment scan returns null and does not call boundary charts", async () => {
const thrown = await fetchLagnaPrecheck({
window: birthWindow,
latitude: 39.9,
longitude: 116.4,
timezoneOffset: 8,
fetchImpl: async () => { throw new Error("down"); },
});
assert.equal(thrown, null);
const failed = await fetchLagnaPrecheck({
window: birthWindow,
latitude: 39.9,
longitude: 116.4,
timezoneOffset: 8,
fetchImpl: async () => new Response("no", { status: 500 }),
});
assert.equal(failed, null);
const seen: { body: Record<string, unknown> | null } = { body: null };
const ok = await fetchLagnaPrecheck({
window: birthWindow,
latitude: 39.9,
longitude: 116.4,
timezoneOffset: 8,
ayanamsa: "raman",
fetchImpl: async (_url, init) => {
seen.body = JSON.parse(String(init?.body)) as Record<string, unknown>;
assert.equal(init?.signal instanceof AbortSignal, true);
return new Response(JSON.stringify({
segments: { D1: [{ sign: 0, start_at: "1990-06-15T11:58", end_at: "1990-06-15T12:02" }] },
}), { status: 200 });
},
});
assert.equal(ok?.state, "fixed");
assert.ok(seen.body);
assert.deepEqual(seen.body.target_charts, ["D1"]);
assert.equal("boundary_charts" in seen.body, false);
assert.equal(seen.body.start_at, "1990-06-15T11:58");
});
test("a rejected precheck is omitted and a failed chart does not wait for it", async () => {
const ok = await assembleChartView({
userId: "user-1",
profile,
postEngine: postEngine(),
asOf: "2026-09-15",
lagnaPrecheck: Promise.resolve(mapLagnaPrecheck(birthWindow, {
segments: { D1: [
{ sign: 0, start_at: "1990-06-15T11:58", end_at: "1990-06-15T12:00" },
{ sign: 1, start_at: "1990-06-15T12:01", end_at: "1990-06-15T12:02" },
] },
})),
});
assert.equal(ok.httpStatus, 200);
assert.equal(ok.body.status, "ok");
assert.equal(ok.body.status === "ok" ? ok.body.lagnaPrecheck?.state : null, "may_change");
const rejected = await assembleChartView({
userId: "user-1",
profile,
postEngine: postEngine(),
asOf: "2026-09-15",
lagnaPrecheck: Promise.reject(new Error("timeout")),
});
assert.equal(rejected.httpStatus, 200);
assert.equal(rejected.body.status, "ok");
assert.equal(rejected.body.status === "ok" && rejected.body.lagnaPrecheck, undefined);
const failedChart = await assembleChartView({
userId: "user-1",
profile,
postEngine: postEngine(true),
asOf: "2026-09-15",
lagnaPrecheck: new Promise(() => {}),
});
assert.equal(failedChart.httpStatus, 200);
assert.notEqual(failedChart.body.status, "ok");
assert.equal("lagnaPrecheck" in failedChart.body, false);
});