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)
This commit is contained in:
2025-12-18 15:23:56 -08:00
parent 31f96660c7
commit fb6417b1a3
27 changed files with 1502 additions and 44 deletions

View File

@@ -0,0 +1,124 @@
import pytest
import asyncio
import sys
import os
from unittest.mock import AsyncMock, MagicMock, patch
# Add the src directory to the path for imports
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
@pytest.mark.asyncio
@patch('src.api.client.httpx.AsyncClient')
async def test_auth_flow_integration(mock_http_client):
"""Integration test for complete authentication flow"""
# Mock the HTTP client response
mock_response = AsyncMock()
mock_response.json.return_value = {
"success": True,
"session_id": "session123",
"access_token": "token123",
"token_type": "Bearer",
"expires_in": 3600,
"user": {"id": "user123", "email": "test@example.com"}
}
mock_response.raise_for_status = MagicMock()
# Setup the mock client
mock_http_client.return_value = mock_response
mock_http_client.return_value.post.return_value = mock_response
# Create real instances (not mocks) for integration test
api_client = ApiClient(base_url="https://test-api.garmin.com")
token_manager = TokenManager()
# Mock the token manager methods to avoid file I/O
token_manager.save_token = MagicMock()
token_manager.load_token = MagicMock()
token_manager.clear_token = MagicMock()
token_manager.token_exists = MagicMock(return_value=False)
auth_manager = AuthManager(api_client, token_manager)
# Perform authentication
session = await auth_manager.authenticate("test@example.com", "password123", "123456")
# Verify the session was created
assert session is not None
assert session.user_id == "user123"
assert session.session_id == "session123"
# Verify token was saved
token_manager.save_token.assert_called_once()
@pytest.mark.asyncio
@patch('src.api.client.httpx.AsyncClient')
async def test_auth_logout_integration(mock_http_client):
"""Integration test for authentication and logout flow"""
# Mock successful auth response
auth_response = AsyncMock()
auth_response.json.return_value = {
"success": True,
"session_id": "session123",
"access_token": "token123",
"token_type": "Bearer",
"expires_in": 3600,
"user": {"id": "user123", "email": "test@example.com"}
}
auth_response.raise_for_status = MagicMock()
# Mock successful logout response (if there was a logout API call)
logout_response = AsyncMock()
logout_response.json.return_value = {"success": True}
logout_response.raise_for_status = MagicMock()
# Setup client mock
mock_http_client.return_value.post.return_value = auth_response
# Create real instances with mocked file operations
api_client = ApiClient(base_url="https://test-api.garmin.com")
token_manager = TokenManager()
# Mock token manager methods
token_manager.save_token = MagicMock()
token_manager.load_token = MagicMock()
token_manager.clear_token = MagicMock(return_value=True)
token_manager.token_exists = MagicMock(return_value=True)
auth_manager = AuthManager(api_client, token_manager)
# Authenticate first
session = await auth_manager.authenticate("test@example.com", "password123")
assert session is not None
# Verify token was saved during auth
token_manager.save_token.assert_called_once()
# Now logout
logout_success = await auth_manager.logout()
assert logout_success is True
# Verify token was cleared
token_manager.clear_token.assert_called_once()
@pytest.mark.asyncio
async def test_auth_status_check():
"""Integration test for authentication status check"""
# Create real instances with mocked file operations
api_client = ApiClient(base_url="https://test-api.garmin.com")
token_manager = TokenManager()
# Mock token manager methods
token_manager.token_exists = MagicMock(return_value=True)
auth_manager = AuthManager(api_client, token_manager)
# Check if authenticated (should return True based on mock)
is_auth = await auth_manager.is_authenticated()
assert is_auth is True