200 lines
6.7 KiB
Python
200 lines
6.7 KiB
Python
"""细胞产品来源链质检 — 节点展平与编排"""
|
|
import unittest
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from app.crud.cell_product import _flatten_lineage_nodes, get_product_lineage_qc
|
|
|
|
|
|
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")
|
|
|
|
|
|
class TestGetProductLineageQc(unittest.TestCase):
|
|
@patch("app.crud.cell_product.get_product_chain")
|
|
@patch("app.crud.cell_product._load_lineage_qc_for_nodes")
|
|
def test_missing_product_returns_none(self, _load, get_chain):
|
|
get_chain.return_value = None
|
|
db = MagicMock()
|
|
self.assertIsNone(get_product_lineage_qc(db, "missing", "zkxw"))
|
|
_load.assert_not_called()
|
|
|
|
@patch("app.crud.cell_product.get_product_chain")
|
|
@patch("app.crud.cell_product._load_lineage_qc_for_nodes")
|
|
def test_returns_product_header_and_nodes(self, load_nodes, get_chain):
|
|
get_chain.return_value = {
|
|
"current": {
|
|
"id": "cp1",
|
|
"productCode": "C1",
|
|
"productName": "产品1",
|
|
"nodeType": "cell_product",
|
|
"passage": "P1",
|
|
"isCurrent": True,
|
|
},
|
|
"ancestors": [],
|
|
}
|
|
load_nodes.return_value = [
|
|
{
|
|
"nodeType": "cell_product",
|
|
"id": "cp1",
|
|
"label": "C1",
|
|
"productName": "产品1",
|
|
"passage": "P1",
|
|
"receptionSource": None,
|
|
"isCurrent": True,
|
|
"workOrders": [
|
|
{
|
|
"id": "wo1",
|
|
"workOrderNo": "WO1",
|
|
"workOrderType": "temporary",
|
|
"workOrderStatus": "ywc",
|
|
"workOrderStatusLabel": "已完成",
|
|
"tasks": [
|
|
{
|
|
"id": "t1",
|
|
"taskNo": "T1",
|
|
"itemName": "无菌",
|
|
"results": [{"paramName": "结果", "paramValue": "阴性"}],
|
|
"attachmentFiles": [],
|
|
"remark": "",
|
|
"conclusion": "tg",
|
|
}
|
|
],
|
|
}
|
|
],
|
|
}
|
|
]
|
|
db = MagicMock()
|
|
out = get_product_lineage_qc(db, "cp1", "zkxw")
|
|
self.assertEqual(out["productId"], "cp1")
|
|
self.assertEqual(out["productCode"], "C1")
|
|
self.assertEqual(len(out["nodes"]), 1)
|
|
self.assertEqual(
|
|
out["nodes"][0]["workOrders"][0]["tasks"][0]["results"][0]["paramValue"],
|
|
"阴性",
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|