79 lines
2.7 KiB
Python
79 lines
2.7 KiB
Python
from __future__ import annotations
|
|
|
|
from collections.abc import Mapping
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from pydantic import BaseModel
|
|
|
|
from resume_harness.io import load_model
|
|
from resume_harness.models import (
|
|
CandidateProfile,
|
|
ContentPlan,
|
|
EvidenceMap,
|
|
GenerationConfig,
|
|
JobAnalysis,
|
|
JobPosting,
|
|
QualityReport,
|
|
ResumeDraft,
|
|
)
|
|
from resume_harness.pipeline import PipelineStatus, ResumePipeline
|
|
from resume_harness.renderer import render_markdown
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
class GoldenFixtureBackend:
|
|
def __init__(self, responses: list[tuple[str, BaseModel]]) -> None:
|
|
self._responses = list(responses)
|
|
|
|
def complete_json(
|
|
self,
|
|
*,
|
|
stage: str,
|
|
system_prompt: str,
|
|
task_prompt: str,
|
|
user_payload: Mapping[str, Any],
|
|
output_model: type[BaseModel],
|
|
) -> BaseModel:
|
|
assert system_prompt and task_prompt and user_payload
|
|
expected_stage, response = self._responses.pop(0)
|
|
assert stage == expected_stage
|
|
assert isinstance(response, output_model)
|
|
return response
|
|
|
|
|
|
def test_professional_golden_fixture_passes_full_pipeline_and_renders() -> None:
|
|
profile = load_model(ROOT / "examples/candidate.sample.yaml", CandidateProfile)
|
|
posting = load_model(ROOT / "examples/job.sample.yaml", JobPosting)
|
|
config = load_model(ROOT / "examples/config.sample.yaml", GenerationConfig)
|
|
analysis = load_model(ROOT / "examples/job-analysis.sample.yaml", JobAnalysis)
|
|
evidence_map = load_model(ROOT / "examples/evidence-map.sample.yaml", EvidenceMap)
|
|
plan = load_model(ROOT / "examples/content-plan.sample.yaml", ContentPlan)
|
|
draft = load_model(ROOT / "examples/draft.sample.yaml", ResumeDraft)
|
|
report = load_model(ROOT / "examples/quality-report.sample.yaml", QualityReport)
|
|
backend = GoldenFixtureBackend(
|
|
[
|
|
("analyze-job", analysis),
|
|
("map-evidence", evidence_map),
|
|
("plan-content", plan),
|
|
("draft-resume", draft),
|
|
("evaluate-resume", report),
|
|
]
|
|
)
|
|
|
|
result = ResumePipeline(backend).run(profile, posting, config)
|
|
|
|
assert result.status is PipelineStatus.PASSED
|
|
assert result.deterministic_findings == []
|
|
assert result.quality_report is not None
|
|
assert result.quality_report.evidence_coverage == 1.0
|
|
assert result.quality_report.requirement_coverage == 1.0
|
|
markdown = render_markdown(result.draft, profile, config, analysis=analysis)
|
|
assert "## 핵심 역량" in markdown
|
|
assert "## 경력" in markdown
|
|
assert "## 주요 프로젝트" in markdown
|
|
assert "## 자격" in markdown
|
|
assert markdown.count("\n- ") >= 16
|