Files
FitTrack_GarminSync/cli/tests/unit/test_auth_manager.py
sstent fb6417b1a3 Implement CLI app for API interaction with MFA
- Create full CLI application with authentication, sync triggering, and status checking
- Implement MFA support for secure authentication
- Add token management with secure local storage
- Create API client for backend communication
- Implement data models for User Session, Sync Job, and Authentication Token
- Add command-line interface with auth and sync commands
- Include unit and integration tests
- Follow project constitution standards for Python 3.13, type hints, and code quality
- Support multiple output formats (table, JSON, CSV)
2025-12-18 15:23:56 -08:00

147 lines
4.7 KiB
Python

import pytest
import asyncio
import sys
import os
from datetime import datetime, timedelta
from unittest.mock import AsyncMock, MagicMock
# Add the src directory to Python path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', 'src'))
from auth.auth_manager import AuthManager
from models.token import AuthenticationToken
from auth.token_manager import TokenManager
@pytest.fixture
def mock_api_client():
"""Mock API client for testing"""
client = AsyncMock()
client.set_token = AsyncMock()
return client
@pytest.fixture
def mock_token_manager():
"""Mock token manager for testing"""
manager = MagicMock()
manager.save_token = MagicMock()
manager.load_token = MagicMock()
manager.clear_token = MagicMock()
manager.token_exists = MagicMock(return_value=False)
return manager
@pytest.fixture
def auth_manager(mock_api_client, mock_token_manager):
"""Create an AuthManager instance with mocked dependencies"""
return AuthManager(mock_api_client, mock_token_manager)
@pytest.mark.asyncio
async def test_authenticate_success(auth_manager, mock_api_client, mock_token_manager):
"""Test successful authentication"""
# Setup mock response
mock_api_client.authenticate_user = AsyncMock(return_value={
"success": True,
"session_id": "session123",
"access_token": "token123",
"token_type": "Bearer",
"expires_in": 3600,
"user": {"id": "user123", "email": "test@example.com"}
})
# Call authenticate
result = await auth_manager.authenticate("test@example.com", "password", "123456")
# Assertions
assert result is not None
assert result.user_id == "user123"
assert result.session_id == "session123"
mock_token_manager.save_token.assert_called_once()
mock_api_client.set_token.assert_called_once()
@pytest.mark.asyncio
async def test_authenticate_with_mfa(auth_manager, mock_api_client, mock_token_manager):
"""Test authentication with MFA code"""
# Setup mock response
mock_api_client.authenticate_user = AsyncMock(return_value={
"success": True,
"session_id": "session123",
"access_token": "token123",
"token_type": "Bearer",
"expires_in": 3600,
"mfa_required": True,
"user": {"id": "user123", "email": "test@example.com"}
})
# Call authenticate with MFA
result = await auth_manager.authenticate("test@example.com", "password", "123456")
# Assertions
assert result is not None
assert result.mfa_enabled is True
mock_api_client.authenticate_user.assert_called_once_with("test@example.com", "password", "123456")
@pytest.mark.asyncio
async def test_authenticate_failure(auth_manager, mock_api_client):
"""Test authentication failure"""
# Setup mock response for failure
mock_api_client.authenticate_user = AsyncMock(return_value={
"success": False,
"error": "Invalid credentials"
})
# Expect exception to be raised
with pytest.raises(Exception, match="Authentication failed: Invalid credentials"):
await auth_manager.authenticate("test@example.com", "wrong_password")
@pytest.mark.asyncio
async def test_logout_success(auth_manager, mock_api_client, mock_token_manager):
"""Test successful logout"""
# Setup
mock_api_client.client.headers = {"Authorization": "Bearer token123"}
# Call logout
result = await auth_manager.logout()
# Assertions
assert result is True
mock_token_manager.clear_token.assert_called_once()
assert "Authorization" not in mock_api_client.client.headers
def test_is_token_expired_false(auth_manager):
"""Test token expiration check for non-expired token"""
# Create a token that expires in the future
from datetime import datetime, timedelta
future_expiry = datetime.now() + timedelta(hours=1)
token = AuthenticationToken(
token_id="token123",
user_id="user123",
access_token="token123",
created_at=datetime.now() - timedelta(minutes=10), # Created 10 minutes ago
expires_in=3600 # Expires in 1 hour
)
# Should not be expired
assert auth_manager.is_token_expired(token) is False
def test_is_token_expired_true(auth_manager):
"""Test token expiration check for expired token"""
# Create a token that should have expired
token = AuthenticationToken(
token_id="token123",
user_id="user123",
access_token="token123",
created_at=datetime.now() - timedelta(hours=2), # Created 2 hours ago
expires_in=3600 # Was supposed to expire after 1 hour
)
# Should be expired
assert auth_manager.is_token_expired(token) is True