test_segment_inputs.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. from __future__ import annotations
  2. import tempfile
  3. import unittest
  4. from pathlib import Path
  5. from production_build_agents.contracts.document_identity import (
  6. exact_document_sha256,
  7. )
  8. from production_build_agents.contracts.production_execution_models import (
  9. ImmutableJsonRef,
  10. )
  11. from production_build_agents.contracts.production_planning_models import (
  12. ProductionPlanningPackage,
  13. )
  14. from production_build_agents.run.artifacts import (
  15. content_identity_for_path,
  16. seal_shared_visual_anchor,
  17. )
  18. from production_build_agents.run.segment_inputs import (
  19. SegmentInputIntegrityError,
  20. accepted_segment_snapshot,
  21. load_verified_segment_inputs,
  22. )
  23. from tests.support.production_fixtures import (
  24. production_plan,
  25. segment_bundle,
  26. )
  27. from tests.support.production_planner_fixtures import (
  28. build_production_planner_scenario,
  29. )
  30. def _ref(path: Path) -> ImmutableJsonRef:
  31. return ImmutableJsonRef(
  32. uri=str(path.resolve()),
  33. document_sha256=exact_document_sha256(path.read_bytes()),
  34. )
  35. class SegmentInputsTest(unittest.TestCase):
  36. def _package_with_global_pool(self, root: Path):
  37. package = segment_bundle(1)[0]
  38. artifacts = []
  39. for index, base in enumerate(
  40. (
  41. package.artifacts[0],
  42. package.artifacts[0].model_copy(
  43. update={
  44. "artifact_id": "Task2-v1-artifact-1",
  45. "artifact_type": "audio",
  46. "description": "unplanned optional audio",
  47. }
  48. ),
  49. ),
  50. start=1,
  51. ):
  52. path = root / f"global-{index}.bin"
  53. path.write_bytes(f"global-{index}".encode())
  54. artifacts.append(
  55. base.model_copy(
  56. update={
  57. "uri": str(path.resolve()),
  58. "source_uri": f"https://mutable.test/{index}",
  59. **content_identity_for_path(path),
  60. }
  61. )
  62. )
  63. delivery = build_production_planner_scenario(
  64. run_id="segment-input-global"
  65. ).global_data_delivery.model_copy(
  66. update={"active_artifacts": artifacts}
  67. )
  68. delivery_path = root / "global_data_stage_delivery.json"
  69. delivery_path.write_text(delivery.model_dump_json(), encoding="utf-8")
  70. anchor_path = root / "anchor.png"
  71. anchor_path.write_bytes(b"anchor")
  72. package = package.model_copy(
  73. update={
  74. "artifacts": [artifacts[0]],
  75. "global_data_delivery_ref": _ref(delivery_path),
  76. "shared_visual_anchor": seal_shared_visual_anchor(anchor_path),
  77. }
  78. )
  79. return package, delivery_path, artifacts
  80. def test_all_active_artifacts_are_available_not_only_planner_selection(
  81. self,
  82. ) -> None:
  83. with tempfile.TemporaryDirectory() as directory:
  84. package, _delivery_path, artifacts = self._package_with_global_pool(
  85. Path(directory)
  86. )
  87. verified = load_verified_segment_inputs(package)
  88. self.assertEqual(
  89. [item.artifact_id for item in verified.global_artifacts],
  90. [item.artifact_id for item in artifacts],
  91. )
  92. self.assertEqual(
  93. [
  94. item.artifact_id
  95. for item in verified.lineage_artifacts(package)
  96. ],
  97. ["Task2-v1-artifact-1"],
  98. )
  99. payload = verified.agent_payload()
  100. self.assertNotIn("tool_calls", str(payload))
  101. self.assertNotIn("source_uri", str(payload))
  102. self.assertNotIn("https://mutable.test/", str(payload))
  103. def test_exact_delivery_and_selected_artifact_identity_fail_closed(
  104. self,
  105. ) -> None:
  106. with tempfile.TemporaryDirectory() as directory:
  107. package, delivery_path, artifacts = (
  108. self._package_with_global_pool(Path(directory))
  109. )
  110. delivery_path.write_text("{}", encoding="utf-8")
  111. with self.assertRaisesRegex(
  112. SegmentInputIntegrityError,
  113. "精确字节已变化",
  114. ):
  115. load_verified_segment_inputs(package)
  116. def test_global_pool_rejects_same_id_or_uri_with_different_identity(
  117. self,
  118. ) -> None:
  119. for collision in ("artifact_id", "uri"):
  120. with self.subTest(collision=collision):
  121. with tempfile.TemporaryDirectory() as directory:
  122. root = Path(directory)
  123. package, delivery_path, artifacts = (
  124. self._package_with_global_pool(root)
  125. )
  126. delivery = build_production_planner_scenario(
  127. run_id="collision-global"
  128. ).global_data_delivery.model_copy(
  129. update={
  130. "active_artifacts": [
  131. artifacts[0],
  132. artifacts[1].model_copy(
  133. update={
  134. collision: getattr(
  135. artifacts[0],
  136. collision,
  137. )
  138. }
  139. ),
  140. ]
  141. }
  142. )
  143. delivery_path.write_text(
  144. delivery.model_dump_json(),
  145. encoding="utf-8",
  146. )
  147. package = package.model_copy(
  148. update={
  149. "global_data_delivery_ref": _ref(delivery_path)
  150. }
  151. )
  152. with self.assertRaisesRegex(
  153. SegmentInputIntegrityError,
  154. "必须唯一|存在歧义",
  155. ):
  156. load_verified_segment_inputs(package)
  157. def test_global_pool_rejects_symlink_uri_alias(self) -> None:
  158. with tempfile.TemporaryDirectory() as directory:
  159. root = Path(directory)
  160. package, delivery_path, artifacts = (
  161. self._package_with_global_pool(root)
  162. )
  163. alias = root / "same-artifact-alias.bin"
  164. alias.symlink_to(Path(artifacts[0].uri))
  165. delivery = build_production_planner_scenario(
  166. run_id="alias-global"
  167. ).global_data_delivery.model_copy(
  168. update={
  169. "active_artifacts": [
  170. artifacts[0],
  171. artifacts[1].model_copy(
  172. update={
  173. "uri": str(alias),
  174. **content_identity_for_path(alias),
  175. }
  176. ),
  177. ]
  178. }
  179. )
  180. delivery_path.write_text(
  181. delivery.model_dump_json(),
  182. encoding="utf-8",
  183. )
  184. package = package.model_copy(
  185. update={"global_data_delivery_ref": _ref(delivery_path)}
  186. )
  187. with self.assertRaisesRegex(
  188. SegmentInputIntegrityError,
  189. "URI.*歧义|uri 必须唯一",
  190. ):
  191. load_verified_segment_inputs(package)
  192. def test_selected_artifact_must_equal_global_delivery_record(self) -> None:
  193. with tempfile.TemporaryDirectory() as directory:
  194. package, _delivery_path, artifacts = (
  195. self._package_with_global_pool(Path(directory))
  196. )
  197. package = package.model_copy(
  198. update={
  199. "artifacts": [
  200. artifacts[0].model_copy(
  201. update={"description": "forged"}
  202. )
  203. ]
  204. }
  205. )
  206. with self.assertRaisesRegex(
  207. SegmentInputIntegrityError,
  208. "不等于正式 Global Data",
  209. ):
  210. load_verified_segment_inputs(package)
  211. def test_snapshot_unions_planning_baseline_and_current_pass(self) -> None:
  212. first = segment_bundle(1)[-1]
  213. second = segment_bundle(2)[-1]
  214. packages = [segment_bundle(1)[0], segment_bundle(2)[0]]
  215. plan = production_plan(packages).model_copy(
  216. update={"regenerate_segment_ids": ["Segment2"]}
  217. )
  218. planning = ProductionPlanningPackage.model_construct(
  219. accepted_segments=[first, second]
  220. )
  221. refreshed_second = second.model_copy(
  222. update={"source_plan_version": 2}
  223. )
  224. snapshot = accepted_segment_snapshot(
  225. planning,
  226. plan,
  227. {"Segment2": refreshed_second},
  228. exclude_segment_id="Segment2",
  229. )
  230. self.assertEqual(list(snapshot), ["Segment1"])
  231. full = accepted_segment_snapshot(
  232. planning,
  233. plan,
  234. {"Segment2": refreshed_second},
  235. )
  236. self.assertIs(full["Segment2"], refreshed_second)
  237. if __name__ == "__main__":
  238. unittest.main()