test_phase3_wp06_provision_outbox_postgres.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. """Real PostgreSQL coverage for the provision outbox lease/fencing contract."""
  2. from __future__ import annotations
  3. import os
  4. import uuid
  5. from concurrent.futures import ThreadPoolExecutor
  6. import pytest
  7. from sqlalchemy import create_engine, text
  8. from app.core.system.trusted_delivery_repository import (
  9. SqlAlchemyTrustedDeliveryRepository,
  10. )
  11. pytestmark = pytest.mark.integration
  12. def _url():
  13. value = os.environ.get("TEST_DATABASE_URL")
  14. if not value:
  15. pytest.skip("TEST_DATABASE_URL is required")
  16. return value
  17. def _runtime_url():
  18. value = os.environ.get("TEST_RUNTIME_DATABASE_URL")
  19. if not value:
  20. pytest.skip("TEST_RUNTIME_DATABASE_URL is required")
  21. return value
  22. def _fixture(engine):
  23. actor, policy, grant, asset = (str(uuid.uuid4()) for _ in range(4))
  24. prefix = f"wp06-test-provision-{actor[:8]}"
  25. with engine.begin() as connection:
  26. connection.execute(text("""INSERT INTO public.users(id,username,display_name,password_hash,status)
  27. VALUES(CAST(:id AS uuid),:name,:name,'test','active')"""), {"id": actor, "name": prefix})
  28. repository = SqlAlchemyTrustedDeliveryRepository(connection)
  29. repository.create_policy_version({
  30. "uid": policy, "code": f"WP06_PROVISION_{actor[:8]}", "version": "1",
  31. "status": "active", "selector": {}, "resource_rules": {},
  32. "created_by": actor, "current_version": 1,
  33. })
  34. repository.enqueue_grant({
  35. "uid": grant, "policy_uid": policy, "subject_uid": actor,
  36. "asset_uid": asset, "provider": "database",
  37. "idempotency_key": f"{prefix}-grant", "request_digest": "a" * 64,
  38. "expires_at": "2099-01-01T00:00:00+00:00", "status": "active",
  39. "reason_code": "approval_bound", "current_version": 1,
  40. "created_by": actor,
  41. })
  42. return actor, policy, grant, prefix
  43. def _cleanup(engine, actor, policy, grant):
  44. with engine.begin() as connection:
  45. connection.execute(text("ALTER TABLE public.trusted_delivery_receipts DISABLE TRIGGER USER"))
  46. connection.execute(text("DELETE FROM public.trusted_delivery_receipts WHERE grant_uid=CAST(:uid AS uuid)"), {"uid": grant})
  47. connection.execute(text("ALTER TABLE public.trusted_delivery_receipts ENABLE TRIGGER USER"))
  48. connection.execute(text("DELETE FROM public.trusted_delivery_deliveries WHERE grant_uid=CAST(:uid AS uuid)"), {"uid": grant})
  49. connection.execute(text("DELETE FROM public.trusted_delivery_policy_transitions WHERE policy_uid=CAST(:uid AS uuid)"), {"uid": policy})
  50. connection.execute(text("DELETE FROM public.trusted_delivery_grants WHERE uid=CAST(:uid AS uuid)"), {"uid": grant})
  51. connection.execute(text("DELETE FROM public.trusted_delivery_policy_versions WHERE uid=CAST(:uid AS uuid)"), {"uid": policy})
  52. connection.execute(text("DELETE FROM public.users WHERE id=CAST(:uid AS uuid)"), {"uid": actor})
  53. def test_provision_outbox_commits_before_lease_and_fences_retrying_workers():
  54. """A provider worker only runs while holding the DB-time current lease."""
  55. engine = create_engine(_url(), pool_pre_ping=True)
  56. actor = policy = grant = None
  57. digest = "b" * 64
  58. try:
  59. actor, policy, grant, prefix = _fixture(engine)
  60. def prepare(key):
  61. with engine.connect() as connection:
  62. repository = SqlAlchemyTrustedDeliveryRepository(connection)
  63. return repository.prepare_provision_operation(grant, key, digest)
  64. # Two different transport keys describe the same durable grant. They
  65. # must converge on one outbox record before either worker can call out.
  66. with ThreadPoolExecutor(max_workers=2) as pool:
  67. first, second = list(pool.map(prepare, (f"{prefix}-one", f"{prefix}-two")))
  68. assert first["delivery_uid"] == second["delivery_uid"]
  69. with engine.connect() as observer:
  70. assert observer.execute(text("SELECT status FROM public.trusted_delivery_deliveries WHERE uid=CAST(:uid AS uuid)"), {"uid": first["delivery_uid"]}).scalar_one() == "pending"
  71. with engine.begin() as connection:
  72. repository = SqlAlchemyTrustedDeliveryRepository(connection)
  73. claimed_a = repository.claim_provision_operation(first["delivery_uid"], "worker-a")
  74. assert claimed_a["state"] == "claimed"
  75. assert repository.claim_provision_operation(first["delivery_uid"], "worker-b")["state"] == "leased"
  76. # A failed adapter is made pending while its live lease is still
  77. # fenced, instead of leaving a permanently processing record.
  78. repository.fail_provision_operation(first["delivery_uid"], "worker-a", claimed_a["lease_fence"])
  79. with engine.begin() as connection:
  80. repository = SqlAlchemyTrustedDeliveryRepository(connection)
  81. claimed_b = repository.claim_provision_operation(first["delivery_uid"], "worker-b")
  82. assert claimed_b["state"] == "claimed"
  83. assert claimed_b["lease_fence"] > claimed_a["lease_fence"]
  84. with pytest.raises(RuntimeError, match="lease conflict"):
  85. repository.complete_provision_operation(
  86. grant, first["delivery_uid"], "worker-a", claimed_a["lease_fence"],
  87. {"receipt_code": "LATE", "response_digest": "c" * 64, "diff_digest": "d" * 64},
  88. )
  89. result = repository.complete_provision_operation(
  90. grant, first["delivery_uid"], "worker-b", claimed_b["lease_fence"],
  91. {"receipt_code": "APPLIED", "response_digest": "c" * 64, "diff_digest": "d" * 64},
  92. )
  93. assert result["status"] == "applied"
  94. assert connection.execute(text("SELECT count(*) FROM public.trusted_delivery_receipts WHERE delivery_uid=CAST(:uid AS uuid)"), {"uid": first["delivery_uid"]}).scalar_one() == 1
  95. grant_state = connection.execute(text("SELECT status,reason_code,current_version FROM public.trusted_delivery_grants WHERE uid=CAST(:uid AS uuid)"), {"uid": grant}).one()
  96. assert grant_state == ("active", "provision_applied", 2)
  97. finally:
  98. if actor:
  99. _cleanup(engine, actor, policy, grant)
  100. engine.dispose()
  101. def test_runtime_provision_gateway_fences_late_worker_and_is_visible_cross_connection(wp06_head):
  102. """The low-privilege runtime identity gets no direct DML escape hatch."""
  103. engine = create_engine(_url(), pool_pre_ping=True)
  104. runtime_engine = create_engine(_runtime_url(), pool_pre_ping=True)
  105. actor = policy = grant = None
  106. try:
  107. actor, policy, grant, prefix = _fixture(engine)
  108. with engine.begin() as connection:
  109. repository = SqlAlchemyTrustedDeliveryRepository(connection)
  110. operation = repository.prepare_provision_operation(grant, f"{prefix}-runtime", "e" * 64)
  111. with runtime_engine.begin() as connection:
  112. claimed_a = connection.execute(text("""SELECT public.trusted_delivery_runtime_write(
  113. 'claim_provision', CAST(:payload AS jsonb))"""), {
  114. "payload": '{"uid":"' + operation["delivery_uid"] + '","worker":"runtime-a"}',
  115. }).scalar_one()
  116. assert claimed_a["state"] == "claimed"
  117. assert connection.execute(text("""SELECT public.trusted_delivery_runtime_write(
  118. 'claim_provision', CAST(:payload AS jsonb))"""), {
  119. "payload": '{"uid":"' + operation["delivery_uid"] + '","worker":"runtime-b"}',
  120. }).scalar_one()["state"] == "leased"
  121. with engine.begin() as connection:
  122. connection.execute(text("""UPDATE public.trusted_delivery_deliveries
  123. SET lease_expires_at=clock_timestamp()-interval '1 second'
  124. WHERE uid=CAST(:uid AS uuid)"""), {"uid": operation["delivery_uid"]})
  125. with runtime_engine.begin() as connection:
  126. claimed_b = connection.execute(text("""SELECT public.trusted_delivery_runtime_write(
  127. 'claim_provision', CAST(:payload AS jsonb))"""), {
  128. "payload": '{"uid":"' + operation["delivery_uid"] + '","worker":"runtime-b"}',
  129. }).scalar_one()
  130. assert claimed_b["state"] == "claimed"
  131. with pytest.raises(Exception, match="lease conflict"), connection.begin_nested():
  132. connection.execute(text("""SELECT public.trusted_delivery_runtime_write(
  133. 'complete_provision', CAST(:payload AS jsonb))"""), {
  134. "payload": '{"grant_uid":"' + grant + '","uid":"' + operation["delivery_uid"] + '","worker":"runtime-a","fence":' + str(claimed_a["lease_fence"]) + ',"receipt_uid":"' + str(uuid.uuid4()) + '","receipt_code":"LATE","response_digest":"' + "f" * 64 + '","diff_digest":"' + "a" * 64 + '"}',
  135. })
  136. connection.execute(text("""SELECT public.trusted_delivery_runtime_write(
  137. 'complete_provision', CAST(:payload AS jsonb))"""), {
  138. "payload": '{"grant_uid":"' + grant + '","uid":"' + operation["delivery_uid"] + '","worker":"runtime-b","fence":' + str(claimed_b["lease_fence"]) + ',"receipt_uid":"' + str(uuid.uuid4()) + '","receipt_code":"APPLIED","response_digest":"' + "f" * 64 + '","diff_digest":"' + "a" * 64 + '"}',
  139. }).scalar_one()
  140. with engine.connect() as observer:
  141. assert observer.execute(text("SELECT status FROM public.trusted_delivery_deliveries WHERE uid=CAST(:uid AS uuid)"), {"uid": operation["delivery_uid"]}).scalar_one() == "applied"
  142. assert observer.execute(text("SELECT reason_code FROM public.trusted_delivery_grants WHERE uid=CAST(:uid AS uuid)"), {"uid": grant}).scalar_one() == "provision_applied"
  143. finally:
  144. if actor:
  145. _cleanup(engine, actor, policy, grant)
  146. runtime_engine.dispose()
  147. engine.dispose()