init: resume 작성 하네스 설계
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from resume_harness.prompts import (
|
||||
DuplicatePromptIdError,
|
||||
PromptFormatError,
|
||||
PromptRepository,
|
||||
PromptRepositoryError,
|
||||
)
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
PACKAGE_PROMPT_ROOT = PROJECT_ROOT / "src" / "resume_harness" / "prompt_templates"
|
||||
DEVELOPMENT_PROMPT_ROOT = PROJECT_ROOT / "prompts"
|
||||
|
||||
|
||||
def _write_prompt(
|
||||
root: Path,
|
||||
filename: str,
|
||||
*,
|
||||
prompt_id: str = "draft-resume",
|
||||
version: str = "1.2.3",
|
||||
output_model: str | None = "ResumeDraft",
|
||||
body: str = "검증된 사실만 사용한다.",
|
||||
) -> Path:
|
||||
output_line = "" if output_model is None else f"output_model: {output_model}\n"
|
||||
path = root / filename
|
||||
path.write_text(
|
||||
"---\n"
|
||||
f"id: {prompt_id}\n"
|
||||
f"version: {version}\n"
|
||||
f"{output_line}"
|
||||
"---\n\n"
|
||||
f"{body}\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
return path
|
||||
|
||||
|
||||
def test_repository_loads_checked_in_prompts_and_optional_output_model() -> None:
|
||||
repository = PromptRepository()
|
||||
|
||||
draft = repository.get("draft-resume")
|
||||
base = repository.load("base-system")
|
||||
|
||||
assert draft.version == "1.0.0"
|
||||
assert draft.output_model == "ResumeDraft"
|
||||
assert "근거" in draft.body
|
||||
assert draft.content == draft.body
|
||||
assert base.output_model is None
|
||||
assert repository.list_ids() == tuple(repository)
|
||||
assert repository.root == PACKAGE_PROMPT_ROOT.resolve()
|
||||
|
||||
|
||||
def test_development_prompt_mirror_matches_packaged_templates() -> None:
|
||||
packaged = {
|
||||
path.name: path.read_bytes() for path in PACKAGE_PROMPT_ROOT.glob("*.md")
|
||||
}
|
||||
development = {
|
||||
path.name: path.read_bytes() for path in DEVELOPMENT_PROMPT_ROOT.glob("*.md")
|
||||
}
|
||||
|
||||
assert packaged
|
||||
assert development == packaged
|
||||
|
||||
|
||||
def test_repository_rejects_requested_path_traversal(tmp_path: Path) -> None:
|
||||
_write_prompt(tmp_path, "safe.md")
|
||||
repository = PromptRepository(tmp_path)
|
||||
|
||||
with pytest.raises(PromptRepositoryError, match="invalid requested prompt id"):
|
||||
repository.get("../safe")
|
||||
with pytest.raises(PromptRepositoryError):
|
||||
repository.load("/etc/passwd")
|
||||
|
||||
|
||||
def test_repository_rejects_duplicate_ids_case_insensitively(tmp_path: Path) -> None:
|
||||
_write_prompt(tmp_path, "first.md", prompt_id="Draft-Resume")
|
||||
_write_prompt(tmp_path, "second.md", prompt_id="draft-resume")
|
||||
|
||||
with pytest.raises(DuplicatePromptIdError, match="duplicate prompt id"):
|
||||
PromptRepository(tmp_path)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"front_matter",
|
||||
[
|
||||
"id: safe\nid: replaced\nversion: 1.0.0\n",
|
||||
"id: safe\nversion: 1.0.0\noutput_model: !!python/name:os.system\n",
|
||||
"id: [safe]\nversion: 1.0.0\n",
|
||||
],
|
||||
)
|
||||
def test_repository_uses_strict_safe_front_matter(
|
||||
tmp_path: Path, front_matter: str
|
||||
) -> None:
|
||||
(tmp_path / "unsafe.md").write_text(
|
||||
f"---\n{front_matter}---\n본문\n", encoding="utf-8"
|
||||
)
|
||||
|
||||
with pytest.raises(PromptFormatError, match="front matter"):
|
||||
PromptRepository(tmp_path)
|
||||
|
||||
|
||||
def test_repository_validates_required_metadata_and_body(tmp_path: Path) -> None:
|
||||
(tmp_path / "missing.md").write_text(
|
||||
"---\nid: only-id\n---\n본문\n", encoding="utf-8"
|
||||
)
|
||||
|
||||
with pytest.raises(PromptFormatError, match="requires id and version"):
|
||||
PromptRepository(tmp_path)
|
||||
|
||||
|
||||
def test_repository_rejects_symlinked_prompt_even_when_target_exists(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
repository_root = tmp_path / "repository"
|
||||
repository_root.mkdir()
|
||||
outside = _write_prompt(tmp_path, "outside.md", prompt_id="outside")
|
||||
(repository_root / "linked.md").symlink_to(outside)
|
||||
|
||||
with pytest.raises(PromptRepositoryError, match="symbolic links"):
|
||||
PromptRepository(repository_root)
|
||||
|
||||
|
||||
def test_repository_reports_unknown_but_well_formed_id(tmp_path: Path) -> None:
|
||||
_write_prompt(tmp_path, "known.md", prompt_id="known")
|
||||
repository = PromptRepository(tmp_path)
|
||||
|
||||
with pytest.raises(KeyError, match="unknown prompt id"):
|
||||
repository.get("missing")
|
||||
Reference in New Issue
Block a user