From 45842361f34c9a2c4bc2ee4641aebad96c62db10 Mon Sep 17 00:00:00 2001 From: jesse-ux Date: Fri, 25 Sep 2026 04:33:07 +0800 Subject: [PATCH] fix(test): audit /people and the retired self chart mirror The capability scan now lists the archive page. Chart profile writes no longer pretend to store a self mirror, and deletion stays an owned other-row transaction. --- tests/test_api_server_security.py | 4 +++ tests/test_supabase_user_data_contract.py | 37 ++++++++++++++++++----- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/tests/test_api_server_security.py b/tests/test_api_server_security.py index 38d27f5c..70578cf5 100644 --- a/tests/test_api_server_security.py +++ b/tests/test_api_server_security.py @@ -1600,6 +1600,10 @@ def test_capability_audit_scans_registry_and_local_sources() -> None: 'ephemeris', 'home', 'login', + # 原值: login 后面直接是 reports。 + # 新值: 中间加入 people。 + # 原因: /people 是星盘档案入口,能力审计必须扫到公开路由。 + 'people', 'reports', 'reports/[reportId]', ] diff --git a/tests/test_supabase_user_data_contract.py b/tests/test_supabase_user_data_contract.py index 1a71fbe0..ec642ce7 100644 --- a/tests/test_supabase_user_data_contract.py +++ b/tests/test_supabase_user_data_contract.py @@ -221,20 +221,38 @@ def test_chart_profile_library_has_cloud_table_api_and_local_fallback() -> None: ): assert token in sql + # 原值: 路由按 role=self 写入 jsonb profile。 + # 新值: 只列他人,拒绝本人镜像,写入类型化列。 + # 原因: 星盘档案停写 role=self,本人资料留在 profiles。 for token in ( 'from("chart_profiles")', 'eq("user_id", user.id)', - 'eq("role", "self")', - "Array.isArray(body.profile)", - 'insert({ user_id: user.id, role, profile: body.profile', - 'insert({ user_id: user.id, role, profile: body.profile, updated_at: updatedAt })', + 'eq("role", "other")', + 'body?.role === "self"', + "self_mirror_retired", + "parseChartSubjectWrite", + 'role: "other"', ): assert token in route + assert 'eq("role", "self")' not in route + assert "Array.isArray(body.profile)" not in route assert 'upsert(record, { onConflict: "id" })' not in route - assert '.delete({ count: "exact" })' in delete_route - assert 'eq("role", "other")' in delete_route - assert "count !== 1" in delete_route + # 原值: 删除路由用 count=exact 且 role=other,删不到一行就拒绝。 + # 新值: 删除走 delete_chart_subject,函数内仍要求本人的 other 行且恰好删 1 行,并连带删对话和报告。 + # 原因: 删人必须连带清理,所有权检查留在数据库事务里。 + subject_sql = re.sub( + r"\s+", + " ", + (CHART_PROFILE_MIGRATION.parent / "20260925020000_chart_subject_typed_columns.sql").read_text(encoding="utf-8").lower(), + ) + assert 'rpc("delete_chart_subject"' in delete_route + assert 'id === "self"' in delete_route + assert "本人不能删除" in delete_route assert "星盘不存在或无权删除" in delete_route + assert "where id = p_id and user_id = owner and role = 'other'" in subject_sql + assert "if removed <> 1 then" in subject_sql + assert "delete from public.chat_sessions" in subject_sql + assert "delete from public.personal_reports" in subject_sql for token in ( "fetchCloudChartLibrary", @@ -254,9 +272,12 @@ def test_chart_profile_library_has_cloud_table_api_and_local_fallback() -> None: "星盘库", "添加其他人的星盘", "用于合盘", - "设为默认", ): assert token in page + # 原值: 首页表面必须有「设为默认」。 + # 新值: 这个入口必须消失。 + # 原因: BUG-1030,设为默认会把页面资料换成别人。 + assert "设为默认" not in page # Former values: synastryHistoryStorageKey, chartLibraryStorageKey, # and the comment "Cloud chart library is best-effort". assert "synastryHistoryStorageKey" not in page