fix: tolerate transient collect login checks
This commit is contained in:
@@ -5260,6 +5260,237 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_collect_worker_retries_midrun_no_session_cookie_then_collects(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
cfg = self.make_config(temp_dir)
|
||||
accounts.create_account("主店", "alias-a", debug_port=9222, config=cfg)
|
||||
batch_id = db.create_batch(["input.xlsx"], path=cfg["db_path"])
|
||||
db.insert_tasks(
|
||||
batch_id,
|
||||
[
|
||||
{
|
||||
"source_file_abs": os.path.join(temp_dir, "input.xlsx"),
|
||||
"source_sheet": "商品",
|
||||
"source_row": 2,
|
||||
"account_name": "Excel主店",
|
||||
"alias": "alias-a",
|
||||
"item_id": "51100639510",
|
||||
},
|
||||
{
|
||||
"source_file_abs": os.path.join(temp_dir, "input.xlsx"),
|
||||
"source_sheet": "商品",
|
||||
"source_row": 3,
|
||||
"account_name": "Excel主店",
|
||||
"alias": "alias-a",
|
||||
"item_id": "51100639511",
|
||||
},
|
||||
],
|
||||
path=cfg["db_path"],
|
||||
)
|
||||
tasks = db.list_tasks(batch_id=batch_id, path=cfg["db_path"])
|
||||
responses = [
|
||||
{"logged_in": False, "reason": "NO_SESSION_COOKIE", "url": "https://seller.shopee.tw/"},
|
||||
{"logged_in": True, "reason": None, "url": "https://seller.shopee.tw/portal/"},
|
||||
{"logged_in": True, "reason": None, "url": "https://seller.shopee.tw/portal/"},
|
||||
]
|
||||
collected_items = []
|
||||
|
||||
def fake_collect(account, task, on_step=None):
|
||||
collected_items.append(task["item_id"])
|
||||
return {"old_title": f"旧标题{task['item_id']}", "old_cover_path": "old.jpg"}
|
||||
|
||||
with mock.patch("app.gui.accounts.detect_login", side_effect=responses) as detect_login, \
|
||||
mock.patch("app.gui.editor.collect", side_effect=fake_collect) as collect, \
|
||||
mock.patch("app.gui.workers.time.sleep") as sleep:
|
||||
summary = CollectWorker(
|
||||
tasks,
|
||||
db_path=cfg["db_path"],
|
||||
config=cfg,
|
||||
preflight=False,
|
||||
).execute()
|
||||
|
||||
self.assertTrue(summary["ok"])
|
||||
self.assertEqual(2, summary["collected"])
|
||||
self.assertEqual(0, summary["skipped"])
|
||||
self.assertEqual([], summary["login_required_accounts"])
|
||||
self.assertEqual(["51100639510", "51100639511"], collected_items)
|
||||
self.assertEqual(3, detect_login.call_count)
|
||||
self.assertEqual(1, sleep.call_count)
|
||||
self.assertEqual(2, collect.call_count)
|
||||
|
||||
updated = db.list_tasks(batch_id=batch_id, path=cfg["db_path"])
|
||||
self.assertTrue(all(task.stage == "collected" for task in updated))
|
||||
self.assertTrue(all(task.status == "success" for task in updated))
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_collect_worker_repeated_midrun_no_session_cookie_does_not_skip(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
cfg = self.make_config(temp_dir)
|
||||
accounts.create_account("主店", "alias-a", debug_port=9222, config=cfg)
|
||||
batch_id = db.create_batch(["input.xlsx"], path=cfg["db_path"])
|
||||
db.insert_tasks(
|
||||
batch_id,
|
||||
[
|
||||
{
|
||||
"source_file_abs": os.path.join(temp_dir, "input.xlsx"),
|
||||
"source_sheet": "商品",
|
||||
"source_row": 2,
|
||||
"account_name": "Excel主店",
|
||||
"alias": "alias-a",
|
||||
"item_id": "51100639510",
|
||||
}
|
||||
],
|
||||
path=cfg["db_path"],
|
||||
)
|
||||
tasks = db.list_tasks(batch_id=batch_id, path=cfg["db_path"])
|
||||
status = {
|
||||
"logged_in": False,
|
||||
"reason": "NO_SESSION_COOKIE",
|
||||
"url": "https://seller.shopee.tw/portal/",
|
||||
"cookie_names": [],
|
||||
}
|
||||
|
||||
with mock.patch("app.gui.accounts.detect_login", return_value=status) as detect_login, \
|
||||
mock.patch(
|
||||
"app.gui.editor.collect",
|
||||
return_value={"old_title": "旧标题", "old_cover_path": "old.jpg"},
|
||||
) as collect, \
|
||||
mock.patch("app.gui.workers.time.sleep") as sleep:
|
||||
summary = CollectWorker(
|
||||
tasks,
|
||||
db_path=cfg["db_path"],
|
||||
config=cfg,
|
||||
preflight=False,
|
||||
).execute()
|
||||
|
||||
self.assertTrue(summary["ok"])
|
||||
self.assertEqual(1, summary["collected"])
|
||||
self.assertEqual(0, summary["skipped"])
|
||||
self.assertEqual([], summary["login_required_accounts"])
|
||||
self.assertEqual(3, detect_login.call_count)
|
||||
self.assertEqual(2, sleep.call_count)
|
||||
collect.assert_called_once()
|
||||
task = db.list_tasks(batch_id=batch_id, path=cfg["db_path"])[0]
|
||||
self.assertEqual("collected", task.stage)
|
||||
self.assertEqual("success", task.status)
|
||||
|
||||
events = db.list_run_log_events(summary["run_id"], path=cfg["db_path"])
|
||||
messages = "\n".join(event.message for event in events)
|
||||
self.assertIn("登录状态检测暂时不稳定", messages)
|
||||
self.assertNotIn("采集中途掉登录", messages)
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_collect_worker_midrun_login_check_failed_does_not_skip_remaining(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
cfg = self.make_config(temp_dir)
|
||||
accounts.create_account("主店", "alias-a", debug_port=9222, config=cfg)
|
||||
batch_id = db.create_batch(["input.xlsx"], path=cfg["db_path"])
|
||||
db.insert_tasks(
|
||||
batch_id,
|
||||
[
|
||||
{
|
||||
"source_file_abs": os.path.join(temp_dir, "input.xlsx"),
|
||||
"source_sheet": "商品",
|
||||
"source_row": 2,
|
||||
"account_name": "Excel主店",
|
||||
"alias": "alias-a",
|
||||
"item_id": "51100639510",
|
||||
},
|
||||
{
|
||||
"source_file_abs": os.path.join(temp_dir, "input.xlsx"),
|
||||
"source_sheet": "商品",
|
||||
"source_row": 3,
|
||||
"account_name": "Excel主店",
|
||||
"alias": "alias-a",
|
||||
"item_id": "51100639511",
|
||||
},
|
||||
],
|
||||
path=cfg["db_path"],
|
||||
)
|
||||
tasks = db.list_tasks(batch_id=batch_id, path=cfg["db_path"])
|
||||
responses = [
|
||||
RuntimeError("CDP 读取失败 token=SECRET-TOKEN"),
|
||||
RuntimeError("CDP 读取失败 token=SECRET-TOKEN"),
|
||||
RuntimeError("CDP 读取失败 token=SECRET-TOKEN"),
|
||||
{"logged_in": True, "reason": None},
|
||||
]
|
||||
|
||||
with mock.patch("app.gui.accounts.detect_login", side_effect=responses), \
|
||||
mock.patch(
|
||||
"app.gui.editor.collect",
|
||||
return_value={"old_title": "旧标题", "old_cover_path": "old.jpg"},
|
||||
) as collect, \
|
||||
mock.patch("app.gui.workers.time.sleep"):
|
||||
summary = CollectWorker(
|
||||
tasks,
|
||||
db_path=cfg["db_path"],
|
||||
config=cfg,
|
||||
preflight=False,
|
||||
).execute()
|
||||
|
||||
self.assertTrue(summary["ok"])
|
||||
self.assertEqual(2, summary["collected"])
|
||||
self.assertEqual(0, summary["skipped"])
|
||||
self.assertEqual([], summary["login_required_accounts"])
|
||||
self.assertEqual(2, collect.call_count)
|
||||
|
||||
events = db.list_run_log_events(summary["run_id"], path=cfg["db_path"])
|
||||
messages = "\n".join(event.message for event in events)
|
||||
self.assertIn("LOGIN_CHECK_FAILED", messages)
|
||||
self.assertNotIn("SECRET-TOKEN", messages)
|
||||
self.assertIn("token=***", messages)
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_collect_worker_preflight_no_session_cookie_does_not_skip_account(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
cfg = self.make_config(temp_dir)
|
||||
accounts.create_account("主店", "alias-a", debug_port=9222, config=cfg)
|
||||
batch_id = db.create_batch(["input.xlsx"], path=cfg["db_path"])
|
||||
db.insert_tasks(
|
||||
batch_id,
|
||||
[
|
||||
{
|
||||
"source_file_abs": os.path.join(temp_dir, "input.xlsx"),
|
||||
"source_sheet": "商品",
|
||||
"source_row": 2,
|
||||
"account_name": "Excel主店",
|
||||
"alias": "alias-a",
|
||||
"item_id": "51100639510",
|
||||
}
|
||||
],
|
||||
path=cfg["db_path"],
|
||||
)
|
||||
tasks = db.list_tasks(batch_id=batch_id, path=cfg["db_path"])
|
||||
responses = [
|
||||
{"logged_in": False, "reason": "NO_SESSION_COOKIE", "url": "https://seller.shopee.tw/"},
|
||||
{"logged_in": False, "reason": "NO_SESSION_COOKIE", "url": "https://seller.shopee.tw/"},
|
||||
{"logged_in": False, "reason": "NO_SESSION_COOKIE", "url": "https://seller.shopee.tw/"},
|
||||
{"logged_in": True, "reason": None, "url": "https://seller.shopee.tw/portal/"},
|
||||
]
|
||||
|
||||
with mock.patch("app.gui.chrome.is_running", return_value=True), \
|
||||
mock.patch("app.gui.accounts.detect_login", side_effect=responses) as detect_login, \
|
||||
mock.patch(
|
||||
"app.gui.editor.collect",
|
||||
return_value={"old_title": "旧标题", "old_cover_path": "old.jpg"},
|
||||
) as collect, \
|
||||
mock.patch("app.gui.workers.time.sleep") as sleep:
|
||||
summary = CollectWorker(tasks, db_path=cfg["db_path"], config=cfg).execute()
|
||||
|
||||
self.assertTrue(summary["ok"])
|
||||
self.assertEqual(1, summary["collected"])
|
||||
self.assertEqual(0, summary["skipped"])
|
||||
self.assertEqual([], summary["logged_out"])
|
||||
self.assertEqual([], summary["login_required_accounts"])
|
||||
self.assertEqual(4, detect_login.call_count)
|
||||
self.assertEqual(2, sleep.call_count)
|
||||
collect.assert_called_once()
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_collect_tab_blocked_preflight_guides_to_accounts_tab(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
opened = []
|
||||
|
||||
Reference in New Issue
Block a user