test_governance_metrics.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. from __future__ import annotations
  2. import pytest
  3. class MemoryGovernanceMetricRepository:
  4. def __init__(self):
  5. self.detail_calls = []
  6. def summary_counts(self, access):
  7. assert access.business_domain_uids == ("domain-a", "domain-b")
  8. return {
  9. "asset_completeness": (2, 3),
  10. "responsibility_coverage": (3, 3),
  11. "entity_mapping": (1, 3),
  12. "issue_closure": (1, 2),
  13. "issue_recurrence": (0, 0),
  14. }
  15. def list_details(self, access, *, metric, state, page, page_size):
  16. self.detail_calls.append(
  17. (access, metric, state, page, page_size)
  18. )
  19. return (
  20. [
  21. {
  22. "asset_uid": "asset-1",
  23. "asset_name": "冷却泵",
  24. "missing_fields": ["organization"],
  25. }
  26. ],
  27. 1,
  28. )
  29. def test_summary_exposes_fixed_definitions_counts_and_rates():
  30. from app.core.data_research.governance_metrics import (
  31. GovernanceMetricAccess,
  32. GovernanceMetricsService,
  33. )
  34. service = GovernanceMetricsService(MemoryGovernanceMetricRepository())
  35. result = service.summary(
  36. GovernanceMetricAccess(
  37. global_access=False,
  38. business_domain_uids=("domain-b", "domain-a", "domain-a"),
  39. )
  40. )
  41. assert result["scope"] == {
  42. "global_access": False,
  43. "business_domain_uids": ["domain-a", "domain-b"],
  44. }
  45. assert [item["code"] for item in result["metrics"]] == [
  46. "asset_completeness",
  47. "responsibility_coverage",
  48. "entity_mapping",
  49. "issue_closure",
  50. "issue_recurrence",
  51. ]
  52. assert result["metrics"][0] == {
  53. "code": "asset_completeness",
  54. "name": "台账完整率",
  55. "definition": (
  56. "名称、位置、组织、责任人及授权有效来源映射均完整的在用设备"
  57. "占可见在用设备的比例"
  58. ),
  59. "numerator": 2,
  60. "denominator": 3,
  61. "rate": 0.666667,
  62. "status": "available",
  63. "states": ["complete", "incomplete"],
  64. }
  65. assert result["metrics"][1]["rate"] == 1.0
  66. assert result["metrics"][2]["rate"] == 0.333333
  67. assert result["metrics"][3]["rate"] == 0.5
  68. assert result["metrics"][4]["rate"] is None
  69. assert result["metrics"][4]["status"] == "no_data"
  70. def test_detail_validates_metric_state_and_pagination():
  71. from app.core.data_research.errors import GovernanceMetricInvalid
  72. from app.core.data_research.governance_metrics import (
  73. GovernanceMetricAccess,
  74. GovernanceMetricsService,
  75. )
  76. repository = MemoryGovernanceMetricRepository()
  77. service = GovernanceMetricsService(repository)
  78. access = GovernanceMetricAccess(global_access=True)
  79. result = service.details(
  80. access,
  81. metric="asset_completeness",
  82. state="incomplete",
  83. page="2",
  84. page_size="25",
  85. )
  86. assert result == {
  87. "metric": {
  88. "code": "asset_completeness",
  89. "name": "台账完整率",
  90. "definition": (
  91. "名称、位置、组织、责任人及授权有效来源映射均完整的在用设备"
  92. "占可见在用设备的比例"
  93. ),
  94. },
  95. "state": "incomplete",
  96. "records": [
  97. {
  98. "asset_uid": "asset-1",
  99. "asset_name": "冷却泵",
  100. "missing_fields": ["organization"],
  101. }
  102. ],
  103. "page": 2,
  104. "page_size": 25,
  105. "total": 1,
  106. }
  107. assert repository.detail_calls[-1][1:] == (
  108. "asset_completeness",
  109. "incomplete",
  110. 2,
  111. 25,
  112. )
  113. with pytest.raises(GovernanceMetricInvalid, match="unsupported metric"):
  114. service.details(access, metric="ranking", state="top")
  115. with pytest.raises(GovernanceMetricInvalid, match="unsupported state"):
  116. service.details(
  117. access,
  118. metric="asset_completeness",
  119. state="mapped",
  120. )
  121. with pytest.raises(GovernanceMetricInvalid, match="page must"):
  122. service.details(
  123. access,
  124. metric="asset_completeness",
  125. state="complete",
  126. page=0,
  127. )
  128. with pytest.raises(GovernanceMetricInvalid, match="page_size must"):
  129. service.details(
  130. access,
  131. metric="asset_completeness",
  132. state="complete",
  133. page_size=101,
  134. )
  135. def test_access_contract_is_immutable_and_normalizes_domains():
  136. from dataclasses import FrozenInstanceError
  137. from app.core.data_research.governance_metrics import GovernanceMetricAccess
  138. access = GovernanceMetricAccess(
  139. global_access=False,
  140. business_domain_uids=(" domain-b ", "", "domain-a", "domain-a"),
  141. )
  142. assert access.business_domain_uids == ("domain-a", "domain-b")
  143. with pytest.raises(FrozenInstanceError):
  144. access.global_access = True