feat: auto-prepare chrome for collection
This commit is contained in:
+179
-9
@@ -5053,7 +5053,7 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_collect_worker_preflight_blocks_when_chrome_not_running(self):
|
||||
def test_collect_worker_preflight_launches_when_chrome_not_running(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)
|
||||
@@ -5075,20 +5075,71 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
||||
tasks = db.list_tasks(batch_id=batch_id, path=cfg["db_path"])
|
||||
|
||||
with mock.patch("app.gui.chrome.is_running", return_value=False) as is_running, \
|
||||
mock.patch(
|
||||
"app.gui.accounts.launch_for_login",
|
||||
return_value={"ok": True, "launched": True, "reused": False},
|
||||
) as launch_for_login, \
|
||||
mock.patch(
|
||||
"app.gui.accounts.detect_login",
|
||||
return_value={"logged_in": True, "reason": None},
|
||||
) as detect_login, \
|
||||
mock.patch(
|
||||
"app.gui.editor.collect",
|
||||
return_value={"old_title": "旧标题", "old_cover_path": "old.jpg"},
|
||||
) as collect:
|
||||
summary = CollectWorker(tasks, db_path=cfg["db_path"], config=cfg).execute()
|
||||
|
||||
self.assertTrue(summary["ok"])
|
||||
self.assertEqual(1, summary["collected"])
|
||||
self.assertEqual("alias-a", summary["launched_accounts"][0]["alias"])
|
||||
is_running.assert_called_once_with(9222)
|
||||
launch_for_login.assert_called_once()
|
||||
self.assertEqual(2, detect_login.call_count)
|
||||
collect.assert_called_once()
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_collect_worker_preflight_blocks_when_chrome_launch_fails(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"])
|
||||
|
||||
with mock.patch("app.gui.chrome.is_running", return_value=False), \
|
||||
mock.patch(
|
||||
"app.gui.accounts.launch_for_login",
|
||||
side_effect=RuntimeError("Chrome 路径错误"),
|
||||
) as launch_for_login, \
|
||||
mock.patch("app.gui.accounts.detect_login") as detect_login, \
|
||||
mock.patch("app.gui.editor.collect") as collect:
|
||||
summary = CollectWorker(tasks, db_path=cfg["db_path"], config=cfg).execute()
|
||||
|
||||
self.assertTrue(summary["blocked"])
|
||||
self.assertEqual("ACCOUNT_NOT_READY", summary["reason"])
|
||||
self.assertEqual("alias-a", summary["not_running"][0]["alias"])
|
||||
is_running.assert_called_once_with(9222)
|
||||
self.assertEqual("CHROME_LAUNCH_FAILED", summary["reason"])
|
||||
self.assertEqual("alias-a", summary["launch_failed"][0]["alias"])
|
||||
self.assertIn("Chrome 启动失败", summary["launch_failed"][0]["reason"])
|
||||
launch_for_login.assert_called_once()
|
||||
detect_login.assert_not_called()
|
||||
collect.assert_not_called()
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_collect_worker_preflight_blocks_when_account_not_logged_in(self):
|
||||
def test_collect_worker_preflight_skips_logged_out_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)
|
||||
@@ -5116,14 +5167,96 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
||||
), mock.patch("app.gui.editor.collect") as collect:
|
||||
summary = CollectWorker(tasks, db_path=cfg["db_path"], config=cfg).execute()
|
||||
|
||||
self.assertTrue(summary["blocked"])
|
||||
self.assertFalse(summary.get("blocked"))
|
||||
self.assertTrue(summary["ok"])
|
||||
self.assertEqual(1, summary["skipped"])
|
||||
self.assertEqual("alias-a", summary["logged_out"][0]["alias"])
|
||||
self.assertIn("账号未登录", summary["logged_out"][0]["reason"])
|
||||
self.assertEqual("alias-a", summary["login_required_accounts"][0]["alias"])
|
||||
collect.assert_not_called()
|
||||
blocked_task = db.list_tasks(batch_id=batch_id, path=cfg["db_path"])[0]
|
||||
self.assertEqual("imported", blocked_task.stage)
|
||||
self.assertEqual("pending", blocked_task.status)
|
||||
self.assertIsNone(blocked_task.last_error)
|
||||
self.assertEqual("skipped", blocked_task.status)
|
||||
self.assertIn("账号未登录", blocked_task.last_error)
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_collect_worker_skips_remaining_tasks_when_login_drops_midrun(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)
|
||||
accounts.create_account("副店", "alias-b", debug_port=9223, 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",
|
||||
},
|
||||
{
|
||||
"source_file_abs": os.path.join(temp_dir, "input.xlsx"),
|
||||
"source_sheet": "商品",
|
||||
"source_row": 4,
|
||||
"account_name": "Excel副店",
|
||||
"alias": "alias-b",
|
||||
"item_id": "51100639512",
|
||||
},
|
||||
],
|
||||
path=cfg["db_path"],
|
||||
)
|
||||
tasks = db.list_tasks(batch_id=batch_id, path=cfg["db_path"])
|
||||
login_calls = []
|
||||
|
||||
def fake_login(account, path=None, config=None):
|
||||
login_calls.append(account.alias)
|
||||
if login_calls == ["alias-a"]:
|
||||
return {"logged_in": True, "reason": None}
|
||||
if login_calls == ["alias-a", "alias-b"]:
|
||||
return {"logged_in": True, "reason": None}
|
||||
if account.alias == "alias-a":
|
||||
return {"logged_in": False, "reason": "LOGIN_PAGE"}
|
||||
return {"logged_in": True, "reason": None}
|
||||
|
||||
def fake_collect(account, task, on_step=None):
|
||||
self.assertEqual("alias-b", account.alias)
|
||||
return {"old_title": "副店旧标题", "old_cover_path": "old-b.jpg"}
|
||||
|
||||
with mock.patch("app.gui.chrome.is_running", return_value=True), \
|
||||
mock.patch("app.gui.accounts.detect_login", side_effect=fake_login) as detect_login, \
|
||||
mock.patch("app.gui.editor.collect", side_effect=fake_collect) as collect:
|
||||
summary = CollectWorker(tasks, db_path=cfg["db_path"], config=cfg).execute()
|
||||
|
||||
self.assertTrue(summary["ok"])
|
||||
self.assertEqual(3, summary["done"])
|
||||
self.assertEqual(1, summary["collected"])
|
||||
self.assertEqual(2, summary["skipped"])
|
||||
self.assertEqual("alias-a", summary["login_required_accounts"][0]["alias"])
|
||||
self.assertIn("采集中途掉登录", summary["login_required_accounts"][0]["reason"])
|
||||
self.assertEqual(["alias-a", "alias-b", "alias-a", "alias-b"], login_calls)
|
||||
self.assertEqual(4, detect_login.call_count)
|
||||
collect.assert_called_once()
|
||||
|
||||
updated = db.list_tasks(batch_id=batch_id, path=cfg["db_path"])
|
||||
by_item = {task.item_id: task for task in updated}
|
||||
self.assertEqual("skipped", by_item["51100639510"].status)
|
||||
self.assertEqual("skipped", by_item["51100639511"].status)
|
||||
self.assertIn("采集中途掉登录", by_item["51100639510"].last_error)
|
||||
self.assertIn("采集中途掉登录", by_item["51100639511"].last_error)
|
||||
self.assertEqual("collected", by_item["51100639512"].stage)
|
||||
self.assertEqual("success", by_item["51100639512"].status)
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
@@ -5155,7 +5288,6 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
||||
message = warning.call_args[0][2]
|
||||
self.assertIn("Chrome 未启动", message)
|
||||
self.assertIn("本轮采集已中止", message)
|
||||
self.assertIn("不会自动打开账号 Chrome", message)
|
||||
self.assertIn("④ 账号管理", message)
|
||||
self.assertEqual([True], opened)
|
||||
self.assertIn("本轮采集已中止", statuses[-1])
|
||||
@@ -5163,6 +5295,44 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_collect_tab_completion_shows_login_summary_and_keep_chrome_hint(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
tab = CollectTab(
|
||||
config=self.make_config(temp_dir),
|
||||
status_callback=lambda _message: None,
|
||||
)
|
||||
self.addCleanup(tab.close)
|
||||
payload = {
|
||||
"collected": 0,
|
||||
"skipped": 2,
|
||||
"failed": 0,
|
||||
"login_required_accounts": [
|
||||
{
|
||||
"account_name": "主店",
|
||||
"alias": "alias-a",
|
||||
"reason": "采集中途掉登录: 账号未登录: LOGIN_PAGE",
|
||||
}
|
||||
],
|
||||
"launched_accounts": [
|
||||
{
|
||||
"account_name": "副店",
|
||||
"alias": "alias-b",
|
||||
"reason": "已启动",
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
with mock.patch("app.gui.QMessageBox.warning") as warning:
|
||||
tab._on_collect_finished(payload)
|
||||
|
||||
message = warning.call_args[0][2]
|
||||
self.assertIn("以下账号需要补登录", message)
|
||||
self.assertIn("主店(alias-a)", message)
|
||||
self.assertIn("已自动启动账号 Chrome", message)
|
||||
self.assertIn("不会自动关闭账号 Chrome", message)
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_write_back_worker_calls_excel_write_back(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
db_path = os.path.join(temp_dir, "db.sqlite")
|
||||
|
||||
Reference in New Issue
Block a user