2dac8bc47b
Deploy staging to test server / deploy (push) Successful in 3m11s
Use persisted self-hosted roles and the isolated admin origin so authorized staging accounts can discover the protected admin surface.
556 lines
21 KiB
TypeScript
556 lines
21 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
import test from "node:test";
|
|
import {
|
|
accountProfilePatchSchema,
|
|
resolveAccountBirthTimeApplicationPatch,
|
|
} from "../src/lib/account-profile-patch.ts";
|
|
import {
|
|
resolveAccountRectificationCase,
|
|
} from "../src/lib/account-rectification-case.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 caseServiceSource = readFileSync(new URL("../src/lib/account-rectification-case.ts", import.meta.url), "utf8");
|
|
const reportedStatusMigration = readFileSync(
|
|
new URL("../supabase/migrations/20260726010000_backfill_reported_birth_time_status.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 returns a server-resolved admin entry URL", () => {
|
|
assert.match(source, /readIdentityConfig\(process\.env\)/);
|
|
assert.match(source, /new URL\("\/admin\/codes", identityConfig\.adminOrigin\)\.toString\(\)/);
|
|
assert.match(source, /identityConfig\.provider === "self-hosted"/);
|
|
assert.match(source, /: "\/admin\/codes"/);
|
|
assert.match(source, /adminUrl,/);
|
|
assert.doesNotMatch(source, /NEXT_PUBLIC_ADMIN|process\.env\.AUTH_ADMIN_ORIGIN/);
|
|
});
|
|
|
|
test("account API projects only the minimum case state needed by the homepage", () => {
|
|
const caseSelect = source.match(/\.from\("birth_time_rectification_cases"\)[\s\S]*?\.limit\(\d+\)/)?.[0] ?? "";
|
|
|
|
assert.match(caseSelect, /id,journey_protocol,status,turn_version,revision_of_case_id,baseline_active_time,declared_birth_input,updated_at/);
|
|
assert.doesNotMatch(caseSelect, /candidate_scan|event_evidence|validation_receipt|pending_consultation_question|journey_snapshot|turn_state/);
|
|
assert.match(caseServiceSource, /caseId:/);
|
|
assert.match(caseServiceSource, /journeyProtocol:/);
|
|
assert.match(caseServiceSource, /turnVersion:/);
|
|
assert.match(caseServiceSource, /preservesActiveTime:/);
|
|
});
|
|
|
|
test("account API scopes the service-role case lookup to the authenticated account", () => {
|
|
const caseSelect = source.match(/\.from\("birth_time_rectification_cases"\)[\s\S]*?\.limit\(\d+\)/)?.[0] ?? "";
|
|
|
|
assert.match(caseSelect, /\.eq\("user_id", user\.id\)/);
|
|
assert.match(caseSelect, /\.eq\("journey_protocol", "conversational-evidence-v3"\)/);
|
|
assert.match(caseSelect, /\.order\("updated_at", \{ ascending: false \}\)/);
|
|
});
|
|
|
|
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/);
|
|
});
|
|
|
|
const currentDeclaredProfile = Object.freeze({
|
|
credits: 7,
|
|
active_birth_time: null,
|
|
birth_time_status: "reported",
|
|
rectification_case_id: null,
|
|
birth_date: "1997-08-08",
|
|
reported_birth_time: "05:30:00",
|
|
birth_time_source: "approximate",
|
|
birth_time_period: null,
|
|
birth_time_clue: "家人记得天亮前后",
|
|
uncertainty_before_minutes: 30,
|
|
uncertainty_after_minutes: 30,
|
|
country_code: "CN",
|
|
province_code: "130000",
|
|
city_code: "130400",
|
|
district_code: "130406",
|
|
latitude: 36.420487,
|
|
longitude: 114.209936,
|
|
timezone_offset: 8,
|
|
});
|
|
|
|
const currentDeclaredInput = Object.freeze({
|
|
source: "approximate",
|
|
birthDate: "1997-08-08",
|
|
reportedTime: "05:30",
|
|
uncertaintyBeforeMinutes: 30,
|
|
uncertaintyAfterMinutes: 30,
|
|
birthTimeClue: "家人记得天亮前后",
|
|
birthplace: {
|
|
countryCode: "CN",
|
|
provinceCode: "130000",
|
|
cityCode: "130400",
|
|
districtCode: "130406",
|
|
latitude: 36.420487,
|
|
longitude: 114.209936,
|
|
timezoneOffset: 8,
|
|
},
|
|
});
|
|
|
|
function unfinishedCase(
|
|
declaredBirthInput: unknown = currentDeclaredInput,
|
|
overrides: Record<string, unknown> = {},
|
|
) {
|
|
return {
|
|
id: "11111111-1111-4111-8111-111111111111",
|
|
journey_protocol: "conversational-evidence-v3",
|
|
status: "paused",
|
|
turn_version: 4,
|
|
revision_of_case_id: null,
|
|
baseline_active_time: null,
|
|
declared_birth_input: declaredBirthInput,
|
|
private_candidate: { calculationVersion: "must-not-leak" },
|
|
pending_consultation_question: "must-not-leak",
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
test("account case projection resumes only an unfinished v3 case matching the current declaration", () => {
|
|
const projected = resolveAccountRectificationCase(
|
|
currentDeclaredProfile,
|
|
[unfinishedCase()],
|
|
);
|
|
|
|
assert.deepEqual(projected, {
|
|
caseId: "11111111-1111-4111-8111-111111111111",
|
|
journeyProtocol: "conversational-evidence-v3",
|
|
status: "paused",
|
|
turnVersion: 4,
|
|
isRevision: false,
|
|
preservesActiveTime: false,
|
|
});
|
|
assert.doesNotMatch(
|
|
JSON.stringify(projected),
|
|
/declared|private_candidate|pending_consultation_question|must-not-leak/,
|
|
);
|
|
assert.equal(currentDeclaredProfile.rectification_case_id, null);
|
|
});
|
|
|
|
test("edited declaration fields make old unfinished cases non-resumable without deleting audit rows", () => {
|
|
const declarationMismatches = [
|
|
{ ...currentDeclaredInput, birthDate: "1997-08-09" },
|
|
{ ...currentDeclaredInput, reportedTime: "05:31" },
|
|
{ ...currentDeclaredInput, birthTimeClue: "另一条线索" },
|
|
{ ...currentDeclaredInput, uncertaintyBeforeMinutes: 60, uncertaintyAfterMinutes: 60 },
|
|
{ ...currentDeclaredInput, birthplace: { ...currentDeclaredInput.birthplace, countryCode: "TW" } },
|
|
{ ...currentDeclaredInput, birthplace: { ...currentDeclaredInput.birthplace, provinceCode: "140000" } },
|
|
{ ...currentDeclaredInput, birthplace: { ...currentDeclaredInput.birthplace, cityCode: "130500" } },
|
|
{ ...currentDeclaredInput, birthplace: { ...currentDeclaredInput.birthplace, districtCode: "130407" } },
|
|
{ ...currentDeclaredInput, birthplace: { ...currentDeclaredInput.birthplace, latitude: 36.420488 } },
|
|
{ ...currentDeclaredInput, birthplace: { ...currentDeclaredInput.birthplace, longitude: 114.209937 } },
|
|
{ ...currentDeclaredInput, birthplace: { ...currentDeclaredInput.birthplace, timezoneOffset: 9 } },
|
|
];
|
|
|
|
for (const declared of declarationMismatches) {
|
|
const row = unfinishedCase(declared);
|
|
assert.equal(resolveAccountRectificationCase(currentDeclaredProfile, [row]), null);
|
|
assert.equal(row.declared_birth_input, declared, "matching must not mutate or delete audit data");
|
|
}
|
|
|
|
const periodProfile = {
|
|
...currentDeclaredProfile,
|
|
reported_birth_time: null,
|
|
birth_time_source: "period_only",
|
|
birth_time_period: "early_morning",
|
|
birth_time_clue: null,
|
|
uncertainty_before_minutes: null,
|
|
uncertainty_after_minutes: null,
|
|
};
|
|
assert.equal(resolveAccountRectificationCase(periodProfile, [unfinishedCase()]), null);
|
|
const periodDeclaration = {
|
|
source: "period_only",
|
|
birthDate: currentDeclaredInput.birthDate,
|
|
reportedPeriod: "early_morning",
|
|
birthTimeClue: null,
|
|
birthplace: currentDeclaredInput.birthplace,
|
|
};
|
|
assert.ok(resolveAccountRectificationCase(periodProfile, [unfinishedCase(periodDeclaration)]));
|
|
assert.equal(resolveAccountRectificationCase(periodProfile, [unfinishedCase({
|
|
...periodDeclaration,
|
|
reportedPeriod: "morning",
|
|
})]), null);
|
|
assert.equal(resolveAccountRectificationCase(currentDeclaredProfile, [
|
|
unfinishedCase(currentDeclaredInput, { status: "completed" }),
|
|
]), null);
|
|
});
|
|
|
|
test("account case matching validates optional canonical place labels and can find a later matching row", () => {
|
|
const wrongLabel = unfinishedCase({
|
|
...currentDeclaredInput,
|
|
birthplace: { ...currentDeclaredInput.birthplace, city: "错误地点" },
|
|
});
|
|
const matching = unfinishedCase(currentDeclaredInput, {
|
|
id: "22222222-2222-4222-8222-222222222222",
|
|
status: "active",
|
|
turn_version: 1,
|
|
});
|
|
const correctlyLabelled = unfinishedCase({
|
|
...currentDeclaredInput,
|
|
birthplace: {
|
|
...currentDeclaredInput.birthplace,
|
|
city: "中国 · 河北省 · 邯郸市 · 峰峰矿区",
|
|
},
|
|
});
|
|
|
|
assert.ok(resolveAccountRectificationCase(currentDeclaredProfile, [correctlyLabelled]));
|
|
|
|
assert.deepEqual(
|
|
resolveAccountRectificationCase(currentDeclaredProfile, [wrongLabel, matching]),
|
|
{
|
|
caseId: "22222222-2222-4222-8222-222222222222",
|
|
journeyProtocol: "conversational-evidence-v3",
|
|
status: "active",
|
|
turnVersion: 1,
|
|
isRevision: false,
|
|
preservesActiveTime: false,
|
|
},
|
|
);
|
|
});
|
|
|
|
test("account case matching resumes a normalized global birthplace without China location codes", () => {
|
|
const globalProfile = {
|
|
...currentDeclaredProfile,
|
|
country_code: "TW",
|
|
province_code: null,
|
|
city_code: null,
|
|
district_code: null,
|
|
birth_place_label: "台湾 · 台北市",
|
|
birth_place_type: "locality",
|
|
birth_place_provider: "geonames",
|
|
birth_place_provider_id: "1668341",
|
|
timezone_id: "Asia/Taipei",
|
|
timezone_source: "iana_historical",
|
|
latitude: 25.033,
|
|
longitude: 121.5654,
|
|
timezone_offset: 8,
|
|
};
|
|
const globalDeclaration = {
|
|
...currentDeclaredInput,
|
|
birthplace: {
|
|
city: "台湾 · 台北市",
|
|
placeId: "1668341",
|
|
placeType: "locality",
|
|
provider: "geonames",
|
|
countryCode: "TW",
|
|
latitude: 25.033,
|
|
longitude: 121.5654,
|
|
timezoneId: "Asia/Taipei",
|
|
timezoneSource: "iana_historical",
|
|
timezoneOffset: 8,
|
|
},
|
|
};
|
|
|
|
assert.ok(resolveAccountRectificationCase(globalProfile, [unfinishedCase(globalDeclaration)]));
|
|
assert.equal(resolveAccountRectificationCase(globalProfile, [unfinishedCase({
|
|
...globalDeclaration,
|
|
birthplace: { ...globalDeclaration.birthplace, placeId: "wrong-id" },
|
|
})]), null);
|
|
});
|
|
|
|
test("account case matching recovers an existing global case when profile offset is null and coordinates need durable rounding", () => {
|
|
const globalProfile = {
|
|
...currentDeclaredProfile,
|
|
country_code: "US",
|
|
province_code: null,
|
|
city_code: null,
|
|
district_code: null,
|
|
birth_place_label: "San Francisco, California, United States",
|
|
birth_place_type: "city",
|
|
birth_place_provider: "geoapify",
|
|
birth_place_provider_id: "sf-place",
|
|
timezone_id: "America/Los_Angeles",
|
|
timezone_source: "iana_historical",
|
|
latitude: 37.7879363,
|
|
longitude: -122.4075201,
|
|
timezone_offset: null,
|
|
};
|
|
const declaration = {
|
|
...currentDeclaredInput,
|
|
birthplace: {
|
|
city: "San Francisco, California, United States",
|
|
placeId: "sf-place",
|
|
placeType: "city",
|
|
provider: "geoapify",
|
|
countryCode: "US",
|
|
latitude: 37.787936,
|
|
longitude: -122.40752,
|
|
timezoneId: "America/Los_Angeles",
|
|
timezoneSource: "iana_historical",
|
|
timezoneOffset: -8,
|
|
},
|
|
};
|
|
|
|
assert.ok(resolveAccountRectificationCase(globalProfile, [unfinishedCase(declaration)]));
|
|
assert.equal(resolveAccountRectificationCase(
|
|
{ ...globalProfile, timezone_id: "America/New_York" },
|
|
[unfinishedCase(declaration)],
|
|
), null);
|
|
});
|
|
|
|
test("account route reads declaration truth only for server matching and does not key resume to profile case id", () => {
|
|
const caseSelect = source.match(/\.from\("birth_time_rectification_cases"\)[\s\S]*?\.limit\(\d+\)/)?.[0] ?? "";
|
|
const responseStart = source.indexOf("return NextResponse.json({\n user:");
|
|
const responseProjection = source.slice(responseStart, source.indexOf(" } catch", responseStart));
|
|
|
|
assert.match(source, /birth_date,reported_birth_time,birth_time_source,birth_time_period,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset/);
|
|
assert.match(caseSelect, /declared_birth_input/);
|
|
assert.match(caseSelect, /\.eq\("user_id", user\.id\)/);
|
|
assert.match(caseSelect, /\.in\("status",/);
|
|
assert.doesNotMatch(caseSelect, /rectification_case_id/);
|
|
assert.match(source, /resolveAccountRectificationCase/);
|
|
assert.doesNotMatch(responseProjection, /declared_birth_input|private_candidate|pending_consultation_question/);
|
|
});
|
|
|
|
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: 10,
|
|
uncertainty_after_minutes: 10,
|
|
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, 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 }).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: null,
|
|
birth_time_status: "reported",
|
|
rectification_case_id: null,
|
|
});
|
|
assert.deepEqual(resolveAccountBirthTimeApplicationPatch(candidate, {
|
|
district_code: "130407",
|
|
}), {
|
|
active_birth_time: null,
|
|
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: 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: null,
|
|
birth_time_status: "reported",
|
|
}, { timezone_offset: 7 }), {
|
|
active_birth_time: null,
|
|
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: 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: null,
|
|
birth_time_status: "reported",
|
|
rectification_case_id: null,
|
|
});
|
|
assert.deepEqual(resolveAccountBirthTimeApplicationPatch(null, edited), {
|
|
active_birth_time: null,
|
|
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: null,
|
|
birth_time_status: null,
|
|
reported_birth_time: edited.reported_birth_time,
|
|
}, edited), {});
|
|
});
|
|
|
|
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("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"]);
|
|
});
|