test_device_asset_postgres.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. from __future__ import annotations
  2. import os
  3. import uuid
  4. from datetime import datetime
  5. import pytest
  6. pytestmark = pytest.mark.integration
  7. def test_device_assets_keep_source_identity_versions_and_search(
  8. monkeypatch,
  9. ):
  10. platform_url = os.environ.get("TEST_DATABASE_URL")
  11. if not platform_url:
  12. pytest.skip("TEST_DATABASE_URL is required")
  13. monkeypatch.setenv("DATABASE_URL", platform_url)
  14. from app import create_app, db
  15. from app.core.data_research.device_asset_repository import (
  16. SqlAlchemyDeviceAssetRepository,
  17. )
  18. from app.core.data_research.device_assets import DeviceAssetService
  19. from app.models.data_research import (
  20. DeviceAsset,
  21. DeviceAssetSourceMapping,
  22. DeviceAssetVersion,
  23. IngestionSource,
  24. )
  25. app = create_app()
  26. app.config.update(TESTING=True)
  27. source_uid = str(uuid.uuid4())
  28. asset_uids = []
  29. try:
  30. with app.app_context():
  31. db.session.add(
  32. IngestionSource(
  33. uid=source_uid,
  34. source_type="database",
  35. name="设备资产集成验收源",
  36. config={
  37. "database_type": "postgresql",
  38. "database": "acceptance",
  39. "schema": "asset",
  40. },
  41. permission_scope={},
  42. status="active",
  43. created_by="integration-test",
  44. )
  45. )
  46. db.session.commit()
  47. repository = SqlAlchemyDeviceAssetRepository(db.session)
  48. service = DeviceAssetService(
  49. repository,
  50. commit=db.session.commit,
  51. rollback=db.session.rollback,
  52. )
  53. created = service.import_records(
  54. {
  55. "source_uid": source_uid,
  56. "source_entity": "asset.equipment",
  57. "records": [
  58. {
  59. "asset_type": "device",
  60. "source_code": "EQ-PG-001",
  61. "name": "循环水泵 A",
  62. "location": "动力车间",
  63. "organization": "设备动力部",
  64. "responsible_person": "张工",
  65. "source_updated_at": (
  66. "2026-07-29T08:00:00+08:00"
  67. ),
  68. "attributes": {"model": "P-100"},
  69. },
  70. {
  71. "asset_type": "component",
  72. "source_code": "PART-PG-001",
  73. "name": "循环水泵 A 轴承",
  74. "location": "动力车间",
  75. "organization": "设备动力部",
  76. "responsible_person": "李工",
  77. "attributes": {"parent_source_code": "EQ-PG-001"},
  78. },
  79. ],
  80. },
  81. actor_uid="integration-test",
  82. )
  83. asset_uids = [item.asset.uid for item in created.items]
  84. stable_uid = created.items[0].asset.uid
  85. unchanged = service.import_records(
  86. {
  87. "source_uid": source_uid,
  88. "source_entity": "asset.equipment",
  89. "records": [
  90. {
  91. "asset_type": "device",
  92. "source_code": "EQ-PG-001",
  93. "name": "循环水泵 A",
  94. "location": "动力车间",
  95. "organization": "设备动力部",
  96. "responsible_person": "张工",
  97. "source_updated_at": (
  98. "2026-07-29T09:00:00+08:00"
  99. ),
  100. "attributes": {"model": "P-100"},
  101. }
  102. ],
  103. },
  104. actor_uid="integration-test",
  105. )
  106. updated = service.import_records(
  107. {
  108. "source_uid": source_uid,
  109. "source_entity": "asset.equipment",
  110. "records": [
  111. {
  112. "asset_type": "device",
  113. "source_code": "EQ-PG-001",
  114. "name": "循环水泵 A",
  115. "location": "二号动力车间",
  116. "organization": "设备动力部",
  117. "responsible_person": "张工",
  118. "source_updated_at": (
  119. "2026-07-29T10:00:00+08:00"
  120. ),
  121. "attributes": {"model": "P-100"},
  122. }
  123. ],
  124. },
  125. actor_uid="integration-test-2",
  126. )
  127. assert unchanged.unchanged_count == 1
  128. assert updated.updated_count == 1
  129. assert updated.items[0].asset.uid == stable_uid
  130. assert updated.items[0].asset.current_version == 2
  131. for keyword in (
  132. "循环水泵",
  133. "EQ-PG-001",
  134. "二号动力车间",
  135. "张工",
  136. ):
  137. rows, total = repository.search(
  138. {"keyword": keyword},
  139. page=1,
  140. page_size=20,
  141. )
  142. assert total >= 1
  143. assert stable_uid in {row.uid for row in rows}
  144. filtered, total = repository.search(
  145. {
  146. "asset_type": "device",
  147. "status": "active",
  148. "source_uid": source_uid,
  149. },
  150. page=1,
  151. page_size=20,
  152. )
  153. assert total == 1
  154. assert filtered[0].uid == stable_uid
  155. detail = service.get(stable_uid)
  156. versions = service.versions(stable_uid)
  157. assert detail.asset.current_version == 2
  158. assert len(detail.mappings) == 1
  159. assert detail.mappings[0].source_code == "EQ-PG-001"
  160. assert detail.mappings[0].source_updated_at == (
  161. datetime.fromisoformat("2026-07-29T10:00:00+08:00")
  162. )
  163. assert [item.version for item in versions] == [2, 1]
  164. assert versions[0].snapshot["location"] == "二号动力车间"
  165. assert versions[1].snapshot["location"] == "动力车间"
  166. finally:
  167. with app.app_context():
  168. if asset_uids:
  169. db.session.query(DeviceAssetVersion).filter(
  170. DeviceAssetVersion.asset_uid.in_(asset_uids)
  171. ).delete(synchronize_session=False)
  172. db.session.query(DeviceAssetSourceMapping).filter(
  173. DeviceAssetSourceMapping.asset_uid.in_(asset_uids)
  174. ).delete(synchronize_session=False)
  175. db.session.query(DeviceAsset).filter(
  176. DeviceAsset.uid.in_(asset_uids)
  177. ).delete(synchronize_session=False)
  178. db.session.query(IngestionSource).filter_by(
  179. uid=source_uid
  180. ).delete(synchronize_session=False)
  181. db.session.commit()