Files
Jyotisha/tests/test_session_management_entrypoints.py
T
Jesse_Chen bf6989eccf refactor(chat): extract profile onboarding and rectification surface hooks
Close the home-page split by moving account/onboarding and rectification glue out of page.tsx without relocating React state, keeping the route static and hook order intact.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-02 05:56:58 +08:00

90 lines
3.7 KiB
Python

from pathlib import Path
PAGE = Path("frontend/src/app/page.tsx")
_FRONTEND_SRC = Path("frontend/src")
HOME_SURFACE_FILES = (
PAGE,
_FRONTEND_SRC / "lib" / "home-types.ts",
_FRONTEND_SRC / "lib" / "home-profile.ts",
_FRONTEND_SRC / "lib" / "home-cloud-sync.ts",
_FRONTEND_SRC / "components" / "birth-location-fields.tsx",
_FRONTEND_SRC / "components" / "profile-fields.tsx",
_FRONTEND_SRC / "components" / "onboarding-chat-message.tsx",
_FRONTEND_SRC / "components" / "chart-library-panel.tsx",
_FRONTEND_SRC / "components" / "starter-home.tsx",
)
OPTIONAL_HOME_HOOKS = (
_FRONTEND_SRC / "hooks" / "use-session-management.ts",
_FRONTEND_SRC / "hooks" / "use-consultation-run.ts",
_FRONTEND_SRC / "hooks" / "use-profile-onboarding.ts",
_FRONTEND_SRC / "hooks" / "use-rectification-surface.ts",
)
STYLES = Path("frontend/src/app/globals.css")
SESSION_ROW = Path("frontend/src/components/sidebar-session-row.tsx")
SESSION_DELETE_ROUTE = Path("frontend/src/app/api/sessions/[id]/route.ts")
SESSION_DELETE_MIGRATION = Path("frontend/supabase/migrations/20260721100000_chat_sessions_delete_grant.sql")
SESSION_WRITE_CONTRACT = Path("frontend/src/lib/chat-session-write-contract.ts")
def _home_surface() -> str:
parts = [path.read_text(encoding="utf-8") for path in HOME_SURFACE_FILES]
parts.extend(path.read_text(encoding="utf-8") for path in OPTIONAL_HOME_HOOKS if path.exists())
return "".join(parts)
def test_chat_history_management_actions_are_exposed() -> None:
source = _home_surface() + STYLES.read_text(encoding="utf-8") + SESSION_ROW.read_text(encoding="utf-8")
for expected in (
"renameSession",
"deleteSession",
"pendingSessionDeletion",
"确认删除",
"session-delete-overlay",
"togglePinnedSession",
"toggleArchivedSession",
"showArchivedSessions",
"已归档,可在左侧归档中恢复。",
"shareSession",
"share_payload_version",
"messages.map",
"onContextMenu",
"session-menu-trigger",
"Escape",
"focus-within",
"收藏",
"重命名",
"归档",
"恢复",
"删除",
"转发",
'fetch(`/api/sessions/${encodeURIComponent(session.id)}`',
):
assert expected in source
def test_chat_session_delete_is_server_controlled_and_granted() -> None:
route = SESSION_DELETE_ROUTE.read_text(encoding="utf-8")
migration = SESSION_DELETE_MIGRATION.read_text(encoding="utf-8")
assert 'from("chat_sessions")' in route
assert '.eq("user_id", user.id)' in route
assert 'count !== 1' in route
assert 'grant delete on table public.chat_sessions to authenticated' in migration.lower()
assert 'create policy chat_sessions_delete_own' in migration.lower()
assert 'using ((select auth.uid()) = user_id)' in migration.lower()
def test_archiving_never_calls_the_delete_endpoint() -> None:
source = _home_surface()
write_contract = SESSION_WRITE_CONTRACT.read_text(encoding="utf-8")
start = source.index("function toggleArchivedSession")
end = source.index("async function shareSession", start)
archive_action = source[start:end]
# Former tokens: setArchivedSessionIds and no `/api/sessions/` in the slice.
# Archive is now a metadata PATCH; the write helper uses PATCH for update.
assert 'writeChatSession(sessionId, { archived_at: nextArchivedAt }, "update")' in archive_action
assert "archived_at: nextArchivedAt" in archive_action
assert 'method: "DELETE"' not in archive_action
assert "DELETE" not in archive_action
assert 'method: mode === "create" ? "POST" : "PATCH"' in write_contract