Files
codex-app-server/cloud/tests/test_chv_muse_p10_checkout.py
T
host b55b49b358 feat(chv): 对齐 Muse P10 支付安全——凭证隔离 + 结账检测 + 每次购买人工审批 + 一次性卡号
- 支付凭证隔离:PaymentCredentialVault(只活内存、白名单
  stripe-link/shop-pay、只存不透明 id + 掩码标签、原始卡号
  PAN 拒绝入库、按次释放绑定商家/金额/货币、单次 + TTL)
- 结账页检测:detect_checkout(URL + 无障碍树信号,确定性
  规则、不读原始 DOM、与 P4 detect_form_risk 同输入形状)
- 每次购买人工审批:PaymentGate 经 P5 对话外通道发审批,
  payload 含确切购买详情;完成时先验签、再验详情摘要
  (篡改即拒且审批保持待处理)、最后单次消费答复
- 一次性卡号:OneTimeCard 绑定商家/金额/货币/有效期,单次
  兑现;SimulatedWalletBackend 沙箱钱包(假 PAN 只在后端
  私有内存、charge 只记账不联网、取消即 void)
- 限额:SpendLimits 三档 minor units 比对,限额不可用时
  不编造上限(limits_checked=False)
- 审计:PaymentAudit 只留允许字段,秘密字段名丢弃 + redacted
- 审批通道最小扩展:ApprovalRequest 增加可选 details 字段,
  不传时 payload 与 P5 完全一致
- 真实支付与扣款不做:全部沙箱验证,真实钱包集成待用户
  另行批准;新测试 79 项,全量 cloud 套件 2037 OK

round-12 集成说明:本提交为 acf98f8 的 cherry-pick(只含 P10
自有 diff,基线 25bcd35 已含的 P3/P5 内容未重复合入;
approval_channel.py 仅加可选 details 字段)。
2026-11-07 00:09:55 +00:00

134 lines
5.4 KiB
Python

"""对齐 Muse P10 结账检测测试:detect_checkout 纯函数。
只用标准库,不启动浏览器、不联网。覆盖:
- URL 信号(结账路径 / 购物车 / 普通页 / 坏 URL);
- 无障碍树信号(支付字段 / 密码字段 / 下单按钮 / 金额文本);
- 中英文特征;节点为空;页面金额只作展示信号。
"""
import sys
import unittest
from pathlib import Path
try:
from cloud.chv.checkout_detect import detect_checkout
except ImportError:
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from cloud.chv.checkout_detect import detect_checkout
def _node(role: str, name: str) -> dict:
return {"role": role, "name": name}
class CheckoutUrlTest(unittest.TestCase):
def test_checkout_path_is_checkout_high(self) -> None:
verdict = detect_checkout("https://shop.example.com/checkout")
self.assertTrue(verdict.is_checkout)
self.assertEqual(verdict.confidence, "medium")
self.assertIn("checkout_url", verdict.reasons)
def test_payment_path_is_checkout(self) -> None:
verdict = detect_checkout(
"https://shop.example.com/order/payment?step=2")
self.assertTrue(verdict.is_checkout)
self.assertIn("checkout_url", verdict.reasons)
def test_cart_path_is_not_checkout(self) -> None:
verdict = detect_checkout("https://shop.example.com/cart")
self.assertFalse(verdict.is_checkout)
self.assertEqual(verdict.confidence, "low")
self.assertIn("cart_url", verdict.reasons)
def test_plain_page_is_not_checkout(self) -> None:
verdict = detect_checkout("https://shop.example.com/products/1")
self.assertFalse(verdict.is_checkout)
self.assertEqual(verdict.confidence, "low")
self.assertEqual(verdict.reasons, [])
def test_bad_url_does_not_crash(self) -> None:
verdict = detect_checkout("::::not a url::::")
self.assertFalse(verdict.is_checkout)
def test_no_nodes_url_only_decision(self) -> None:
verdict = detect_checkout(
"https://shop.example.com/checkout", None)
self.assertTrue(verdict.is_checkout)
class CheckoutNodesTest(unittest.TestCase):
def test_payment_fields_make_checkout_high(self) -> None:
nodes = [_node("textbox", "Card number"),
_node("textbox", "Expiry date"),
_node("textbox", "Security code (CVC)")]
verdict = detect_checkout("https://shop.example.com/buy",
nodes)
self.assertTrue(verdict.is_checkout)
self.assertEqual(verdict.confidence, "high")
self.assertEqual(verdict.form_risk, "payment")
self.assertIn("payment_fields", verdict.reasons)
def test_password_field_marks_form_risk(self) -> None:
nodes = [_node("textbox", "Password")]
verdict = detect_checkout("https://shop.example.com/login",
nodes)
self.assertFalse(verdict.is_checkout)
self.assertEqual(verdict.form_risk, "password")
def test_order_button_plus_amount_is_checkout(self) -> None:
nodes = [_node("button", "Place order"),
_node("text", "Total: $50.00")]
verdict = detect_checkout("https://shop.example.com/review",
nodes)
self.assertTrue(verdict.is_checkout)
self.assertEqual(verdict.confidence, "medium")
self.assertEqual(verdict.amount_text, "$50.00")
def test_order_button_alone_is_not_checkout(self) -> None:
nodes = [_node("button", "Place order")]
verdict = detect_checkout("https://shop.example.com/review",
nodes)
self.assertFalse(verdict.is_checkout)
def test_amount_only_is_not_checkout(self) -> None:
nodes = [_node("text", "Total: $50.00")]
verdict = detect_checkout("https://shop.example.com/review",
nodes)
self.assertFalse(verdict.is_checkout)
self.assertEqual(verdict.amount_text, "$50.00")
def test_chinese_payment_hints(self) -> None:
nodes = [_node("textbox", "卡号"),
_node("button", "立即支付")]
verdict = detect_checkout("https://shop.example.com/buy",
nodes)
self.assertTrue(verdict.is_checkout)
self.assertEqual(verdict.form_risk, "payment")
def test_checkout_url_plus_payment_fields_high(self) -> None:
nodes = [_node("textbox", "Card number"),
_node("button", "Pay now")]
verdict = detect_checkout("https://shop.example.com/checkout",
nodes)
self.assertTrue(verdict.is_checkout)
self.assertEqual(verdict.confidence, "high")
self.assertIn("checkout_url", verdict.reasons)
self.assertIn("payment_fields", verdict.reasons)
def test_yen_and_currency_code_amounts(self) -> None:
nodes = [_node("text", "合計:¥1,200"),
_node("text", "Total 25.00 USD")]
verdict = detect_checkout("https://shop.example.com/review",
nodes)
self.assertEqual(verdict.amount_text, "¥1,200")
def test_empty_nodes_plain_page(self) -> None:
verdict = detect_checkout("https://shop.example.com/", [])
self.assertFalse(verdict.is_checkout)
self.assertIsNone(verdict.form_risk)
self.assertIsNone(verdict.amount_text)
if __name__ == "__main__":
unittest.main()