feat(status): default legacy batches to normal
This commit is contained in:
+114
-1
@@ -122,12 +122,125 @@ class DbTests(TempDirMixin, unittest.TestCase):
|
||||
task = db.get_task(1, conn=conn)
|
||||
self.assertEqual(0, task.cover_reset_count)
|
||||
self.assertIsNone(task.cover_reset_at)
|
||||
self.assertIsNone(task.product_status)
|
||||
self.assertEqual("normal", task.product_status)
|
||||
self.assertEqual(
|
||||
db.LEGACY_PRODUCT_STATUS_DEFAULT_NOTE,
|
||||
task.product_status_note,
|
||||
)
|
||||
self.assertIsNone(task.product_status_at)
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_init_db_defaults_only_pre_feature_active_missing_status_tasks(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
db_path = os.path.join(temp_dir, "cmshopee.db")
|
||||
db.init_db(db_path)
|
||||
batch_ids = {}
|
||||
for index, name in enumerate(
|
||||
(
|
||||
"legacy-null",
|
||||
"legacy-blank",
|
||||
"legacy-unknown",
|
||||
"boundary",
|
||||
"after",
|
||||
"deleted",
|
||||
),
|
||||
start=1,
|
||||
):
|
||||
batch_id = db.create_batch([f"{name}.xlsx"], path=db_path)
|
||||
batch_ids[name] = batch_id
|
||||
db.insert_tasks(
|
||||
batch_id,
|
||||
[
|
||||
{
|
||||
"source_file_abs": os.path.join(temp_dir, f"{name}.xlsx"),
|
||||
"source_sheet": "Sheet1",
|
||||
"source_row": 2,
|
||||
"account_name": "店铺",
|
||||
"alias": "alias",
|
||||
"item_id": str(51100639700 + index),
|
||||
}
|
||||
],
|
||||
path=db_path,
|
||||
)
|
||||
|
||||
conn = db.connect(db_path)
|
||||
try:
|
||||
with conn:
|
||||
conn.execute(
|
||||
"UPDATE batches SET created_at = ? WHERE id IN (?, ?, ?, ?)",
|
||||
(
|
||||
"2026-07-18T16:34:36",
|
||||
batch_ids["legacy-null"],
|
||||
batch_ids["legacy-blank"],
|
||||
batch_ids["legacy-unknown"],
|
||||
batch_ids["deleted"],
|
||||
),
|
||||
)
|
||||
conn.execute(
|
||||
"UPDATE batches SET created_at = ? WHERE id = ?",
|
||||
(db.PRODUCT_STATUS_FEATURE_INTRODUCED_AT, batch_ids["boundary"]),
|
||||
)
|
||||
conn.execute(
|
||||
"UPDATE batches SET created_at = ? WHERE id = ?",
|
||||
("2026-07-18T16:34:38", batch_ids["after"]),
|
||||
)
|
||||
blank_task = db.list_tasks(
|
||||
batch_id=batch_ids["legacy-blank"],
|
||||
conn=conn,
|
||||
)[0]
|
||||
unknown_task = db.list_tasks(
|
||||
batch_id=batch_ids["legacy-unknown"],
|
||||
conn=conn,
|
||||
)[0]
|
||||
conn.execute(
|
||||
"UPDATE tasks SET product_status = ' ' WHERE id = ?",
|
||||
(blank_task.id,),
|
||||
)
|
||||
conn.execute(
|
||||
"UPDATE tasks SET product_status = 'unknown', product_status_note = '已有未知状态' WHERE id = ?",
|
||||
(unknown_task.id,),
|
||||
)
|
||||
conn.execute(
|
||||
"UPDATE batches SET deleted_at = ? WHERE id = ?",
|
||||
("2026-07-19T00:00:00", batch_ids["deleted"]),
|
||||
)
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
db.init_db(db_path)
|
||||
legacy_null = db.list_tasks(batch_id=batch_ids["legacy-null"], path=db_path)[0]
|
||||
legacy_blank = db.list_tasks(batch_id=batch_ids["legacy-blank"], path=db_path)[0]
|
||||
legacy_unknown = db.list_tasks(batch_id=batch_ids["legacy-unknown"], path=db_path)[0]
|
||||
boundary = db.list_tasks(batch_id=batch_ids["boundary"], path=db_path)[0]
|
||||
after = db.list_tasks(batch_id=batch_ids["after"], path=db_path)[0]
|
||||
deleted = db.list_tasks(
|
||||
batch_id=batch_ids["deleted"],
|
||||
path=db_path,
|
||||
include_deleted=True,
|
||||
)[0]
|
||||
|
||||
for task in (legacy_null, legacy_blank):
|
||||
self.assertEqual("normal", task.product_status)
|
||||
self.assertEqual(db.LEGACY_PRODUCT_STATUS_DEFAULT_NOTE, task.product_status_note)
|
||||
self.assertIsNone(task.product_status_at)
|
||||
self.assertEqual("unknown", legacy_unknown.product_status)
|
||||
self.assertEqual("已有未知状态", legacy_unknown.product_status_note)
|
||||
self.assertIsNone(boundary.product_status)
|
||||
self.assertIsNone(after.product_status)
|
||||
self.assertIsNone(deleted.product_status)
|
||||
|
||||
first_updated_at = legacy_null.updated_at
|
||||
db.init_db(db_path)
|
||||
self.assertEqual(
|
||||
first_updated_at,
|
||||
db.get_task(legacy_null.id, path=db_path).updated_at,
|
||||
)
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_account_batch_task_lifecycle(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
db_path = os.path.join(temp_dir, "cmshopee.db")
|
||||
|
||||
@@ -941,48 +941,6 @@ class EditorLoginTests(unittest.TestCase):
|
||||
self.assertIsNone(result["product_status_note"])
|
||||
self.assertIn("状态读取失败", result["product_status_error"])
|
||||
|
||||
def test_recheck_product_status_reads_only_status_and_closes_new_tab(self):
|
||||
cdp = FakeProductCDP("ws-status-recheck", alerts=[])
|
||||
steps = []
|
||||
|
||||
with mock.patch("app.editor.open_product", return_value=cdp), mock.patch(
|
||||
"app.editor.read_title"
|
||||
) as read_title, mock.patch("app.editor.read_cover_src") as read_cover_src, mock.patch(
|
||||
"app.editor.download_cover"
|
||||
) as download_cover:
|
||||
result = editor.recheck_product_status(
|
||||
{"debug_port": 9222},
|
||||
{"item_id": "51100639510"},
|
||||
on_step=steps.append,
|
||||
)
|
||||
|
||||
self.assertEqual("normal", result["product_status"])
|
||||
self.assertIsNone(result["close_target_confirmed"])
|
||||
self.assertEqual(["read_product_status"], steps)
|
||||
self.assertTrue(cdp.closed)
|
||||
read_title.assert_not_called()
|
||||
read_cover_src.assert_not_called()
|
||||
download_cover.assert_not_called()
|
||||
|
||||
def test_recheck_product_status_does_not_return_unknown_snapshot_on_read_error(self):
|
||||
cdp = FakeProductCDP("ws-status-recheck", alerts=[])
|
||||
|
||||
with mock.patch("app.editor.open_product", return_value=cdp), mock.patch(
|
||||
"app.editor.read_product_status",
|
||||
return_value={
|
||||
"product_status": "unknown",
|
||||
"product_status_note": None,
|
||||
"product_status_error": "CDP 页面读取失败",
|
||||
},
|
||||
):
|
||||
with self.assertRaises(editor.EditorError):
|
||||
editor.recheck_product_status(
|
||||
{"debug_port": 9222},
|
||||
{"item_id": "51100639510"},
|
||||
)
|
||||
|
||||
self.assertTrue(cdp.closed)
|
||||
|
||||
def test_collect_reads_product_status_before_title_and_returns_snapshot(self):
|
||||
cdp = FakeProductCDP("ws-new", alerts=[])
|
||||
steps = []
|
||||
|
||||
+13
-70
@@ -7079,8 +7079,7 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
||||
message = warning.call_args[0][2]
|
||||
self.assertIn("未上架:1 条", message)
|
||||
self.assertIn("审核中:1 条", message)
|
||||
self.assertIn("尚未检测商品状态:1 条", message)
|
||||
self.assertIn("点击「重新检测商品状态」", message)
|
||||
self.assertIn("状态未知:1 条", message)
|
||||
self.assertNotIn("缺少新标题", message)
|
||||
question.assert_not_called()
|
||||
run_worker.assert_not_called()
|
||||
@@ -8375,74 +8374,6 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_status_recheck_uses_current_generated_filter_without_changing_task_fields(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
cfg = self.make_config(temp_dir)
|
||||
db.init_db(cfg["db_path"])
|
||||
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,
|
||||
"alias": "alias-a",
|
||||
"item_id": "51100639510",
|
||||
},
|
||||
{
|
||||
"source_file_abs": os.path.join(temp_dir, "input.xlsx"),
|
||||
"source_sheet": "商品",
|
||||
"source_row": 3,
|
||||
"alias": "alias-a",
|
||||
"item_id": "51100639511",
|
||||
},
|
||||
],
|
||||
path=cfg["db_path"],
|
||||
)
|
||||
tasks = db.list_tasks(batch_id=batch_id, path=cfg["db_path"])
|
||||
for task in tasks:
|
||||
db.set_collected(task.id, "旧标题", "old.jpg", path=cfg["db_path"])
|
||||
db.set_generated(task.id, "新标题", "new.jpg", path=cfg["db_path"])
|
||||
tab = CollectTab(config=cfg)
|
||||
self.addCleanup(tab.close)
|
||||
tab.status_filter.setCurrentIndex(tab.status_filter.findData("generated"))
|
||||
|
||||
class FakeWorker:
|
||||
def __init__(self, worker_tasks, **kwargs):
|
||||
self.tasks = list(worker_tasks)
|
||||
self.kwargs = kwargs
|
||||
self.progress = DummySignal()
|
||||
self.row_updated = DummySignal()
|
||||
self.log = DummySignal()
|
||||
self.failed = DummySignal()
|
||||
self.finished = DummySignal()
|
||||
self.cancelled = DummySignal()
|
||||
|
||||
def cancel(self):
|
||||
pass
|
||||
|
||||
thread = FakeThread()
|
||||
with mock.patch(
|
||||
"app.gui.QMessageBox.question",
|
||||
return_value=gui.QMessageBox.Yes,
|
||||
) as question, mock.patch(
|
||||
"app.gui.StatusRecheckWorker", FakeWorker
|
||||
), mock.patch("app.gui.run_worker", return_value=thread):
|
||||
tab.recheck_product_status()
|
||||
|
||||
self.assertTrue(thread.started)
|
||||
self.assertEqual([task.id for task in tasks], [task.id for task in tab.status_recheck_worker.tasks])
|
||||
self.assertEqual(cfg["db_path"], tab.status_recheck_worker.kwargs["db_path"])
|
||||
self.assertIn("不会读取或覆盖旧标题", question.call_args[0][2])
|
||||
self.assertFalse(tab.collect_button.isEnabled())
|
||||
self.assertFalse(tab.status_recheck_button.isEnabled())
|
||||
unchanged = db.list_tasks(batch_id=batch_id, path=cfg["db_path"])
|
||||
self.assertTrue(all(task.stage == "generated" for task in unchanged))
|
||||
self.assertTrue(all(task.product_status is None for task in unchanged))
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_collect_scope_dialog_uses_safe_default_and_warning_all_status_choice(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
tab = CollectTab(config=self.make_config(temp_dir))
|
||||
@@ -8487,6 +8418,18 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_collect_tab_does_not_show_legacy_status_recheck_entry(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
cfg = self.make_config(temp_dir)
|
||||
db.init_db(cfg["db_path"])
|
||||
tab = CollectTab(config=cfg)
|
||||
self.addCleanup(tab.close)
|
||||
|
||||
self.assertIsNone(tab.findChild(QPushButton, "statusRecheckButton"))
|
||||
self.assertFalse(hasattr(tab, "recheck_product_status"))
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_collect_scope_dialog_accepts_default_and_escape_cancels(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
tab = CollectTab(config=self.make_config(temp_dir))
|
||||
|
||||
@@ -190,35 +190,10 @@ class ProductStatusTests(unittest.TestCase):
|
||||
self.assertEqual([1], [task.id for task in plan["executable"]])
|
||||
self.assertEqual([2], [task.id for task in plan["unlisted"]])
|
||||
self.assertEqual([3], [task.id for task in plan["reviewing"]])
|
||||
self.assertEqual([], [task.id for task in plan["unknown"]])
|
||||
self.assertEqual([4], [task.id for task in plan["unverified"]])
|
||||
self.assertEqual([4], [task.id for task in plan["unknown"]])
|
||||
self.assertEqual([5], [task.id for task in plan["missing_title"]])
|
||||
self.assertEqual([5], [task.id for task in plan["missing_cover"]])
|
||||
self.assertEqual(3, plan["status_scope_excluded"])
|
||||
self.assertEqual(1, plan["content_scope_excluded"])
|
||||
self.assertEqual(4, plan["scope_excluded"])
|
||||
self.assertNotEqual(plan["fingerprint"], changed["fingerprint"])
|
||||
|
||||
def test_apply_plan_separates_missing_status_from_confirmed_unknown(self):
|
||||
tasks = [
|
||||
SimpleNamespace(
|
||||
id=1,
|
||||
updated_at="2026-07-18T10:00:00",
|
||||
product_status=None,
|
||||
new_title="新标题",
|
||||
new_cover_path="new.jpg",
|
||||
),
|
||||
SimpleNamespace(
|
||||
id=2,
|
||||
updated_at="2026-07-18T10:00:01",
|
||||
product_status="unknown",
|
||||
new_title="新标题",
|
||||
new_cover_path="new.jpg",
|
||||
),
|
||||
]
|
||||
|
||||
plan = product_status.build_apply_plan(tasks, "cover")
|
||||
|
||||
self.assertEqual([1], [task.id for task in plan["unverified"]])
|
||||
self.assertEqual([2], [task.id for task in plan["unknown"]])
|
||||
self.assertEqual(2, plan["status_scope_excluded"])
|
||||
|
||||
@@ -25,7 +25,6 @@ from app.gui.workers import (
|
||||
ProductSuiteGenerateWorker,
|
||||
ProductSuiteHistoryExportWorker,
|
||||
CollectWorker,
|
||||
StatusRecheckWorker,
|
||||
)
|
||||
|
||||
|
||||
@@ -281,192 +280,6 @@ class WorkerTests(unittest.TestCase):
|
||||
all(task.stage == "collected" for task in db.list_tasks(batch_id=batch_id, path=db_path))
|
||||
)
|
||||
|
||||
def test_status_recheck_worker_updates_only_status_for_historical_generated_tasks(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
db_path = os.path.join(temp_dir, "cmshopee.db")
|
||||
db.init_db(db_path)
|
||||
batch_id = db.create_batch(["input.xlsx"], path=db_path)
|
||||
db.insert_tasks(
|
||||
batch_id,
|
||||
[
|
||||
{
|
||||
"source_file_abs": os.path.join(temp_dir, "input.xlsx"),
|
||||
"source_sheet": "Sheet1",
|
||||
"source_row": index,
|
||||
"account_name": "店铺",
|
||||
"alias": "alias",
|
||||
"item_id": str(51100639700 + index),
|
||||
}
|
||||
for index in range(2, 6)
|
||||
],
|
||||
path=db_path,
|
||||
)
|
||||
tasks = db.list_tasks(batch_id=batch_id, path=db_path)
|
||||
for index, task in enumerate(tasks):
|
||||
db.set_collected(task.id, f"旧标题{index}", f"old-{index}.jpg", path=db_path)
|
||||
db.set_generated(task.id, f"新标题{index}", f"new-{index}.jpg", path=db_path)
|
||||
db.set_applied(tasks[-1].id, committed=True, path=db_path)
|
||||
statuses = ["normal", "unlisted", "reviewing", "unknown"]
|
||||
account = SimpleNamespace(alias="alias", account_name="店铺", debug_port=9222)
|
||||
|
||||
def fake_recheck(_account, task, on_step=None):
|
||||
on_step("read_product_status")
|
||||
status = statuses.pop(0)
|
||||
return {
|
||||
"product_status": status,
|
||||
"product_status_note": f"{status} 提示",
|
||||
"close_target_confirmed": True,
|
||||
}
|
||||
|
||||
with mock.patch(
|
||||
"app.gui.workers.accounts.list_accounts", return_value=[account]
|
||||
), mock.patch(
|
||||
"app.gui.workers.accounts.detect_login",
|
||||
return_value={"logged_in": True, "reason": None},
|
||||
), mock.patch(
|
||||
"app.gui.workers.editor.recheck_product_status",
|
||||
side_effect=fake_recheck,
|
||||
):
|
||||
summary = StatusRecheckWorker(
|
||||
tasks,
|
||||
db_path=db_path,
|
||||
preflight=False,
|
||||
).execute()
|
||||
|
||||
self.assertEqual(4, summary["rechecked"])
|
||||
self.assertEqual(0, summary["failed"])
|
||||
self.assertEqual(
|
||||
{"normal": 1, "unlisted": 1, "reviewing": 1, "unknown": 1},
|
||||
summary["product_status_counts"],
|
||||
)
|
||||
refreshed = db.list_tasks(batch_id=batch_id, path=db_path)
|
||||
self.assertEqual(
|
||||
["generated", "generated", "generated", "applied"],
|
||||
[task.stage for task in refreshed],
|
||||
)
|
||||
self.assertEqual(
|
||||
["success", "success", "success", "success"],
|
||||
[task.status for task in refreshed],
|
||||
)
|
||||
self.assertEqual(
|
||||
[f"旧标题{index}" for index in range(4)],
|
||||
[task.old_title for task in refreshed],
|
||||
)
|
||||
self.assertEqual(
|
||||
[f"新标题{index}" for index in range(4)],
|
||||
[task.new_title for task in refreshed],
|
||||
)
|
||||
self.assertEqual(
|
||||
[f"new-{index}.jpg" for index in range(4)],
|
||||
[task.new_cover_path for task in refreshed],
|
||||
)
|
||||
self.assertEqual(
|
||||
["normal", "unlisted", "reviewing", "unknown"],
|
||||
[task.product_status for task in refreshed],
|
||||
)
|
||||
self.assertTrue(all(task.product_status_at for task in refreshed))
|
||||
run_log = db.list_run_logs(limit=1, run_type="status_recheck", path=db_path)[0]
|
||||
self.assertEqual("done", run_log.status)
|
||||
self.assertEqual(4, run_log.success_count)
|
||||
|
||||
def test_status_recheck_worker_failure_keeps_existing_snapshot_and_workflow(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
db_path = os.path.join(temp_dir, "cmshopee.db")
|
||||
db.init_db(db_path)
|
||||
batch_id = db.create_batch(["input.xlsx"], path=db_path)
|
||||
db.insert_tasks(
|
||||
batch_id,
|
||||
[
|
||||
{
|
||||
"source_file_abs": os.path.join(temp_dir, "input.xlsx"),
|
||||
"source_sheet": "Sheet1",
|
||||
"source_row": 2,
|
||||
"account_name": "店铺",
|
||||
"alias": "alias",
|
||||
"item_id": "51100639801",
|
||||
}
|
||||
],
|
||||
path=db_path,
|
||||
)
|
||||
task = db.list_tasks(batch_id=batch_id, path=db_path)[0]
|
||||
db.set_collected(
|
||||
task.id,
|
||||
"旧标题",
|
||||
"old.jpg",
|
||||
product_status_value="normal",
|
||||
product_status_note="历史快照",
|
||||
product_status_at="2026-07-01T10:00:00",
|
||||
path=db_path,
|
||||
)
|
||||
db.set_generated(task.id, "新标题", "new.jpg", path=db_path)
|
||||
task = db.get_task(task.id, path=db_path)
|
||||
account = SimpleNamespace(alias="alias", account_name="店铺", debug_port=9222)
|
||||
|
||||
with mock.patch(
|
||||
"app.gui.workers.accounts.list_accounts", return_value=[account]
|
||||
), mock.patch(
|
||||
"app.gui.workers.accounts.detect_login",
|
||||
return_value={"logged_in": True, "reason": None},
|
||||
), mock.patch(
|
||||
"app.gui.workers.editor.recheck_product_status",
|
||||
side_effect=RuntimeError("CDP 商品页读取失败"),
|
||||
):
|
||||
summary = StatusRecheckWorker(
|
||||
[task],
|
||||
db_path=db_path,
|
||||
preflight=False,
|
||||
).execute()
|
||||
|
||||
refreshed = db.get_task(task.id, path=db_path)
|
||||
self.assertEqual(1, summary["failed"])
|
||||
self.assertEqual("generated", refreshed.stage)
|
||||
self.assertEqual("success", refreshed.status)
|
||||
self.assertEqual("normal", refreshed.product_status)
|
||||
self.assertEqual("历史快照", refreshed.product_status_note)
|
||||
self.assertEqual("2026-07-01T10:00:00", refreshed.product_status_at)
|
||||
self.assertEqual("新标题", refreshed.new_title)
|
||||
self.assertEqual("new.jpg", refreshed.new_cover_path)
|
||||
|
||||
def test_status_recheck_worker_cancel_keeps_generated_task_unchanged(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
db_path = os.path.join(temp_dir, "cmshopee.db")
|
||||
db.init_db(db_path)
|
||||
batch_id = db.create_batch(["input.xlsx"], path=db_path)
|
||||
db.insert_tasks(
|
||||
batch_id,
|
||||
[
|
||||
{
|
||||
"source_file_abs": os.path.join(temp_dir, "input.xlsx"),
|
||||
"source_sheet": "Sheet1",
|
||||
"source_row": 2,
|
||||
"account_name": "店铺",
|
||||
"alias": "alias",
|
||||
"item_id": "51100639802",
|
||||
}
|
||||
],
|
||||
path=db_path,
|
||||
)
|
||||
task = db.list_tasks(batch_id=batch_id, path=db_path)[0]
|
||||
db.set_collected(task.id, "旧标题", "old.jpg", path=db_path)
|
||||
db.set_generated(task.id, "新标题", "new.jpg", path=db_path)
|
||||
task = db.get_task(task.id, path=db_path)
|
||||
worker = StatusRecheckWorker([task], db_path=db_path, preflight=False)
|
||||
worker.cancel()
|
||||
|
||||
with mock.patch("app.gui.workers.accounts.list_accounts", return_value=[]), mock.patch(
|
||||
"app.gui.workers.editor.recheck_product_status"
|
||||
) as recheck:
|
||||
summary = worker.execute()
|
||||
|
||||
refreshed = db.get_task(task.id, path=db_path)
|
||||
self.assertEqual(0, summary["done"])
|
||||
self.assertEqual(0, summary["rechecked"])
|
||||
self.assertEqual("generated", refreshed.stage)
|
||||
self.assertEqual("success", refreshed.status)
|
||||
recheck.assert_not_called()
|
||||
run_log = db.list_run_logs(limit=1, run_type="status_recheck", path=db_path)[0]
|
||||
self.assertEqual("cancelled", run_log.status)
|
||||
|
||||
def test_run_worker_rejects_plain_object(self):
|
||||
with self.assertRaises(TypeError):
|
||||
run_worker(object(), start=False)
|
||||
|
||||
Reference in New Issue
Block a user