test_exceptions.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. """
  2. 共享领域异常单元测试
  3. 测试领域异常类型的创建和行为。
  4. """
  5. import pytest
  6. from src.domain.shared.exceptions import (
  7. BusinessRuleViolationException,
  8. DomainException,
  9. DomainValidationException,
  10. EntityNotFoundException,
  11. InvalidValueException,
  12. )
  13. class TestDomainException:
  14. """DomainException 基类单元测试"""
  15. def test_create_domain_exception_with_message(self):
  16. """测试使用消息创建领域异常"""
  17. # Act
  18. exception = DomainException("Something went wrong")
  19. # Assert
  20. assert exception.message == "Something went wrong"
  21. assert exception.details == {}
  22. assert str(exception) == "Something went wrong"
  23. def test_create_domain_exception_with_message_and_details(self):
  24. """测试使用消息和详细信息创建领域异常"""
  25. # Arrange
  26. details = {"field": "name", "value": "invalid"}
  27. # Act
  28. exception = DomainException("Invalid input", details)
  29. # Assert
  30. assert exception.message == "Invalid input"
  31. assert exception.details == details
  32. assert "details:" in str(exception)
  33. def test_domain_exception_repr(self):
  34. """测试领域异常的详细表示"""
  35. # Arrange
  36. exception = DomainException("Test error", {"key": "value"})
  37. # Act
  38. repr_str = repr(exception)
  39. # Assert
  40. assert "DomainException" in repr_str
  41. assert "Test error" in repr_str
  42. assert "key" in repr_str
  43. def test_domain_exception_can_be_raised(self):
  44. """测试领域异常可以被抛出和捕获"""
  45. # Act & Assert
  46. with pytest.raises(DomainException) as exc_info:
  47. raise DomainException("Test error")
  48. assert exc_info.value.message == "Test error"
  49. class TestInvalidValueException:
  50. """InvalidValueException 单元测试"""
  51. def test_create_invalid_value_exception(self):
  52. """测试创建无效值异常"""
  53. # Act
  54. exception = InvalidValueException(
  55. field_name="email",
  56. invalid_value="invalid-email",
  57. reason="Email format is invalid"
  58. )
  59. # Assert
  60. assert exception.field_name == "email"
  61. assert exception.invalid_value == "invalid-email"
  62. assert exception.reason == "Email format is invalid"
  63. assert "email" in exception.message
  64. assert "Email format is invalid" in exception.message
  65. def test_invalid_value_exception_details(self):
  66. """测试无效值异常的详细信息"""
  67. # Act
  68. exception = InvalidValueException(
  69. field_name="age",
  70. invalid_value=-5,
  71. reason="Age must be positive"
  72. )
  73. # Assert
  74. assert exception.details["field_name"] == "age"
  75. assert exception.details["invalid_value"] == "-5"
  76. assert exception.details["reason"] == "Age must be positive"
  77. def test_invalid_value_exception_with_additional_details(self):
  78. """测试带额外详细信息的无效值异常"""
  79. # Arrange
  80. additional_details = {"min_value": 0, "max_value": 150}
  81. # Act
  82. exception = InvalidValueException(
  83. field_name="age",
  84. invalid_value=-5,
  85. reason="Age out of range",
  86. details=additional_details
  87. )
  88. # Assert
  89. assert exception.details["min_value"] == 0
  90. assert exception.details["max_value"] == 150
  91. assert exception.details["field_name"] == "age"
  92. def test_invalid_value_exception_can_be_raised(self):
  93. """测试无效值异常可以被抛出和捕获"""
  94. # Act & Assert
  95. with pytest.raises(InvalidValueException) as exc_info:
  96. raise InvalidValueException(
  97. field_name="test",
  98. invalid_value="bad",
  99. reason="Test reason"
  100. )
  101. assert exc_info.value.field_name == "test"
  102. class TestBusinessRuleViolationException:
  103. """BusinessRuleViolationException 单元测试"""
  104. def test_create_business_rule_violation_exception(self):
  105. """测试创建业务规则违反异常"""
  106. # Act
  107. exception = BusinessRuleViolationException(
  108. rule_name="DocumentPublishRule",
  109. violation_reason="Cannot delete a published document"
  110. )
  111. # Assert
  112. assert exception.rule_name == "DocumentPublishRule"
  113. assert exception.violation_reason == "Cannot delete a published document"
  114. assert "DocumentPublishRule" in exception.message
  115. assert "Cannot delete a published document" in exception.message
  116. def test_business_rule_violation_exception_details(self):
  117. """测试业务规则违反异常的详细信息"""
  118. # Act
  119. exception = BusinessRuleViolationException(
  120. rule_name="QuotaLimitRule",
  121. violation_reason="User has exceeded document quota"
  122. )
  123. # Assert
  124. assert exception.details["rule_name"] == "QuotaLimitRule"
  125. assert exception.details["violation_reason"] == "User has exceeded document quota"
  126. def test_business_rule_violation_exception_with_additional_details(self):
  127. """测试带额外详细信息的业务规则违反异常"""
  128. # Arrange
  129. additional_details = {
  130. "document_id": "doc_123",
  131. "status": "published",
  132. "user_id": "user_456"
  133. }
  134. # Act
  135. exception = BusinessRuleViolationException(
  136. rule_name="DocumentDeletionRule",
  137. violation_reason="Published documents cannot be deleted",
  138. details=additional_details
  139. )
  140. # Assert
  141. assert exception.details["document_id"] == "doc_123"
  142. assert exception.details["status"] == "published"
  143. assert exception.details["user_id"] == "user_456"
  144. def test_business_rule_violation_exception_can_be_raised(self):
  145. """测试业务规则违反异常可以被抛出和捕获"""
  146. # Act & Assert
  147. with pytest.raises(BusinessRuleViolationException) as exc_info:
  148. raise BusinessRuleViolationException(
  149. rule_name="TestRule",
  150. violation_reason="Test violation"
  151. )
  152. assert exc_info.value.rule_name == "TestRule"
  153. class TestEntityNotFoundException:
  154. """EntityNotFoundException 单元测试"""
  155. def test_create_entity_not_found_exception(self):
  156. """测试创建实体未找到异常"""
  157. # Act
  158. exception = EntityNotFoundException(
  159. entity_type="Document",
  160. entity_id="doc_123"
  161. )
  162. # Assert
  163. assert exception.entity_type == "Document"
  164. assert exception.entity_id == "doc_123"
  165. assert "Document" in exception.message
  166. assert "doc_123" in exception.message
  167. assert "not found" in exception.message
  168. def test_entity_not_found_exception_details(self):
  169. """测试实体未找到异常的详细信息"""
  170. # Act
  171. exception = EntityNotFoundException(
  172. entity_type="KnowledgeBase",
  173. entity_id="kb_456"
  174. )
  175. # Assert
  176. assert exception.details["entity_type"] == "KnowledgeBase"
  177. assert exception.details["entity_id"] == "kb_456"
  178. def test_entity_not_found_exception_with_additional_details(self):
  179. """测试带额外详细信息的实体未找到异常"""
  180. # Arrange
  181. additional_details = {"search_criteria": {"name": "test"}}
  182. # Act
  183. exception = EntityNotFoundException(
  184. entity_type="User",
  185. entity_id="user_789",
  186. details=additional_details
  187. )
  188. # Assert
  189. assert exception.details["search_criteria"] == {"name": "test"}
  190. def test_entity_not_found_exception_can_be_raised(self):
  191. """测试实体未找到异常可以被抛出和捕获"""
  192. # Act & Assert
  193. with pytest.raises(EntityNotFoundException) as exc_info:
  194. raise EntityNotFoundException(
  195. entity_type="Test",
  196. entity_id="test_123"
  197. )
  198. assert exc_info.value.entity_type == "Test"
  199. class TestDomainValidationException:
  200. """DomainValidationException 单元测试"""
  201. def test_create_domain_validation_exception(self):
  202. """测试创建领域验证异常"""
  203. # Arrange
  204. validation_errors = [
  205. {"field": "name", "error": "Name is required"},
  206. {"field": "email", "error": "Email format is invalid"}
  207. ]
  208. # Act
  209. exception = DomainValidationException(validation_errors)
  210. # Assert
  211. assert exception.validation_errors == validation_errors
  212. assert "2 error(s)" in exception.message
  213. def test_domain_validation_exception_details(self):
  214. """测试领域验证异常的详细信息"""
  215. # Arrange
  216. validation_errors = [
  217. {"field": "age", "error": "Age must be positive"}
  218. ]
  219. # Act
  220. exception = DomainValidationException(validation_errors)
  221. # Assert
  222. assert exception.details["validation_errors"] == validation_errors
  223. assert exception.details["error_count"] == 1
  224. def test_domain_validation_exception_with_multiple_errors(self):
  225. """测试包含多个错误的领域验证异常"""
  226. # Arrange
  227. validation_errors = [
  228. {"field": "name", "error": "Name is required"},
  229. {"field": "email", "error": "Email is required"},
  230. {"field": "age", "error": "Age must be positive"}
  231. ]
  232. # Act
  233. exception = DomainValidationException(validation_errors)
  234. # Assert
  235. assert len(exception.validation_errors) == 3
  236. assert exception.details["error_count"] == 3
  237. assert "3 error(s)" in exception.message
  238. def test_domain_validation_exception_with_additional_details(self):
  239. """测试带额外详细信息的领域验证异常"""
  240. # Arrange
  241. validation_errors = [{"field": "test", "error": "Test error"}]
  242. additional_details = {"context": "user_registration"}
  243. # Act
  244. exception = DomainValidationException(
  245. validation_errors,
  246. details=additional_details
  247. )
  248. # Assert
  249. assert exception.details["context"] == "user_registration"
  250. def test_domain_validation_exception_can_be_raised(self):
  251. """测试领域验证异常可以被抛出和捕获"""
  252. # Arrange
  253. validation_errors = [{"field": "test", "error": "Test error"}]
  254. # Act & Assert
  255. with pytest.raises(DomainValidationException) as exc_info:
  256. raise DomainValidationException(validation_errors)
  257. assert len(exc_info.value.validation_errors) == 1
  258. class TestExceptionHierarchy:
  259. """测试异常层次结构"""
  260. def test_all_domain_exceptions_inherit_from_domain_exception(self):
  261. """测试所有领域异常都继承自 DomainException"""
  262. # Assert
  263. assert issubclass(InvalidValueException, DomainException)
  264. assert issubclass(BusinessRuleViolationException, DomainException)
  265. assert issubclass(EntityNotFoundException, DomainException)
  266. assert issubclass(DomainValidationException, DomainException)
  267. def test_can_catch_all_domain_exceptions_with_base_class(self):
  268. """测试可以使用基类捕获所有领域异常"""
  269. # Test InvalidValueException
  270. with pytest.raises(DomainException):
  271. raise InvalidValueException("field", "value", "reason")
  272. # Test BusinessRuleViolationException
  273. with pytest.raises(DomainException):
  274. raise BusinessRuleViolationException("rule", "reason")
  275. # Test EntityNotFoundException
  276. with pytest.raises(DomainException):
  277. raise EntityNotFoundException("Entity", "id")
  278. # Test DomainValidationException
  279. with pytest.raises(DomainException):
  280. raise DomainValidationException([{"field": "test", "error": "error"}])