from __future__ import annotations from app.resume_expansion import ( FallbackEntryExpander, OpenAIEntryExpander, _EXPANSION_REPAIR_PROMPT, _system_prompt, build_expander, ) from app.resume_expansion_prompts import _repair_prompt from app.settings import Settings def test_light_expansion_prompt_prioritizes_fact_completeness() -> None: prompt = _system_prompt("project_experience") assert "Completeness first" in prompt assert "do not drop meaningful facts for brevity" in prompt assert "covered_fact_ids" not in prompt def test_light_expansion_prompt_keeps_hard_boundaries_and_star() -> None: prompt = _system_prompt("work_experience") assert "hard_required_facts" in prompt assert "quantity with its original object" in prompt assert "responsibility level" in prompt assert "STAR" in prompt assert prompt.index("STAR") < prompt.index("- ") def test_education_prompt_polishes_without_star_or_bullets() -> None: prompt = _system_prompt("education") assert "Do not use a STAR" in prompt assert "education entries" in prompt assert "bullet points" not in prompt class _SequentialCompletion: def __init__(self, outputs: list[dict[str, object] | Exception]) -> None: self.outputs = outputs self.calls: list[dict[str, object]] = [] self.call_options: list[dict[str, object]] = [] self.schema_names: list[str] = [] self.system_prompts: list[str] = [] def complete(self, *, schema, schema_name, system_prompt, payload, **kwargs): self.calls.append(payload) self.call_options.append(kwargs) self.schema_names.append(schema_name) self.system_prompts.append(system_prompt) value = self.outputs[min(len(self.calls) - 1, len(self.outputs) - 1)] if isinstance(value, Exception): raise value return schema.model_validate({"optimized_description": value["optimized_description"]}) def _output(text: str) -> dict[str, object]: return {"optimized_description": text} def test_missing_coverage_declaration_does_not_add_a_repair_round() -> None: completion = _SequentialCompletion([_output("\u5b8c\u6210\u5df2\u786e\u8ba4\u7684\u5de5\u4f5c\u3002")]) expander = OpenAIEntryExpander(completion) entry = {"description": "\u8fdb\u884c\u9700\u6c42\u5206\u6790\u3002\n\u5b8c\u6210\u63a5\u53e3\u8bbe\u8ba1\u3002\n\u6267\u884c\u4e0a\u7ebf\u652f\u6301\u3002"} proposal = expander.expand(entry, context={"entry_type": "project_experience"}) assert len(completion.calls) == 1 assert proposal["changes"] == [] assert "coverage_targets" not in completion.calls[0] assert "covered_fact_ids" not in proposal def test_hard_fact_omission_repairs_with_atomic_anchor() -> None: entry = {"description": "\u4f7f\u7528 FastAPI \u5f00\u53d1\u670d\u52a1\uff0c\u652f\u6301 300 \u540d\u7528\u6237\u3002"} completion = _SequentialCompletion([ _output("\u652f\u6301 300 \u540d\u7528\u6237\u3002"), _output("\u4f7f\u7528 FastAPI \u5f00\u53d1\u670d\u52a1\uff0c\u652f\u6301 300 \u540d\u7528\u6237\u3002"), ]) expander = OpenAIEntryExpander(completion) proposal = expander.expand(entry, context={"entry_type": "project_experience"}) assert len(completion.calls) == 2 assert completion.calls[1]["rejected_reason"] == "hard_fact_omitted" assert completion.calls[1]["omitted_facts"] == ["fastapi"] assert proposal["uncovered_facts"] == [] def test_failed_repair_keeps_the_first_pass_candidate() -> None: entry = {"description": "\u4f7f\u7528 FastAPI \u5f00\u53d1\u670d\u52a1\uff0c\u652f\u6301 300 \u540d\u7528\u6237\u3002"} completion = _SequentialCompletion([ _output("\u652f\u6301 300 \u540d\u7528\u6237\u3002"), RuntimeError("network failure"), ]) expander = OpenAIEntryExpander(completion) proposal = expander.expand(entry, context={"entry_type": "project_experience"}) assert len(completion.calls) == 2 assert proposal["optimized_description"].endswith("300 \u540d\u7528\u6237\u3002") assert "repair_failed" in proposal["validation_warnings"] def test_non_education_bullets_are_normalized_locally() -> None: completion = _SequentialCompletion([_output("- \u8d1f\u8d23\u9700\u6c42\u5206\u6790\u3002\n2. \u5b8c\u6210\u90e8\u7f72\u4e0a\u7ebf\u3002")]) expander = OpenAIEntryExpander(completion) proposal = expander.expand( {"description": "\u8d1f\u8d23\u9700\u6c42\u5206\u6790\u3002\u5b8c\u6210\u90e8\u7f72\u4e0a\u7ebf\u3002"}, context={"entry_type": "project_experience"}, ) assert proposal["optimized_description"].splitlines() == [ "\u2022 \u8d1f\u8d23\u9700\u6c42\u5206\u6790\u3002", "\u2022 \u5b8c\u6210\u90e8\u7f72\u4e0a\u7ebf\u3002", ] def test_education_never_gets_local_bullets() -> None: completion = _SequentialCompletion([_output("\u5b8c\u6210\u6570\u636e\u5e93\u8bfe\u7a0b\u9879\u76ee\uff0cGPA 3.8/4.0\u3002")]) expander = OpenAIEntryExpander(completion) proposal = expander.expand( {"description": "\u5b8c\u6210\u6570\u636e\u5e93\u8bfe\u7a0b\u9879\u76ee\uff0cGPA 3.8/4.0\u3002"}, context={"entry_type": "education"}, ) assert proposal["optimized_description"] == "\u5b8c\u6210\u6570\u636e\u5e93\u8bfe\u7a0b\u9879\u76ee\uff0cGPA 3.8/4.0\u3002" def test_repair_prompt_keeps_star_and_dash_bullets() -> None: prompt = _repair_prompt("project_experience") assert _EXPANSION_REPAIR_PROMPT in prompt assert "STAR" in prompt assert prompt.index("STAR") < prompt.index("- ") assert "bullet points" in prompt def test_entry_expansion_uses_one_attempt_and_a_remaining_repair_budget() -> None: entry = {"description": "Built a FastAPI service for 300 users."} completion = _SequentialCompletion([ _output("Supported 300 users."), _output("Built a FastAPI service for 300 users."), ]) expander = OpenAIEntryExpander(completion, timeout_seconds=30.0) expander.expand(entry, context={"entry_type": "project_experience"}) assert completion.call_options[0]["max_attempts"] == 1 assert completion.call_options[0]["timeout_seconds"] <= 30.0 assert completion.call_options[1]["max_attempts"] == 1 assert 0 < completion.call_options[1]["timeout_seconds"] <= completion.call_options[0]["timeout_seconds"] def test_repair_is_skipped_when_the_first_pass_exhausts_the_budget() -> None: entry = {"description": "Built a FastAPI service for 300 users."} completion = _SequentialCompletion([_output("Supported 300 users.")]) expander = OpenAIEntryExpander(completion, timeout_seconds=5.0) proposal = expander.expand(entry, context={"entry_type": "project_experience"}) assert len(completion.calls) == 1 assert proposal["optimized_description"].endswith("Supported 300 users.") assert proposal["optimized_description"].splitlines()[0].lstrip("\u2022 ").startswith("Supported") assert "repair_skipped_budget" in proposal["validation_warnings"] def test_repair_that_does_not_reduce_hard_omissions_keeps_first_pass() -> None: entry = {"description": "Built a FastAPI service for 300 users."} completion = _SequentialCompletion([ _output("Supported 300 users."), _output("Supported 300 users."), ]) expander = OpenAIEntryExpander(completion) proposal = expander.expand(entry, context={"entry_type": "project_experience"}) assert proposal["optimized_description"].endswith("Supported 300 users.") assert "repair_rejected_quality_regression" in proposal["validation_warnings"] def test_repair_that_loses_a_retained_hard_fact_keeps_first_pass() -> None: entry = {"description": "Built a FastAPI service for 300 users."} completion = _SequentialCompletion([ _output("Built a FastAPI service."), _output("Supported 300 users."), ]) expander = OpenAIEntryExpander(completion) proposal = expander.expand(entry, context={"entry_type": "project_experience"}) assert proposal["optimized_description"].endswith("Built a FastAPI service.") assert "repair_rejected_quality_regression" in proposal["validation_warnings"] def test_generic_api_term_does_not_trigger_repair() -> None: completion = _SequentialCompletion([_output("Developed the service endpoint.")]) expander = OpenAIEntryExpander(completion) proposal = expander.expand( {"description": "Built an API endpoint."}, context={"entry_type": "project_experience"}, ) assert len(completion.calls) == 1 assert proposal["uncovered_facts"] == [] def test_build_expander_honors_rule_fallback_setting() -> None: settings = Settings( llm_provider="openai", openai_api_key="test-key-not-a-secret", fallback_to_rules=False, ) expander = build_expander(settings, _SequentialCompletion([])) assert isinstance(expander, OpenAIEntryExpander) def test_repair_cannot_flatten_a_structured_first_draft() -> None: entry = {"description": "Built a FastAPI and Redis service for 300 users."} completion = _SequentialCompletion([ _output("Built a FastAPI service for 300 users.\nDesigned service modules.\nReleased documentation."), _output("Built a FastAPI and Redis service for 300 users."), ]) expander = OpenAIEntryExpander(completion) proposal = expander.expand(entry, context={"entry_type": "project_experience"}) assert len(proposal["optimized_description"].splitlines()) == 3 assert "repair_rejected_quality_regression" in proposal["validation_warnings"]