test_meta_node_add_optimization.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. """
  2. 元数据新增接口优化测试用例
  3. 测试 meta_node_add 接口的冗余检测和处理逻辑
  4. """
  5. import json
  6. from unittest.mock import MagicMock, patch
  7. class TestMetaNodeAddOptimization:
  8. """测试元数据新增接口的优化逻辑"""
  9. def test_exact_match_should_not_create_node(self, client):
  10. """
  11. 测试场景1:完全匹配
  12. 预期:返回失败,提示元数据已存在,不创建新节点
  13. """
  14. # 模拟冗余检测返回完全匹配
  15. with patch("app.api.meta_data.routes.check_redundancy_for_add") as mock_check:
  16. mock_check.return_value = {
  17. "has_exact_match": True,
  18. "exact_match_id": 12345,
  19. "has_candidates": True,
  20. "candidates": [
  21. {
  22. "id": 12345,
  23. "name_zh": "测试元数据",
  24. "name_en": "test_meta",
  25. "data_type": "varchar(255)",
  26. "tag_ids": [1, 2],
  27. }
  28. ],
  29. }
  30. response = client.post(
  31. "/api/meta/node/add",
  32. json={
  33. "name_zh": "测试元数据",
  34. "name_en": "test_meta",
  35. "data_type": "varchar(255)",
  36. "tag": [1, 2],
  37. },
  38. )
  39. data = json.loads(response.data)
  40. assert data["code"] != 200
  41. assert "已存在" in data["message"]
  42. assert "12345" in data["message"]
  43. def test_suspicious_duplicate_should_create_node_and_review(self, client):
  44. """
  45. 测试场景2:疑似重复
  46. 预期:创建新节点,创建审核记录,返回成功并提示疑似重复
  47. """
  48. # 模拟冗余检测返回疑似重复
  49. with patch(
  50. "app.api.meta_data.routes.check_redundancy_for_add"
  51. ) as mock_check, patch(
  52. "app.api.meta_data.routes.neo4j_driver.get_session"
  53. ) as mock_session, patch(
  54. "app.api.meta_data.routes.write_redundancy_review_record_with_new_id"
  55. ) as mock_write_review:
  56. # 模拟冗余检测结果
  57. mock_check.return_value = {
  58. "has_exact_match": False,
  59. "exact_match_id": None,
  60. "has_candidates": True,
  61. "candidates": [
  62. {
  63. "id": 12345,
  64. "name_zh": "测试元数据",
  65. "name_en": "test_meta_old",
  66. "data_type": "varchar(255)",
  67. "tag_ids": [1],
  68. }
  69. ],
  70. }
  71. # 模拟 Neo4j 创建节点
  72. mock_node = MagicMock()
  73. mock_node.id = 99999
  74. mock_node.__getitem__ = lambda self, key: {
  75. "name_zh": "测试元数据",
  76. "name_en": "test_meta_new",
  77. "data_type": "varchar(255)",
  78. }.get(key)
  79. mock_node.get = lambda key, default=None: {
  80. "name_zh": "测试元数据",
  81. "name_en": "test_meta_new",
  82. "data_type": "varchar(255)",
  83. }.get(key, default)
  84. mock_result = MagicMock()
  85. mock_result.single.return_value = {"n": mock_node}
  86. mock_session_instance = MagicMock()
  87. mock_session_instance.run.return_value = mock_result
  88. mock_session.return_value.__enter__.return_value = mock_session_instance
  89. response = client.post(
  90. "/api/meta/node/add",
  91. json={
  92. "name_zh": "测试元数据",
  93. "name_en": "test_meta_new",
  94. "data_type": "varchar(255)",
  95. "tag": [1, 2],
  96. },
  97. )
  98. data = json.loads(response.data)
  99. assert data["code"] == 200
  100. assert "疑似重复" in data["message"]
  101. assert "审核" in data["message"]
  102. assert data["data"]["id"] == 99999
  103. # 验证审核记录已创建
  104. mock_write_review.assert_called_once()
  105. call_args = mock_write_review.call_args
  106. assert call_args[1]["new_meta"]["id"] == 99999
  107. assert len(call_args[1]["candidates"]) == 1
  108. def test_no_duplicate_should_create_node_normally(self, client):
  109. """
  110. 测试场景3:无重复
  111. 预期:创建新节点,不创建审核记录,正常返回
  112. """
  113. with patch(
  114. "app.api.meta_data.routes.check_redundancy_for_add"
  115. ) as mock_check, patch(
  116. "app.api.meta_data.routes.neo4j_driver.get_session"
  117. ) as mock_session:
  118. # 模拟冗余检测返回无重复
  119. mock_check.return_value = {
  120. "has_exact_match": False,
  121. "exact_match_id": None,
  122. "has_candidates": False,
  123. "candidates": [],
  124. }
  125. # 模拟 Neo4j 创建节点
  126. mock_node = MagicMock()
  127. mock_node.id = 88888
  128. mock_node.__getitem__ = lambda self, key: {
  129. "name_zh": "全新元数据",
  130. "name_en": "brand_new_meta",
  131. "data_type": "varchar(255)",
  132. }.get(key)
  133. mock_node.get = lambda key, default=None: {
  134. "name_zh": "全新元数据",
  135. "name_en": "brand_new_meta",
  136. "data_type": "varchar(255)",
  137. }.get(key, default)
  138. mock_result = MagicMock()
  139. mock_result.single.return_value = {"n": mock_node}
  140. mock_session_instance = MagicMock()
  141. mock_session_instance.run.return_value = mock_result
  142. mock_session.return_value.__enter__.return_value = mock_session_instance
  143. response = client.post(
  144. "/api/meta/node/add",
  145. json={
  146. "name_zh": "全新元数据",
  147. "name_en": "brand_new_meta",
  148. "data_type": "varchar(255)",
  149. },
  150. )
  151. data = json.loads(response.data)
  152. assert data["code"] == 200
  153. assert "疑似重复" not in data.get("message", "")
  154. assert data["data"]["id"] == 88888
  155. def test_force_create_should_skip_redundancy_check(self, client):
  156. """
  157. 测试场景4:强制创建
  158. 预期:跳过冗余检测,直接创建节点
  159. """
  160. with patch(
  161. "app.api.meta_data.routes.check_redundancy_for_add"
  162. ) as mock_check, patch(
  163. "app.api.meta_data.routes.neo4j_driver.get_session"
  164. ) as mock_session:
  165. # 模拟 Neo4j 创建节点
  166. mock_node = MagicMock()
  167. mock_node.id = 77777
  168. mock_node.__getitem__ = lambda self, key: {
  169. "name_zh": "强制创建元数据",
  170. "name_en": "force_create_meta",
  171. "data_type": "varchar(255)",
  172. }.get(key)
  173. mock_node.get = lambda key, default=None: {
  174. "name_zh": "强制创建元数据",
  175. "name_en": "force_create_meta",
  176. "data_type": "varchar(255)",
  177. }.get(key, default)
  178. mock_result = MagicMock()
  179. mock_result.single.return_value = {"n": mock_node}
  180. mock_session_instance = MagicMock()
  181. mock_session_instance.run.return_value = mock_result
  182. mock_session.return_value.__enter__.return_value = mock_session_instance
  183. response = client.post(
  184. "/api/meta/node/add",
  185. json={
  186. "name_zh": "强制创建元数据",
  187. "name_en": "force_create_meta",
  188. "data_type": "varchar(255)",
  189. "force_create": True,
  190. },
  191. )
  192. data = json.loads(response.data)
  193. assert data["code"] == 200
  194. assert data["data"]["id"] == 77777
  195. # 验证冗余检测未被调用
  196. mock_check.assert_not_called()
  197. class TestRedundancyCheckFunctions:
  198. """测试冗余检测辅助函数"""
  199. def test_check_redundancy_for_add_should_not_create_review(self):
  200. """
  201. 测试 check_redundancy_for_add 函数
  202. 预期:只进行检测,不创建审核记录
  203. """
  204. from app.core.meta_data.redundancy_check import check_redundancy_for_add
  205. with patch(
  206. "app.core.meta_data.redundancy_check.neo4j_driver.get_session"
  207. ) as mock_session, patch(
  208. "app.core.meta_data.redundancy_check.write_redundancy_review_record"
  209. ) as mock_write:
  210. # 模拟查询返回疑似重复
  211. mock_result = MagicMock()
  212. mock_result.__iter__ = lambda self: iter(
  213. [
  214. {
  215. "id": 12345,
  216. "m": MagicMock(
  217. get=lambda key, default=None: {
  218. "name_zh": "测试元数据",
  219. "name_en": "test_meta",
  220. "data_type": "varchar(255)",
  221. }.get(key, default)
  222. ),
  223. }
  224. ]
  225. )
  226. mock_session_instance = MagicMock()
  227. mock_session_instance.run.return_value = mock_result
  228. mock_session.return_value.__enter__.return_value = mock_session_instance
  229. result = check_redundancy_for_add(
  230. name_zh="测试元数据",
  231. name_en="test_meta_new",
  232. data_type="varchar(255)",
  233. tag_ids=[1, 2],
  234. )
  235. # 验证返回结果
  236. assert result["has_exact_match"] is False
  237. assert result["has_candidates"] is True
  238. assert len(result["candidates"]) > 0
  239. # 验证未创建审核记录
  240. mock_write.assert_not_called()
  241. def test_write_redundancy_review_record_with_new_id(self):
  242. """
  243. 测试 write_redundancy_review_record_with_new_id 函数
  244. 预期:创建包含新节点ID的审核记录
  245. """
  246. from app.core.meta_data.redundancy_check import (
  247. write_redundancy_review_record_with_new_id,
  248. )
  249. with patch("app.core.meta_data.redundancy_check.db.session") as mock_session:
  250. new_meta = {
  251. "id": 99999, # 新创建的节点ID
  252. "name_zh": "测试元数据",
  253. "name_en": "test_meta_new",
  254. "data_type": "varchar(255)",
  255. "tag_ids": [1, 2],
  256. }
  257. candidates = [
  258. {
  259. "id": 12345,
  260. "name_zh": "测试元数据",
  261. "name_en": "test_meta_old",
  262. "data_type": "varchar(255)",
  263. "tag_ids": [1],
  264. }
  265. ]
  266. write_redundancy_review_record_with_new_id(
  267. new_meta=new_meta, candidates=candidates, source="api"
  268. )
  269. # 验证数据库操作
  270. mock_session.add.assert_called_once()
  271. mock_session.commit.assert_called_once()
  272. # 获取添加的审核记录
  273. added_review = mock_session.add.call_args[0][0]
  274. assert added_review.record_type == "redundancy"
  275. assert added_review.source == "api"
  276. assert added_review.new_meta["id"] == 99999
  277. assert len(added_review.candidates) == 1