from __future__ import annotations from typing import Any from app.import_parser import ImportParseOutput, OpenAIResumeImportParser from app.resume_import_service import RuleBasedResumeImportParser class FakeCompletion: def __init__(self, result: ImportParseOutput | Exception) -> None: self.result = result self.payload: dict[str, Any] | None = None def complete(self, **kwargs: Any) -> ImportParseOutput: self.payload = kwargs["payload"] if isinstance(self.result, Exception): raise self.result return self.result def test_llm_parser_redacts_sensitive_content_and_builds_reviewable_sections() -> None: completion = FakeCompletion( ImportParseOutput.model_validate( { "basics": {"name": "张三", "city": "广州"}, "target": {"job_type": "校招", "position": "后端开发工程师"}, "sections": [ { "kind": "education", "heading": "教育经历", "items": [ { "fields": { "school": "示例大学", "major": "软件工程", "start_date": "2022-09", "end_date_or_present": "2026-06", }, "evidence": ["示例大学 软件工程 2022-09 至 2026-06"], } ], }, { "kind": "project_experience", "heading": "项目经历", "items": [ { "fields": { "project_name": "简历助手", "project_role": "后端开发", "description": "使用 FastAPI 开发简历解析接口", }, "evidence": ["简历助手 后端开发 使用 FastAPI 开发简历解析接口"], } ], }, ], "skill_groups": [ { "category": "编程语言", "skills": ["Python", "SQL"], "evidence": ["Python SQL"], } ], } ) ) parser = OpenAIResumeImportParser(completion=completion) draft = parser.parse( source_name="resume.docx", text=( "张三 13800138000 zhang@example.com 微信: zhangsan88\n" "示例大学 软件工程 2022-09 至 2026-06\n" "简历助手 后端开发 使用 FastAPI 开发简历解析接口\nPython SQL" ), ) assert completion.payload is not None sent = completion.payload["resume_text"] assert "13800138000" not in sent assert "zhang@example.com" not in sent assert "zhangsan88" not in sent assert draft.document["basics"] == {"name": "张三", "city": "广州"} assert [section["heading"] for section in draft.document["sections"]] == [ "教育经历", "项目经历", ] assert draft.document["sections"][1]["items"][0]["project_name"] == "简历助手" assert draft.document["skill_groups"] == [{"category": "编程语言", "skills": ["Python", "SQL"]}] assert all(review.status == "needs_review" for review in draft.field_reviews) assert any(review.field_path == "sections[1].items[0].project_name" for review in draft.field_reviews) assert all(review.evidence for review in draft.field_reviews) def test_llm_parser_falls_back_to_rule_parser_when_model_is_unavailable() -> None: parser = OpenAIResumeImportParser( completion=FakeCompletion(RuntimeError("model gateway unavailable")), fallback=RuleBasedResumeImportParser(), ) draft = parser.parse(source_name="resume.docx", text="这是导入的简历正文") assert draft.document["sections"][0]["heading"] == "导入内容" assert draft.field_reviews[0].status == "needs_review"