"""记录模块卡片流测试:校招/社招/实习队列、AI 整理确认、卡内调整、revision 单调性。""" from __future__ import annotations from typing import Any from fastapi.testclient import TestClient from test_api import ( ANCHOR_CARD_VALUES, BASE, active_component, event, fill_anchor, start_manual_profile, ) from test_enrichment_flow import ( continue_enriching, created_session, find_component, skip_current, submit_to, ) def choose(client: TestClient, session_id: str, body: dict[str, Any], value: str) -> dict[str, Any]: response = event(client, session_id, body, "select", {"value": value}) assert response.status_code == 200, response.text return response.json() def confirm_active( client: TestClient, session_id: str, body: dict[str, Any], *, use_optimized: bool = False, ) -> dict[str, Any]: response = event( client, session_id, body, "confirm", {"confirmed": True, "use_optimized": use_optimized}, ) assert response.status_code == 200, response.text return response.json() def latest_patch(body: dict[str, Any]) -> dict[str, Any]: turns = body.get("turns") or ([body["turn"]] if body.get("turn") else []) for turn in reversed(turns): for block in reversed(turn["blocks"]): if block["type"] == "resume_patch" and "revision" in block["data"]: return block["data"] raise AssertionError("no resume patch with revision") def latest_patch_revision(body: dict[str, Any]) -> int: return latest_patch(body)["revision"] def anchor_via_card( client: TestClient, job_type: str, anchor_type: str | None, values: dict[str, str] ): session_id, body = start_manual_profile(client, job_type=job_type) if anchor_type is not None: assert body["stage"] == "ANCHOR_TYPE_SELECT" response = event(client, session_id, body, "select", {"anchor_type": anchor_type}) assert response.status_code == 200, response.text body = response.json() body = fill_anchor(client, session_id, body, values) assert body["stage"] == "ANCHOR_CONFIRM" body = confirm_active(client, session_id, body) created = client.post(f"{BASE}/sessions/{session_id}/create", json={}) assert created.status_code == 200, created.text return session_id, created.json() INTERNSHIP_ENTRY = { "company": "星河科技", "position": "后端实习生", "start_date": "2024-03", "end_date_or_present": "2024-09", "description": "负责接口开发,将响应时间降低了30%。", } WORK_ANCHOR = { "company": "星河科技", "position": "后端工程师", "start_date": "2020-01", "end_date_or_present": "2023-05", } def test_campus_queue_full_happy_path(client: TestClient): session_id, body = created_session(client) body = continue_enriching(client, session_id, body) block = active_component(body) assert block["data"]["component"] == "record_fields" assert block["data"]["module"] == "internship" assert block["data"]["record_type"] == "internship_experience" assert block["data"]["skippable"] is True # 卡片标题与内容一致(项1回归) assert "实习" in block["data"]["title"] bad = submit_to(client, session_id, body, "record_fields", {"company": "星河科技"}) assert bad.status_code == 422 body = submit_to(client, session_id, body, "record_fields", dict(INTERNSHIP_ENTRY)).json() card = active_component(body) assert card["data"]["component"] == "experience_confirm_card" assert card["data"]["confirmation_kind"] == "module_entry" assert "highlights" not in card["data"]["value"] proposal = card["data"]["ai_proposal"] assert proposal["optimized_description"] == "承担接口开发,推动响应时间降低30%。" assert proposal["source"] == "rule_polish" body = confirm_active(client, session_id, body, use_optimized=True) internship = latest_patch(body)["value"]["sections"][-1]["items"][0] assert internship["description"] == proposal["optimized_description"] assert "resume_bullets" not in internship assert internship["provenance"] == "rule_polish" assert body["stage"] == "RESUME_ENRICHING" assert active_component(body)["data"]["component"] == "add_another" assert latest_patch_revision(body) == 2 body = choose(client, session_id, body, "next") # 实习之后项目作为独立模块出现(项4回归) assert active_component(body)["data"]["module"] == "project" body = skip_current(client, session_id, body) # → competition assert find_component(body, "competition_fields") body = skip_current(client, session_id, body) # → skills assert active_component(body)["data"]["component"] == "tags_input" body = submit_to(client, session_id, body, "tags_input", {"field": "skills", "value": ["Python"]}).json() assert latest_patch_revision(body) == 3 body = submit_to(client, session_id, body, "tags_input", {"field": "certificates", "value": []}).json() assert latest_patch_revision(body) == 4 assert body["stage"] == "RESUME_ENRICHING" assert active_component(body)["data"]["component"] == "custom_card_picker" body = choose(client, session_id, body, "finish") assert body["stage"] == "CONTENT_READY" def test_social_queue_starts_with_more_work(client: TestClient): session_id, body = anchor_via_card(client, "social", None, dict(WORK_ANCHOR)) body = continue_enriching(client, session_id, body) assert find_component(body, "progress_card")["data"]["module"] == "more_work" block = active_component(body) assert block["data"]["component"] == "record_fields" assert block["data"]["record_type"] == "work_experience" body = submit_to(client, session_id, body, "record_fields", { "company": "云图网络", "position": "开发工程师", "start_date": "2023-06", "end_date_or_present": "present", }).json() body = confirm_active(client, session_id, body) assert active_component(body)["data"]["component"] == "add_another" body = choose(client, session_id, body, "again") assert find_component(body, "progress_card")["data"]["module"] == "more_work" body = skip_current(client, session_id, body) assert find_component(body, "progress_card")["data"]["module"] == "project" def test_internship_queue_campus_and_project_modules_are_independent(client: TestClient): session_id, body = anchor_via_card( client, "internship", None, dict(ANCHOR_CARD_VALUES) ) body = continue_enriching(client, session_id, body) assert find_component(body, "progress_card")["data"]["module"] == "campus_experience" block = active_component(body) assert block["data"]["record_type"] == "campus_experience" body = submit_to(client, session_id, body, "record_fields", { "organization": "计算机协会", "role": "技术部负责人", "start_date": "2023-09", "end_date_or_present": "2024-06", "description": "组织校内编程训练营。", }).json() body = confirm_active(client, session_id, body, use_optimized=True) campus_item = latest_patch(body)["value"]["sections"][-1]["items"][0] assert campus_item["organization"] == "计算机协会" body = choose(client, session_id, body, "next") assert active_component(body)["data"]["module"] == "project" body = submit_to(client, session_id, body, "record_fields", { "project_name": "校园二手交易平台", "project_role": "前端负责人", "start_date": "2023-03", "end_date_or_present": "2023-09", }).json() body = confirm_active(client, session_id, body) assert active_component(body)["data"]["component"] == "add_another" kinds = [section["kind"] for section in latest_patch(body)["value"]["sections"]] assert "campus_experience" in kinds assert "project_experience" in kinds def test_module_confirm_edit_reissues_prefilled_card(client: TestClient): session_id, body = created_session(client) body = continue_enriching(client, session_id, body) body = submit_to(client, session_id, body, "record_fields", dict(INTERNSHIP_ENTRY)).json() edited = event(client, session_id, body, "edit", {}) assert edited.status_code == 200, edited.text body = edited.json() block = active_component(body) assert block["data"]["component"] == "record_fields" assert block["data"]["value"]["company"] == "星河科技" entry = dict(INTERNSHIP_ENTRY, company="云图网络") body = submit_to(client, session_id, body, "record_fields", entry).json() assert active_component(body)["data"]["value"]["company"] == "云图网络" def test_module_confirmation_can_keep_original_description(client: TestClient): session_id, body = created_session(client) body = continue_enriching(client, session_id, body) body = submit_to(client, session_id, body, "record_fields", dict(INTERNSHIP_ENTRY)).json() proposal = active_component(body)["data"]["ai_proposal"] assert proposal["optimized_description"] != INTERNSHIP_ENTRY["description"] body = confirm_active(client, session_id, body, use_optimized=False) internship = latest_patch(body)["value"]["sections"][-1]["items"][0] assert internship["description"] == INTERNSHIP_ENTRY["description"] assert internship["provenance"] == "user_provided" def test_empty_description_gets_conservative_proposal(client: TestClient): session_id, body = created_session(client) body = continue_enriching(client, session_id, body) entry = {key: value for key, value in INTERNSHIP_ENTRY.items() if key != "description"} body = submit_to(client, session_id, body, "record_fields", entry).json() card = active_component(body) assert card["data"]["value"].get("description") is None assert card["data"]["ai_proposal"]["optimized_description"] == ( "在星河科技担任后端实习生。" ) body = confirm_active(client, session_id, body, use_optimized=False) internship = latest_patch(body)["value"]["sections"][-1]["items"][0] assert "description" not in internship