import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import test from "node:test"; import { resolveConsultationSubject } from "../src/lib/consultation-subject-resolver.ts"; import { reportBelongsToSubject, resolveSubjectBirth, sessionBelongsToSubject, } from "../src/lib/subject-birth.ts"; const selfRow = { name: "虚构甲", birth_date: "1991-04-07", reported_birth_time: "09:15:00", birth_time_source: "family_exact", birth_time_status: "reported", birth_place_label: "虚构城", latitude: 31.2, longitude: 121.4, timezone_offset: 8, timezone_id: "Asia/Shanghai", ayanamsa: "lahiri", }; const otherRow = { id: "11111111-1111-4111-8111-111111111111", user_id: "user-1", role: "other", name: "虚构乙", birth_date: "1988-03-04", reported_birth_time: "07:40:00", birth_time_source: "hospital_record", birth_time_status: "reported", birth_place_label: "虚构港", latitude: 22.3, longitude: 114.1, timezone_offset: 8, timezone_id: "Asia/Shanghai", ayanamsa: "raman", }; test("resolveSubjectBirth reads self from profiles and another person from typed columns", async () => { const self = await resolveSubjectBirth({ userId: "user-1", subjectId: "self", loadSelfRow: async () => selfRow, loadOtherRow: async () => { throw new Error("self must not read another person"); }, }); assert.equal(self.role, "self"); assert.equal(self.row.birth_date, "1991-04-07"); assert.equal(self.chartable, true); let requested = ""; const other = await resolveSubjectBirth({ userId: "user-1", subjectId: otherRow.id, loadSelfRow: async () => { throw new Error("another person must not fall back to self"); }, loadOtherRow: async (id) => { requested = id; return otherRow; }, }); assert.equal(requested, otherRow.id); assert.equal(other.name, "虚构乙"); assert.equal(other.row.reported_birth_time, "07:40:00"); assert.equal(other.row.latitude, 22.3); }); test("a missing or foreign person fails closed", async () => { await assert.rejects( () => resolveSubjectBirth({ userId: "user-1", subjectId: otherRow.id, loadSelfRow: async () => selfRow, loadOtherRow: async () => null, }), /subject_not_found/, ); await assert.rejects( () => resolveSubjectBirth({ userId: "user-1", subjectId: otherRow.id, loadSelfRow: async () => selfRow, loadOtherRow: async () => ({ ...otherRow, user_id: "user-2" }), }), /subject_not_found/, ); }); test("legacy self sessions stay visible when the filter is self", () => { assert.equal(sessionBelongsToSubject({ chart_profile_id: null }, "self"), true); assert.equal(sessionBelongsToSubject({ chart_profile_id: "self" }, "self"), true); assert.equal(sessionBelongsToSubject({ chart_profile_id: otherRow.id }, "self"), false); assert.equal(sessionBelongsToSubject({ chart_profile_id: otherRow.id }, otherRow.id), true); assert.equal(reportBelongsToSubject({ chart_profile_id: null }, "self"), true); assert.equal(reportBelongsToSubject({ chart_profile_id: otherRow.id }, otherRow.id), true); }); test("ordinary chat resolves another person through resolveSubjectBirth", async () => { const resolved = await resolveConsultationSubject({ userId: "user-1", binding: { chartProfileId: otherRow.id, chartProfileRole: "other", chartProfileName: "伪造" }, loadSelfProfile: async () => selfRow, loadOwnedChartProfile: async () => ({ id: otherRow.id, userId: "user-1", role: "other", profile: otherRow, }), }); assert.equal(resolved.name, "虚构乙"); assert.equal((resolved.profile as { latitude?: number }).latitude, 22.3); assert.notEqual((resolved.profile as { name?: string }).name, "伪造"); }); test("chart, ephemeris, reports, daily language and consult all use the one birth lookup", () => { const files = [ "../src/lib/chart-view-service.ts", "../src/app/api/ephemeris/route.ts", "../src/app/api/reports/route.ts", "../src/app/api/daily-starlanguage/route.ts", "../src/lib/consultation-subject-resolver.ts", ]; for (const file of files) { const source = readFileSync(new URL(file, import.meta.url), "utf8"); assert.match(source, /loadSubjectBirth|resolveSubjectBirth/); } const consult = readFileSync(new URL("../src/app/api/consult/route.ts", import.meta.url), "utf8"); assert.match(consult, /CHART_SUBJECT_SELECT/); assert.match(consult, /prepareConsultationRoute/); });