feat(client): productionize proven sku reveal flow

This commit is contained in:
QiuSW
2026-08-05 16:55:10 +08:00
parent fec21ae857
commit 60cf05b08b
5 changed files with 895 additions and 487 deletions
+23 -13
View File
@@ -18,17 +18,19 @@ import cmbuyer_client.pdd.sku_reveal_spike as reveal_module
from cmbuyer_client.device.adb import AdbDevice, DeviceInspection
from cmbuyer_client.pdd.sku_reveal_spike import (
_annotate_reveal_failure,
_candidate_projection,
_RevealEvidenceAdapter,
safe_reveal_failure_stage,
SkuRevealSpikeCapturer,
SkuRevealSpikeError,
_require_safe_reveal_path,
)
from cmbuyer_client.pdd.sku_selection import (
SkuSelectionError,
_annotate_sku_entry_failure,
_parse_nodes,
_require_safe_reveal_path,
_revealed_unselected_projection as _candidate_projection,
)
from cmbuyer_client.pdd.sku_selection_runner import (
UiautomatorSkuPanelAdapter as _RevealEvidenceAdapter,
)
@@ -350,7 +352,7 @@ def _candidate_cross_variant_residue(variant: str, kind: str) -> str:
def _candidate_with_color_unselected() -> str:
root = ElementTree.fromstring(_candidate_unselected())
root = ElementTree.fromstring(_candidate_current_only())
target = next(
node
for node in root.iter("node")
@@ -364,7 +366,7 @@ def _candidate_with_color_unselected() -> str:
def _candidate_with_submit_risk(kind: str) -> str:
root = ElementTree.fromstring(_candidate_unselected())
root = ElementTree.fromstring(_candidate_current_only())
parents = {child: parent for parent in root.iter() for child in parent}
submit = next(
node
@@ -379,6 +381,13 @@ def _candidate_with_submit_risk(kind: str) -> str:
parent.append(ElementTree.fromstring(ElementTree.tostring(submit, encoding="unicode")))
elif kind == "moved_up":
submit.set("bounds", "[285,1900][795,1959]")
elif kind in {"extra_altered", "extra_moved_up"}:
extra = ElementTree.fromstring(ElementTree.tostring(submit, encoding="unicode"))
if kind == "extra_altered":
extra.set("text", "异常提交订单文案")
else:
extra.set("bounds", "[285,1900][795,1959]")
parent.append(extra)
else:
raise AssertionError(kind)
return ElementTree.tostring(root, encoding="unicode")
@@ -416,9 +425,9 @@ def _png() -> str:
class _FakeDevice:
def __init__(self) -> None:
self.hierarchy = "<hierarchy />"
self.empty_hierarchy = _EMPTY
self.color_hierarchy = _COLOR_ONLY
self.after_hierarchy = _candidate_unselected()
self.empty_hierarchy = _CURRENT_ONLY_EMPTY
self.color_hierarchy = _CURRENT_ONLY_COLOR
self.after_hierarchy = _candidate_current_only()
self.calls: list[tuple[object, ...]] = []
self.swipe_error = False
@@ -545,8 +554,9 @@ class SkuRevealSpikeTests(unittest.TestCase):
self.assertTrue((target / "after" / "hierarchy.xml").is_file())
def test_candidate_price_variants_reject_hybrid_duplicate_unknown_and_parent_drift(self) -> None:
# 两个真实嵌套 variant 各自唯一命中;任意混搭、额外子节点或父子关系漂移均拒绝。
_candidate_projection(_parse_nodes(_candidate_unselected()))
# 生产只放行 current-only;历史 dual 及任意混搭、额外节点、父链漂移均拒绝。
with self.assertRaises(SkuSelectionError):
_candidate_projection(_parse_nodes(_candidate_unselected()))
_candidate_projection(_parse_nodes(_candidate_current_only()))
for variant in ("dual", "current_only"):
kinds = ["hybrid", "duplicate", "unknown", "unknown_child", "parent_drift"]
@@ -683,7 +693,7 @@ class SkuRevealSpikeTests(unittest.TestCase):
self._assert_after_rejected_once(_candidate_with_color_unselected())
def test_submit_hard_reject_zone_risk_is_not_published_or_retried(self) -> None:
for kind in ("missing", "duplicate", "moved_up"):
for kind in ("missing", "duplicate", "moved_up", "extra_altered", "extra_moved_up"):
with self.subTest(kind=kind):
self._assert_after_rejected_once(_candidate_with_submit_risk(kind))
@@ -739,7 +749,7 @@ class SkuRevealSpikeTests(unittest.TestCase):
device.jsonrpc_call = drift # type: ignore[method-assign]
with TemporaryDirectory() as directory:
target = Path(directory) / "evidence"
with self.assertRaises(SkuRevealSpikeError) as raised:
with self.assertRaises((SkuRevealSpikeError, SkuSelectionError)) as raised:
self._capturer(device).capture(
"192.168.0.173:5555",
"937122477375",
@@ -850,7 +860,7 @@ class SkuRevealSpikeTests(unittest.TestCase):
"not-a-bound",
),
):
with self.subTest(), self.assertRaises(SkuRevealSpikeError):
with self.subTest(), self.assertRaises((SkuRevealSpikeError, SkuSelectionError)):
_require_safe_reveal_path(_parse_nodes(hierarchy))