|
|
@@ -0,0 +1,134 @@
|
|
|
+"""细胞产品来源链质检 — 节点展平与编排"""
|
|
|
+import unittest
|
|
|
+from unittest.mock import MagicMock, patch
|
|
|
+
|
|
|
+from app.crud.cell_product import _flatten_lineage_nodes
|
|
|
+
|
|
|
+
|
|
|
+class TestFlattenLineageNodes(unittest.TestCase):
|
|
|
+ def test_current_only_when_no_ancestors(self):
|
|
|
+ chain = {
|
|
|
+ "current": {
|
|
|
+ "id": "cp-cur",
|
|
|
+ "productCode": "C-CUR",
|
|
|
+ "productName": "当前",
|
|
|
+ "nodeType": "cell_product",
|
|
|
+ "passage": "P2",
|
|
|
+ "isCurrent": True,
|
|
|
+ },
|
|
|
+ "ancestors": [],
|
|
|
+ }
|
|
|
+ nodes = _flatten_lineage_nodes(chain)
|
|
|
+ self.assertEqual(len(nodes), 1)
|
|
|
+ self.assertEqual(nodes[0]["id"], "cp-cur")
|
|
|
+ self.assertTrue(nodes[0]["isCurrent"])
|
|
|
+
|
|
|
+ def test_sample_then_parent_then_current_order(self):
|
|
|
+ sample = {
|
|
|
+ "id": "s1",
|
|
|
+ "nodeType": "sample",
|
|
|
+ "sampleCode": "YB001",
|
|
|
+ "receptionSource": "cell_storage",
|
|
|
+ "receptionSourceLabel": "细胞存储",
|
|
|
+ }
|
|
|
+ parent = {
|
|
|
+ "id": "cp-p1",
|
|
|
+ "productCode": "C-P1",
|
|
|
+ "productName": "父代",
|
|
|
+ "nodeType": "cell_product",
|
|
|
+ "passage": "P1",
|
|
|
+ "isCurrent": False,
|
|
|
+ }
|
|
|
+ current = {
|
|
|
+ "id": "cp-cur",
|
|
|
+ "productCode": "C-CUR",
|
|
|
+ "productName": "当前",
|
|
|
+ "nodeType": "cell_product",
|
|
|
+ "passage": "P2",
|
|
|
+ "isCurrent": True,
|
|
|
+ }
|
|
|
+ chain = {
|
|
|
+ "current": current,
|
|
|
+ "ancestors": [
|
|
|
+ {
|
|
|
+ "node": parent,
|
|
|
+ "edge": {},
|
|
|
+ "parent": {"node": sample, "edge": {}, "parent": None},
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ }
|
|
|
+ nodes = _flatten_lineage_nodes(chain)
|
|
|
+ self.assertEqual([n["id"] for n in nodes], ["s1", "cp-p1", "cp-cur"])
|
|
|
+ self.assertEqual(nodes[0]["nodeType"], "sample")
|
|
|
+ self.assertTrue(nodes[-1]["isCurrent"])
|
|
|
+
|
|
|
+ def test_dedupe_same_node_type_and_id(self):
|
|
|
+ parent = {
|
|
|
+ "id": "cp-p1",
|
|
|
+ "nodeType": "cell_product",
|
|
|
+ "productCode": "C-P1",
|
|
|
+ "productName": "父",
|
|
|
+ "passage": "P1",
|
|
|
+ "isCurrent": False,
|
|
|
+ }
|
|
|
+ current = {
|
|
|
+ "id": "cp-cur",
|
|
|
+ "nodeType": "cell_product",
|
|
|
+ "productCode": "C-CUR",
|
|
|
+ "productName": "当前",
|
|
|
+ "passage": "P2",
|
|
|
+ "isCurrent": True,
|
|
|
+ }
|
|
|
+ chain = {
|
|
|
+ "current": current,
|
|
|
+ "ancestors": [
|
|
|
+ {"node": parent, "edge": {}, "parent": None},
|
|
|
+ {"node": dict(parent), "edge": {}, "parent": None},
|
|
|
+ ],
|
|
|
+ }
|
|
|
+ nodes = _flatten_lineage_nodes(chain)
|
|
|
+ ids = [n["id"] for n in nodes]
|
|
|
+ self.assertEqual(ids.count("cp-p1"), 1)
|
|
|
+ self.assertEqual(ids[-1], "cp-cur")
|
|
|
+
|
|
|
+ def test_parent_array_multi_input(self):
|
|
|
+ s1 = {"id": "s1", "nodeType": "sample", "sampleCode": "A", "receptionSource": "cell_storage"}
|
|
|
+ s2 = {"id": "s2", "nodeType": "sample", "sampleCode": "B", "receptionSource": "cell_storage"}
|
|
|
+ parent = {
|
|
|
+ "id": "cp-p1",
|
|
|
+ "nodeType": "cell_product",
|
|
|
+ "productCode": "P1",
|
|
|
+ "productName": "父",
|
|
|
+ "passage": "P1",
|
|
|
+ "isCurrent": False,
|
|
|
+ }
|
|
|
+ current = {
|
|
|
+ "id": "cp-cur",
|
|
|
+ "nodeType": "cell_product",
|
|
|
+ "productCode": "CUR",
|
|
|
+ "productName": "当前",
|
|
|
+ "passage": "P2",
|
|
|
+ "isCurrent": True,
|
|
|
+ }
|
|
|
+ chain = {
|
|
|
+ "current": current,
|
|
|
+ "ancestors": [
|
|
|
+ {
|
|
|
+ "node": parent,
|
|
|
+ "edge": {},
|
|
|
+ "parent": [
|
|
|
+ {"node": s1, "edge": {}, "parent": None},
|
|
|
+ {"node": s2, "edge": {}, "parent": None},
|
|
|
+ ],
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ }
|
|
|
+ nodes = _flatten_lineage_nodes(chain)
|
|
|
+ ids = [n["id"] for n in nodes]
|
|
|
+ self.assertIn("s1", ids)
|
|
|
+ self.assertIn("s2", ids)
|
|
|
+ self.assertEqual(ids[-1], "cp-cur")
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ unittest.main()
|