test_value_objects.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. """
  2. 共享值对象单元测试
  3. 测试 EntityId 和 Timestamp 值对象的创建、验证和行为。
  4. """
  5. import pytest
  6. from datetime import datetime, timedelta
  7. from uuid import UUID, uuid4
  8. from src.domain.shared.value_objects import EntityId, Timestamp
  9. class TestEntityId:
  10. """EntityId 值对象单元测试"""
  11. def test_create_entity_id_with_valid_value(self):
  12. """测试使用有效值创建实体 ID"""
  13. # Arrange & Act
  14. entity_id = EntityId("doc_123")
  15. # Assert
  16. assert entity_id.value == "doc_123"
  17. def test_create_entity_id_with_empty_string_raises_error(self):
  18. """测试使用空字符串创建实体 ID 会抛出错误"""
  19. # Act & Assert
  20. with pytest.raises(ValueError, match="Entity ID cannot be empty"):
  21. EntityId("")
  22. def test_create_entity_id_with_whitespace_only_raises_error(self):
  23. """测试使用仅空格的字符串创建实体 ID 会抛出错误"""
  24. # Act & Assert
  25. with pytest.raises(ValueError, match="Entity ID cannot be whitespace only"):
  26. EntityId(" ")
  27. def test_create_entity_id_with_non_string_raises_error(self):
  28. """测试使用非字符串值创建实体 ID 会抛出错误"""
  29. # Act & Assert
  30. with pytest.raises(ValueError, match="Entity ID must be a string"):
  31. EntityId(123) # type: ignore
  32. def test_generate_creates_unique_id(self):
  33. """测试 generate 方法创建唯一 ID"""
  34. # Act
  35. id1 = EntityId.generate()
  36. id2 = EntityId.generate()
  37. # Assert
  38. assert id1.value != id2.value
  39. # 验证是有效的 UUID 格式
  40. UUID(id1.value)
  41. UUID(id2.value)
  42. def test_from_uuid_creates_entity_id(self):
  43. """测试从 UUID 创建实体 ID"""
  44. # Arrange
  45. uuid_value = uuid4()
  46. # Act
  47. entity_id = EntityId.from_uuid(uuid_value)
  48. # Assert
  49. assert entity_id.value == str(uuid_value)
  50. def test_entity_id_is_immutable(self):
  51. """测试实体 ID 是不可变的"""
  52. # Arrange
  53. entity_id = EntityId("doc_123")
  54. # Act & Assert
  55. with pytest.raises(AttributeError):
  56. entity_id.value = "doc_456" # type: ignore
  57. def test_entity_id_str_representation(self):
  58. """测试实体 ID 的字符串表示"""
  59. # Arrange
  60. entity_id = EntityId("doc_123")
  61. # Act & Assert
  62. assert str(entity_id) == "doc_123"
  63. def test_entity_id_repr_representation(self):
  64. """测试实体 ID 的详细表示"""
  65. # Arrange
  66. entity_id = EntityId("doc_123")
  67. # Act & Assert
  68. assert repr(entity_id) == "EntityId(value='doc_123')"
  69. def test_entity_id_equality(self):
  70. """测试实体 ID 的相等性比较"""
  71. # Arrange
  72. id1 = EntityId("doc_123")
  73. id2 = EntityId("doc_123")
  74. id3 = EntityId("doc_456")
  75. # Act & Assert
  76. assert id1 == id2
  77. assert id1 != id3
  78. class TestTimestamp:
  79. """Timestamp 值对象单元测试"""
  80. def test_create_timestamp_with_valid_datetime(self):
  81. """测试使用有效 datetime 创建时间戳"""
  82. # Arrange
  83. dt = datetime(2025, 1, 15, 10, 30, 0)
  84. # Act
  85. timestamp = Timestamp(dt)
  86. # Assert
  87. assert timestamp.value == dt
  88. def test_create_timestamp_with_non_datetime_raises_error(self):
  89. """测试使用非 datetime 对象创建时间戳会抛出错误"""
  90. # Act & Assert
  91. with pytest.raises(ValueError, match="Timestamp value must be a datetime object"):
  92. Timestamp("2025-01-15") # type: ignore
  93. def test_now_creates_current_timestamp(self):
  94. """测试 now 方法创建当前时间戳"""
  95. # Arrange
  96. before = datetime.now()
  97. # Act
  98. timestamp = Timestamp.now()
  99. # Arrange
  100. after = datetime.now()
  101. # Assert
  102. assert before <= timestamp.value <= after
  103. def test_from_iso_string_creates_timestamp(self):
  104. """测试从 ISO 字符串创建时间戳"""
  105. # Arrange
  106. iso_string = "2025-01-15T10:30:00"
  107. # Act
  108. timestamp = Timestamp.from_iso_string(iso_string)
  109. # Assert
  110. assert timestamp.value == datetime(2025, 1, 15, 10, 30, 0)
  111. def test_from_iso_string_with_invalid_format_raises_error(self):
  112. """测试从无效 ISO 字符串创建时间戳会抛出错误"""
  113. # Act & Assert
  114. with pytest.raises(ValueError, match="Invalid ISO timestamp string"):
  115. Timestamp.from_iso_string("invalid-date")
  116. def test_to_iso_string_converts_to_iso_format(self):
  117. """测试转换为 ISO 字符串"""
  118. # Arrange
  119. dt = datetime(2025, 1, 15, 10, 30, 0)
  120. timestamp = Timestamp(dt)
  121. # Act
  122. iso_string = timestamp.to_iso_string()
  123. # Assert
  124. assert iso_string == "2025-01-15T10:30:00"
  125. def test_is_before_returns_true_for_earlier_timestamp(self):
  126. """测试 is_before 对于更早的时间戳返回 True"""
  127. # Arrange
  128. ts1 = Timestamp(datetime(2025, 1, 15, 10, 0, 0))
  129. ts2 = Timestamp(datetime(2025, 1, 15, 11, 0, 0))
  130. # Act & Assert
  131. assert ts1.is_before(ts2)
  132. assert not ts2.is_before(ts1)
  133. def test_is_after_returns_true_for_later_timestamp(self):
  134. """测试 is_after 对于更晚的时间戳返回 True"""
  135. # Arrange
  136. ts1 = Timestamp(datetime(2025, 1, 15, 10, 0, 0))
  137. ts2 = Timestamp(datetime(2025, 1, 15, 11, 0, 0))
  138. # Act & Assert
  139. assert ts2.is_after(ts1)
  140. assert not ts1.is_after(ts2)
  141. def test_timestamp_is_immutable(self):
  142. """测试时间戳是不可变的"""
  143. # Arrange
  144. timestamp = Timestamp.now()
  145. # Act & Assert
  146. with pytest.raises(AttributeError):
  147. timestamp.value = datetime.now() # type: ignore
  148. def test_timestamp_str_representation(self):
  149. """测试时间戳的字符串表示"""
  150. # Arrange
  151. dt = datetime(2025, 1, 15, 10, 30, 0)
  152. timestamp = Timestamp(dt)
  153. # Act & Assert
  154. assert str(timestamp) == "2025-01-15T10:30:00"
  155. def test_timestamp_repr_representation(self):
  156. """测试时间戳的详细表示"""
  157. # Arrange
  158. dt = datetime(2025, 1, 15, 10, 30, 0)
  159. timestamp = Timestamp(dt)
  160. # Act & Assert
  161. assert "Timestamp(value=" in repr(timestamp)
  162. def test_timestamp_equality(self):
  163. """测试时间戳的相等性比较"""
  164. # Arrange
  165. dt = datetime(2025, 1, 15, 10, 30, 0)
  166. ts1 = Timestamp(dt)
  167. ts2 = Timestamp(dt)
  168. ts3 = Timestamp(dt + timedelta(seconds=1))
  169. # Act & Assert
  170. assert ts1 == ts2
  171. assert ts1 != ts3
  172. def test_timestamp_comparison_operators(self):
  173. """测试时间戳的比较运算符"""
  174. # Arrange
  175. ts1 = Timestamp(datetime(2025, 1, 15, 10, 0, 0))
  176. ts2 = Timestamp(datetime(2025, 1, 15, 11, 0, 0))
  177. ts3 = Timestamp(datetime(2025, 1, 15, 11, 0, 0))
  178. # Act & Assert
  179. assert ts1 < ts2
  180. assert ts1 <= ts2
  181. assert ts2 > ts1
  182. assert ts2 >= ts1
  183. assert ts2 <= ts3
  184. assert ts2 >= ts3
  185. def test_timestamp_equality_with_non_timestamp_returns_false(self):
  186. """测试时间戳与非时间戳对象比较返回 False"""
  187. # Arrange
  188. timestamp = Timestamp.now()
  189. # Act & Assert
  190. assert timestamp != "not a timestamp"
  191. assert timestamp != 123
  192. assert timestamp != None