结算流程用真实下单证据修正:新卡代填 iframe vault、订单号正则、登录态判据
- 新卡代填改用真实 DOM 结构:卡号/有效期分别托管在 Rakuten PCI 代付 vault 的 跨域 iframe 里,此前按 autocomplete/name 猜的 selector 在主文档里根本找不到 元素;持卡人姓名字段改用「名義人」标签相对定位,不用 placeholder 示例文案 (TARO RAKUTEN 只是示例用户名,不是稳定标识) - 订单号正则修正 实体导致的分隔符匹配失败(2026-08-13 真实下单验证) - login_runner._is_logged_in 改用与 AuthSession 一致的 __INITIAL_STATE__ 判据, 修掉此前误判「已登录」导致保存无效 storage_state 的问题 - 新增 CheckoutBlockedError:站点风控拦截(session upgrade/3DS)转 needs_human, 不当普通失败重试 - account.yaml 支持 phone / payment.credit-card 字段 - data/evidence/(真实 PII)加入 .gitignore Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,7 @@ from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
from app.shared.errors import OrderGuardError
|
||||
from app.shared.errors import CheckoutBlockedError, OrderGuardError
|
||||
from app.shared.task_state import OrderState, TaskStatus
|
||||
from app.trading.worker import verify
|
||||
from app.trading.worker.evidence import EvidenceStore
|
||||
@@ -231,18 +231,27 @@ async def test_recovery_task_with_already_ordered_verdict_reports_succeeded(
|
||||
async def test_unimplemented_site_interaction_becomes_needs_human(
|
||||
runner: WorkerRunner, local_db: LocalDB
|
||||
):
|
||||
"""SiteInteractor 默认 enter_checkout 抛 NotImplementedError → runner 转 needs_human
|
||||
"""站点交互抛 NotImplementedError → runner 转 needs_human
|
||||
|
||||
add_to_cart 与 verify_cart 已实现(但站点未启动时会从 Playwright 调用失败,
|
||||
测试里先替换为 noop 让流程跑到 enter_checkout)。
|
||||
add_to_cart / verify_cart / enter_checkout / parse_checkout / submit_order /
|
||||
pay 现在均已实现(monitor 仍未实现),这里用桩显式模拟「某一步没实现」,
|
||||
覆盖 runner._execute_with_renewal 里 NotImplementedError → needs_human 的分支,
|
||||
与具体哪个方法真的未实现解耦。
|
||||
"""
|
||||
|
||||
# 让已实现的两步 noop,触发未实现的 enter_checkout
|
||||
async def _noop(task): # noqa: ANN001
|
||||
return None
|
||||
|
||||
async def _checkout_html(task): # noqa: ANN001
|
||||
return "<html>checkout</html>"
|
||||
|
||||
async def _unimplemented(html): # noqa: ANN001
|
||||
raise NotImplementedError("模拟:假装 parse_checkout 还没实现")
|
||||
|
||||
runner._site.add_to_cart = _noop # type: ignore[assignment]
|
||||
runner._site.verify_cart = _noop # type: ignore[assignment]
|
||||
runner._site.enter_checkout = _checkout_html # type: ignore[assignment]
|
||||
runner._site.parse_checkout = _unimplemented # type: ignore[assignment]
|
||||
|
||||
await runner.handle(_make_task(task_id="t1"))
|
||||
|
||||
@@ -252,6 +261,36 @@ async def test_unimplemented_site_interaction_becomes_needs_human(
|
||||
assert "未实现" in terminal["detail"]
|
||||
|
||||
|
||||
# ---- 结算被站点风控拦截 → needs_human(规格 §10.1)----
|
||||
|
||||
|
||||
async def test_checkout_blocked_becomes_needs_human(
|
||||
runner: WorkerRunner, local_db: LocalDB
|
||||
):
|
||||
"""enter_checkout 抛 CheckoutBlockedError(session upgrade 被拦截)→ 转 needs_human
|
||||
|
||||
与普通 AppError(转 FAILED)区分开:这类阻断是站点主动要求人工验证,
|
||||
不是本服务的失败,需要人工用有头浏览器接管当前登录态完成验证。
|
||||
"""
|
||||
|
||||
async def _noop(task): # noqa: ANN001
|
||||
return None
|
||||
|
||||
async def _blocked(task): # noqa: ANN001
|
||||
raise CheckoutBlockedError("session upgrade 提交密码后 30s 内 URL 未变化,判定被站点风控拦截")
|
||||
|
||||
runner._site.add_to_cart = _noop # type: ignore[assignment]
|
||||
runner._site.verify_cart = _noop # type: ignore[assignment]
|
||||
runner._site.enter_checkout = _blocked # type: ignore[assignment]
|
||||
|
||||
await runner.handle(_make_task(task_id="t1"))
|
||||
|
||||
gateway: FakeGateway = runner._gateway_for_test # type: ignore[attr-defined]
|
||||
terminal = gateway.last_terminal_report()
|
||||
assert terminal["terminal_status"] == TaskStatus.NEEDS_HUMAN
|
||||
assert "风控" in terminal["detail"]
|
||||
|
||||
|
||||
# ---- ラクマ 不在交易范围 → needs_human ----
|
||||
|
||||
|
||||
@@ -372,13 +411,21 @@ async def test_evidence_files_exist_before_each_report(
|
||||
|
||||
runner._gateway_for_test.report = _spy_report # type: ignore[attr-defined]
|
||||
|
||||
# 让 add_to_cart / verify_cart 正常,enter_checkout 抛 NotImplemented,
|
||||
# 触发前两步写证据 + report,到第三步转 needs_human
|
||||
# 让 add_to_cart / verify_cart / enter_checkout 正常,parse_checkout 抛
|
||||
# NotImplemented(模拟某一步没实现),触发前几步写证据 + report,最终转 needs_human
|
||||
async def _noop(task): # noqa: ANN001
|
||||
return None
|
||||
|
||||
async def _checkout_html(task): # noqa: ANN001
|
||||
return "<html>checkout</html>"
|
||||
|
||||
async def _unimplemented(html): # noqa: ANN001
|
||||
raise NotImplementedError("模拟:假装 parse_checkout 还没实现")
|
||||
|
||||
runner._site.add_to_cart = _noop # type: ignore[assignment]
|
||||
runner._site.verify_cart = _noop # type: ignore[assignment]
|
||||
runner._site.enter_checkout = _checkout_html # type: ignore[assignment]
|
||||
runner._site.parse_checkout = _unimplemented # type: ignore[assignment]
|
||||
|
||||
await runner.handle(_make_task(task_id="t1"))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user