fix(chv-muse-r12fix-4): detect_checkout 接进 CDP broker(P10)
R12 真机:detect_checkout 在 chv-guest/、host.py、 executor.py 里零引用,结账页无任何拦截。按 P10 gap §4.1 接线:broker 加载 cloud/chv/checkout_detect.py(同目录 优先,仓库路径兜底;缺失时降级关闭检测、写警告,绝不 crash-loop);snapshot() 每次重算 verdict(aria 行拼伪节点, 与 _form_risk 同形),open() 导航前后两道门——落点是结账 页时返回 form_submit ask 并暂停,上一页是结账页时下一次 导航先审批;ask 批过后同 URL 不再重复 ask(批准即放行, 与 form_risk 同口径);verdict 进 snapshot()/status() 回包。 build.sh:checkout_detect.py 条件拷入 /usr/local/bin/(0644, P3/P6 同款只警告口径,M4 不硬要求 cloud/), extra_contents 与两处 lockfile 登记补齐。钱包三腿不在本轮 范围,未动。
This commit is contained in:
1 parent
1da0d86159
commit
0ebcdfb2a7
7 files changed
+258
-8
No files matched your search
@@ -6,7 +6,10 @@
|
||||
- 中英文特征;节点为空;页面金额只作展示信号。
|
||||
"""
|
||||
|
||||
import importlib.util
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
@@ -129,5 +132,43 @@ class CheckoutNodesTest(unittest.TestCase):
|
||||
self.assertIsNone(verdict.amount_text)
|
||||
|
||||
|
||||
class BuildInfoContentsTest(unittest.TestCase):
|
||||
"""round-12 修复轮(项 4,P10):checkout_detect 是条件拷入
|
||||
件——存在时经 extra_contents 登记进 BUILDINFO contents
|
||||
(不在 BASE_CONTENTS 里,缺失的构建绝不谎称已进镜像,
|
||||
与 P3 叶子模块同款口径)。"""
|
||||
|
||||
def test_extra_contents_include_checkout_detect(self):
|
||||
root = Path(__file__).resolve().parents[2]
|
||||
path = root / "chv-guest" / "buildinfo.py"
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"chv_guest_p10_buildinfo", str(path))
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
self.assertNotIn("checkout_detect",
|
||||
module.BASE_CONTENTS)
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
out = os.path.join(tmp, "out")
|
||||
os.makedirs(out)
|
||||
for name, content in (("disk.raw", b"D"),
|
||||
("vmlinux", b"K")):
|
||||
with open(os.path.join(out, name), "wb") as fh:
|
||||
fh.write(content)
|
||||
for name, lines in (
|
||||
("guest-versions-dpkg.txt", ["git 9.9.9-9ubuntu9"]),
|
||||
("guest-versions-pip.txt", ["playwright==9.9.9"]),
|
||||
("guest-versions-gt.txt", ["gt version 1.0.0"]),
|
||||
("guest-versions-chromium.txt",
|
||||
["chromium_headless_shell-1248"])):
|
||||
with open(os.path.join(out, name), "w") as fh:
|
||||
fh.write("\n".join(lines) + "\n")
|
||||
info = module.build(
|
||||
out, "chv-guest-24.04-0.164.0-alpha.9-abc1234", "24.04",
|
||||
"0.164.0-alpha.9", "abc1234", "0" * 64, True, "1248",
|
||||
["cloud/chv/checkout_detect.py"])
|
||||
self.assertIn("cloud/chv/checkout_detect.py",
|
||||
info["contents"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -645,6 +645,12 @@ class BuildDryRunTest(unittest.TestCase):
|
||||
dest = os.path.join(repo, rel)
|
||||
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
||||
shutil.copy2(ROOT / rel, dest)
|
||||
# round-12 修复轮(项 4,P10):checkout_detect 是 cloud/
|
||||
# 下的条件拷入件,夹具须一并拷入才走拷入分支。
|
||||
rel = "cloud/chv/checkout_detect.py"
|
||||
dest = os.path.join(repo, rel)
|
||||
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
||||
shutil.copy2(ROOT / rel, dest)
|
||||
if with_vsock:
|
||||
rel = "cloud/chv/guest/vsock_notify.py"
|
||||
dest = os.path.join(repo, rel)
|
||||
@@ -709,7 +715,10 @@ class BuildDryRunTest(unittest.TestCase):
|
||||
# 三件进镜像,干跑输出须含拷入与 enable。
|
||||
"chv-gitea-worker",
|
||||
"chv-gitea-worker.service",
|
||||
"systemctl enable chv-gitea-worker"):
|
||||
"systemctl enable chv-gitea-worker",
|
||||
# round-12 修复轮(项 4,P10):结账检测
|
||||
# 模块进镜像,干跑输出须含条件拷入行。
|
||||
"checkout_detect"):
|
||||
self.assertIn(needle, out, needle)
|
||||
# P1 文件不在本分支:干跑只警告、不失败。
|
||||
self.assertIn("vsock_notify.py 不在仓库里", out)
|
||||
|
||||
@@ -462,6 +462,58 @@ class CdpBrokerTest(unittest.TestCase):
|
||||
third = broker.open("https://example.com/other")
|
||||
self.assertTrue(third["ok"], third)
|
||||
|
||||
def test_checkout_open_returns_ask_and_pauses(self):
|
||||
# round-12 修复轮(项 4,P10):落点是结账页时,open
|
||||
# 导航返回 ask 并暂停(P10 gap §4.1)。
|
||||
aria = ("- generic [ref=e1]:\n"
|
||||
" - heading \"Checkout\" [ref=e2]\n"
|
||||
" - button \"Pay now\" [ref=e3]\n"
|
||||
" - text \"Total: $49.99\" [ref=e4]")
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
broker, page, _f = _make_broker(tmp, aria=aria)
|
||||
first = broker.open("https://shop.example.com/checkout")
|
||||
self.assertFalse(first["ok"])
|
||||
self.assertTrue(first.get("ask"))
|
||||
self.assertIn("ask_id", first)
|
||||
self.assertEqual(len(page.goto_calls), 1)
|
||||
self.assertTrue(broker.status()["paused"])
|
||||
asks = broker.list_asks()["asks"]
|
||||
self.assertEqual(len(asks), 1)
|
||||
self.assertEqual(asks[0]["kind"], "form_submit")
|
||||
self.assertIn("结账页命中", asks[0]["detail"])
|
||||
|
||||
def test_checkout_allowed_then_open_ok(self):
|
||||
# 批过 ask 后同 URL 再 open 不再重复 ask(与 form_risk
|
||||
#“批准即放行”同口径);但随后导航到别的 URL 时,上一页
|
||||
# 仍是结账页,导航前门再次拦下。
|
||||
aria = ("- generic [ref=e1]:\n"
|
||||
" - heading \"Checkout\" [ref=e2]\n"
|
||||
" - button \"Pay now\" [ref=e3]\n"
|
||||
" - text \"Total: $49.99\" [ref=e4]")
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
broker, page, _f = _make_broker(tmp, aria=aria)
|
||||
first = broker.open("https://shop.example.com/checkout")
|
||||
self.assertTrue(first.get("ask"))
|
||||
asks = broker.list_asks()["asks"]
|
||||
ok = broker.resolve_ask(asks[0]["ask_id"], "allowed")
|
||||
self.assertTrue(ok["ok"])
|
||||
second = broker.open("https://shop.example.com/checkout")
|
||||
self.assertTrue(second["ok"], second)
|
||||
self.assertTrue(second["checkout"]["is_checkout"])
|
||||
self.assertEqual(second["checkout"]["confidence"], "high")
|
||||
self.assertFalse(broker.status()["paused"])
|
||||
third = broker.open("https://example.com/other")
|
||||
self.assertFalse(third["ok"])
|
||||
self.assertTrue(third.get("ask"))
|
||||
self.assertEqual(len(page.goto_calls), 2)
|
||||
|
||||
def test_non_checkout_open_ok(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
broker, _page, _f = _make_broker(tmp)
|
||||
resp = broker.open("https://example.com")
|
||||
self.assertTrue(resp["ok"], resp)
|
||||
self.assertFalse(resp["checkout"]["is_checkout"])
|
||||
|
||||
def test_post_navigation_request_auto_pauses(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
broker, page, _f = _make_broker(tmp)
|
||||
@@ -820,6 +872,12 @@ class BuildDryRunTest(unittest.TestCase):
|
||||
dest = os.path.join(repo, rel)
|
||||
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
||||
shutil.copy2(ROOT / rel, dest)
|
||||
# round-12 修复轮(项 4,P10):checkout_detect 是
|
||||
# cloud/ 下的条件拷入件,夹具须一并拷入才走拷入分支。
|
||||
rel = "cloud/chv/checkout_detect.py"
|
||||
dest = os.path.join(repo, rel)
|
||||
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
||||
shutil.copy2(ROOT / rel, dest)
|
||||
codex = os.path.join(bindir, "codex")
|
||||
with open(codex, "w") as fh:
|
||||
fh.write("#!/bin/sh\necho 'codex-cli 0.164.0-alpha.9'\n")
|
||||
@@ -862,7 +920,10 @@ class BuildDryRunTest(unittest.TestCase):
|
||||
# 三件进镜像,干跑输出须含拷入与 enable。
|
||||
"chv-gitea-worker",
|
||||
"chv-gitea-worker.service",
|
||||
"systemctl enable chv-gitea-worker"):
|
||||
"systemctl enable chv-gitea-worker",
|
||||
# round-12 修复轮(项 4,P10):结账检测
|
||||
# 模块进镜像,干跑输出须含条件拷入行。
|
||||
"checkout_detect"):
|
||||
self.assertIn(needle, out, needle)
|
||||
# P1 文件不在本分支:干跑只警告、不失败。
|
||||
self.assertIn("vsock_notify.py 不在仓库里", out)
|
||||
|
||||
Reference in new issue
Block a user