Files
web-page-backend/music-lab/tests/test_pipeline_retry.py
2026-06-12 00:20:29 +09:00

84 lines
2.8 KiB
Python

import pytest
from app import db
from app.pipeline import orchestrator
@pytest.fixture
def fresh_db(monkeypatch, tmp_path):
db_path = tmp_path / "music.db"
monkeypatch.setattr(db, "DB_PATH", str(db_path))
db.init_db()
return db_path
@pytest.fixture(autouse=True)
def _no_backoff(monkeypatch):
monkeypatch.setattr(orchestrator, "STEP_RETRY_BACKOFF_SEC", [0, 0])
def test_get_last_failed_step_returns_step(fresh_db):
pid = db.create_pipeline(track_id=1)
job_id = db.create_pipeline_job(pid, "video")
db.update_pipeline_job(job_id, status="failed", error="boom")
db.update_pipeline_state(pid, "failed", failed_reason="video: boom")
assert db.get_last_failed_step(pid) == "video"
def test_get_last_failed_step_none_when_no_failure(fresh_db):
pid = db.create_pipeline(track_id=1)
db.create_pipeline_job(pid, "cover")
assert db.get_last_failed_step(pid) is None
async def test_retryable_step_retries_then_succeeds(fresh_db, monkeypatch):
pid = db.create_pipeline(track_id=1)
calls = {"n": 0}
async def flaky(step, p, ctx, feedback):
calls["n"] += 1
if calls["n"] < 3:
raise RuntimeError("transient")
return {"next_state": "video_pending", "fields": {}}
monkeypatch.setattr(orchestrator, "_dispatch_step", flaky)
monkeypatch.setattr(
orchestrator, "_resolve_input",
lambda p: {"genre": "x", "title": "t", "moods": [], "tracks": [], "audio_path": "", "duration_sec": 0},
)
await orchestrator.run_step(pid, "cover")
assert calls["n"] == 3
assert db.get_pipeline(pid)["state"] == "video_pending"
async def test_retryable_step_exhausts_to_failed(fresh_db, monkeypatch):
pid = db.create_pipeline(track_id=1)
async def always_fail(step, p, ctx, feedback):
raise RuntimeError("permanent")
monkeypatch.setattr(orchestrator, "_dispatch_step", always_fail)
monkeypatch.setattr(
orchestrator, "_resolve_input",
lambda p: {"genre": "x", "title": "t", "moods": [], "tracks": [], "audio_path": "", "duration_sec": 0},
)
await orchestrator.run_step(pid, "cover")
assert db.get_pipeline(pid)["state"] == "failed"
async def test_publish_not_retried(fresh_db, monkeypatch):
pid = db.create_pipeline(track_id=1)
calls = {"n": 0}
async def fail_publish(step, p, ctx, feedback):
calls["n"] += 1
raise RuntimeError("upload error")
monkeypatch.setattr(orchestrator, "_dispatch_step", fail_publish)
monkeypatch.setattr(
orchestrator, "_resolve_input",
lambda p: {"genre": "x", "title": "t", "moods": [], "tracks": [], "audio_path": "", "duration_sec": 0},
)
await orchestrator.run_step(pid, "publish")
assert calls["n"] == 1
assert db.get_pipeline(pid)["state"] == "failed"