Files
Jyotisha/frontend/tests/account-api.test.ts
T
Jesse_Chen fb69e43c90 ci: remove GitHub mirror workflows and dead Gitea manual workflows
Gitea (git.copse.top) is the only CI/CD control plane; GitHub is a
read-only mirror whose Actions are being disabled in repository settings.

- Delete all 11 `.github/workflows/*.yml` (stale copies of the old design).
- Delete unused Gitea manual workflows `ci.yml`, `test.yml`,
  `publish-pypi.yml`, `apply-supabase-profile-migrations.yml`
  (0-1 historical runs, no remaining target).
- Fold the full `python -m pytest` tree and `tests/run_all.py` into
  `release-quality-gate.yml`, which previously only ran the curated
  release profile; update the `run_quality_gate.py` comment accordingly.
- Port `reset-staging-account.yml` to Gitea: `runs-on: xiaoxin`, bounded
  exact-SHA checkout, `refs/heads/staging` only, `staging-mutation`
  concurrency, same email/confirmation/host/port/user/path assertions.
- Repoint frontend workflow tests at `.gitea/workflows/`, drop the
  GitHub-only assertions, add coverage for the new reset workflow, and
  remove `tests/test_supabase_profile_migration_workflow.py`.
- Update AGENTS.md §6.8, README.md, and deploy/README.md to the current
  production/mirror facts and document the staging account reset.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VawU7Xfd5jS9wUEXz1XYmS
2026-09-02 04:02:04 +00:00

535 lines
20 KiB
TypeScript

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 selfHostedMigrator = readFileSync(
new URL("../scripts/db-migrate.mjs", 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: "period_only",
birth_time_period: "evening",
uncertainty_before_minutes: null,
uncertainty_after_minutes: null,
}).success, true);
assert.equal(accountProfilePatchSchema.safeParse({
...valid,
reported_birth_time: null,
birth_time_source: "period_only",
birth_time_period: null,
declared_window_start: "14:30",
declared_window_end: "15:00",
uncertainty_before_minutes: null,
uncertainty_after_minutes: null,
}).success, true);
assert.equal(accountProfilePatchSchema.safeParse({
...valid,
reported_birth_time: null,
birth_time_source: "period_only",
birth_time_period: null,
declared_window_start: "14:30",
declared_window_end: "14:30",
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 applied by the self-hosted migrator", () => {
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/);
// Staging and production migrations run through db-migrate.mjs, which applies
// frontend/supabase/migrations as the compatibility directory; the repair file
// must stay there rather than in a retired workflow's explicit file list.
assert.ok(
existsSync(new URL("../supabase/migrations/20260726010000_backfill_reported_birth_time_status.sql", import.meta.url)),
);
assert.match(selfHostedMigrator, /"\.\.\/supabase\/migrations"/);
assert.match(selfHostedMigrator, /supabaseCompatibilityDirectory,/);
});
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"]);
});