test_device_quality_postgres.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. from __future__ import annotations
  2. import os
  3. import uuid
  4. import pytest
  5. from sqlalchemy import create_engine, text
  6. pytestmark = pytest.mark.integration
  7. def test_device_quality_responsibility_type_is_allowed_by_postgres():
  8. platform_url = os.environ.get("TEST_DATABASE_URL")
  9. if not platform_url:
  10. pytest.skip("TEST_DATABASE_URL is required")
  11. engine = create_engine(platform_url)
  12. try:
  13. with engine.connect() as connection:
  14. definition = connection.execute(
  15. text(
  16. """
  17. SELECT pg_get_constraintdef(oid)
  18. FROM pg_constraint
  19. WHERE conname =
  20. 'governance_responsibility_scopes_resource_type_check'
  21. """
  22. )
  23. ).scalar_one()
  24. assert "device_quality" in definition
  25. finally:
  26. engine.dispose()
  27. def test_device_quality_postgres_keeps_version_run_and_source_evidence(
  28. monkeypatch,
  29. ):
  30. platform_url = os.environ.get("TEST_DATABASE_URL")
  31. if not platform_url:
  32. pytest.skip("TEST_DATABASE_URL is required")
  33. monkeypatch.setenv("DATABASE_URL", platform_url)
  34. from app import create_app, db
  35. from app.core.data_research.device_quality import DeviceQualityService
  36. from app.core.data_research.device_quality_repository import (
  37. SqlAlchemyDeviceQualityRepository,
  38. )
  39. from app.models.data_research import (
  40. DeviceAsset,
  41. DeviceAssetSourceMapping,
  42. DeviceQualityAssetScore,
  43. DeviceQualityProfile,
  44. DeviceQualityProfileVersion,
  45. DeviceQualityRuleResult,
  46. DeviceQualityRun,
  47. DeviceQualityViolationSample,
  48. IngestionSource,
  49. )
  50. app = create_app()
  51. app.config.update(TESTING=True)
  52. source_uid = str(uuid.uuid4())
  53. asset_uid = str(uuid.uuid4())
  54. mapping_uid = str(uuid.uuid4())
  55. profile_uids = []
  56. run_uids = []
  57. try:
  58. with app.app_context():
  59. db.session.add(
  60. IngestionSource(
  61. uid=source_uid,
  62. source_type="database",
  63. name="WP07 PostgreSQL 质量验收源",
  64. config={
  65. "database_type": "postgresql",
  66. "database": "acceptance",
  67. "schema": "asset",
  68. },
  69. permission_scope={},
  70. status="active",
  71. created_by="integration-test",
  72. )
  73. )
  74. db.session.add(
  75. DeviceAsset(
  76. uid=asset_uid,
  77. asset_type="device",
  78. name="WP07 验收设备",
  79. status="active",
  80. current_version=1,
  81. content_hash="a" * 64,
  82. location="动力车间",
  83. organization="设备动力部",
  84. responsible_person="张工",
  85. attributes={"model": "P-100"},
  86. created_by="integration-test",
  87. updated_by="integration-test",
  88. )
  89. )
  90. db.session.flush()
  91. db.session.add(
  92. DeviceAssetSourceMapping(
  93. uid=mapping_uid,
  94. asset_uid=asset_uid,
  95. source_uid=source_uid,
  96. source_entity="asset.equipment",
  97. asset_type="device",
  98. source_code="EQ-WP07-001",
  99. )
  100. )
  101. db.session.commit()
  102. repository = SqlAlchemyDeviceQualityRepository(db.session)
  103. quality = DeviceQualityService(
  104. repository,
  105. publish_authorizer=lambda _actor: None,
  106. commit=db.session.commit,
  107. rollback=db.session.rollback,
  108. )
  109. had_profile = repository.latest_version() is not None
  110. draft = quality.bootstrap(actor_uid="integration-test")
  111. if not had_profile:
  112. profile_uids.append(draft.profile_uid)
  113. published = (
  114. quality.publish(
  115. draft.uid,
  116. actor_uid="integration-test",
  117. )
  118. if draft.status == "draft"
  119. else draft
  120. )
  121. run = quality.run(
  122. actor_uid="integration-test",
  123. source_uid=source_uid,
  124. )
  125. run_uids.append(run.uid)
  126. second_run = quality.run(
  127. actor_uid="integration-test",
  128. source_uid=source_uid,
  129. )
  130. run_uids.append(second_run.uid)
  131. loaded, results = quality.get_run(run.uid)
  132. violations, violation_total = quality.violations(
  133. run.uid,
  134. rule_code=None,
  135. page=1,
  136. page_size=100,
  137. )
  138. scores, score_total = quality.asset_scores(
  139. run.uid,
  140. page=1,
  141. page_size=100,
  142. )
  143. recent_runs, run_total = quality.runs(
  144. page=1,
  145. page_size=100,
  146. )
  147. assert published.status == "published"
  148. assert loaded.policy_version_uid == published.uid
  149. assert loaded.policy_hash == published.content_hash
  150. assert loaded.total_assets == 1
  151. assert loaded.score == 100
  152. assert len(results) == 7
  153. assert violation_total == 0
  154. assert violations == []
  155. assert score_total == 1
  156. assert scores[0].asset_uid == asset_uid
  157. assert scores[0].score == 100
  158. assert run_total >= 2
  159. assert {run.uid, second_run.uid} <= {
  160. item.uid for item in recent_runs
  161. }
  162. snapshots, total = repository.load_assets(
  163. source_uid=source_uid,
  164. limit=5_001,
  165. )
  166. assert total == 1
  167. assert snapshots[0].uid == asset_uid
  168. assert snapshots[0].current_version == 1
  169. assert snapshots[0].mappings[0].uid == mapping_uid
  170. finally:
  171. with app.app_context():
  172. if run_uids:
  173. db.session.query(DeviceQualityViolationSample).filter(
  174. DeviceQualityViolationSample.run_uid.in_(run_uids)
  175. ).delete(synchronize_session=False)
  176. db.session.query(DeviceQualityAssetScore).filter(
  177. DeviceQualityAssetScore.run_uid.in_(run_uids)
  178. ).delete(synchronize_session=False)
  179. db.session.query(DeviceQualityRuleResult).filter(
  180. DeviceQualityRuleResult.run_uid.in_(run_uids)
  181. ).delete(synchronize_session=False)
  182. db.session.query(DeviceQualityRun).filter(
  183. DeviceQualityRun.uid.in_(run_uids)
  184. ).delete(synchronize_session=False)
  185. if profile_uids:
  186. db.session.query(DeviceQualityProfileVersion).filter(
  187. DeviceQualityProfileVersion.profile_uid.in_(profile_uids)
  188. ).delete(synchronize_session=False)
  189. db.session.query(DeviceQualityProfile).filter(
  190. DeviceQualityProfile.uid.in_(profile_uids)
  191. ).delete(synchronize_session=False)
  192. db.session.query(DeviceAssetSourceMapping).filter_by(
  193. uid=mapping_uid
  194. ).delete(synchronize_session=False)
  195. db.session.query(DeviceAsset).filter_by(
  196. uid=asset_uid
  197. ).delete(synchronize_session=False)
  198. db.session.query(IngestionSource).filter_by(
  199. uid=source_uid
  200. ).delete(synchronize_session=False)
  201. db.session.commit()