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)
151 lines
5.3 KiB
TypeScript
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);
|
|
});
|