from __future__ import annotations import unittest from types import SimpleNamespace from biliup_next.app.retry_meta import retry_meta_for_step from biliup_next.app.task_engine import next_runnable_step from biliup_next.app.task_policies import resolve_failure from biliup_next.core.errors import ModuleError from biliup_next.core.models import TaskStep class _Repo: def __init__(self) -> None: self.steps = [TaskStep(None, "task-1", "transcribe", "running", None, None, 0, None, None)] self.step_updates: list[tuple] = [] self.task_updates: list[tuple] = [] def list_steps(self, task_id: str): # noqa: ANN001 return list(self.steps) def get_task(self, task_id: str): # noqa: ANN001 return SimpleNamespace(id=task_id, status="running") def update_step_status(self, task_id: str, step_name: str, status: str, **kwargs) -> None: # noqa: ANN001 self.step_updates.append((task_id, step_name, status, kwargs)) self.steps = [TaskStep(None, task_id, step_name, status, kwargs.get("error_code"), kwargs.get("error_message"), kwargs.get("retry_count", 0), kwargs.get("started_at"), kwargs.get("finished_at"))] def update_task_status(self, task_id: str, status: str, updated_at: str) -> None: self.task_updates.append((task_id, status, updated_at)) class TranscribeRetryPolicyTests(unittest.TestCase): def test_retry_meta_reports_wait_window_for_transcribe(self) -> None: step = TaskStep(None, "task-1", "transcribe", "failed_retryable", "ERR", "boom", 1, None, "2099-01-01T00:00:00+00:00") payload = retry_meta_for_step(step, {"transcribe": {"retry_schedule_minutes": [10]}}) self.assertIsNotNone(payload) self.assertFalse(payload["retry_due"]) self.assertEqual(payload["retry_wait_seconds"], 600) def test_next_runnable_step_waits_for_retryable_transcribe(self) -> None: task = SimpleNamespace(id="task-1", status="failed_retryable") steps = { "transcribe": TaskStep(None, "task-1", "transcribe", "failed_retryable", "ERR", "boom", 1, None, "2099-01-01T00:00:00+00:00"), } state = { "settings": { "transcribe": {"retry_schedule_minutes": [10]}, "comment": {"enabled": True}, "collection": {"enabled": True}, "paths": {}, "publish": {}, } } step_name, waiting_payload = next_runnable_step(task, steps, state) self.assertIsNone(step_name) self.assertIsNotNone(waiting_payload) self.assertEqual(waiting_payload["step"], "transcribe") def test_resolve_failure_adds_transcribe_retry_delay(self) -> None: repo = _Repo() task = SimpleNamespace(id="task-1", status="running") state = { "settings": { "transcribe": {"retry_schedule_minutes": [5, 10]}, "publish": {}, "comment": {}, "paths": {}, "collection": {"enabled": True}, } } result = resolve_failure(task, repo, state, ModuleError(code="GROQ_TRANSCRIBE_FAILED", message="boom", retryable=True)) self.assertEqual(result["payload"]["retry_status"], "failed_retryable") self.assertEqual(result["payload"]["next_retry_delay_seconds"], 300) if __name__ == "__main__": unittest.main()