fc3d: DownloadService finalize hook — Source.consecutive_failures/last_error/last_checked_at

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-21 16:00:40 -04:00
parent 0493fdc466
commit 87fb534722
2 changed files with 150 additions and 6 deletions
+98
View File
@@ -272,3 +272,101 @@ async def test_patreon_retry_caches_campaign_id(
select(Source.config_overrides).where(Source.id == source.id)
).scalar_one()
assert overrides.get("patreon_campaign_id") == "99"
# --- FC-3d: finalize hook updates Source health columns -------------------
async def _seed_source_with_health(db, *, failures=0, last_error=None, suffix="fz"):
artist = Artist(name=f"alice-{suffix}", slug=f"alice-{suffix}")
db.add(artist)
await db.flush()
source = Source(
artist_id=artist.id, platform="patreon",
url=f"https://patreon.com/alice-{suffix}", enabled=True,
consecutive_failures=failures, last_error=last_error,
)
db.add(source)
await db.flush()
event = DownloadEvent(source_id=source.id, status="running")
db.add(event)
await db.commit()
return source.id, event.id
@pytest.mark.asyncio
async def test_finalize_ok_resets_failures_and_clears_error(db):
from backend.app.services.download_service import DownloadService
source_id, event_id = await _seed_source_with_health(
db, failures=3, last_error="prev", suffix="ok",
)
svc = DownloadService(
async_session=db, sync_session=None,
gdl=None, importer=None, cred_service=None,
)
await svc._finalize_event_and_source(
event_id=event_id, source_id=source_id, status="ok",
error_message=None,
)
row = (await db.execute(
select(
Source.consecutive_failures, Source.last_error, Source.last_checked_at,
).where(Source.id == source_id)
)).one()
assert row.consecutive_failures == 0
assert row.last_error is None
assert row.last_checked_at is not None
@pytest.mark.asyncio
async def test_finalize_error_increments_failures_and_sets_error(db):
from backend.app.services.download_service import DownloadService
source_id, event_id = await _seed_source_with_health(
db, failures=2, last_error=None, suffix="err",
)
svc = DownloadService(
async_session=db, sync_session=None,
gdl=None, importer=None, cred_service=None,
)
await svc._finalize_event_and_source(
event_id=event_id, source_id=source_id, status="error",
error_message="boom",
)
row = (await db.execute(
select(
Source.consecutive_failures, Source.last_error, Source.last_checked_at,
).where(Source.id == source_id)
)).one()
assert row.consecutive_failures == 3
assert row.last_error == "boom"
assert row.last_checked_at is not None
@pytest.mark.asyncio
async def test_finalize_skipped_preserves_failures_clears_error(db):
from backend.app.services.download_service import DownloadService
source_id, event_id = await _seed_source_with_health(
db, failures=4, last_error="stale", suffix="skip",
)
svc = DownloadService(
async_session=db, sync_session=None,
gdl=None, importer=None, cred_service=None,
)
await svc._finalize_event_and_source(
event_id=event_id, source_id=source_id, status="skipped",
error_message=None,
)
row = (await db.execute(
select(
Source.consecutive_failures, Source.last_error, Source.last_checked_at,
).where(Source.id == source_id)
)).one()
assert row.consecutive_failures == 4
assert row.last_error is None
assert row.last_checked_at is not None