init: document-haness 하네스 설계
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from claridoc.models import PipelineConfig
|
||||
from claridoc.pipeline import _mock_provider_warning, run_pipeline
|
||||
from claridoc.templates import mock_pipeline_config
|
||||
from tests.helpers import make_brief, make_sources
|
||||
|
||||
|
||||
class PipelineTests(unittest.TestCase):
|
||||
def test_mixed_mock_pipeline_is_explicitly_flagged(self) -> None:
|
||||
config_data = mock_pipeline_config()
|
||||
config_data["writer"] = {"provider": "claude"}
|
||||
warning = _mock_provider_warning(PipelineConfig.from_dict(config_data))
|
||||
self.assertIn("mixes external providers", warning)
|
||||
self.assertIn("synthetic", warning)
|
||||
|
||||
def test_end_to_end_mock_run_creates_auditable_artifacts(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as temp:
|
||||
output = Path(temp) / "run"
|
||||
result = run_pipeline(
|
||||
make_brief(),
|
||||
make_sources(),
|
||||
PipelineConfig.from_dict(mock_pipeline_config()),
|
||||
output,
|
||||
)
|
||||
self.assertTrue(result.passed)
|
||||
self.assertTrue(result.final_path.is_file())
|
||||
self.assertTrue(result.report_path.is_file())
|
||||
self.assertTrue(result.manifest_path.is_file())
|
||||
run_data = json.loads((output / "run.json").read_text(encoding="utf-8"))
|
||||
self.assertTrue(run_data["passed"])
|
||||
self.assertTrue(any("deterministic mocks" in warning for warning in result.warnings))
|
||||
report_text = result.report_path.read_text(encoding="utf-8")
|
||||
self.assertIn("Provider topology", report_text)
|
||||
self.assertIn("synthetic", report_text)
|
||||
manifest = json.loads(result.manifest_path.read_text(encoding="utf-8"))
|
||||
paths = {item["path"] for item in manifest["files"]}
|
||||
self.assertIn("final/document.md", paths)
|
||||
self.assertIn("provider-events.jsonl", paths)
|
||||
self.assertNotIn("manifest.json", paths)
|
||||
for item in manifest["files"]:
|
||||
artifact = output / item["path"]
|
||||
self.assertEqual(artifact.stat().st_size, item["bytes"])
|
||||
self.assertEqual(hashlib.sha256(artifact.read_bytes()).hexdigest(), item["sha256"])
|
||||
|
||||
def test_revision_limit_is_enforced_when_gate_cannot_pass(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as temp:
|
||||
output = Path(temp) / "run"
|
||||
config_data = mock_pipeline_config()
|
||||
config_data["quality_gate"]["minimum_score"] = 99
|
||||
config_data["quality_gate"]["max_revisions"] = 1
|
||||
result = run_pipeline(
|
||||
make_brief(),
|
||||
make_sources(),
|
||||
PipelineConfig.from_dict(config_data),
|
||||
output,
|
||||
)
|
||||
self.assertFalse(result.passed)
|
||||
self.assertEqual(len(result.rounds), 2)
|
||||
self.assertTrue((output / "rounds" / "round-01" / "revision.raw.txt").is_file())
|
||||
|
||||
def test_reviewer_role_cannot_escape_artifact_directory(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as temp:
|
||||
root = Path(temp)
|
||||
output = root / "run"
|
||||
config_data = mock_pipeline_config()
|
||||
config_data["reviewers"] = [{"role": "../../logic reviewer", "provider": "mock"}]
|
||||
run_pipeline(
|
||||
make_brief(),
|
||||
make_sources(),
|
||||
PipelineConfig.from_dict(config_data),
|
||||
output,
|
||||
)
|
||||
review_files = list((output / "rounds" / "round-01").glob("review-*"))
|
||||
self.assertTrue(any(path.name.endswith("logic-reviewer.json") for path in review_files))
|
||||
self.assertEqual(list(root.glob("logic*")), [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user