feat: Implement single sync job management and progress tracking

This commit is contained in:
2025-10-11 18:36:19 -07:00
parent 3819e4f5e2
commit 723ca04aa8
51 changed files with 1625 additions and 596 deletions

View File

@@ -2,25 +2,30 @@ from datetime import datetime, timedelta
from unittest.mock import AsyncMock, patch
import pytest
from backend.src.main import app
from backend.src.schemas import GarminCredentials
from httpx import AsyncClient
from fastapi.testclient import TestClient
from src.main import app
from src.schemas import GarminCredentials
@pytest.fixture
def mock_garmin_auth_service():
with patch('backend.src.api.garmin_auth.GarminAuthService') as MockGarminAuthService:
with patch("src.api.garmin_auth.GarminAuthService") as MockGarminAuthService:
service_instance = MockGarminAuthService.return_value
yield service_instance
@pytest.fixture
def mock_central_db_service():
with patch('backend.src.api.garmin_auth.CentralDBService') as MockCentralDBService:
with patch("src.api.garmin_auth.CentralDBService") as MockCentralDBService:
service_instance = MockCentralDBService.return_value
yield service_instance
@pytest.mark.asyncio
async def test_garmin_login_success_new_credentials(mock_garmin_auth_service, mock_central_db_service):
async def test_garmin_login_success_new_credentials(
mock_garmin_auth_service, mock_central_db_service
):
username = "test@example.com"
password = "password123"
@@ -29,28 +34,33 @@ async def test_garmin_login_success_new_credentials(mock_garmin_auth_service, mo
garmin_password_plaintext=password,
access_token="mock_access_token",
access_token_secret="mock_access_token_secret",
token_expiration_date=datetime.utcnow() + timedelta(hours=1)
token_expiration_date=datetime.utcnow() + timedelta(hours=1),
)
mock_central_db_service.get_garmin_credentials.return_value = None # No existing credentials
mock_central_db_service.create_garmin_credentials.return_value = AsyncMock() # Simulate successful creation
mock_central_db_service.get_garmin_credentials.return_value = (
None # No existing credentials
)
mock_central_db_service.create_garmin_credentials.return_value = (
AsyncMock()
) # Simulate successful creation
async with AsyncClient(app=app, base_url="http://test") as client:
response = await client.post(
"/api/garmin/login",
json={
"username": username,
"password": password
}
with TestClient(app=app) as client:
response = client.post(
"/api/garmin/login", json={"username": username, "password": password}
)
assert response.status_code == 200
assert response.json() == {"message": "Garmin account linked successfully."}
mock_garmin_auth_service.initial_login.assert_called_once_with(username, password)
mock_central_db_service.get_garmin_credentials.assert_called_once_with(1) # Assuming user_id 1
mock_central_db_service.get_garmin_credentials.assert_called_once_with(
1
) # Assuming user_id 1
mock_central_db_service.create_garmin_credentials.assert_called_once()
@pytest.mark.asyncio
async def test_garmin_login_success_update_credentials(mock_garmin_auth_service, mock_central_db_service):
async def test_garmin_login_success_update_credentials(
mock_garmin_auth_service, mock_central_db_service
):
username = "test@example.com"
password = "password123"
@@ -59,24 +69,22 @@ async def test_garmin_login_success_update_credentials(mock_garmin_auth_service,
garmin_password_plaintext=password,
access_token="mock_access_token_new",
access_token_secret="mock_access_token_secret_new",
token_expiration_date=datetime.utcnow() + timedelta(hours=1)
token_expiration_date=datetime.utcnow() + timedelta(hours=1),
)
mock_central_db_service.get_garmin_credentials.return_value = GarminCredentials(
garmin_username=username,
garmin_password_plaintext="old_password",
access_token="old_access_token",
access_token_secret="old_access_token_secret",
token_expiration_date=datetime.utcnow() - timedelta(hours=1)
) # Existing credentials
mock_central_db_service.update_garmin_credentials.return_value = AsyncMock() # Simulate successful update
token_expiration_date=datetime.utcnow() - timedelta(hours=1),
) # Existing credentials
mock_central_db_service.update_garmin_credentials.return_value = (
AsyncMock()
) # Simulate successful update
async with AsyncClient(app=app, base_url="http://test") as client:
response = await client.post(
"/api/garmin/login",
json={
"username": username,
"password": password
}
with TestClient(app=app) as client:
response = client.post(
"/api/garmin/login", json={"username": username, "password": password}
)
assert response.status_code == 200
@@ -85,20 +93,21 @@ async def test_garmin_login_success_update_credentials(mock_garmin_auth_service,
mock_central_db_service.get_garmin_credentials.assert_called_once_with(1)
mock_central_db_service.update_garmin_credentials.assert_called_once()
@pytest.mark.asyncio
async def test_garmin_login_failure_invalid_credentials(mock_garmin_auth_service, mock_central_db_service):
async def test_garmin_login_failure_invalid_credentials(
mock_garmin_auth_service, mock_central_db_service
):
username = "invalid@example.com"
password = "wrongpassword"
mock_garmin_auth_service.initial_login.return_value = None # Simulate failed Garmin login
mock_garmin_auth_service.initial_login.return_value = (
None # Simulate failed Garmin login
)
async with AsyncClient(app=app, base_url="http://test") as client:
response = await client.post(
"/api/garmin/login",
json={
"username": username,
"password": password
}
with TestClient(app=app) as client:
response = client.post(
"/api/garmin/login", json={"username": username, "password": password}
)
assert response.status_code == 401
@@ -108,8 +117,11 @@ async def test_garmin_login_failure_invalid_credentials(mock_garmin_auth_service
mock_central_db_service.create_garmin_credentials.assert_not_called()
mock_central_db_service.update_garmin_credentials.assert_not_called()
@pytest.mark.asyncio
async def test_garmin_login_failure_central_db_create_error(mock_garmin_auth_service, mock_central_db_service):
async def test_garmin_login_failure_central_db_create_error(
mock_garmin_auth_service, mock_central_db_service
):
username = "test@example.com"
password = "password123"
@@ -118,25 +130,28 @@ async def test_garmin_login_failure_central_db_create_error(mock_garmin_auth_ser
garmin_password_plaintext=password,
access_token="mock_access_token",
access_token_secret="mock_access_token_secret",
token_expiration_date=datetime.utcnow() + timedelta(hours=1)
token_expiration_date=datetime.utcnow() + timedelta(hours=1),
)
mock_central_db_service.get_garmin_credentials.return_value = None
mock_central_db_service.create_garmin_credentials.return_value = None # Simulate CentralDB create failure
mock_central_db_service.create_garmin_credentials.return_value = (
None # Simulate CentralDB create failure
)
async with AsyncClient(app=app, base_url="http://test") as client:
response = await client.post(
"/api/garmin/login",
json={
"username": username,
"password": password
}
with TestClient(app=app) as client:
response = client.post(
"/api/garmin/login", json={"username": username, "password": password}
)
assert response.status_code == 500
assert response.json() == {"detail": "Failed to store Garmin credentials in CentralDB."}
assert response.json() == {
"detail": "Failed to store Garmin credentials in CentralDB."
}
@pytest.mark.asyncio
async def test_garmin_login_failure_central_db_update_error(mock_garmin_auth_service, mock_central_db_service):
async def test_garmin_login_failure_central_db_update_error(
mock_garmin_auth_service, mock_central_db_service
):
username = "test@example.com"
password = "password123"
@@ -145,25 +160,25 @@ async def test_garmin_login_failure_central_db_update_error(mock_garmin_auth_ser
garmin_password_plaintext=password,
access_token="mock_access_token_new",
access_token_secret="mock_access_token_secret_new",
token_expiration_date=datetime.utcnow() + timedelta(hours=1)
token_expiration_date=datetime.utcnow() + timedelta(hours=1),
)
mock_central_db_service.get_garmin_credentials.return_value = GarminCredentials(
garmin_username=username,
garmin_password_plaintext="old_password",
access_token="old_access_token",
access_token_secret="old_access_token_secret",
token_expiration_date=datetime.utcnow() - timedelta(hours=1)
token_expiration_date=datetime.utcnow() - timedelta(hours=1),
)
mock_central_db_service.update_garmin_credentials.return_value = (
None # Simulate CentralDB update failure
)
mock_central_db_service.update_garmin_credentials.return_value = None # Simulate CentralDB update failure
async with AsyncClient(app=app, base_url="http://test") as client:
response = await client.post(
"/api/garmin/login",
json={
"username": username,
"password": password
}
with TestClient(app=app) as client:
response = client.post(
"/api/garmin/login", json={"username": username, "password": password}
)
assert response.status_code == 500
assert response.json() == {"detail": "Failed to update Garmin credentials in CentralDB."}
assert response.json() == {
"detail": "Failed to update Garmin credentials in CentralDB."
}