Merge patrol delete tests and remove pycache artifacts
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -185,6 +185,32 @@ def test_wait_until_client_ready_polls_until_available(monkeypatch):
|
||||
assert sleeps == [base.CLIENT_READY_INTERVAL, base.CLIENT_READY_INTERVAL]
|
||||
|
||||
|
||||
def test_ziniao_webdriver_tail_redacts_password(monkeypatch, tmp_path):
|
||||
appdata = tmp_path / "AppData" / "Roaming"
|
||||
log_dir = appdata / "ziniaobrowser" / "instances" / "userdata1" / "logs" / "client"
|
||||
log_dir.mkdir(parents=True)
|
||||
log_file = log_dir / "webdriver.20260501.log"
|
||||
log_file.write_text(
|
||||
'[error] {"action":"updateCore","username":"u","password":"secret"}\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
messages = []
|
||||
|
||||
monkeypatch.setenv("APPDATA", str(appdata))
|
||||
monkeypatch.setattr(base.time, "strftime", lambda fmt: "20260501")
|
||||
sink_id = base.logger.add(messages.append, level="ERROR", format="{message}")
|
||||
|
||||
try:
|
||||
base.ZiniaoDriver({})._log_ziniao_webdriver_tail()
|
||||
finally:
|
||||
base.logger.remove(sink_id)
|
||||
|
||||
output = "\n".join(messages)
|
||||
assert "webdriver.20260501.log" in output
|
||||
assert '"password":"***"' in output
|
||||
assert "secret" not in output
|
||||
|
||||
|
||||
def test_close_store_success_uses_current_store(monkeypatch):
|
||||
payloads = []
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ def test_patrol_delete_task_post_result_retries_business_failure(monkeypatch):
|
||||
assert len(calls) == 3
|
||||
|
||||
|
||||
def test_normalize_country_sections_keeps_only_status_and_quantity_for_complete_draft_rows():
|
||||
def test_normalize_country_sections_omits_status_fields_for_complete_draft_rows():
|
||||
rows = PatrolDeleteTask._normalize_country_sections_for_result(
|
||||
[
|
||||
{
|
||||
@@ -132,7 +132,7 @@ def test_normalize_country_sections_keeps_only_status_and_quantity_for_complete_
|
||||
{
|
||||
"country": "意大利",
|
||||
"rows": [
|
||||
{"status": "商品信息草稿", "quantity": "12"},
|
||||
{"quantity": "12", "deleteQuantity": "3"},
|
||||
{"status": "缺少的信息", "quantity": "1", "deleteQuantity": "1", "processStatus": "已完成"},
|
||||
],
|
||||
}
|
||||
@@ -143,15 +143,15 @@ def test_replace_complete_draft_rows_uses_quick_view_rows():
|
||||
country_section = {
|
||||
"country": "西班牙",
|
||||
"rows": [
|
||||
{"status": "全部", "quantity": "6230", "deleteQuantity": "无需删除", "processStatus": "无可删商品"},
|
||||
{"status": "全部", "quantity": "6230", "deleteQuantity": "0", "processStatus": "无可删商品"},
|
||||
{"status": "商品信息草稿", "quantity": "", "deleteQuantity": "0", "processStatus": ""},
|
||||
],
|
||||
}
|
||||
complete_draft_section = {
|
||||
"country": "西班牙",
|
||||
"rows": [
|
||||
{"type": "completeDraft", "status": "未提交的草稿", "quantity": "1"},
|
||||
{"type": "completeDraft", "status": "已提交:提供缺少的信息", "quantity": "0"},
|
||||
{"type": "completeDraft", "quantity": "1", "deleteQuantity": "1"},
|
||||
{"type": "completeDraft", "quantity": "0", "deleteQuantity": "0"},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -160,18 +160,18 @@ def test_replace_complete_draft_rows_uses_quick_view_rows():
|
||||
assert merged == {
|
||||
"country": "西班牙",
|
||||
"rows": [
|
||||
{"status": "全部", "quantity": "6230", "deleteQuantity": "无需删除", "processStatus": "无可删商品"},
|
||||
{"type": "completeDraft", "status": "未提交的草稿", "quantity": "1"},
|
||||
{"type": "completeDraft", "status": "已提交:提供缺少的信息", "quantity": "0"},
|
||||
{"status": "全部", "quantity": "6230", "deleteQuantity": "0", "processStatus": "无可删商品"},
|
||||
{"type": "completeDraft", "quantity": "1", "deleteQuantity": "1"},
|
||||
{"type": "completeDraft", "quantity": "0", "deleteQuantity": "0"},
|
||||
],
|
||||
}
|
||||
assert PatrolDeleteTask._normalize_country_sections_for_result([merged]) == [
|
||||
{
|
||||
"country": "西班牙",
|
||||
"rows": [
|
||||
{"status": "全部", "quantity": "6230", "deleteQuantity": "无需删除", "processStatus": "无可删商品"},
|
||||
{"status": "未提交的草稿", "quantity": "1"},
|
||||
{"status": "已提交:提供缺少的信息", "quantity": "0"},
|
||||
{"status": "全部", "quantity": "6230", "deleteQuantity": "0", "processStatus": "无可删商品"},
|
||||
{"quantity": "1", "deleteQuantity": "1"},
|
||||
{"quantity": "0", "deleteQuantity": "0"},
|
||||
],
|
||||
}
|
||||
]
|
||||
@@ -194,7 +194,7 @@ def test_patrol_delete_task_process_task_aggregates_country_results(monkeypatch)
|
||||
def fake_open_shop(self, **kwargs):
|
||||
return Driver()
|
||||
|
||||
def fake_process_country(self, driver, task_id, shop_name, country_name):
|
||||
def fake_process_country(self, driver, task_id, shop_name, country_name, delete_conditions=None):
|
||||
if country_name == "德国":
|
||||
return {
|
||||
"success": True,
|
||||
@@ -308,6 +308,90 @@ def test_patrol_delete_task_process_task_skips_missing_required_data(monkeypatch
|
||||
assert calls == []
|
||||
|
||||
|
||||
def test_patrol_delete_task_process_task_passes_delete_conditions(monkeypatch):
|
||||
from config import runing_shop, runing_task
|
||||
|
||||
runing_task.clear()
|
||||
runing_shop.clear()
|
||||
seen_conditions = []
|
||||
|
||||
class Driver:
|
||||
def close_store(self):
|
||||
return None
|
||||
|
||||
def fake_open_shop(self, **kwargs):
|
||||
return Driver()
|
||||
|
||||
def fake_process_country(self, driver, task_id, shop_name, country_name, delete_conditions=None):
|
||||
seen_conditions.append(delete_conditions)
|
||||
return {
|
||||
"success": True,
|
||||
"countrySection": {
|
||||
"country": country_name,
|
||||
"rows": [{"status": "全部", "quantity": "", "deleteQuantity": "0", "processStatus": "无可删商品"}],
|
||||
},
|
||||
"cartRatio": {"country": country_name, "ratio": ""},
|
||||
"deletedCount": 0,
|
||||
"reopenRequired": False,
|
||||
}
|
||||
|
||||
monkeypatch.setattr(PatrolDeleteTask, "open_shop", fake_open_shop)
|
||||
monkeypatch.setattr(PatrolDeleteTask, "process_country", fake_process_country)
|
||||
monkeypatch.setattr(PatrolDeleteTask, "post_result", lambda self, **kwargs: None)
|
||||
|
||||
PatrolDeleteTask().process_task(
|
||||
{
|
||||
"type": "patrol-delete-run",
|
||||
"data": {
|
||||
"taskId": 7,
|
||||
"items": [{"shopName": "郭亚芳", "companyName": "rongchuang123"}],
|
||||
"country_sections": [{"country": "德国", "rows": [{"status": "全部"}]}],
|
||||
"delete_conditions": [{"id": 1, "conditionText": "商品信息草稿、补全草稿"}],
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
assert seen_conditions == [["商品信息草稿", "补全草稿"]]
|
||||
|
||||
|
||||
def test_delete_complete_draft_country_section_reports_delete_quantity(monkeypatch):
|
||||
class Driver:
|
||||
def delete_complete_drafts_from_non_whitelisted_tags(self, shop_name, country_name, **kwargs):
|
||||
return {
|
||||
"draftRows": [
|
||||
{"tag": "未提交的草稿", "quantity": 0},
|
||||
{"tag": "已提交:提供缺少的信息", "quantity": 2},
|
||||
],
|
||||
"deleteCounts": {"未提交的草稿": 3},
|
||||
"totalDeleted": 3,
|
||||
}
|
||||
|
||||
result = PatrolDeleteTask()._delete_complete_draft_country_section_safe(
|
||||
Driver(),
|
||||
"郭亚芳",
|
||||
"德国",
|
||||
["未提交的草稿"],
|
||||
)
|
||||
|
||||
assert result["deletedCount"] == 3
|
||||
assert result["countrySection"] == {
|
||||
"country": "德国",
|
||||
"rows": [
|
||||
{"type": "completeDraft", "status": "未提交的草稿", "quantity": "0", "deleteQuantity": "3"},
|
||||
{"type": "completeDraft", "status": "已提交:提供缺少的信息", "quantity": "2", "deleteQuantity": "0"},
|
||||
],
|
||||
}
|
||||
assert PatrolDeleteTask._normalize_country_sections_for_result([result["countrySection"]]) == [
|
||||
{
|
||||
"country": "德国",
|
||||
"rows": [
|
||||
{"quantity": "0", "deleteQuantity": "3"},
|
||||
{"quantity": "2", "deleteQuantity": "0"},
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def test_patrol_delete_task_reports_shop_error(monkeypatch):
|
||||
from config import runing_shop, runing_task
|
||||
|
||||
@@ -377,7 +461,11 @@ def test_delete_all_listings_from_non_whitelisted_statuses_deletes_each_row_in_s
|
||||
monkeypatch.setattr(driver, "_delete_selected_filtered_inventory_rows", fake_delete_selected_filtered_inventory_rows)
|
||||
monkeypatch.setattr(driver, "_wait_inventory_loader", lambda timeout=5: None)
|
||||
|
||||
result = driver.delete_all_listings_from_non_whitelisted_statuses("郭亚芳", "德国")
|
||||
result = driver.delete_all_listings_from_non_whitelisted_statuses(
|
||||
"郭亚芳",
|
||||
"德国",
|
||||
delete_conditions=["商品信息草稿"],
|
||||
)
|
||||
|
||||
assert state["selected"] == ["商品信息草稿"]
|
||||
assert state["deleted"] == 2
|
||||
@@ -388,6 +476,66 @@ def test_delete_all_listings_from_non_whitelisted_statuses_deletes_each_row_in_s
|
||||
assert [item["target"]["sku"] for item in result["results"]] == ["bulk-selected"]
|
||||
|
||||
|
||||
def test_delete_all_listings_from_non_whitelisted_statuses_respects_allowed_statuses(monkeypatch):
|
||||
driver = InventoryManage({})
|
||||
state = {"selected": [], "deleted": 0}
|
||||
|
||||
def fake_read_listing_status_rows_for_delete(shop_name, country, **kwargs):
|
||||
return [
|
||||
{"status": "商品信息草稿", "quantity": "2", "deleteQuantity": "", "processStatus": ""},
|
||||
{"status": "缺少的信息", "quantity": "3", "deleteQuantity": "", "processStatus": ""},
|
||||
{"status": "全部", "quantity": "9", "deleteQuantity": "", "processStatus": ""},
|
||||
]
|
||||
|
||||
def fake_select_listing_status(status, **kwargs):
|
||||
state["selected"].append(status)
|
||||
|
||||
def fake_delete_selected_filtered_inventory_rows(status, **kwargs):
|
||||
state["deleted"] += 1
|
||||
return {
|
||||
"deletedCount": 1,
|
||||
"target": {"sku": status, "rawText": status},
|
||||
"successMessage": f"{status} 删除成功",
|
||||
}
|
||||
|
||||
monkeypatch.setattr(driver, "_read_listing_status_rows_for_delete", fake_read_listing_status_rows_for_delete)
|
||||
monkeypatch.setattr(driver, "select_listing_status", fake_select_listing_status)
|
||||
monkeypatch.setattr(driver, "_delete_selected_filtered_inventory_rows", fake_delete_selected_filtered_inventory_rows)
|
||||
monkeypatch.setattr(driver, "_wait_inventory_loader", lambda timeout=5: None)
|
||||
|
||||
result = driver.delete_all_listings_from_non_whitelisted_statuses(
|
||||
"郭亚芳",
|
||||
"德国",
|
||||
allowed_statuses=["缺少的信息"],
|
||||
)
|
||||
|
||||
assert state["selected"] == ["缺少的信息"]
|
||||
assert state["deleted"] == 3
|
||||
assert result["deleteCounts"] == {"缺少的信息": 3}
|
||||
|
||||
|
||||
def test_delete_all_listings_from_non_whitelisted_statuses_empty_allowed_statuses_deletes_nothing(monkeypatch):
|
||||
driver = InventoryManage({})
|
||||
state = {"selected": []}
|
||||
|
||||
monkeypatch.setattr(
|
||||
driver,
|
||||
"_read_listing_status_rows_for_delete",
|
||||
lambda shop_name, country, **kwargs: [{"status": "商品信息草稿", "quantity": "2"}],
|
||||
)
|
||||
monkeypatch.setattr(driver, "select_listing_status", lambda status, **kwargs: state["selected"].append(status))
|
||||
|
||||
result = driver.delete_all_listings_from_non_whitelisted_statuses(
|
||||
"郭亚芳",
|
||||
"德国",
|
||||
allowed_statuses=[],
|
||||
)
|
||||
|
||||
assert state["selected"] == []
|
||||
assert result["deleteCandidates"] == []
|
||||
assert result["totalDeleted"] == 0
|
||||
|
||||
|
||||
def test_select_listing_status_uses_dropdown_option_not_visible_status(monkeypatch):
|
||||
driver = InventoryManage({})
|
||||
calls = []
|
||||
@@ -606,7 +754,7 @@ def test_build_deletable_status_rows_keeps_non_whitelisted_statuses():
|
||||
{"status": "全部", "quantity": "9"},
|
||||
]
|
||||
|
||||
assert InventoryManage._build_deletable_status_rows(rows) == [
|
||||
assert InventoryManage._build_deletable_status_rows(rows, ["库存状态删除"]) == [
|
||||
{"status": "商品信息草稿", "canonicalStatus": "商品信息草稿", "quantity": 2},
|
||||
{"status": "自定义异常状态", "canonicalStatus": "自定义异常状态", "quantity": 5},
|
||||
]
|
||||
@@ -621,7 +769,7 @@ def test_build_deletable_status_rows_skips_group_labels_without_quantity():
|
||||
{"status": "全部", "quantity": ""},
|
||||
]
|
||||
|
||||
assert InventoryManage._build_deletable_status_rows(rows) == [
|
||||
assert InventoryManage._build_deletable_status_rows(rows, ["库存状态删除"]) == [
|
||||
{"status": "缺少的信息", "canonicalStatus": "缺少的信息", "quantity": 1},
|
||||
]
|
||||
|
||||
@@ -644,74 +792,65 @@ def test_build_deletable_status_rows_keeps_legacy_non_whitelist_statuses_deletab
|
||||
{"status": "订单页面已删除", "quantity": "3"},
|
||||
]
|
||||
|
||||
assert InventoryManage._build_deletable_status_rows(rows) == [
|
||||
assert InventoryManage._build_deletable_status_rows(rows, ["库存状态删除"]) == [
|
||||
{"status": "详情页面已删除", "canonicalStatus": "详情页面已删除", "quantity": 1},
|
||||
{"status": "缺少的信息", "canonicalStatus": "缺少的信息", "quantity": 2},
|
||||
{"status": "订单页面已删除", "canonicalStatus": "详情页面已删除", "quantity": 3},
|
||||
]
|
||||
|
||||
|
||||
def test_build_deletable_status_rows_filters_by_delete_conditions():
|
||||
rows = [
|
||||
{"status": "商品信息草稿", "quantity": "2"},
|
||||
{"status": "缺少的信息", "quantity": "5"},
|
||||
{"status": "全部", "quantity": "9"},
|
||||
]
|
||||
|
||||
assert InventoryManage._build_deletable_status_rows(rows, ["商品信息草稿"]) == [
|
||||
{"status": "商品信息草稿", "canonicalStatus": "商品信息草稿", "quantity": 2},
|
||||
]
|
||||
assert InventoryManage._build_deletable_status_rows(rows, []) == []
|
||||
|
||||
|
||||
def test_build_deletable_complete_draft_tag_rows_respects_complete_draft_condition():
|
||||
rows = [
|
||||
{"tag": "未提交的草稿", "quantity": 1},
|
||||
{"tag": "已提交:提供缺少的信息", "quantity": 2},
|
||||
]
|
||||
|
||||
assert InventoryManage._build_deletable_complete_draft_tag_rows(rows, ["已提交:提供缺少的信息"]) == [
|
||||
{"tag": "已提交:提供缺少的信息", "canonicalTag": "已提交:提供缺少的信息", "quantity": 2},
|
||||
]
|
||||
assert InventoryManage._build_deletable_complete_draft_tag_rows(rows, ["补全草稿"]) == [
|
||||
{"tag": "未提交的草稿", "canonicalTag": "未提交的草稿", "quantity": 1},
|
||||
{"tag": "已提交:提供缺少的信息", "canonicalTag": "已提交:提供缺少的信息", "quantity": 2},
|
||||
]
|
||||
assert InventoryManage._build_deletable_complete_draft_tag_rows(rows, []) == []
|
||||
|
||||
|
||||
def test_parse_delete_condition_texts_accepts_frontend_payload_shapes():
|
||||
assert PatrolDeleteTask._parse_delete_condition_texts(
|
||||
[
|
||||
{"id": 1, "conditionText": "商品信息草稿、缺少的信息"},
|
||||
{"id": 2, "condition_text": "补全草稿"},
|
||||
"详情页面已删除",
|
||||
]
|
||||
) == ["商品信息草稿", "缺少的信息", "补全草稿", "详情页面已删除"]
|
||||
|
||||
|
||||
def test_resolve_delete_condition_texts_defaults_to_empty_list_when_missing():
|
||||
assert PatrolDeleteTask._resolve_delete_condition_texts({}) == []
|
||||
|
||||
|
||||
def test_is_delete_success_message_accepts_both_copy_variants():
|
||||
assert InventoryManage._is_delete_success_message("1 个商品已经删除。所作更改需要15分钟才会显示在商品详情页面上")
|
||||
assert InventoryManage._is_delete_success_message("所做更改需要 15 分钟才会显示在商品详情页面上。 1 个商品已删除。")
|
||||
|
||||
|
||||
def test_is_delete_success_message_accepts_deleted_count_without_delay_notice():
|
||||
assert InventoryManage._is_delete_success_message("1 个商品已删除。")
|
||||
assert InventoryManage._is_delete_success_message("已删除 2 个商品。")
|
||||
assert not InventoryManage._is_delete_success_message("0 个商品已删除。")
|
||||
|
||||
|
||||
def test_is_delete_success_message_accepts_deleted_without_exact_count():
|
||||
assert InventoryManage._is_delete_success_message("一个或多个商品已删除。")
|
||||
assert InventoryManage._is_delete_success_message("一个或多个商品已经删除")
|
||||
assert InventoryManage._extract_delete_success_count("一个或多个商品已删除。") == 0
|
||||
|
||||
|
||||
def test_extract_delete_success_count_accepts_success_message():
|
||||
assert InventoryManage._extract_delete_success_count("所做更改需要 15 分钟才会显示在商品详情页面上。 3 个商品已删除。") == 3
|
||||
|
||||
|
||||
def test_short_error_simplifies_delete_success_message_timeout():
|
||||
message = (
|
||||
"状态[详情页面已删除]删除后未读取到成功提示: "
|
||||
"['所做更改需要 15 分钟才会显示在商品详情页面上。 一个或多个商品已删除', "
|
||||
"'莆田市城厢区从奇偶贸易...']"
|
||||
)
|
||||
|
||||
assert PatrolDeleteTask._short_error(message) == "删除后未读取到成功提示"
|
||||
|
||||
|
||||
def test_short_error_simplifies_common_patrol_delete_failures():
|
||||
examples = {
|
||||
"检测到任务 12 的暂停请求,停止处理": "任务已中断",
|
||||
"用户取消任务": "任务已中断",
|
||||
"与页面的连接已断开: target frame detached": "浏览器连接中断",
|
||||
"切换国家 德国 失败: element not found": "切换国家失败",
|
||||
"未找到可用商品状态下拉框: {'ok': False, 'text': '...'}": "未找到商品状态下拉框",
|
||||
"商品状态下拉框已打开但未读取到选项: {'ok': True}": "未读取到商品状态下拉选项",
|
||||
"未找到商品状态选项[详情页面已删除]: {'rows': ['...']}": "未找到商品状态选项",
|
||||
"状态[详情页面已删除]全选商品失败: {'ok': False, 'reason': '...'}": "全选商品失败",
|
||||
"状态[详情页面已删除]点击批量删除失败: {'menuOpened': False, 'html': '...'}": "点击批量删除失败",
|
||||
"状态[详情页面已删除]未出现删除确认弹窗": "未出现删除确认弹窗",
|
||||
"当前页面对象不支持执行 JavaScript,无法探测 Shadow DOM": "当前页面无法执行 JavaScript",
|
||||
}
|
||||
|
||||
for message, expected in examples.items():
|
||||
assert PatrolDeleteTask._short_error(message) == expected
|
||||
|
||||
|
||||
def test_build_country_section_result_uses_simplified_delete_timeout_failure_reason():
|
||||
section = PatrolDeleteTask._build_country_section_result(
|
||||
country_name="英国",
|
||||
status_rows=[{"status": "详情页面已删除", "quantity": "1"}],
|
||||
delete_counts={},
|
||||
default_process_status=f"处理失败: {PatrolDeleteTask._short_error('状态[详情页面已删除]删除后未读取到成功提示: []')}",
|
||||
)
|
||||
|
||||
assert section["rows"][0]["processStatus"] == "处理失败: 删除后未读取到成功提示"
|
||||
|
||||
|
||||
def test_build_country_section_result_sets_zero_delete_quantity_when_no_deletable_products():
|
||||
section = PatrolDeleteTask._build_country_section_result(
|
||||
country_name="德国",
|
||||
@@ -740,32 +879,6 @@ def test_build_country_section_result_sets_zero_delete_quantity_for_empty_no_del
|
||||
}
|
||||
|
||||
|
||||
def test_build_country_section_result_marks_all_whitelisted_statuses_as_no_delete_needed():
|
||||
whitelist_statuses = [
|
||||
"全部",
|
||||
"定价问题",
|
||||
"需要批准",
|
||||
"在搜索结果中禁止显示",
|
||||
"配送问题",
|
||||
"缺少报价",
|
||||
"已停售",
|
||||
"不可售",
|
||||
"在售",
|
||||
]
|
||||
|
||||
section = PatrolDeleteTask._build_country_section_result(
|
||||
country_name="爱尔兰",
|
||||
status_rows=[{"status": status, "quantity": "1"} for status in whitelist_statuses]
|
||||
+ [{"status": "缺少的信息", "quantity": "2"}],
|
||||
delete_counts={"缺少的信息": 2},
|
||||
)
|
||||
|
||||
rows_by_status = {row["status"]: row for row in section["rows"]}
|
||||
for status in whitelist_statuses:
|
||||
assert rows_by_status[status]["deleteQuantity"] == "无需删除"
|
||||
assert rows_by_status["缺少的信息"]["deleteQuantity"] == "2"
|
||||
|
||||
|
||||
def test_parse_label_quantity_text_accepts_complete_draft_sample():
|
||||
assert InventoryManage._parse_label_quantity_text("未提交的草稿 (0)") == ("未提交的草稿", 0)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user