from __future__ import annotations import sys import unittest from pathlib import Path from pydantic import ValidationError BACKEND_ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(BACKEND_ROOT)) from src.domain.contracts import ( # noqa: E402 ARTICLE_WORKFLOW_STATUSES, ARTICLE_WORKFLOW_TRANSITIONS, ArticleCreateRequest, ArticleWorkflowStatus, Role, UserSummary, ) class DomainContractTest(unittest.TestCase): def test_role_enum_is_canonical(self) -> None: self.assertEqual(["ADMIN", "EDITOR"], [role.value for role in Role]) with self.assertRaises(ValidationError): UserSummary.model_validate( { "id": "00000000-0000-0000-0000-000000000001", "display_name": "Writer", "role": "WRITER", } ) def test_article_create_rejects_missing_required_fields(self) -> None: with self.assertRaises(ValidationError) as error: ArticleCreateRequest.model_validate({}) missing_fields = { issue["loc"][0] for issue in error.exception.errors() if issue["type"] == "missing" } self.assertEqual({"brief_description", "target_site_id"}, missing_fields) def test_workflow_statuses_and_transitions_are_shared_constants(self) -> None: self.assertEqual( [ "ARTICLE_BRIEF_CREATED", "BOUNDARY_QUESTIONS_GENERATED", "BOUNDARY_ANSWERS_SUBMITTED", "PLAN_GENERATED", "PLAN_REVIEW_REQUIRED", "PLAN_REVISION_REQUIRED", "RESEARCH_RUNNING", "EVIDENCE_MATRIX_READY", "PARALLEL_PRODUCTION_RUNNING", "DRAFT_ASSEMBLED", "SEO_AND_LANGUAGE_REVIEW_READY", "FINAL_REVIEW_REQUIRED", "FINAL_REVISION_REQUIRED", "PUBLISH_DRY_RUN_REQUIRED", "PUBLISH_COMMIT_READY", "PUBLISH_COMMIT_CREATED", ], [status.value for status in ARTICLE_WORKFLOW_STATUSES], ) self.assertEqual(set(ArticleWorkflowStatus), set(ARTICLE_WORKFLOW_TRANSITIONS)) for status, next_statuses in ARTICLE_WORKFLOW_TRANSITIONS.items(): self.assertIsInstance(status, ArticleWorkflowStatus) for next_status in next_statuses: self.assertIsInstance(next_status, ArticleWorkflowStatus) if __name__ == "__main__": unittest.main()