fix(product-suite): keep retries in current results
This commit is contained in:
@@ -856,6 +856,7 @@ class ProductSuiteGuiTests(TempDirMixin, unittest.TestCase):
|
||||
state.thread = mock.Mock()
|
||||
state.generation_run_token = "watchdog-run"
|
||||
state.current_job_ids = [job.id for job in jobs]
|
||||
state.generation_job_ids = [job.id for job in jobs]
|
||||
state.total = len(jobs)
|
||||
state.started_at = time.monotonic()
|
||||
tab._generation_run_states["watchdog-run"] = state.key
|
||||
@@ -972,6 +973,7 @@ class ProductSuiteGuiTests(TempDirMixin, unittest.TestCase):
|
||||
state.thread = mock.Mock()
|
||||
state.generation_run_token = "thread-fallback"
|
||||
state.current_job_ids = [job.id]
|
||||
state.generation_job_ids = [job.id]
|
||||
state.total = 1
|
||||
state.started_at = time.monotonic()
|
||||
tab._generation_run_states["thread-fallback"] = state.key
|
||||
@@ -1127,6 +1129,260 @@ class ProductSuiteGuiTests(TempDirMixin, unittest.TestCase):
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_failed_job_retry_replaces_current_slot_and_keeps_history(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
config = self._config(temp_dir)
|
||||
project, sources = self._create_project_with_assets(temp_dir, config, 1)
|
||||
source = sources[0]
|
||||
|
||||
success_path = os.path.join(temp_dir, "success.jpg")
|
||||
retry_path = os.path.join(temp_dir, "retry.jpg")
|
||||
self._write_image(success_path)
|
||||
self._write_image(retry_path)
|
||||
success_asset = image_studio.add_asset(
|
||||
project.id,
|
||||
"generated_main",
|
||||
local_path=success_path,
|
||||
parent_asset_id=source.id,
|
||||
path=config["db_path"],
|
||||
)
|
||||
success_job = image_studio.create_job(
|
||||
project.id,
|
||||
source_asset_id=source.id,
|
||||
job_type="白底图",
|
||||
prompt="成功图",
|
||||
path=config["db_path"],
|
||||
)
|
||||
success_job = image_studio.update_job_status(
|
||||
success_job.id,
|
||||
"succeeded",
|
||||
output_asset_id=success_asset.id,
|
||||
path=config["db_path"],
|
||||
)
|
||||
failed_job = image_studio.create_job(
|
||||
project.id,
|
||||
source_asset_id=source.id,
|
||||
job_type="场景图",
|
||||
prompt="失败图",
|
||||
path=config["db_path"],
|
||||
)
|
||||
failed_job = image_studio.update_job_status(
|
||||
failed_job.id,
|
||||
"failed",
|
||||
error="上游生成失败",
|
||||
path=config["db_path"],
|
||||
)
|
||||
|
||||
tab = ProductSuiteTab(config=config, db_path=config["db_path"])
|
||||
self.addCleanup(tab.close)
|
||||
state = tab._displayed_state
|
||||
state.account_alias = "alias-a"
|
||||
state.item_id = project.item_id
|
||||
state.project_id = project.id
|
||||
state.project_binding_state = project.binding_state
|
||||
state.current_job_ids = [success_job.id, failed_job.id]
|
||||
tab._load_state(state)
|
||||
messages = []
|
||||
tab._message = lambda title, message, **kwargs: messages.append(
|
||||
(title, message)
|
||||
)
|
||||
|
||||
def fake_run_jobs(jobs, **kwargs):
|
||||
job = list(jobs)[0]
|
||||
retry_asset = image_studio.add_asset(
|
||||
project.id,
|
||||
"generated_main",
|
||||
local_path=retry_path,
|
||||
parent_asset_id=source.id,
|
||||
path=config["db_path"],
|
||||
)
|
||||
image_studio.update_job_status(
|
||||
job.id,
|
||||
"succeeded",
|
||||
output_asset_id=retry_asset.id,
|
||||
path=config["db_path"],
|
||||
)
|
||||
return {
|
||||
"total": 1,
|
||||
"success": 1,
|
||||
"failed": 0,
|
||||
"cancelled": 0,
|
||||
"jobs": [],
|
||||
}
|
||||
|
||||
with mock.patch(
|
||||
"app.gui.workers.image_studio_generation.run_jobs",
|
||||
side_effect=fake_run_jobs,
|
||||
):
|
||||
tab.retry_job(failed_job)
|
||||
generation_thread = state.thread
|
||||
deadline = time.monotonic() + 3
|
||||
while state.worker is not None and time.monotonic() < deadline:
|
||||
QTest.qWait(20)
|
||||
self.app.processEvents()
|
||||
while (
|
||||
generation_thread is not None
|
||||
and generation_thread.isRunning()
|
||||
and time.monotonic() < deadline
|
||||
):
|
||||
QTest.qWait(20)
|
||||
self.app.processEvents()
|
||||
self.assertFalse(generation_thread.isRunning())
|
||||
|
||||
all_jobs = image_studio.list_jobs(project.id, path=config["db_path"])
|
||||
retry_jobs = [
|
||||
job
|
||||
for job in all_jobs
|
||||
if job.id not in {success_job.id, failed_job.id}
|
||||
]
|
||||
self.assertEqual(1, len(retry_jobs))
|
||||
retry_job = retry_jobs[0]
|
||||
self.assertEqual(
|
||||
[success_job.id, retry_job.id],
|
||||
state.current_job_ids,
|
||||
)
|
||||
self.assertEqual([], state.generation_job_ids)
|
||||
self.assertEqual(
|
||||
[success_job.id, retry_job.id],
|
||||
[job.id for job in tab._jobs_for_state(state)],
|
||||
)
|
||||
self.assertEqual("图片重试成功", messages[-1][0])
|
||||
|
||||
state.show_history = True
|
||||
history_ids = {job.id for job in tab._jobs_for_state(state)}
|
||||
self.assertEqual(
|
||||
{success_job.id, failed_job.id, retry_job.id},
|
||||
history_ids,
|
||||
)
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_retry_tracks_only_new_job_and_preserves_history_view(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
config = self._config(temp_dir)
|
||||
project, sources = self._create_project_with_assets(temp_dir, config, 1)
|
||||
source = sources[0]
|
||||
success_job = image_studio.create_job(
|
||||
project.id,
|
||||
source_asset_id=source.id,
|
||||
job_type="白底图",
|
||||
prompt="成功图",
|
||||
path=config["db_path"],
|
||||
)
|
||||
success_job = image_studio.update_job_status(
|
||||
success_job.id,
|
||||
"succeeded",
|
||||
path=config["db_path"],
|
||||
)
|
||||
failed_job = image_studio.create_job(
|
||||
project.id,
|
||||
source_asset_id=source.id,
|
||||
job_type="场景图",
|
||||
prompt="失败图",
|
||||
path=config["db_path"],
|
||||
)
|
||||
failed_job = image_studio.update_job_status(
|
||||
failed_job.id,
|
||||
"failed",
|
||||
path=config["db_path"],
|
||||
)
|
||||
retry_job = image_studio.create_job(
|
||||
project.id,
|
||||
source_asset_id=source.id,
|
||||
job_type="场景图",
|
||||
prompt="重试图",
|
||||
path=config["db_path"],
|
||||
)
|
||||
|
||||
tab = ProductSuiteTab(config=config, db_path=config["db_path"])
|
||||
self.addCleanup(tab.close)
|
||||
state = tab._displayed_state
|
||||
state.account_alias = "alias-a"
|
||||
state.item_id = project.item_id
|
||||
state.project_id = project.id
|
||||
state.project_binding_state = project.binding_state
|
||||
state.current_job_ids = [success_job.id, failed_job.id]
|
||||
state.generation_mode = "retry"
|
||||
state.generation_retry_job_id = failed_job.id
|
||||
state.total = 1
|
||||
|
||||
tab._set_generation_job_ids(state, [retry_job.id])
|
||||
|
||||
self.assertEqual(
|
||||
[success_job.id, retry_job.id],
|
||||
state.current_job_ids,
|
||||
)
|
||||
self.assertEqual([retry_job.id], state.generation_job_ids)
|
||||
snapshot = tab._generation_job_snapshot(state)
|
||||
self.assertEqual(1, snapshot["job_ids"])
|
||||
self.assertEqual(1, snapshot["active"])
|
||||
|
||||
image_studio.update_job_status(
|
||||
retry_job.id,
|
||||
"failed",
|
||||
error="重试仍失败",
|
||||
path=config["db_path"],
|
||||
)
|
||||
state.worker = mock.Mock()
|
||||
state.thread = mock.Mock()
|
||||
state.generation_run_token = "retry-failed"
|
||||
state.started_at = time.monotonic()
|
||||
tab._generation_run_states["retry-failed"] = state.key
|
||||
messages = []
|
||||
tab._message = lambda title, message, **kwargs: messages.append(
|
||||
(title, message)
|
||||
)
|
||||
|
||||
self.assertTrue(
|
||||
tab._finalize_generation(
|
||||
state,
|
||||
"retry-failed",
|
||||
{"total": 1, "success": 0, "failed": 1},
|
||||
source="worker",
|
||||
)
|
||||
)
|
||||
self.assertEqual("图片重试失败", messages[-1][0])
|
||||
retry_cards = [
|
||||
card
|
||||
for card in tab.findChildren(SuiteResultCard)
|
||||
if card.job.id == retry_job.id
|
||||
]
|
||||
self.assertEqual(1, len(retry_cards))
|
||||
self.assertTrue(
|
||||
any(
|
||||
button.text() == "重试"
|
||||
for button in retry_cards[0].findChildren(QPushButton)
|
||||
)
|
||||
)
|
||||
|
||||
state.show_history = True
|
||||
tab._load_state(state)
|
||||
with mock.patch.object(
|
||||
tab,
|
||||
"_start_thread",
|
||||
return_value=mock.Mock(),
|
||||
):
|
||||
self.assertTrue(
|
||||
tab.start_generation(
|
||||
state,
|
||||
specs=[
|
||||
{
|
||||
"source_asset_id": source.id,
|
||||
"job_type": failed_job.job_type,
|
||||
"prompt": failed_job.prompt,
|
||||
}
|
||||
],
|
||||
retry_job_id=failed_job.id,
|
||||
)
|
||||
)
|
||||
self.assertTrue(state.show_history)
|
||||
self.assertTrue(tab.history_button.isChecked())
|
||||
state.worker = None
|
||||
state.thread = None
|
||||
state.generation_run_token = ""
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_original_list_expands_without_internal_scrollbars(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
config = self._config(temp_dir)
|
||||
|
||||
Reference in New Issue
Block a user