/** * 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 }; 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 | 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; 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); });