test_frontend_delivery_presentation.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import asyncio
  2. import json
  3. import os
  4. from uuid import UUID, uuid4
  5. import pytest
  6. from anyio import Path
  7. from sqlalchemy import select
  8. from test_after_sales import confirm_proposal, make_proposal, scenario # noqa: F401
  9. from test_platform_management import operator, receiver # noqa: F401
  10. from zhifu.modules.after_sales.models import BusinessEvent
  11. from zhifu.modules.identity.auth import now
  12. from zhifu.modules.merchant.models import ChannelApplication
  13. from zhifu.modules.platform.delivery import enqueue, execute_one
  14. from zhifu.modules.platform.models import ChannelDelivery
  15. pytestmark = pytest.mark.integration
  16. async def test_real_delivery_details_in_browser(scenario, operator, receiver): # noqa: F811
  17. # 真实方案创建与本人确认分别产生业务事件,fixture 在浏览器检查后清理独立商家。
  18. proposal = await make_proposal(scenario)
  19. await confirm_proposal(scenario, proposal)
  20. sessions = scenario["app"].state.sessions
  21. receiver["accepted"].set()
  22. async with sessions.begin() as session:
  23. session.add(ChannelApplication(
  24. tenant_id=scenario["tenant"].bytes, store_id=scenario["store"].bytes,
  25. name="浏览器投递详情验收", callback_url=receiver["url"],
  26. credential_env=receiver["credential_env"], enabled=True, version=1,
  27. scopes=["tickets:results"], updated_at=now(),
  28. ))
  29. await enqueue(sessions, tenant_id=scenario["tenant"].bytes)
  30. async with sessions() as session:
  31. deliveries = (await session.scalars(select(ChannelDelivery).join(
  32. BusinessEvent, BusinessEvent.id == ChannelDelivery.event_id,
  33. ).where(
  34. ChannelDelivery.tenant_id == scenario["tenant"].bytes,
  35. ).order_by(BusinessEvent.aggregate_version))).all()
  36. assert len(deliveries) >= 2
  37. # 常驻投递进程可能先领取本轮事件;凭据只提供给当前接收器,必要时通过真实重试接口恢复。
  38. async with asyncio.timeout(20):
  39. for delivery in deliveries:
  40. for _ in range(3):
  41. await execute_one(sessions, delivery.id)
  42. async with sessions() as session:
  43. result = await session.get(ChannelDelivery, delivery.id)
  44. if result.status == "SENT":
  45. assert result.response_status == 204
  46. break
  47. assert result.status == "FAILED", {
  48. "status": result.status, "error": result.last_error,
  49. "response_status": result.response_status,
  50. }
  51. response = await operator["call"](
  52. "POST", f"/deliveries/{UUID(bytes=delivery.id)}/retry",
  53. {"expected_version": result.version},
  54. )
  55. assert response.status_code == 200, response.text
  56. else:
  57. raise AssertionError("本轮真实投递未在三次受理内完成")
  58. snapshot = (await operator["call"]("GET", "/state")).json()
  59. ids = {str(UUID(bytes=item.id)) for item in deliveries}
  60. visible = [item for item in snapshot["deliveries"] if item["id"] in ids]
  61. assert len(visible) == len(ids)
  62. assert {item["delivery_id"] for item in receiver["received"]} == ids
  63. folder = Path(".work/ui-presentation")
  64. await folder.mkdir(parents=True, exist_ok=True)
  65. case_path = folder / f"delivery-case-{uuid4().hex}.json"
  66. await case_path.write_text(json.dumps({
  67. "username": operator["username"], "password": operator["password"],
  68. "deliveries": visible,
  69. }, ensure_ascii=False), encoding="utf-8")
  70. try:
  71. environment = {**os.environ, "DELIVERY_CASE_FILE": str(await case_path.resolve())}
  72. process = await asyncio.create_subprocess_exec(
  73. "node", "frontend/tests/platform-delivery-presentation.mjs",
  74. env=environment, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
  75. )
  76. try:
  77. async with asyncio.timeout(60):
  78. stdout, stderr = await process.communicate()
  79. finally:
  80. if process.returncode is None:
  81. process.terminate()
  82. await process.wait()
  83. assert process.returncode == 0, stderr.decode("utf-8", errors="replace")
  84. report = json.loads(stdout.decode("utf-8"))
  85. assert report["records"] == len(ids)
  86. print(json.dumps(report, ensure_ascii=False))
  87. finally:
  88. await case_path.unlink(missing_ok=True)