test_agent_order_eligibility.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import json
  2. from datetime import UTC, datetime, timedelta
  3. import pytest
  4. from sqlalchemy import func, select
  5. from test_after_sales import scenario # noqa: F401
  6. from test_agent_runtime import tool_run
  7. from test_business_boundaries import order
  8. from test_fulfillment import connected # noqa: F401
  9. from zhifu.modules.after_sales.models import Ticket
  10. from zhifu.modules.agent_runtime.models import AgentRun
  11. from zhifu.modules.agent_runtime.runtime import build_tool_graph
  12. from zhifu.modules.agent_runtime.worker import claim
  13. async def lookup(context, proposal, quantity=1):
  14. run = await tool_run(context, "get_order", {
  15. "order_id": proposal["order_id"], "quantity": quantity})
  16. sessions = context["app"].state.sessions
  17. run_id, token = await claim(sessions, 2, run_ids=[run.id])
  18. await build_tool_graph(sessions).ainvoke({"run_id": run_id, "token": token})
  19. async with sessions() as session:
  20. current = await session.get(AgentRun, run_id)
  21. result = json.loads(current.messages[-1]["data"]["content"])
  22. assert await session.scalar(select(func.count()).select_from(Ticket).where(
  23. Ticket.tenant_id == context["tenant"].bytes)) == 0
  24. return result
  25. @pytest.mark.parametrize("condition,code", [
  26. ("valid", None), ("expired", "RULE_NOT_SATISFIED"),
  27. ("unsigned", "RULE_NOT_SATISFIED"), ("quantity", "QUANTITY_CONFLICT"),
  28. ("product", "RULE_NOT_SATISFIED"),
  29. ])
  30. async def test_order_tool_uses_real_current_rule_and_business_eligibility(
  31. connected, condition, code, # noqa: F811
  32. ):
  33. changes = {"signed_at": (datetime.now(UTC) - timedelta(days=3)).isoformat()}
  34. if condition == "expired":
  35. changes["signed_at"] = (datetime.now(UTC) - timedelta(days=8)).isoformat()
  36. if condition == "unsigned":
  37. changes["signed_at"] = None
  38. if condition == "product":
  39. changes["product_type"] = "excluded-product"
  40. proposal = await order(connected, **changes)
  41. result = await lookup(connected, proposal, 2 if condition == "quantity" else 1)
  42. assert result["product_type"] == changes.get("product_type", "keyboard")
  43. assert (result["signed_at"] is None) == (condition == "unsigned")
  44. assert result["exchange_rule"]["version"] == 1
  45. assert result["exchange_rule"]["window_days"] == 7
  46. assert result["exchange_rule"]["citation"]
  47. eligibility = result["exchange_eligibility"]
  48. assert eligibility["eligible"] == (code is None)
  49. assert eligibility["code"] == code
  50. assert eligibility["evaluated_at"].endswith("Z")
  51. if code:
  52. assert result["exchange_rule"]["citation"] in eligibility["message"]
  53. response = await connected["call"]("POST", "/openapi/v1/after-sales/proposals",
  54. payload={**proposal, "quantity": eligibility["quantity"]})
  55. assert response.status_code == eligibility["status"]
  56. assert response.json()["code"] == code
  57. async def test_order_tool_reads_new_rule_version_and_creation_rechecks_revocation(connected): # noqa: F811
  58. proposal = await order(connected,
  59. signed_at=(datetime.now(UTC) - timedelta(days=10)).isoformat())
  60. assert (await lookup(connected, proposal))["exchange_eligibility"]["eligible"] is False
  61. path = f"/admin/v1/stores/{connected['store']}/exchange-rules"
  62. response = await connected["call"]("POST", path, "agent", {
  63. "expected_version": 1, "window_days": 15, "product_types": ["keyboard"],
  64. "citation": "当前十五天内换货规则",
  65. })
  66. assert response.status_code == 201, response.text
  67. current = await lookup(connected, proposal)
  68. assert current["exchange_eligibility"]["eligible"] is True
  69. assert current["exchange_rule"]["version"] == 2
  70. # 查询成功后撤销规则,创建事务必须重新检查当前规则有效性。
  71. response = await connected["call"]("POST", path + "/revoke", "agent", {
  72. "expected_version": 2, "reason": "核对提交时规则有效性"})
  73. assert response.status_code == 200, response.text
  74. response = await connected["call"](
  75. "POST", "/openapi/v1/after-sales/proposals", payload=proposal)
  76. assert response.status_code == 422 and response.json()["code"] == "RULE_NOT_SATISFIED"