Files
FitTrack_GarminSync/cli/tests/unit/test_auth_manager.py
sstent 2f0b5e6bad feat: Implement MFA authentication flow with garth for CLI
This commit implements the multi-factor authentication (MFA) flow for the CLI
using the garth library, as specified in task 007.

Changes include:
- Created  to handle API communication with robust error handling.
- Refactored  to correctly implement the logout logic
  and ensure proper handling of API client headers.
- Updated  with Black, Flake8, Mypy, and Isort configurations.
- Implemented and refined integration tests for authentication, sync operations,
  and sync status checking, including mocking for the API client.
- Renamed integration test files for clarity and consistency.
- Updated  to reflect task completion.
2025-12-20 14:46:50 -08:00

139 lines
5.1 KiB
Python

"""Unit tests for commands functionality"""
import pytest
import asyncio
from unittest.mock import AsyncMock, MagicMock
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
from src.auth.auth_manager import AuthManager
from src.api.client import ApiClient
from src.auth.token_manager import TokenManager
from src.models.session import UserSession
from src.models.token import AuthenticationToken
@pytest.mark.asyncio
class TestAuthManager:
async def test_authenticate_success(self):
"""Test successful authentication"""
# Create mocks
mock_api_client = AsyncMock(spec=ApiClient)
mock_token_manager = MagicMock(spec=TokenManager)
# Setup mock responses
mock_api_client.authenticate_user.return_value = {
"success": True,
"session_id": "session123",
"access_token": "token123",
"token_type": "Bearer",
"expires_in": 3600,
"user": {"id": "user123"}
}
# Create AuthManager instance
auth_manager = AuthManager(mock_api_client, mock_token_manager)
# Perform authentication
result = await auth_manager.authenticate("test@example.com", "password123")
# Verify the result
assert result is not None
assert result.user_id == "user123"
# Verify the token was saved
mock_token_manager.save_token.assert_called_once()
async def test_authenticate_with_mfa_success(self):
"""Test authentication with MFA code"""
# Create mocks
mock_api_client = AsyncMock(spec=ApiClient)
mock_token_manager = MagicMock(spec=TokenManager)
# Setup mock responses
mock_api_client.authenticate_user.return_value = {
"success": True,
"session_id": "session456",
"access_token": "token456",
"token_type": "Bearer",
"expires_in": 3600,
"mfa_required": True,
"user": {"id": "user456"}
}
# Create AuthManager instance
auth_manager = AuthManager(mock_api_client, mock_token_manager)
# Perform authentication with MFA code
result = await auth_manager.authenticate("test@example.com", "password123", "123456")
# Verify the result
assert result is not None
assert result.user_id == "user456"
assert result.mfa_enabled is True
async def test_authenticate_failure(self):
"""Test authentication failure"""
# Create mocks
mock_api_client = AsyncMock(spec=ApiClient)
mock_token_manager = MagicMock(spec=TokenManager)
# Setup mock responses for failure
mock_api_client.authenticate_user.return_value = {
"success": False,
"error": "Invalid credentials"
}
# Create AuthManager instance
auth_manager = AuthManager(mock_api_client, mock_token_manager)
# Expect an exception for failed authentication
with pytest.raises(Exception, match="Authentication failed: Invalid credentials"):
await auth_manager.authenticate("test@example.com", "wrongpassword")
async def test_logout(self):
"""Test logout functionality"""
# Create mocks
mock_api_client = AsyncMock(spec=ApiClient)
mock_token_manager = MagicMock(spec=TokenManager)
# Mock the internal httpx client within ApiClient
mock_httpx_client = MagicMock()
mock_httpx_client.headers = {"Authorization": "Bearer some_token"} # Mock headers to be a dict
mock_api_client.client = mock_httpx_client # Assign the mocked httpx client to api_client.client
mock_api_client.close.return_value = None # Mock the aclose method
# Set up token manager to return that a token exists
mock_token_manager.token_exists.return_value = True
# Create AuthManager instance
auth_manager = AuthManager(mock_api_client, mock_token_manager)
# Perform logout
result = await auth_manager.logout()
# Verify logout success
assert result is True
mock_token_manager.clear_token.assert_called_once()
# Verify authorization header was removed
assert "Authorization" not in mock_api_client.client.headers
async def test_is_authenticated(self):
"""Test authentication status check"""
# Create mocks
mock_api_client = AsyncMock(spec=ApiClient)
mock_token_manager = MagicMock(spec=TokenManager)
# Create AuthManager instance
auth_manager = AuthManager(mock_api_client, mock_token_manager)
# Test when token exists
mock_token_manager.token_exists.return_value = True
result = await auth_manager.is_authenticated()
assert result is True
# Test when token doesn't exist
mock_token_manager.token_exists.return_value = False
result = await auth_manager.is_authenticated()
assert result is False