test_point_builder.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. from __future__ import annotations
  2. def _point_map(snapshot):
  3. return {point.point_key: point for point in snapshot.points}
  4. def test_dataflow_point_keys_are_stable_across_order_and_display_name_changes():
  5. from app.core.knowledge.point_builder import build_knowledge_snapshot
  6. source = {
  7. "uid": "01900000-0000-7000-8000-000000000101",
  8. "version": 7,
  9. "name_zh": "客户同步",
  10. "purpose": "同步客户主数据",
  11. "owner": "治理组",
  12. "business_domain_uid": "01900000-0000-7000-8000-000000000201",
  13. "aliases": ["customer_sync", "客户主数据同步"],
  14. "relations": [
  15. {
  16. "type": "READS_FROM",
  17. "target_uid": "01900000-0000-7000-8000-000000000301",
  18. "target_name": "客户源表",
  19. },
  20. {
  21. "type": "WRITES_TO",
  22. "target_uid": "01900000-0000-7000-8000-000000000302",
  23. "target_name": "客户主表",
  24. },
  25. ],
  26. }
  27. first = build_knowledge_snapshot("DataFlow", source)
  28. second = build_knowledge_snapshot(
  29. "DataFlow",
  30. {
  31. **source,
  32. "name_zh": "客户资料同步",
  33. "aliases": list(reversed(source["aliases"])),
  34. "relations": list(reversed(source["relations"])),
  35. },
  36. )
  37. first_points = _point_map(first)
  38. second_points = _point_map(second)
  39. assert set(first_points) == set(second_points)
  40. assert first.point_set_hash != second.point_set_hash
  41. assert (
  42. first_points[
  43. "DataFlow/01900000-0000-7000-8000-000000000101/purpose"
  44. ].content_hash
  45. == second_points[
  46. "DataFlow/01900000-0000-7000-8000-000000000101/purpose"
  47. ].content_hash
  48. )
  49. def test_business_domain_builder_redacts_secrets_and_requires_stable_child_uid():
  50. import pytest
  51. from app.core.knowledge.point_builder import build_knowledge_snapshot
  52. snapshot = build_knowledge_snapshot(
  53. "BusinessDomain",
  54. {
  55. "uid": "01900000-0000-7000-8000-000000000401",
  56. "version": 2,
  57. "name_zh": "客户域",
  58. "definition": "统一客户信息",
  59. "password": "must-not-leak",
  60. "fields": [
  61. {
  62. "uid": "01900000-0000-7000-8000-000000000402",
  63. "name": "customer_id",
  64. "definition": "客户唯一标识",
  65. }
  66. ],
  67. },
  68. )
  69. serialized = "\n".join(point.content for point in snapshot.points)
  70. assert "must-not-leak" not in serialized
  71. assert (
  72. "BusinessDomain/01900000-0000-7000-8000-000000000401/"
  73. "fields/01900000-0000-7000-8000-000000000402/definition"
  74. ) in _point_map(snapshot)
  75. with pytest.raises(ValueError, match="stable uid"):
  76. build_knowledge_snapshot(
  77. "BusinessDomain",
  78. {
  79. "uid": "01900000-0000-7000-8000-000000000401",
  80. "version": 3,
  81. "fields": [{"name": "customer_name", "definition": "姓名"}],
  82. },
  83. )
  84. def test_same_semantic_source_produces_same_snapshot_hash():
  85. from app.core.knowledge.point_builder import build_knowledge_snapshot
  86. source = {
  87. "uid": "01900000-0000-7000-8000-000000000501",
  88. "version": 1,
  89. "name": "Customer",
  90. "definition": "Customer domain",
  91. "permission_scope": {"business_domains": ["b", "a"]},
  92. }
  93. first = build_knowledge_snapshot("BusinessDomain", source)
  94. second = build_knowledge_snapshot(
  95. "BusinessDomain",
  96. {
  97. "definition": "Customer domain",
  98. "name": "Customer",
  99. "version": 1,
  100. "uid": source["uid"],
  101. "permission_scope": {"business_domains": ["a", "b"]},
  102. },
  103. )
  104. assert first.source_snapshot_hash == second.source_snapshot_hash
  105. assert first.point_set_hash == second.point_set_hash
  106. def test_semantic_assets_build_versioned_knowledge_points():
  107. from app.core.knowledge.point_builder import build_knowledge_snapshot
  108. term_uid = "01900000-0000-7000-8000-000000000601"
  109. metric_uid = "01900000-0000-7000-8000-000000000602"
  110. term = build_knowledge_snapshot(
  111. "BusinessTerm",
  112. {
  113. "uid": term_uid,
  114. "version": 3,
  115. "name": "物料",
  116. "definition": "统一采购、库存和维修语义。",
  117. "aliases": ["备件", "物资"],
  118. "business_domain_uid": "01900000-0000-7000-8000-000000000603",
  119. },
  120. )
  121. metric = build_knowledge_snapshot(
  122. "MetricDefinition",
  123. {
  124. "uid": metric_uid,
  125. "version": 2,
  126. "name": "物料完整率",
  127. "definition": "完整物料占全部物料的比例。",
  128. "formula": "完整物料数 / 全部物料数",
  129. "unit": "%",
  130. "dimensions": [
  131. {
  132. "uid": "category",
  133. "name": "物料分类",
  134. "definition": "按物料分类观察。",
  135. }
  136. ],
  137. },
  138. )
  139. assert term.source_type == "BusinessTerm"
  140. assert f"BusinessTerm/{term_uid}/aliases/" in "\n".join(
  141. point.point_key for point in term.points
  142. )
  143. metric_points = _point_map(metric)
  144. assert metric_points[f"MetricDefinition/{metric_uid}/formula"].content == (
  145. "完整物料数 / 全部物料数"
  146. )
  147. assert (
  148. f"MetricDefinition/{metric_uid}/dimensions/category/definition"
  149. in metric_points
  150. )
  151. def test_ontology_builds_stable_class_and_property_points():
  152. from app.core.knowledge.point_builder import build_knowledge_snapshot
  153. snapshot = build_knowledge_snapshot(
  154. "Ontology",
  155. {
  156. "uid": "01900000-0000-7000-8000-000000000901",
  157. "version": 2,
  158. "name": "客户本体",
  159. "classes": [
  160. {
  161. "uid": "01900000-0000-7000-8000-000000000902",
  162. "name": "客户",
  163. "definition": "企业服务对象",
  164. }
  165. ],
  166. "properties": [
  167. {
  168. "uid": "01900000-0000-7000-8000-000000000903",
  169. "name": "客户等级",
  170. "data_type": "string",
  171. }
  172. ],
  173. },
  174. )
  175. assert snapshot.source_type == "Ontology"
  176. point_keys = {point.point_key for point in snapshot.points}
  177. assert any("classes/" in key and key.endswith("/name") for key in point_keys)
  178. assert any(
  179. "properties/" in key and key.endswith("/data_type")
  180. for key in point_keys
  181. )