101 lines
3.4 KiB
Python
101 lines
3.4 KiB
Python
from __future__ import annotations
|
|
|
|
import math
|
|
import unittest
|
|
|
|
from claridoc.models import (
|
|
REVIEW_DIMENSIONS,
|
|
Brief,
|
|
DocumentType,
|
|
ModelReview,
|
|
PipelineConfig,
|
|
QualityGate,
|
|
SourcePack,
|
|
ValidationError,
|
|
)
|
|
from claridoc.templates import mock_pipeline_config
|
|
from tests.helpers import brief_dict, source_dict
|
|
|
|
|
|
class ModelTests(unittest.TestCase):
|
|
def test_valid_brief_round_trip(self) -> None:
|
|
brief = Brief.from_dict(brief_dict())
|
|
self.assertEqual(brief.document_type, DocumentType.TECHNICAL_BLOG)
|
|
self.assertEqual(Brief.from_dict(brief.to_dict()).title, brief.title)
|
|
|
|
def test_readme_brief_round_trip(self) -> None:
|
|
brief = Brief.from_dict(brief_dict("readme"))
|
|
self.assertEqual(brief.document_type, DocumentType.README)
|
|
self.assertEqual(
|
|
Brief.from_dict(brief.to_dict()).document_type,
|
|
DocumentType.README,
|
|
)
|
|
|
|
def test_invalid_document_type_is_rejected(self) -> None:
|
|
data = brief_dict()
|
|
data["document_type"] = "essay"
|
|
with self.assertRaises(ValidationError):
|
|
Brief.from_dict(data)
|
|
|
|
def test_duplicate_source_ids_are_rejected(self) -> None:
|
|
data = source_dict()
|
|
data["sources"].append(dict(data["sources"][0]))
|
|
with self.assertRaises(ValidationError):
|
|
SourcePack.from_dict(data)
|
|
|
|
def test_target_words_range_is_enforced(self) -> None:
|
|
data = brief_dict()
|
|
data["constraints"]["target_words"] = 50
|
|
with self.assertRaises(ValidationError):
|
|
Brief.from_dict(data)
|
|
|
|
def test_quality_gate_rejects_non_finite_weights(self) -> None:
|
|
with self.assertRaises(ValidationError):
|
|
QualityGate.from_dict({"deterministic_weight": math.nan, "model_weight": math.nan})
|
|
|
|
def test_pipeline_requires_explicit_reviewers(self) -> None:
|
|
data = mock_pipeline_config()
|
|
data["reviewers"] = []
|
|
with self.assertRaises(ValidationError):
|
|
PipelineConfig.from_dict(data)
|
|
|
|
def test_pipeline_rejects_duplicate_reviewer_roles(self) -> None:
|
|
data = mock_pipeline_config()
|
|
data["reviewers"].append({"role": "logic", "provider": "mock"})
|
|
with self.assertRaises(ValidationError):
|
|
PipelineConfig.from_dict(data)
|
|
|
|
def test_review_requires_every_scoring_dimension(self) -> None:
|
|
review = self._valid_review()
|
|
del review["dimension_scores"][REVIEW_DIMENSIONS[0]]
|
|
with self.assertRaises(ValidationError):
|
|
ModelReview.from_dict(review, role="logic", provider="mock")
|
|
|
|
def test_review_rejects_unknown_issue_severity(self) -> None:
|
|
review = self._valid_review()
|
|
review["issues"] = [
|
|
{
|
|
"section": "Mechanism",
|
|
"problem": "A causal step is missing.",
|
|
"why_it_matters": "The conclusion cannot be reproduced.",
|
|
"fix": "Add the missing state transition.",
|
|
"severity": "critical",
|
|
}
|
|
]
|
|
with self.assertRaises(ValidationError):
|
|
ModelReview.from_dict(review, role="logic", provider="mock")
|
|
|
|
@staticmethod
|
|
def _valid_review() -> dict:
|
|
return {
|
|
"score": 90,
|
|
"dimension_scores": {name: 90 for name in REVIEW_DIMENSIONS},
|
|
"issues": [],
|
|
"strengths": ["The structure is explicit."],
|
|
"questions": [],
|
|
}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|