Files
Jyotisha/frontend/tests/consultation-route-service.test.ts
T
2026-07-21 07:54:04 +08:00

150 lines
5.0 KiB
TypeScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import {
ConsultationProfileTruthError,
prepareConsultationRoute,
} from "../src/lib/consultation-route-service.ts";
const profile = Object.freeze({
name: "岳辰",
birth_date: "1997-08-08",
reported_birth_time: "05:30:00",
active_birth_time: "05:18:00",
birth_time_source: "approximate",
birth_time_status: "candidate",
country_code: "CN",
province_code: "130000",
city_code: "130400",
district_code: "130406",
latitude: 36.420487,
longitude: 114.209936,
timezone_offset: 8,
});
test("route service loads complete server truth before billing and uses only reported time when unverified", async () => {
const order: string[] = [];
const prepared = await prepareConsultationRoute({
userId: "user-1",
mode: "unverified_birth_time",
async loadProfile(userId) {
assert.equal(userId, "user-1");
order.push("profile");
return profile;
},
async reserve() {
order.push("reserve");
return { reservation: "ok" };
},
});
assert.deepEqual(order, ["profile", "reserve"]);
assert.deepEqual(prepared.reservation, { reservation: "ok" });
assert.deepEqual(prepared.serverChart?.toolInput, {
year: 1997,
month: 8,
day: 8,
hour: 5,
minute: 30,
city: "中国 · 河北省 · 邯郸市 · 峰峰矿区",
lat: 36.420487,
lon: 114.209936,
tz: 8,
});
assert.equal(prepared.serverChart?.name, "岳辰");
assert.equal(prepared.serverChart?.truth.birthTimeSource, "approximate");
assert.equal(prepared.serverChart?.truth.birthTimeStatus, "candidate");
assert.deepEqual(prepared.serverChart?.truth.placeCodes, {
countryCode: "CN",
provinceCode: "130000",
cityCode: "130400",
districtCode: "130406",
});
});
test("verified route uses only server active time", async () => {
const prepared = await prepareConsultationRoute({
userId: "user-1",
mode: "verified_chart",
loadProfile: async () => ({ ...profile, birth_time_status: "confirmed" }),
reserve: async () => "reserved",
});
assert.equal(prepared.serverChart?.toolInput.hour, 5);
assert.equal(prepared.serverChart?.toolInput.minute, 18);
assert.equal(prepared.serverChart?.truth.selectedTimeKind, "active");
});
test("incomplete, inconsistent, or mode-mismatched profile fails before billing", async () => {
for (const [expectedCode, invalid] of [
["profile_incomplete", { ...profile, birth_date: null }],
["profile_inconsistent", { ...profile, latitude: 36.5 }],
["profile_inconsistent", { ...profile, timezone_offset: 9 }],
["mode_changed", { ...profile, birth_time_status: "confirmed" }],
] as const) {
let reserveCalls = 0;
await assert.rejects(
prepareConsultationRoute({
userId: "user-1",
mode: "unverified_birth_time",
loadProfile: async () => invalid,
reserve: async () => {
reserveCalls += 1;
return "reserved";
},
}),
(error: unknown) => error instanceof ConsultationProfileTruthError
&& error.code === expectedCode,
);
assert.equal(reserveCalls, 0);
}
});
test("profile storage failure is stable and never reaches billing", async () => {
let reserveCalls = 0;
await assert.rejects(
prepareConsultationRoute({
userId: "user-1",
mode: "verified_chart",
loadProfile: async () => { throw new Error("raw database detail"); },
reserve: async () => {
reserveCalls += 1;
return "reserved";
},
}),
(error: unknown) => error instanceof ConsultationProfileTruthError
&& error.code === "profile_unavailable"
&& !error.message.includes("raw database detail"),
);
assert.equal(reserveCalls, 0);
});
test("general route reserves without loading chart profile", async () => {
let profileLoads = 0;
const prepared = await prepareConsultationRoute({
userId: "user-1",
mode: "general_no_birth_time",
loadProfile: async () => {
profileLoads += 1;
return profile;
},
reserve: async () => "reserved",
});
assert.equal(profileLoads, 0);
assert.equal(prepared.serverChart, null);
assert.equal(prepared.reservation, "reserved");
});
test("consult route constructs workflow input from the route service rather than client chart fields", () => {
const route = readFileSync(new URL("../src/app/api/consult/route.ts", import.meta.url), "utf8");
const select = "name,birth_date,reported_birth_time,active_birth_time,birth_time_source,birth_time_status,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset";
assert.match(route, new RegExp(select));
assert.match(route, /prepareConsultationRoute/);
assert.match(route, /\.\.\.prepared\.serverChart\.toolInput/);
const toolInput = route.slice(route.indexOf("const toolInput = consultationInputSchema.parse"));
assert.doesNotMatch(toolInput.slice(0, toolInput.indexOf("const workflowContext")), /\.\.\.parsed\.data/);
});