feat: add durable sync state transitions

Add state-transition methods to ActivityRepository for advancing activity
stages (mark_downloaded, mark_converted, mark_imported, mark_duplicate,
mark_failed) with proper retention of last_completed_stage on failure.
Add list_pending_for_user to filter activities for processing. Implement
SyncRunRepository for creating and finalizing sync runs with counts and
summary errors.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Bastian Wagner
2026-08-15 15:33:46 +02:00
parent 2f65c0178c
commit 1d5bbdb2a2
3 changed files with 268 additions and 4 deletions

View File

@@ -8,8 +8,8 @@ from sqlalchemy.orm import Session, sessionmaker
from sqlalchemy.pool import StaticPool
from app.config import Settings
from app.db.models import Base
from app.db.repositories import ActivityRepository, UserRepository
from app.db.models import Activity, Base, HealthState
from app.db.repositories import ActivityRepository, SyncRunRepository, UserRepository
from app.main import create_app
@@ -39,6 +39,11 @@ def activity_repository(db_session: Session) -> ActivityRepository:
return ActivityRepository(db_session)
@pytest.fixture
def sync_run_repository(db_session: Session) -> SyncRunRepository:
return SyncRunRepository(db_session)
@pytest.fixture
def client(tmp_path: Path) -> TestClient:
settings = Settings(
@@ -54,3 +59,23 @@ def client(tmp_path: Path) -> TestClient:
yield TestClient(app)
finally:
app.state.db_engine.dispose()
@pytest.fixture
def seeded_activity(db_session: Session, user_repository: UserRepository, activity_repository: ActivityRepository) -> Activity:
user = user_repository.create(
name="Test User",
enabled=True,
health_state=HealthState.HEALTHY,
mywhoosh_email_enc="test@example.com",
mywhoosh_password_enc="password",
garmin_email_enc="test@garmin.com",
garmin_password_enc="garmin_password",
)
activity, _ = activity_repository.get_or_create_discovered(
user_id=user.id,
mywhoosh_activity_id="mw-test-123",
activity_name="Test Activity",
activity_timestamp=None,
)
return activity