feat: initialize resume agent with OfferPai sync

This commit is contained in:
Codex
2026-08-05 20:20:18 +08:00
commit 61ec750031
197 changed files with 33291 additions and 0 deletions
+166
View File
@@ -0,0 +1,166 @@
from __future__ import annotations
from typing import Any
from fastapi.testclient import TestClient
from app.resume_document_mutations import set_generated_profile_summary
from builder_flow_helpers import (
active_component,
confirm_card,
create_builder_session,
event,
finish_education,
select_section,
send_message,
start_education,
submit_identity,
)
def test_create_resume_suggests_experience_type_before_showing_identity_card(client: TestClient) -> None:
_, body = create_builder_session(client, job_type="campus")
card = active_component(body)
assert card["data"]["component"] == "choice_chips"
assert card["data"]["module"] == "builder_next_section"
assert card["data"]["value"] == "education"
assert all(block["data"].get("component") != "record_fields" for block in body["turn"]["blocks"] if block["type"] == "component")
def test_next_step_offers_skill_recommendation_and_finish_actions(client: TestClient) -> None:
_, body = create_builder_session(client)
card = active_component(body)
values = {option["value"] for option in card["data"]["options"]}
assert "builder_recommend_skills" in values
assert "builder_finish" in values
def test_builder_skill_recommendations_require_confirmation_before_write(client: TestClient) -> None:
session_id, body = create_builder_session(client)
recommended = event(client, session_id, body, "select", {"value": "builder_recommend_skills"})
assert recommended.status_code == 200, recommended.text
recommendation_card = active_component(recommended.json())
assert recommendation_card["data"]["module"] == "builder_skill_select"
assert recommendation_card["data"]["multiple"] is True
options = recommendation_card["data"]["options"]
assert options
assert recommended.json()["resume"]["content"].get("skill_groups") == []
chosen = options[0]["value"]
confirmed = event(
client,
session_id,
recommended.json(),
"select",
{"values": [chosen], "value": chosen},
)
assert confirmed.status_code == 200, confirmed.text
skills = [
skill
for group in confirmed.json()["resume"]["content"]["skill_groups"]
for skill in group["skills"]
]
assert chosen in skills
assert any(block["type"] == "resume_patch" for block in confirmed.json()["turn"]["blocks"])
def test_builder_finish_generates_summary_from_current_resume(client: TestClient) -> None:
session_id, body = create_builder_session(client)
finished = event(client, session_id, body, "select", {"value": "builder_finish"})
assert finished.status_code == 200, finished.text
payload = finished.json()
summary = payload["resume"]["content"]["profile_summary"]
assert summary["source"] == "ai_generated"
assert summary["stale"] is False
assert summary["content"]
assert "resume_patch" in {block["type"] for block in payload["turn"]["blocks"]}
assert payload.get("builder_stream_phases") == ["saving"]
def test_generated_summary_replaces_only_stale_builder_summary() -> None:
stale = {
"profile_summary": {"content": "旧的个人总结内容足够长,可以被新的总结替换。", "stale": True},
}
refreshed = set_generated_profile_summary(stale, "根据最新简历信息生成的个人总结内容足够长。", replace_stale=True)
assert refreshed["profile_summary"]["content"] == "根据最新简历信息生成的个人总结内容足够长。"
assert refreshed["profile_summary"]["stale"] is False
current = {
"profile_summary": {"content": "用户手工维护的总结内容足够长,不应被自动覆盖。", "stale": False},
}
preserved = set_generated_profile_summary(current, "新的自动总结内容足够长。", replace_stale=True)
assert preserved["profile_summary"]["content"] == current["profile_summary"]["content"]
def test_social_builder_suggests_work_but_allows_another_experience_type(client: TestClient) -> None:
session_id, body = create_builder_session(client, job_type="social")
assert active_component(body)["data"]["value"] == "work_experience"
response = select_section(client, session_id, body, "project_experience")
assert active_component(response)["data"]["record_type"] == "project_experience"
def test_education_missing_facts_asks_follow_up_before_confirmation(client: TestClient) -> None:
session_id, body = create_builder_session(client)
start_education(client, session_id, body)
follow_up = send_message(client, session_id, "学习了数据结构和数据库课程。")
assert "GPA/均分" in follow_up["turn"]["content"]
assert "课程项目" in follow_up["turn"]["content"]
assert not any(block["data"].get("component") == "experience_confirm_card" for block in follow_up["turn"]["blocks"] if block["type"] == "component")
proposal = send_message(client, session_id, "GPA 3.7/4.0,专业前 20%,完成数据库课程项目。")
card = active_component(proposal)
assert card["data"]["component"] == "experience_confirm_card"
assert "学习了数据结构" in card["data"]["value"]["description"]
assert "GPA 3.7/4.0" in card["data"]["value"]["description"]
def test_no_information_reply_skips_asked_gaps_and_then_shows_confirmation(client: TestClient) -> None:
session_id, body = create_builder_session(client)
start_education(client, session_id, body)
first_follow_up = send_message(client, session_id, "学习了软件工程相关课程。")
assert "没有也可以直接说没有" in first_follow_up["turn"]["content"]
proposal = send_message(client, session_id, "没有")
card = active_component(proposal)
assert card["data"]["component"] == "experience_confirm_card"
assert card["data"]["value"]["description"] == "学习了软件工程相关课程。"
def test_confirmed_campus_education_recommends_project_experience(client: TestClient) -> None:
session_id, body = create_builder_session(client)
start_education(client, session_id, body)
proposal = finish_education(client, session_id, body, "GPA 3.7/4.0,获得两次校级奖学金,完成数据库课程项目。")
confirmed = confirm_card(client, session_id, proposal)
entry = next(block for block in confirmed["turn"]["blocks"] if block["type"] == "resume_patch")["data"]["value"]["sections"][0]["items"][0]
assert entry["description"].startswith("GPA 3.7/4.0")
next_card = active_component(confirmed)
assert next_card["data"]["component"] == "choice_chips"
assert next_card["data"]["value"] == "project_experience"
def test_project_gaps_are_limited_and_only_candidate_after_follow_up(client: TestClient) -> None:
session_id, body = create_builder_session(client)
identity_card = select_section(client, session_id, body, "project_experience")
detail_prompt = submit_identity(
client,
session_id,
identity_card,
{
"project_name": "Resume Agent",
"project_role": "Developer",
"start_date": "2024-01",
"end_date_or_present": "2024-06",
},
)
assert "没有也可以直接说没有" in detail_prompt["turn"]["content"]
first_follow_up = send_message(client, session_id, "负责后端接口开发,使用 Python 和 FastAPI。")
questions = [line for line in first_follow_up["turn"]["content"].splitlines() if line]
assert len(questions) == 2
assert "交付物" in first_follow_up["turn"]["content"]
assert "量化信息" in first_follow_up["turn"]["content"]
proposal = send_message(client, session_id, "交付 REST API 并上线,覆盖 3 个业务流程。")
assert active_component(proposal)["data"]["component"] == "experience_confirm_card"