feat: 完整采集采购尺码候选 (#256)
This commit is contained in:
@@ -1,12 +1,17 @@
|
||||
"""uiautomator2 采购演练 Adapter 测试;不连接真实手机。"""
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import unittest
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from src.pdd_device_service import PddDeviceService
|
||||
from src.pdd_purchase_adapter import PddPurchaseError
|
||||
from src.pdd_collect_service import DimensionValue, PddCollectError, SpecDimension
|
||||
from src.pdd_purchase_adapter import (
|
||||
PddPurchaseError,
|
||||
PddPurchaseSpecResolutionRequired,
|
||||
)
|
||||
from src.performance_timing import TaskPerformanceTrace
|
||||
from src.pdd_u2_purchase_adapter import (
|
||||
U2PddLivePurchaseAdapter,
|
||||
@@ -922,6 +927,150 @@ class U2PddPurchaseAdapterTest(unittest.TestCase):
|
||||
self.assertEqual(device.editor_values, [])
|
||||
self.assertEqual(device.app_wait_calls, 0)
|
||||
|
||||
def test_missing_size_returns_complete_structured_candidate_snapshot(self):
|
||||
device = FakeDevice()
|
||||
collected = []
|
||||
|
||||
def collect_candidates(current_device):
|
||||
collected.append(current_device)
|
||||
return SpecDimension(
|
||||
"size",
|
||||
"尺码",
|
||||
(
|
||||
DimensionValue("M", True),
|
||||
DimensionValue("L(售罄)", False),
|
||||
DimensionValue("XL", True),
|
||||
),
|
||||
)
|
||||
|
||||
adapter = U2PddPurchaseAdapter(
|
||||
"USB-001",
|
||||
device_service=PddDeviceService(connector=lambda _serial: device),
|
||||
sleeper=lambda _seconds: None,
|
||||
select_color_fn=lambda *_args, **_kwargs: True,
|
||||
select_size_fn=lambda *_args, **_kwargs: False,
|
||||
size_candidate_collector=collect_candidates,
|
||||
now=lambda: datetime(2026, 8, 17, 8, 0, tzinfo=timezone.utc),
|
||||
)
|
||||
adapter.open_goods(GOODS_URL)
|
||||
|
||||
with self.assertRaises(PddPurchaseSpecResolutionRequired) as raised:
|
||||
adapter.select_options({"color": "黑色", "size": "XXL"})
|
||||
|
||||
snapshot = raised.exception.snapshot
|
||||
self.assertEqual(collected, [device])
|
||||
self.assertEqual(snapshot.goods_id, "753136429979")
|
||||
self.assertEqual(snapshot.dimension_name, "尺码")
|
||||
self.assertEqual(snapshot.observed_at, "2026-08-17T08:00:00Z")
|
||||
self.assertEqual(
|
||||
[(item.raw_text, item.available) for item in snapshot.observations],
|
||||
[("M", True), ("L(售罄)", False), ("XL", True)],
|
||||
)
|
||||
self.assertEqual(
|
||||
[item.to_dict() for item in snapshot.candidates],
|
||||
[
|
||||
{
|
||||
"candidate_id": "c1",
|
||||
"raw_text": "M",
|
||||
"options": {"color": "黑色", "size": "M"},
|
||||
},
|
||||
{
|
||||
"candidate_id": "c2",
|
||||
"raw_text": "XL",
|
||||
"options": {"color": "黑色", "size": "XL"},
|
||||
},
|
||||
],
|
||||
)
|
||||
self.assertEqual(
|
||||
snapshot.candidate_snapshot_hash,
|
||||
"fcc35e4429625c830e6b2cbde9c618b63536339c18d9d452c34ec8347d1f1630",
|
||||
)
|
||||
self.assertEqual(
|
||||
raised.exception.diagnostics["candidate_snapshot_hash"],
|
||||
snapshot.candidate_snapshot_hash,
|
||||
)
|
||||
adapter.close()
|
||||
|
||||
def test_candidate_snapshot_is_not_used_when_target_exists_but_click_fails(self):
|
||||
device = FakeDevice()
|
||||
adapter = U2PddPurchaseAdapter(
|
||||
"USB-001",
|
||||
device_service=PddDeviceService(connector=lambda _serial: device),
|
||||
sleeper=lambda _seconds: None,
|
||||
select_color_fn=lambda *_args, **_kwargs: True,
|
||||
select_size_fn=lambda *_args, **_kwargs: False,
|
||||
size_candidate_collector=lambda _device: SpecDimension(
|
||||
"size", "尺码", (DimensionValue("XXL", True),)
|
||||
),
|
||||
)
|
||||
adapter.open_goods(GOODS_URL)
|
||||
|
||||
with self.assertRaises(PddPurchaseError) as raised:
|
||||
adapter.select_options({"color": "黑色", "size": "XXL"})
|
||||
|
||||
self.assertNotIsInstance(
|
||||
raised.exception, PddPurchaseSpecResolutionRequired
|
||||
)
|
||||
self.assertEqual(raised.exception.code, "PURCHASE_OPTIONS_MISMATCH")
|
||||
self.assertEqual(
|
||||
raised.exception.diagnostics["selection_failure"],
|
||||
"selection_unconfirmed",
|
||||
)
|
||||
adapter.close()
|
||||
|
||||
def test_incomplete_candidate_scan_remains_normal_purchase_failure(self):
|
||||
device = FakeDevice()
|
||||
|
||||
def incomplete_scan(_device):
|
||||
raise PddCollectError(
|
||||
"PDD_DATA_SPEC_INCOMPLETE", "达到滑动上限"
|
||||
)
|
||||
|
||||
adapter = U2PddPurchaseAdapter(
|
||||
"USB-001",
|
||||
device_service=PddDeviceService(connector=lambda _serial: device),
|
||||
sleeper=lambda _seconds: None,
|
||||
select_color_fn=lambda *_args, **_kwargs: True,
|
||||
select_size_fn=lambda *_args, **_kwargs: False,
|
||||
size_candidate_collector=incomplete_scan,
|
||||
)
|
||||
adapter.open_goods(GOODS_URL)
|
||||
|
||||
with self.assertRaises(PddPurchaseError) as raised:
|
||||
adapter.select_options({"color": "黑色", "size": "XXL"})
|
||||
|
||||
self.assertNotIsInstance(
|
||||
raised.exception, PddPurchaseSpecResolutionRequired
|
||||
)
|
||||
self.assertEqual(
|
||||
raised.exception.code,
|
||||
"PURCHASE_SIZE_CANDIDATE_SCAN_INCOMPLETE",
|
||||
)
|
||||
self.assertEqual(
|
||||
raised.exception.diagnostics["candidate_scan_error"],
|
||||
"PDD_DATA_SPEC_INCOMPLETE",
|
||||
)
|
||||
adapter.close()
|
||||
|
||||
def test_size_without_selected_color_never_requests_remote_resolution(self):
|
||||
device = FakeDevice()
|
||||
scans = []
|
||||
adapter = U2PddPurchaseAdapter(
|
||||
"USB-001",
|
||||
device_service=PddDeviceService(connector=lambda _serial: device),
|
||||
sleeper=lambda _seconds: None,
|
||||
select_size_fn=lambda *_args, **_kwargs: False,
|
||||
size_candidate_collector=lambda _device: scans.append(True),
|
||||
)
|
||||
adapter.open_goods(GOODS_URL)
|
||||
|
||||
with self.assertRaises(PddPurchaseError) as raised:
|
||||
adapter.select_options({"size": "XXL"})
|
||||
|
||||
self.assertEqual(raised.exception.code, "PURCHASE_OPTIONS_MISMATCH")
|
||||
self.assertEqual(scans, [])
|
||||
adapter.close()
|
||||
|
||||
def test_contextual_confirm_panel_is_reliable_without_clicking_confirm(self):
|
||||
device = ContextualConfirmPanelDevice()
|
||||
calls = []
|
||||
|
||||
Reference in New Issue
Block a user