feat: 完整采集采购尺码候选 (#256)

This commit is contained in:
chengma
2026-08-17 17:04:43 +08:00
parent f187bfe0fe
commit 58dc9022eb
7 changed files with 458 additions and 7 deletions
+2 -2
View File
@@ -853,7 +853,7 @@ class PddCollectParserTest(unittest.TestCase):
sleeper=lambda _seconds: None,
)
dimension = service._collect_size_dimension(device)
dimension = service.collect_second_dimension_candidates(device)
self.assertIsNotNone(dimension)
self.assertEqual(dimension.name, "尺码")
@@ -1499,7 +1499,7 @@ class PddCollectParserTest(unittest.TestCase):
sleeper=lambda _seconds: None,
)
result = service._collect_size_dimension(device)
result = service.collect_second_dimension_candidates(device)
self.assertIsNotNone(result)
self.assertEqual(result.name, "套餐(7)")
+150 -1
View File
@@ -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 = []