"""Tests for authentication endpoints and service.""" import pytest from unittest.mock import AsyncMock, patch from app.services.auth_service import AuthService from app.models.auth import LoginRequest, UserResponse class TestAuthEndpoints: """Tests for /api/v1/auth endpoints.""" def test_login_success(self, unauthenticated_client, mock_db_client, test_user_response): """Test successful login with valid 8-digit login.""" # Mock DB client response mock_db_client.login_user = AsyncMock(return_value=test_user_response) # Override dependency from app.main import app from app.dependencies import get_db_client app.dependency_overrides[get_db_client] = lambda: mock_db_client try: response = unauthenticated_client.post("/api/v1/auth/login?login=12345678") assert response.status_code == 200 data = response.json() assert "access_token" in data assert data["token_type"] == "bearer" assert "user" in data assert data["user"]["login"] == "12345678" # Verify DB client was called mock_db_client.login_user.assert_called_once() finally: app.dependency_overrides.clear() def test_login_invalid_format(self, unauthenticated_client): """Test login with invalid format (not 8 digits).""" # Test with 7 digits response = unauthenticated_client.post("/api/v1/auth/login?login=1234567") assert response.status_code == 400 assert "must be 8 digits" in response.json()["detail"].lower() # Test with 9 digits response = unauthenticated_client.post("/api/v1/auth/login?login=123456789") assert response.status_code == 400 # Test with letters response = unauthenticated_client.post("/api/v1/auth/login?login=abcd1234") assert response.status_code == 400 def test_login_db_api_error(self, unauthenticated_client, mock_db_client): """Test login when DB API fails.""" # Mock DB client to raise exception mock_db_client.login_user = AsyncMock(side_effect=Exception("DB API unavailable")) from app.main import app from app.dependencies import get_db_client app.dependency_overrides[get_db_client] = lambda: mock_db_client try: response = unauthenticated_client.post("/api/v1/auth/login?login=12345678") assert response.status_code == 500 assert "failed" in response.json()["detail"].lower() finally: app.dependency_overrides.clear() class TestAuthService: """Tests for AuthService.""" @pytest.mark.asyncio async def test_login_success(self, mock_db_client, test_user_response): """Test successful login via AuthService.""" mock_db_client.login_user = AsyncMock(return_value=test_user_response) auth_service = AuthService(mock_db_client) result = await auth_service.login("12345678", "192.168.1.1") assert result.access_token is not None assert result.token_type == "bearer" assert result.user.login == "12345678" assert result.user.user_id == "test-user-123" # Verify DB client was called with correct params call_args = mock_db_client.login_user.call_args[0][0] assert call_args.login == "12345678" assert call_args.client_ip == "192.168.1.1" @pytest.mark.asyncio async def test_login_invalid_format(self, mock_db_client): """Test login with invalid format raises ValueError.""" auth_service = AuthService(mock_db_client) with pytest.raises(ValueError, match="8 digits"): await auth_service.login("1234567", "192.168.1.1") with pytest.raises(ValueError, match="8 digits"): await auth_service.login("abcd1234", "192.168.1.1") # Verify DB client was never called mock_db_client.login_user.assert_not_called() @pytest.mark.asyncio async def test_login_db_api_failure(self, mock_db_client): """Test login when DB API fails.""" mock_db_client.login_user = AsyncMock(side_effect=Exception("DB error")) auth_service = AuthService(mock_db_client) with pytest.raises(Exception, match="DB error"): await auth_service.login("12345678", "192.168.1.1")