test_cell_product_lineage_qc.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. """细胞产品来源链质检 — 节点展平与编排"""
  2. import unittest
  3. from unittest.mock import MagicMock, patch
  4. from app.crud.cell_product import _flatten_lineage_nodes, get_product_lineage_qc
  5. class TestFlattenLineageNodes(unittest.TestCase):
  6. def test_current_only_when_no_ancestors(self):
  7. chain = {
  8. "current": {
  9. "id": "cp-cur",
  10. "productCode": "C-CUR",
  11. "productName": "当前",
  12. "nodeType": "cell_product",
  13. "passage": "P2",
  14. "isCurrent": True,
  15. },
  16. "ancestors": [],
  17. }
  18. nodes = _flatten_lineage_nodes(chain)
  19. self.assertEqual(len(nodes), 1)
  20. self.assertEqual(nodes[0]["id"], "cp-cur")
  21. self.assertTrue(nodes[0]["isCurrent"])
  22. def test_sample_then_parent_then_current_order(self):
  23. sample = {
  24. "id": "s1",
  25. "nodeType": "sample",
  26. "sampleCode": "YB001",
  27. "receptionSource": "cell_storage",
  28. "receptionSourceLabel": "细胞存储",
  29. }
  30. parent = {
  31. "id": "cp-p1",
  32. "productCode": "C-P1",
  33. "productName": "父代",
  34. "nodeType": "cell_product",
  35. "passage": "P1",
  36. "isCurrent": False,
  37. }
  38. current = {
  39. "id": "cp-cur",
  40. "productCode": "C-CUR",
  41. "productName": "当前",
  42. "nodeType": "cell_product",
  43. "passage": "P2",
  44. "isCurrent": True,
  45. }
  46. chain = {
  47. "current": current,
  48. "ancestors": [
  49. {
  50. "node": parent,
  51. "edge": {},
  52. "parent": {"node": sample, "edge": {}, "parent": None},
  53. }
  54. ],
  55. }
  56. nodes = _flatten_lineage_nodes(chain)
  57. self.assertEqual([n["id"] for n in nodes], ["s1", "cp-p1", "cp-cur"])
  58. self.assertEqual(nodes[0]["nodeType"], "sample")
  59. self.assertTrue(nodes[-1]["isCurrent"])
  60. def test_dedupe_same_node_type_and_id(self):
  61. parent = {
  62. "id": "cp-p1",
  63. "nodeType": "cell_product",
  64. "productCode": "C-P1",
  65. "productName": "父",
  66. "passage": "P1",
  67. "isCurrent": False,
  68. }
  69. current = {
  70. "id": "cp-cur",
  71. "nodeType": "cell_product",
  72. "productCode": "C-CUR",
  73. "productName": "当前",
  74. "passage": "P2",
  75. "isCurrent": True,
  76. }
  77. chain = {
  78. "current": current,
  79. "ancestors": [
  80. {"node": parent, "edge": {}, "parent": None},
  81. {"node": dict(parent), "edge": {}, "parent": None},
  82. ],
  83. }
  84. nodes = _flatten_lineage_nodes(chain)
  85. ids = [n["id"] for n in nodes]
  86. self.assertEqual(ids.count("cp-p1"), 1)
  87. self.assertEqual(ids[-1], "cp-cur")
  88. def test_parent_array_multi_input(self):
  89. s1 = {"id": "s1", "nodeType": "sample", "sampleCode": "A", "receptionSource": "cell_storage"}
  90. s2 = {"id": "s2", "nodeType": "sample", "sampleCode": "B", "receptionSource": "cell_storage"}
  91. parent = {
  92. "id": "cp-p1",
  93. "nodeType": "cell_product",
  94. "productCode": "P1",
  95. "productName": "父",
  96. "passage": "P1",
  97. "isCurrent": False,
  98. }
  99. current = {
  100. "id": "cp-cur",
  101. "nodeType": "cell_product",
  102. "productCode": "CUR",
  103. "productName": "当前",
  104. "passage": "P2",
  105. "isCurrent": True,
  106. }
  107. chain = {
  108. "current": current,
  109. "ancestors": [
  110. {
  111. "node": parent,
  112. "edge": {},
  113. "parent": [
  114. {"node": s1, "edge": {}, "parent": None},
  115. {"node": s2, "edge": {}, "parent": None},
  116. ],
  117. }
  118. ],
  119. }
  120. nodes = _flatten_lineage_nodes(chain)
  121. ids = [n["id"] for n in nodes]
  122. self.assertIn("s1", ids)
  123. self.assertIn("s2", ids)
  124. self.assertEqual(ids[-1], "cp-cur")
  125. class TestGetProductLineageQc(unittest.TestCase):
  126. @patch("app.crud.cell_product.get_product_chain")
  127. @patch("app.crud.cell_product._load_lineage_qc_for_nodes")
  128. def test_missing_product_returns_none(self, _load, get_chain):
  129. get_chain.return_value = None
  130. db = MagicMock()
  131. self.assertIsNone(get_product_lineage_qc(db, "missing", "zkxw"))
  132. _load.assert_not_called()
  133. @patch("app.crud.cell_product.get_product_chain")
  134. @patch("app.crud.cell_product._load_lineage_qc_for_nodes")
  135. def test_returns_product_header_and_nodes(self, load_nodes, get_chain):
  136. get_chain.return_value = {
  137. "current": {
  138. "id": "cp1",
  139. "productCode": "C1",
  140. "productName": "产品1",
  141. "nodeType": "cell_product",
  142. "passage": "P1",
  143. "isCurrent": True,
  144. },
  145. "ancestors": [],
  146. }
  147. load_nodes.return_value = [
  148. {
  149. "nodeType": "cell_product",
  150. "id": "cp1",
  151. "label": "C1",
  152. "productName": "产品1",
  153. "passage": "P1",
  154. "receptionSource": None,
  155. "isCurrent": True,
  156. "workOrders": [
  157. {
  158. "id": "wo1",
  159. "workOrderNo": "WO1",
  160. "workOrderType": "temporary",
  161. "workOrderStatus": "ywc",
  162. "workOrderStatusLabel": "已完成",
  163. "tasks": [
  164. {
  165. "id": "t1",
  166. "taskNo": "T1",
  167. "itemName": "无菌",
  168. "results": [{"paramName": "结果", "paramValue": "阴性"}],
  169. "attachmentFiles": [],
  170. "remark": "",
  171. "conclusion": "tg",
  172. }
  173. ],
  174. }
  175. ],
  176. }
  177. ]
  178. db = MagicMock()
  179. out = get_product_lineage_qc(db, "cp1", "zkxw")
  180. self.assertEqual(out["productId"], "cp1")
  181. self.assertEqual(out["productCode"], "C1")
  182. self.assertEqual(len(out["nodes"]), 1)
  183. self.assertEqual(
  184. out["nodes"][0]["workOrders"][0]["tasks"][0]["results"][0]["paramValue"],
  185. "阴性",
  186. )
  187. if __name__ == "__main__":
  188. unittest.main()