87 lines
3.8 KiB
Python
87 lines
3.8 KiB
Python
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()
|