feat: add resumable per-user sync pipeline
This commit is contained in:
87
tests/sync/test_manager.py
Normal file
87
tests/sync/test_manager.py
Normal file
@@ -0,0 +1,87 @@
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from app.db.models import ActivityStatus, SyncRun, SyncRunStatus, SyncUser
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_new_activity_downloads_converts_and_imports(manager, seeded_user: SyncUser, load_only_activity) -> None:
|
||||
outcome = await manager.sync_user(seeded_user.id)
|
||||
|
||||
assert outcome.discovered == 1
|
||||
assert outcome.imported == 1
|
||||
assert outcome.failed == 0
|
||||
|
||||
activity = load_only_activity(seeded_user.id)
|
||||
assert activity.status == ActivityStatus.IMPORTED
|
||||
assert Path(activity.source_fit_path).exists()
|
||||
assert Path(activity.converted_fit_path).exists()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
("status", "last_stage", "expected_downloads", "expected_conversions", "expected_imports"),
|
||||
[
|
||||
(ActivityStatus.DOWNLOADED, ActivityStatus.DOWNLOADED, 0, 1, 1),
|
||||
(ActivityStatus.CONVERTED, ActivityStatus.CONVERTED, 0, 0, 1),
|
||||
(ActivityStatus.IMPORTED, ActivityStatus.IMPORTED, 0, 0, 0),
|
||||
(ActivityStatus.FAILED, ActivityStatus.CONVERTED, 0, 0, 1),
|
||||
],
|
||||
)
|
||||
async def test_resume_from_durable_stage(
|
||||
manager_factory,
|
||||
seeded_activity_factory,
|
||||
status,
|
||||
last_stage,
|
||||
expected_downloads,
|
||||
expected_conversions,
|
||||
expected_imports,
|
||||
) -> None:
|
||||
activity = seeded_activity_factory(status=status, last_completed_stage=last_stage, retryable=True)
|
||||
manager, mywhoosh, converter, garmin = manager_factory(activity)
|
||||
await manager.sync_user(activity.user_id)
|
||||
assert mywhoosh.download_calls == expected_downloads
|
||||
assert converter.calls == expected_conversions
|
||||
assert garmin.calls == expected_imports
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_non_retryable_failed_activity_is_never_retried(
|
||||
manager_factory,
|
||||
seeded_activity_factory,
|
||||
load_only_activity,
|
||||
) -> None:
|
||||
activity = seeded_activity_factory(
|
||||
status=ActivityStatus.FAILED,
|
||||
last_completed_stage=ActivityStatus.CONVERTED,
|
||||
retryable=False,
|
||||
)
|
||||
manager, mywhoosh, converter, garmin = manager_factory(activity)
|
||||
|
||||
outcome = await manager.sync_user(activity.user_id)
|
||||
|
||||
assert mywhoosh.download_calls == 0
|
||||
assert converter.calls == 0
|
||||
assert garmin.calls == 0
|
||||
assert outcome.imported == 0
|
||||
assert outcome.skipped == 0
|
||||
assert outcome.failed == 0
|
||||
|
||||
reloaded = load_only_activity(activity.user_id)
|
||||
assert reloaded.status == ActivityStatus.FAILED
|
||||
assert reloaded.retryable is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sync_run_repository_wiring_records_run(manager, seeded_user: SyncUser, session_factory) -> None:
|
||||
await manager.sync_user(seeded_user.id)
|
||||
|
||||
with session_factory() as session:
|
||||
runs = session.query(SyncRun).filter_by(user_id=seeded_user.id).all()
|
||||
assert len(runs) == 1
|
||||
run = runs[0]
|
||||
assert run.status == SyncRunStatus.SUCCESS
|
||||
assert run.discovered_count == 1
|
||||
assert run.imported_count == 1
|
||||
assert run.finished_at is not None
|
||||
Reference in New Issue
Block a user