Files
cmautobuy/client/test/test_task_detail_view.py
T

270 lines
9.5 KiB
Python
Raw Normal View History

2026-08-08 09:46:58 +08:00
"""任务详情数据整理和窗口的离屏测试。"""
import os
import unittest
2026-08-08 10:29:59 +08:00
from dataclasses import replace
2026-08-08 09:46:58 +08:00
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
from PyQt5.QtCore import Qt
from PyQt5.QtTest import QTest
from PyQt5.QtWidgets import QApplication, QWidget
2026-08-08 09:46:58 +08:00
from src.task_detail_view import (
CopyableBodyLabel,
CopyableStrongBodyLabel,
TaskDetailWindow,
build_task_detail_view_data,
)
2026-08-08 09:46:58 +08:00
from src.task_models import TaskDetail, TaskStatus, TaskType
def make_detail(pdd_data=None) -> TaskDetail:
return TaskDetail(
id=1,
remote_task_id="COL-001",
task_type=TaskType.COLLECT,
goods_id="123",
goods_url="https://mobile.yangkeduo.com/goods.html?goods_id=123",
title="本地标题",
target_color=None,
target_size=None,
price_cent=None,
quantity=None,
status=TaskStatus.SUCCEEDED,
current_step="completed",
priority=0,
version=1,
admin_payload={},
pdd_data=pdd_data,
retry_count=0,
last_error_code=None,
last_error_message=None,
received_at="2026-08-08T08:00:00Z",
started_at="2026-08-08T08:01:00Z",
finished_at="2026-08-08T08:02:00Z",
created_at="2026-08-08T08:00:00Z",
updated_at="2026-08-08T08:02:00Z",
)
def collected_data():
return {
"goods_id": "123",
"title": "采集标题",
"shop_name": "测试店铺",
"captured_at": "2026-08-08T08:02:00Z",
"metrics": {
"sales": {"value": 1200, "raw": "已拼1.2千件"},
"reviews": {"value": 68, "raw": "68条评价"},
},
"dimensions": [
{
"key": "color",
"name": "颜色",
"values": [
{"text": "黑色", "available": True},
{"text": "白色", "available": False},
{"text": "黑色", "available": True},
],
},
{
"key": "size",
"name": "尺码",
"values": [
{"text": "M", "available": True},
{"text": "L", "available": True},
{"text": "M", "available": True},
],
},
],
"skus": [
{"options": {"color": "黑色", "size": "M"}, "price_cent": 1000},
{"options": {"color": "黑色", "size": "L"}, "price_cent": 1200},
{"options": {"color": "白色", "size": "M"}, "price_cent": None},
],
}
class TaskDetailViewTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.app = QApplication.instance() or QApplication([])
def test_colors_keep_order_deduplicate_and_show_price_state(self):
data = build_task_detail_view_data(make_detail(collected_data()))
self.assertEqual([item.text for item in data.colors], ["黑色", "白色"])
self.assertEqual(data.colors[0].price_text, "¥10.00~¥12.00")
self.assertTrue(data.colors[0].price_inconsistent)
self.assertEqual(data.colors[1].price_text, "价格未采集")
self.assertFalse(data.colors[1].available)
self.assertEqual(data.sizes, ["M", "L"])
def test_old_data_can_restore_dimensions_from_skus(self):
data = collected_data()
data["dimensions"] = []
view_data = build_task_detail_view_data(make_detail(data))
self.assertEqual([item.text for item in view_data.colors], ["黑色", "白色"])
self.assertEqual(view_data.sizes, ["M", "L"])
def test_empty_collection_data_has_clear_empty_values(self):
data = build_task_detail_view_data(make_detail())
self.assertEqual(data.colors, [])
self.assertEqual(data.sizes, [])
self.assertEqual(data.shop_name, "未采集")
self.assertEqual(data.sales, "未采集")
2026-08-08 10:29:59 +08:00
self.assertEqual(data.current_step, "已完成")
def test_unknown_current_step_does_not_show_english_to_buyer(self):
detail = replace(make_detail(), current_step="future_internal_step")
data = build_task_detail_view_data(detail)
self.assertEqual(data.current_step, "未知步骤")
2026-08-08 09:46:58 +08:00
def test_failure_status_uses_task_type_and_keeps_reason(self):
collect = replace(
make_detail(),
status=TaskStatus.RETRY_WAIT,
last_error_code="PDD_PAGE_TIMEOUT",
last_error_message="商品页面加载超时",
)
purchase = replace(
make_detail(),
remote_task_id="PUR-001",
task_type=TaskType.PURCHASE,
status=TaskStatus.FAILED,
last_error_code="PURCHASE_PRICE_EXCEEDED",
last_error_message="价格超过上限",
)
collect_data = build_task_detail_view_data(collect)
purchase_data = build_task_detail_view_data(purchase)
self.assertEqual(collect_data.status, "采集失败")
self.assertIn("商品页面加载超时", collect_data.last_error)
self.assertEqual(purchase_data.status, "采购失败")
self.assertIn("价格超过上限", purchase_data.last_error)
2026-08-10 00:27:52 +08:00
def test_purchase_recovery_steps_are_clear_chinese(self):
cases = {
"purchase_dry_run_stopped": "采购演练已在提交前停止",
"reconcile_purchase": "订单已提交,只允许核对未付款订单",
"purchase_order_matched_pending_report": (
"已提交待付款,等待向 Admin 上报"
),
"reconcile_manual_review": (
"订单结果不确定或存在多个候选,需人工处理"
),
2026-08-10 00:27:52 +08:00
}
for step, expected in cases.items():
with self.subTest(step=step):
detail = replace(make_detail(), current_step=step)
self.assertEqual(
build_task_detail_view_data(detail).current_step,
expected,
)
def test_purchase_result_shows_order_and_prefers_raw_order_time(self):
purchase_data = {
"purchase": {
"mode": "live",
"confirmed": {
"options": {"color": "黑色", "size": "L"},
"quantity": 2,
"unit_price_cent": 3990,
"total_price_cent": 7980,
},
"order_no": "ORDER-20260811",
"ordered_at": "2026-08-11T08:00:00+08:00",
"ordered_at_raw": "2026-08-11 08:00:00",
"match_status": "matched",
"payment_status": "unpaid",
}
}
detail = replace(
make_detail(purchase_data),
remote_task_id="PUR-001",
task_type=TaskType.PURCHASE,
)
data = build_task_detail_view_data(detail)
self.assertEqual(data.purchase.mode, "真实采购")
self.assertEqual(data.purchase.confirmed_spec, "黑色 / L")
self.assertEqual(data.purchase.order_no, "ORDER-20260811")
self.assertEqual(data.purchase.ordered_at, "2026-08-11 08:00:00")
self.assertEqual(data.purchase.match_status, "已匹配")
self.assertEqual(data.purchase.payment_status, "待付款")
window = TaskDetailWindow(detail)
self.assertIsNotNone(window.findChild(QWidget, "purchaseResultCard"))
self.assertTrue(
any(
label.text() == "ORDER-20260811"
for label in window.findChildren(CopyableBodyLabel)
)
)
window.close()
self.app.processEvents()
def test_collect_detail_does_not_show_purchase_result_card(self):
window = TaskDetailWindow(make_detail(collected_data()))
self.assertIsNone(window.findChild(QWidget, "purchaseResultCard"))
window.close()
self.app.processEvents()
2026-08-08 09:46:58 +08:00
def test_window_builds_color_section_before_size_section(self):
window = TaskDetailWindow(make_detail(collected_data()))
self.assertEqual(len(window.colorItems), 2)
self.assertEqual(len(window.sizeItems), 2)
self.assertIn("COL-001", window.windowTitle())
self.assertTrue(window.isWindow())
window.close()
self.app.processEvents()
def test_double_click_copies_full_field_and_updates_feedback(self):
window = TaskDetailWindow(make_detail(collected_data()))
title = next(
label
for label in window.findChildren(CopyableBodyLabel)
if label.text() == "采集标题"
)
QTest.mouseDClick(title, Qt.LeftButton)
self.assertEqual(QApplication.clipboard().text(), "采集标题")
self.assertEqual(window.copyStatusLabel.text(), "已复制:商品标题")
self.assertTrue(
title.textInteractionFlags() & Qt.TextSelectableByKeyboard
)
window.close()
self.app.processEvents()
def test_ctrl_c_copies_price_without_changing_link_behavior(self):
window = TaskDetailWindow(make_detail(collected_data()))
price = next(
label
for label in window.findChildren(CopyableStrongBodyLabel)
if label.text() == "¥10.00~¥12.00"
)
price.setFocus()
QTest.keyClick(price, Qt.Key_C, Qt.ControlModifier)
self.assertEqual(QApplication.clipboard().text(), "¥10.00~¥12.00")
self.assertEqual(window.copyStatusLabel.text(), "已复制:颜色价格")
window.close()
self.app.processEvents()
2026-08-08 09:46:58 +08:00
if __name__ == "__main__":
unittest.main()