generated from kgod/ai-review-template
239 lines
10 KiB
Python
239 lines
10 KiB
Python
"""记录模块卡片流测试:校招/社招/实习队列、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 |