init: resume 작성 하네스 설계
This commit is contained in:
@@ -0,0 +1,222 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from resume_harness.cli import main
|
||||
from resume_harness.io import InputError, load_mapping
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def test_validate_examples_without_exposing_contact(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
exit_code = main(
|
||||
[
|
||||
"validate",
|
||||
"--candidate",
|
||||
str(ROOT / "examples/candidate.sample.yaml"),
|
||||
"--job",
|
||||
str(ROOT / "examples/job.sample.yaml"),
|
||||
"--config",
|
||||
str(ROOT / "examples/config.sample.yaml"),
|
||||
]
|
||||
)
|
||||
|
||||
captured = capsys.readouterr()
|
||||
payload = json.loads(captured.out)
|
||||
assert exit_code == 0
|
||||
assert payload["status"] == "valid"
|
||||
assert payload["evidence_count"] == 8
|
||||
assert "haneul.kim@example.com" not in captured.out
|
||||
assert "010-1234-5678" not in captured.out
|
||||
|
||||
|
||||
def test_schema_command_emits_json_schema(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
assert main(["schema", "candidate"]) == 0
|
||||
payload = json.loads(capsys.readouterr().out)
|
||||
assert payload["title"] == "CandidateProfile"
|
||||
|
||||
|
||||
def test_render_emits_only_quality_gated_markdown(
|
||||
capsys: pytest.CaptureFixture[str],
|
||||
) -> None:
|
||||
exit_code = main(
|
||||
[
|
||||
"render",
|
||||
"--candidate",
|
||||
str(ROOT / "examples/candidate.sample.yaml"),
|
||||
"--job",
|
||||
str(ROOT / "examples/job.sample.yaml"),
|
||||
"--draft",
|
||||
str(ROOT / "examples/draft.sample.yaml"),
|
||||
"--config",
|
||||
str(ROOT / "examples/config.sample.yaml"),
|
||||
"--analysis",
|
||||
str(ROOT / "examples/job-analysis.sample.yaml"),
|
||||
"--evidence-map",
|
||||
str(ROOT / "examples/evidence-map.sample.yaml"),
|
||||
"--content-plan",
|
||||
str(ROOT / "examples/content-plan.sample.yaml"),
|
||||
"--quality-report",
|
||||
str(ROOT / "examples/quality-report.sample.yaml"),
|
||||
]
|
||||
)
|
||||
|
||||
output = capsys.readouterr().out
|
||||
assert exit_code == 0
|
||||
assert output.startswith("# 김하늘\n")
|
||||
assert "## 경력" in output
|
||||
assert "근거 ID" not in output
|
||||
|
||||
|
||||
def test_render_rejects_stale_quality_report_after_draft_changes(
|
||||
tmp_path: Path,
|
||||
capsys: pytest.CaptureFixture[str],
|
||||
) -> None:
|
||||
draft = load_mapping(ROOT / "examples/draft.sample.yaml")
|
||||
draft["sections"][0]["claims"][0]["text"] += " 변경"
|
||||
changed_draft = tmp_path / "changed-draft.json"
|
||||
changed_draft.write_text(
|
||||
json.dumps(
|
||||
draft,
|
||||
ensure_ascii=False,
|
||||
default=lambda value: value.isoformat(),
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
exit_code = main(
|
||||
[
|
||||
"render",
|
||||
"--candidate",
|
||||
str(ROOT / "examples/candidate.sample.yaml"),
|
||||
"--job",
|
||||
str(ROOT / "examples/job.sample.yaml"),
|
||||
"--draft",
|
||||
str(changed_draft),
|
||||
"--config",
|
||||
str(ROOT / "examples/config.sample.yaml"),
|
||||
"--analysis",
|
||||
str(ROOT / "examples/job-analysis.sample.yaml"),
|
||||
"--evidence-map",
|
||||
str(ROOT / "examples/evidence-map.sample.yaml"),
|
||||
"--content-plan",
|
||||
str(ROOT / "examples/content-plan.sample.yaml"),
|
||||
"--quality-report",
|
||||
str(ROOT / "examples/quality-report.sample.yaml"),
|
||||
]
|
||||
)
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert exit_code == 2
|
||||
assert "현재 초안 내용과 일치하지 않습니다" in captured.err
|
||||
assert captured.out == ""
|
||||
|
||||
|
||||
def test_render_rejects_quality_report_when_analysis_context_changes(
|
||||
tmp_path: Path,
|
||||
capsys: pytest.CaptureFixture[str],
|
||||
) -> None:
|
||||
analysis = load_mapping(ROOT / "examples/job-analysis.sample.yaml")
|
||||
analysis["summary"] = "내용은 같아 보여도 다른 평가 컨텍스트"
|
||||
changed_analysis = tmp_path / "changed-analysis.json"
|
||||
# YAML timestamps are loaded as datetime objects; serialise them exactly as
|
||||
# the CLI's JSON input contract expects.
|
||||
changed_analysis.write_text(
|
||||
json.dumps(
|
||||
analysis,
|
||||
ensure_ascii=False,
|
||||
default=lambda value: value.isoformat(),
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
exit_code = main(
|
||||
[
|
||||
"render",
|
||||
"--candidate",
|
||||
str(ROOT / "examples/candidate.sample.yaml"),
|
||||
"--job",
|
||||
str(ROOT / "examples/job.sample.yaml"),
|
||||
"--draft",
|
||||
str(ROOT / "examples/draft.sample.yaml"),
|
||||
"--config",
|
||||
str(ROOT / "examples/config.sample.yaml"),
|
||||
"--analysis",
|
||||
str(changed_analysis),
|
||||
"--evidence-map",
|
||||
str(ROOT / "examples/evidence-map.sample.yaml"),
|
||||
"--content-plan",
|
||||
str(ROOT / "examples/content-plan.sample.yaml"),
|
||||
"--quality-report",
|
||||
str(ROOT / "examples/quality-report.sample.yaml"),
|
||||
]
|
||||
)
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert exit_code == 2
|
||||
assert "평가 컨텍스트와 일치하지 않습니다" in captured.err
|
||||
assert captured.out == ""
|
||||
|
||||
|
||||
def test_render_rejects_quality_report_when_candidate_context_changes(
|
||||
tmp_path: Path,
|
||||
capsys: pytest.CaptureFixture[str],
|
||||
) -> None:
|
||||
candidate = load_mapping(ROOT / "examples/candidate.sample.yaml")
|
||||
candidate["headline"] = "평가 이후에 변경된 프로필 정보"
|
||||
changed_candidate = tmp_path / "changed-candidate.json"
|
||||
changed_candidate.write_text(
|
||||
json.dumps(
|
||||
candidate,
|
||||
ensure_ascii=False,
|
||||
default=lambda value: value.isoformat(),
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
exit_code = main(
|
||||
[
|
||||
"render",
|
||||
"--candidate",
|
||||
str(changed_candidate),
|
||||
"--job",
|
||||
str(ROOT / "examples/job.sample.yaml"),
|
||||
"--draft",
|
||||
str(ROOT / "examples/draft.sample.yaml"),
|
||||
"--config",
|
||||
str(ROOT / "examples/config.sample.yaml"),
|
||||
"--analysis",
|
||||
str(ROOT / "examples/job-analysis.sample.yaml"),
|
||||
"--evidence-map",
|
||||
str(ROOT / "examples/evidence-map.sample.yaml"),
|
||||
"--content-plan",
|
||||
str(ROOT / "examples/content-plan.sample.yaml"),
|
||||
"--quality-report",
|
||||
str(ROOT / "examples/quality-report.sample.yaml"),
|
||||
]
|
||||
)
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert exit_code == 2
|
||||
assert "평가 컨텍스트와 일치하지 않습니다" in captured.err
|
||||
assert captured.out == ""
|
||||
|
||||
|
||||
def test_load_mapping_rejects_non_mapping(tmp_path: Path) -> None:
|
||||
path = tmp_path / "input.yaml"
|
||||
path.write_text("- item\n", encoding="utf-8")
|
||||
|
||||
with pytest.raises(InputError, match="mapping"):
|
||||
load_mapping(path)
|
||||
|
||||
|
||||
def test_load_mapping_rejects_unknown_extension(tmp_path: Path) -> None:
|
||||
path = tmp_path / "input.txt"
|
||||
path.write_text("value: 1\n", encoding="utf-8")
|
||||
|
||||
with pytest.raises(InputError, match="지원 형식"):
|
||||
load_mapping(path)
|
||||
Reference in New Issue
Block a user