import assert from "node:assert/strict"; import { existsSync, readFileSync } from "node:fs"; import test from "node:test"; import { accountProfilePatchSchema, resolveAccountBirthTimeApplicationPatch, resolveAppliedAccountBirthTime, } from "../src/lib/account-profile-patch.ts"; const source = readFileSync(new URL("../src/app/api/account/route.ts", import.meta.url), "utf8"); const patchSource = readFileSync(new URL("../src/lib/account-profile-patch.ts", import.meta.url), "utf8"); const reportedStatusMigration = readFileSync( new URL("../supabase/migrations/20260726010000_backfill_reported_birth_time_status.sql", import.meta.url), "utf8", ); const acceptedExactFamilyMigration = readFileSync( new URL("../supabase/migrations/20260816010000_accept_exact_family_birth_times.sql", import.meta.url), "utf8", ); const productionMigrationWorkflow = readFileSync( new URL("../../.github/workflows/apply-production-rectification-migrations.yml", import.meta.url), "utf8", ); test("account API reads and returns the server-configured rectification price", () => { assert.match(source, /parseRectificationPriceCredits\(\s*process\.env\.RECTIFICATION_PRICE_CREDITS,?\s*\)/); assert.match(source, /rectificationPriceCredits/); assert.doesNotMatch(source, /RECTIFICATION_PRICE_CREDITS[^\n]*\?\?\s*["']1["']/); }); test("account API separates engine confirmation from an accepted usable chart time", () => { assert.match(source, /hasConfirmedBirthTime:\s*profile\.birth_time_status === "confirmed"/); assert.match(source, /hasUsableBirthTime:\s*\(profile\.birth_time_status === "accepted" \|\| profile\.birth_time_status === "confirmed"\)/); }); test("account API routes staging admins to the configured exact admin host", () => { assert.match(source, /readSelfHostedIdentityConfig\(process\.env\)\.adminOrigin/); assert.match(source, /const adminUrl = isAdmin \? adminEntryUrl\(\) : null/); assert.match(source, /adminUrl,/); assert.doesNotMatch(source, /NEXT_PUBLIC_ADMIN|AUTH_ADMIN_ORIGIN/); }); test("account GET falls back when global birthplace columns are not migrated yet", () => { const getSource = source.slice(source.indexOf("export async function GET"), source.indexOf("export async function PATCH")); assert.match(getSource, /profileError && isMissingProfileColumn\(profileError\)/); assert.match(getSource, /select\("credits,active_birth_time[^"]*timezone_offset(?:,[^"]*)?"\)/); assert.match(getSource, /birth_place_label: undefined/); assert.match(getSource, /timezone_id: undefined/); }); test("account API no longer reads or returns retired rectification cases", () => { assert.doesNotMatch(source, /birth_time_rectification_v4_cases|birth_time_rectification_cases/); assert.doesNotMatch(source, /rectificationCase/); }); test("profile patch schema validates calendar, clock, source requirements, and location bounds", () => { const valid = { name: "岳辰", birth_date: "2000-02-29", reported_birth_time: "05:30", birth_time_source: "family_exact", birth_time_period: null, birth_time_clue: null, uncertainty_before_minutes: 0, uncertainty_after_minutes: 0, country_code: "CN", province_code: "130000", city_code: "130400", district_code: "130406", latitude: 36.420487, longitude: 114.209936, timezone_offset: 8, }; assert.equal(accountProfilePatchSchema.safeParse(valid).success, true); assert.equal(accountProfilePatchSchema.safeParse({ ...valid, uncertainty_before_minutes: 10, uncertainty_after_minutes: 10 }).success, true); assert.equal(accountProfilePatchSchema.safeParse({ ...valid, birth_date: "2001-02-29" }).success, false); assert.equal(accountProfilePatchSchema.safeParse({ ...valid, reported_birth_time: "24:00" }).success, false); assert.equal(accountProfilePatchSchema.safeParse({ ...valid, uncertainty_before_minutes: 7, uncertainty_after_minutes: 7 }).success, false); assert.equal(accountProfilePatchSchema.safeParse({ ...valid, latitude: 91 }).success, false); assert.equal(accountProfilePatchSchema.safeParse({ reported_birth_time: "05:30" }).success, false); assert.equal(accountProfilePatchSchema.safeParse({ ...valid, longitude: null }).success, false); assert.equal(accountProfilePatchSchema.safeParse({ ...valid, timezone_offset: null, birth_place_label: "New York, New York, United States", birth_place_type: "place", birth_place_provider: "mapbox", birth_place_provider_id: "place.123", timezone_id: "America/New_York", timezone_source: "iana_historical", }).success, true); assert.equal(accountProfilePatchSchema.safeParse({ name: "只改称呼" }).success, true); assert.equal(accountProfilePatchSchema.safeParse({ ...valid, reported_birth_time: null, birth_time_source: "period_only", birth_time_period: null, uncertainty_before_minutes: null, uncertainty_after_minutes: null, }).success, false); assert.equal(accountProfilePatchSchema.safeParse({ ...valid, reported_birth_time: null, birth_time_source: "unknown", birth_time_period: "morning", uncertainty_before_minutes: null, uncertainty_after_minutes: null, }).success, false); }); test("ordinary declaration edits clear stale candidate application but never overwrite confirmed active time", () => { const candidate = { birth_date: "1997-08-08", reported_birth_time: "05:30", birth_time_source: "approximate", birth_time_period: null, birth_time_clue: null, uncertainty_before_minutes: 30, uncertainty_after_minutes: 30, active_birth_time: "05:18", birth_time: "05:18", birth_time_status: "candidate", rectification_case_id: "11111111-1111-4111-8111-111111111111", latitude: 36.420487, longitude: 114.209936, timezone_offset: 8, } as const; const edited = { birth_date: candidate.birth_date, reported_birth_time: "06:10", birth_time_source: candidate.birth_time_source, birth_time_period: null, birth_time_clue: null, uncertainty_before_minutes: 30, uncertainty_after_minutes: 30, } as const; assert.deepEqual(resolveAccountBirthTimeApplicationPatch(candidate, edited), { active_birth_time: null, birth_time_status: "reported", rectification_case_id: null, }); assert.deepEqual(resolveAccountBirthTimeApplicationPatch(candidate, { district_code: "130407", }), { active_birth_time: null, birth_time_status: "reported", rectification_case_id: null, }); for (const coordinatePatch of [ { latitude: 36.420488 }, { longitude: 114.209937 }, { timezone_offset: 9 }, ]) { assert.deepEqual(resolveAccountBirthTimeApplicationPatch(candidate, coordinatePatch), { active_birth_time: null, birth_time_status: "reported", rectification_case_id: null, }); } assert.deepEqual(resolveAccountBirthTimeApplicationPatch({ ...candidate, birth_time_status: "accepted", }, edited), { active_birth_time: null, birth_time_status: "reported", rectification_case_id: null, }); assert.deepEqual(resolveAccountBirthTimeApplicationPatch({ ...candidate, birth_time_status: "confirmed", }, edited), {}); assert.deepEqual(resolveAccountBirthTimeApplicationPatch({ ...candidate, active_birth_time: null, birth_time_status: "reported", }, { timezone_offset: 7 }), { active_birth_time: null, birth_time_status: "reported", rectification_case_id: null, }); assert.deepEqual(resolveAccountBirthTimeApplicationPatch({ ...candidate, active_birth_time: null, birth_time: null, birth_time_status: null, rectification_case_id: null, }, edited), { active_birth_time: null, birth_time_status: "reported", rectification_case_id: null, }); assert.deepEqual(resolveAccountBirthTimeApplicationPatch({ ...candidate, active_birth_time: null, birth_time: null, birth_time_status: null, rectification_case_id: null, reported_birth_time: edited.reported_birth_time, }, edited), { active_birth_time: null, birth_time_status: "reported", rectification_case_id: null, }); assert.deepEqual(resolveAccountBirthTimeApplicationPatch(null, edited), { active_birth_time: null, birth_time_status: "reported", rectification_case_id: null, }); assert.deepEqual(resolveAccountBirthTimeApplicationPatch({ ...candidate, birth_time_status: "accepted", }, edited), { active_birth_time: null, birth_time_status: "reported", rectification_case_id: null, }); assert.deepEqual(resolveAccountBirthTimeApplicationPatch({ ...candidate, birth_time_status: "confirmed", }, edited), {}); assert.deepEqual(resolveAccountBirthTimeApplicationPatch({ ...candidate, active_birth_time: null, birth_time_status: null, reported_birth_time: edited.reported_birth_time, }, edited), {}); }); test("zero-uncertainty family exact time becomes an accepted usable chart time", () => { const exactDeclaration = { birth_date: "1997-08-08", reported_birth_time: "05:00", birth_time_source: "family_exact", birth_time_period: null, birth_time_clue: null, uncertainty_before_minutes: 0, uncertainty_after_minutes: 0, } as const; const reportedExactProfile = { ...exactDeclaration, reported_birth_time: "05:00:00", active_birth_time: null, birth_time: null, birth_time_status: "reported", rectification_case_id: null, } as const; assert.deepEqual(resolveAccountBirthTimeApplicationPatch(null, exactDeclaration), { active_birth_time: "05:00", birth_time_status: "accepted", rectification_case_id: null, }); assert.deepEqual(resolveAccountBirthTimeApplicationPatch(reportedExactProfile, exactDeclaration), { active_birth_time: "05:00", birth_time_status: "accepted", rectification_case_id: null, }); assert.deepEqual(resolveAccountBirthTimeApplicationPatch({ ...reportedExactProfile, active_birth_time: "05:00:00", birth_time_status: "accepted", }, { ...exactDeclaration, reported_birth_time: "05:40", }), { active_birth_time: "05:40", birth_time_status: "accepted", rectification_case_id: null, }); assert.deepEqual(resolveAccountBirthTimeApplicationPatch({ ...reportedExactProfile, active_birth_time: "05:00:00", birth_time_status: "confirmed", }, { ...exactDeclaration, reported_birth_time: "05:40", }), {}); assert.deepEqual(resolveAccountBirthTimeApplicationPatch({ ...reportedExactProfile, active_birth_time: "05:00:00", birth_time: "05:00:00", birth_time_status: null, }, { ...exactDeclaration, reported_birth_time: "05:40", }), {}); }); test("account PATCH answers with the birth-time truth it derived server-side", () => { const exactDeclaration = { birth_date: "1997-08-08", reported_birth_time: "05:00", birth_time_source: "family_exact", birth_time_period: null, birth_time_clue: null, uncertainty_before_minutes: 0, uncertainty_after_minutes: 0, } as const; const reportedExactProfile = { ...exactDeclaration, reported_birth_time: "05:00:00", active_birth_time: null, birth_time: null, birth_time_status: "reported", rectification_case_id: null, } as const; // A client that keeps the declaration it submitted would consult as // unverified_birth_time and be rejected by the consultation truth check. assert.deepEqual( resolveAppliedAccountBirthTime( null, resolveAccountBirthTimeApplicationPatch(null, exactDeclaration), ), { status: "accepted", activeTime: "05:00" }, ); assert.deepEqual( resolveAppliedAccountBirthTime( reportedExactProfile, resolveAccountBirthTimeApplicationPatch(reportedExactProfile, exactDeclaration), ), { status: "accepted", activeTime: "05:00" }, ); const periodDeclaration = { ...exactDeclaration, reported_birth_time: null, birth_time_source: "period_only", birth_time_period: "early_morning", uncertainty_before_minutes: null, uncertainty_after_minutes: null, } as const; assert.deepEqual( resolveAppliedAccountBirthTime( reportedExactProfile, resolveAccountBirthTimeApplicationPatch(reportedExactProfile, periodDeclaration), ), { status: "reported", activeTime: null }, ); // An untouched application keeps the stored truth, including a legacy confirmed minute. const confirmedProfile = { ...reportedExactProfile, active_birth_time: "05:18:00", birth_time_status: "confirmed", } as const; assert.deepEqual( resolveAppliedAccountBirthTime( confirmedProfile, resolveAccountBirthTimeApplicationPatch(confirmedProfile, { district_code: "130407" }), ), { status: "confirmed", activeTime: "05:18" }, ); const legacyProfile = { ...reportedExactProfile, birth_time: "05:18:00", birth_time_status: null, } as const; assert.deepEqual( resolveAppliedAccountBirthTime( legacyProfile, resolveAccountBirthTimeApplicationPatch(legacyProfile, { district_code: "130407" }), ), { status: "confirmed", activeTime: "05:18" }, ); assert.deepEqual( resolveAppliedAccountBirthTime(null, resolveAccountBirthTimeApplicationPatch(null, { name: "岳辰" })), { status: null, activeTime: null }, ); assert.match(source, /birthTime: resolveAppliedAccountBirthTime\(currentProfile, applicationPatch\)/); }); test("only a strict family exact zero-uncertainty declaration is auto-accepted", () => { const base = { birth_date: "1997-08-08", reported_birth_time: "05:00", birth_time_source: "family_exact", birth_time_period: null, birth_time_clue: null, uncertainty_before_minutes: 0, uncertainty_after_minutes: 0, } as const; const declarations = [ { ...base, uncertainty_before_minutes: 5, uncertainty_after_minutes: 5 }, { ...base, uncertainty_before_minutes: 10, uncertainty_after_minutes: 10 }, { ...base, uncertainty_before_minutes: 15, uncertainty_after_minutes: 15 }, { ...base, birth_time_source: "approximate", uncertainty_before_minutes: 30, uncertainty_after_minutes: 30 }, { ...base, reported_birth_time: null, birth_time_source: "period_only", birth_time_period: "morning", uncertainty_before_minutes: null, uncertainty_after_minutes: null, }, { ...base, reported_birth_time: null, birth_time_source: "unknown", uncertainty_before_minutes: null, uncertainty_after_minutes: null, }, ] as const; for (const declaration of declarations) { assert.deepEqual(resolveAccountBirthTimeApplicationPatch(null, declaration), { active_birth_time: null, birth_time_status: "reported", rectification_case_id: null, }); } }); test("reported birth-time status repair is forward-only and wired into production migration flow", () => { assert.match(reportedStatusMigration, /birth_time_status is null/); assert.match(reportedStatusMigration, /birth_time_status = 'reported'/); assert.match(reportedStatusMigration, /active_birth_time is null/); assert.match(reportedStatusMigration, /rectification_case_id is null/); assert.equal( productionMigrationWorkflow.match(/20260726010000_backfill_reported_birth_time_status\.sql/g)?.length, 2, ); }); test("existing exact family declarations are forward-repaired without claiming confirmation", () => { assert.match(acceptedExactFamilyMigration, /update public\.profiles/); assert.match(acceptedExactFamilyMigration, /active_birth_time = reported_birth_time/); assert.match(acceptedExactFamilyMigration, /birth_time_status = 'accepted'/); assert.match(acceptedExactFamilyMigration, /birth_time_source = 'family_exact'/); assert.match(acceptedExactFamilyMigration, /uncertainty_before_minutes = 0/); assert.match(acceptedExactFamilyMigration, /uncertainty_after_minutes = 0/); assert.match(acceptedExactFamilyMigration, /birth_time_status = 'reported'/); assert.match(acceptedExactFamilyMigration, /active_birth_time is null/); assert.match(acceptedExactFamilyMigration, /rectification_case_id is null/); assert.doesNotMatch(acceptedExactFamilyMigration, /birth_time_status = 'confirmed'/); assert.equal( existsSync(new URL("../db/migrations/20260816010000_accept_exact_family_birth_times.sql", import.meta.url)), false, ); }); test("account PATCH uses the shared validator and never writes client birth_time over account truth", () => { assert.match(source, /accountProfilePatchSchema\.safeParse/); assert.match(source, /resolveAccountBirthTimeApplicationPatch/); assert.match(patchSource, /birthTimeSource[\s\S]*birth_time_status: "reported"/); assert.doesNotMatch(source, /birth_time:\s*nullableString\(payload\.birth_time\)/); assert.match(source, /invalidatesUnconfirmedApplication/); assert.match(patchSource, /"birth_time_status"/); assert.match(patchSource, /"active_birth_time"/); assert.match(source, /latitude,longitude,timezone_offset/); assert.match(source, /birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source/); assert.match(source, /applyAccountProfileConcurrencyGuards/); assert.match(source, /最新确认结果已保留/); }); test("candidate invalidation compares coordinates and timezone in the conditional write", async () => { const { applyAccountProfileConcurrencyGuards } = await import("../src/lib/account-profile-patch.ts"); const calls: Array<["eq" | "is", string, unknown]> = []; const query = { eq(column: string, value: unknown) { calls.push(["eq", column, value]); return this; }, is(column: string, value: null) { calls.push(["is", column, value]); return this; }, }; const current = { birth_date: "1997-08-08", reported_birth_time: "05:30", birth_time_source: "approximate", birth_time_period: null, birth_time_clue: null, uncertainty_before_minutes: 30, uncertainty_after_minutes: 30, active_birth_time: "05:18", birth_time: "05:18", birth_time_status: "candidate", rectification_case_id: "11111111-1111-4111-8111-111111111111", country_code: "CN", province_code: "130000", city_code: "130400", district_code: "130406", latitude: 36.420487, longitude: 114.209936, timezone_offset: 8, }; applyAccountProfileConcurrencyGuards(query, current); assert.deepEqual(calls.filter((call) => ["latitude", "longitude", "timezone_offset"].includes(call[1])), [ ["eq", "latitude", 36.420487], ["eq", "longitude", 114.209936], ["eq", "timezone_offset", 8], ]); assert.deepEqual(calls.find((call) => call[1] === "active_birth_time"), ["eq", "active_birth_time", "05:18"]); assert.deepEqual(calls.find((call) => call[1] === "birth_time_status"), ["eq", "birth_time_status", "candidate"]); });