test_exceptions.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. """
  2. 应用层异常单元测试
  3. 测试应用层异常类的创建、属性和行为。
  4. """
  5. import pytest
  6. from src.application.shared.exceptions import (
  7. ApplicationException,
  8. ResourceNotFoundException,
  9. ValidationException,
  10. )
  11. class TestApplicationException:
  12. """ApplicationException 单元测试"""
  13. def test_create_with_message_only(self):
  14. """测试仅使用消息创建异常"""
  15. # Arrange & Act
  16. exception = ApplicationException("Test error")
  17. # Assert
  18. assert exception.message == "Test error"
  19. assert exception.details == {}
  20. assert str(exception) == "Test error"
  21. def test_create_with_message_and_details(self):
  22. """测试使用消息和详细信息创建异常"""
  23. # Arrange
  24. details = {"key": "value", "code": 123}
  25. # Act
  26. exception = ApplicationException("Test error", details=details)
  27. # Assert
  28. assert exception.message == "Test error"
  29. assert exception.details == details
  30. assert "Test error" in str(exception)
  31. assert "Details:" in str(exception)
  32. def test_str_representation(self):
  33. """测试异常的字符串表示"""
  34. # Arrange
  35. exception = ApplicationException(
  36. "Error occurred",
  37. details={"reason": "Invalid input"}
  38. )
  39. # Act
  40. result = str(exception)
  41. # Assert
  42. assert "Error occurred" in result
  43. assert "Details:" in result
  44. assert "reason" in result
  45. def test_repr_representation(self):
  46. """测试异常的详细表示"""
  47. # Arrange
  48. exception = ApplicationException(
  49. "Error occurred",
  50. details={"reason": "Invalid input"}
  51. )
  52. # Act
  53. result = repr(exception)
  54. # Assert
  55. assert "ApplicationException" in result
  56. assert "message=" in result
  57. assert "details=" in result
  58. def test_exception_can_be_raised_and_caught(self):
  59. """测试异常可以被抛出和捕获"""
  60. # Arrange & Act & Assert
  61. with pytest.raises(ApplicationException) as exc_info:
  62. raise ApplicationException("Test error")
  63. assert exc_info.value.message == "Test error"
  64. def test_exception_inherits_from_exception(self):
  65. """测试异常继承自 Exception"""
  66. # Arrange
  67. exception = ApplicationException("Test error")
  68. # Act & Assert
  69. assert isinstance(exception, Exception)
  70. class TestResourceNotFoundException:
  71. """ResourceNotFoundException 单元测试"""
  72. def test_create_with_resource_type_and_id(self):
  73. """测试使用资源类型和 ID 创建异常"""
  74. # Arrange & Act
  75. exception = ResourceNotFoundException(
  76. resource_type="Document",
  77. resource_id="doc_123"
  78. )
  79. # Assert
  80. assert exception.resource_type == "Document"
  81. assert exception.resource_id == "doc_123"
  82. assert "Document" in exception.message
  83. assert "doc_123" in exception.message
  84. assert "not found" in exception.message
  85. def test_details_include_resource_info(self):
  86. """测试详细信息包含资源信息"""
  87. # Arrange & Act
  88. exception = ResourceNotFoundException(
  89. resource_type="KnowledgeBase",
  90. resource_id="kb_456"
  91. )
  92. # Assert
  93. assert exception.details["resource_type"] == "KnowledgeBase"
  94. assert exception.details["resource_id"] == "kb_456"
  95. def test_create_with_additional_details(self):
  96. """测试使用额外详细信息创建异常"""
  97. # Arrange
  98. additional_details = {"reason": "Database query returned no results"}
  99. # Act
  100. exception = ResourceNotFoundException(
  101. resource_type="Document",
  102. resource_id="doc_789",
  103. details=additional_details
  104. )
  105. # Assert
  106. assert exception.details["resource_type"] == "Document"
  107. assert exception.details["resource_id"] == "doc_789"
  108. assert exception.details["reason"] == "Database query returned no results"
  109. def test_exception_message_format(self):
  110. """测试异常消息格式"""
  111. # Arrange & Act
  112. exception = ResourceNotFoundException(
  113. resource_type="ParsedDocument",
  114. resource_id="parsed_001"
  115. )
  116. # Act
  117. message = exception.message
  118. # Assert
  119. assert message == "ParsedDocument with id 'parsed_001' not found"
  120. def test_exception_inherits_from_application_exception(self):
  121. """测试异常继承自 ApplicationException"""
  122. # Arrange
  123. exception = ResourceNotFoundException(
  124. resource_type="Document",
  125. resource_id="doc_123"
  126. )
  127. # Act & Assert
  128. assert isinstance(exception, ApplicationException)
  129. assert isinstance(exception, Exception)
  130. class TestValidationException:
  131. """ValidationException 单元测试"""
  132. def test_create_with_message_only(self):
  133. """测试仅使用消息创建异常"""
  134. # Arrange & Act
  135. exception = ValidationException("Validation failed")
  136. # Assert
  137. assert exception.message == "Validation failed"
  138. assert exception.field is None
  139. assert exception.reason is None
  140. assert exception.details == {}
  141. def test_create_with_field_and_reason(self):
  142. """测试使用字段和原因创建异常"""
  143. # Arrange & Act
  144. exception = ValidationException(
  145. message="Invalid content",
  146. field="content",
  147. reason="Content cannot be empty"
  148. )
  149. # Assert
  150. assert exception.message == "Invalid content"
  151. assert exception.field == "content"
  152. assert exception.reason == "Content cannot be empty"
  153. assert exception.details["field"] == "content"
  154. assert exception.details["reason"] == "Content cannot be empty"
  155. def test_create_with_field_only(self):
  156. """测试仅使用字段创建异常"""
  157. # Arrange & Act
  158. exception = ValidationException(
  159. message="Invalid input",
  160. field="email"
  161. )
  162. # Assert
  163. assert exception.field == "email"
  164. assert exception.reason is None
  165. assert "field" in exception.details
  166. assert "reason" not in exception.details
  167. def test_create_with_reason_only(self):
  168. """测试仅使用原因创建异常"""
  169. # Arrange & Act
  170. exception = ValidationException(
  171. message="Invalid input",
  172. reason="Must be a positive number"
  173. )
  174. # Assert
  175. assert exception.field is None
  176. assert exception.reason == "Must be a positive number"
  177. assert "field" not in exception.details
  178. assert "reason" in exception.details
  179. def test_create_with_additional_details(self):
  180. """测试使用额外详细信息创建异常"""
  181. # Arrange
  182. additional_details = {
  183. "min_length": 10,
  184. "max_length": 100,
  185. "actual_length": 5
  186. }
  187. # Act
  188. exception = ValidationException(
  189. message="Invalid length",
  190. field="description",
  191. reason="Length out of range",
  192. details=additional_details
  193. )
  194. # Assert
  195. assert exception.details["field"] == "description"
  196. assert exception.details["reason"] == "Length out of range"
  197. assert exception.details["min_length"] == 10
  198. assert exception.details["max_length"] == 100
  199. assert exception.details["actual_length"] == 5
  200. def test_exception_inherits_from_application_exception(self):
  201. """测试异常继承自 ApplicationException"""
  202. # Arrange
  203. exception = ValidationException("Validation failed")
  204. # Act & Assert
  205. assert isinstance(exception, ApplicationException)
  206. assert isinstance(exception, Exception)
  207. def test_multiple_validation_errors(self):
  208. """测试多个验证错误"""
  209. # Arrange
  210. errors = [
  211. {"field": "email", "reason": "Invalid format"},
  212. {"field": "age", "reason": "Must be positive"}
  213. ]
  214. # Act
  215. exception = ValidationException(
  216. message="Multiple validation errors",
  217. details={"errors": errors}
  218. )
  219. # Assert
  220. assert "errors" in exception.details
  221. assert len(exception.details["errors"]) == 2