134 lines
4.0 KiB
Python
134 lines
4.0 KiB
Python
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")
|