104 lines
3.9 KiB
Python
104 lines
3.9 KiB
Python
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
|
|
from biliup_next.modules.song_detect.providers.qwen_cli import QwenCliSongDetector
|
|
|
|
|
|
class _Repo:
|
|
def __init__(self) -> None:
|
|
self.steps = [TaskStep(None, "task-1", "song_detect", "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 SongDetectRetryPolicyTests(unittest.TestCase):
|
|
def test_retry_meta_reports_wait_window_for_song_detect(self) -> None:
|
|
step = TaskStep(None, "task-1", "song_detect", "failed_retryable", "ERR", "boom", 1, None, "2099-01-01T00:00:00+00:00")
|
|
|
|
payload = retry_meta_for_step(step, {"song_detect": {"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_song_detect(self) -> None:
|
|
task = SimpleNamespace(id="task-1", status="failed_retryable")
|
|
steps = {
|
|
"song_detect": TaskStep(None, "task-1", "song_detect", "failed_retryable", "ERR", "boom", 1, None, "2099-01-01T00:00:00+00:00"),
|
|
}
|
|
state = {
|
|
"settings": {
|
|
"transcribe": {},
|
|
"song_detect": {"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"], "song_detect")
|
|
|
|
def test_resolve_failure_adds_song_detect_retry_delay(self) -> None:
|
|
repo = _Repo()
|
|
task = SimpleNamespace(id="task-1", status="running")
|
|
state = {
|
|
"settings": {
|
|
"transcribe": {},
|
|
"song_detect": {"retry_schedule_minutes": [5, 10]},
|
|
"publish": {},
|
|
"comment": {},
|
|
"paths": {},
|
|
"collection": {"enabled": True},
|
|
}
|
|
}
|
|
|
|
result = resolve_failure(task, repo, state, ModuleError(code="SONG_DETECT_FAILED", message="boom", retryable=True))
|
|
|
|
self.assertEqual(result["payload"]["retry_status"], "failed_retryable")
|
|
self.assertEqual(result["payload"]["next_retry_delay_seconds"], 300)
|
|
|
|
def test_qwen_auth_errors_are_not_retryable(self) -> None:
|
|
self.assertTrue(QwenCliSongDetector._is_auth_error("[API Error: 401 invalid access token or token expired]"))
|
|
self.assertFalse(QwenCliSongDetector._is_auth_error("temporary network failure"))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|