From 32af6bbdb52923c6e3184bd0c8224e035aa7f2a7 Mon Sep 17 00:00:00 2001 From: Stefano Date: Tue, 27 May 2025 18:44:05 -0500 Subject: [PATCH] Added unit tests for all auth classes --- tests/auth/test_dependencies.py | 162 ++++++++++++++++++++++++++++++++ tests/auth/test_mock_auth.py | 36 +++++++ 2 files changed, 198 insertions(+) create mode 100644 tests/auth/test_dependencies.py create mode 100644 tests/auth/test_mock_auth.py diff --git a/tests/auth/test_dependencies.py b/tests/auth/test_dependencies.py new file mode 100644 index 0000000..48085b3 --- /dev/null +++ b/tests/auth/test_dependencies.py @@ -0,0 +1,162 @@ +import os +import pytest +import importlib +from fastapi.security import OAuth2PasswordBearer +from fastapi import HTTPException, Depends, Request +from app.auth.dependencies import get_current_user, require_roles, oauth2_scheme +from app.models.auth import CognitoUser + +# Mock user for testing +TEST_USER = CognitoUser( + username="testuser", + email="test@example.com", + roles=["admin", "user"], + groups=["test_group"] +) + +# Mock the underlying get_user_from_token function +def mock_get_user_from_token(token: str) -> CognitoUser: + if token == "valid_token": + return TEST_USER + raise HTTPException(status_code=401, detail="Invalid token") + +# Mock endpoint for testing the require_roles decorator +@require_roles("admin") +async def mock_protected_endpoint(user: CognitoUser = Depends(get_current_user)): + return {"message": "Success", "user": user.username} + +# Patch the get_user_from_token function for testing +@pytest.fixture(autouse=True) +def mock_auth(monkeypatch): + monkeypatch.setattr("app.auth.dependencies.get_user_from_token", mock_get_user_from_token) + +# Test get_current_user dependency +def test_get_current_user_success(): + user = get_current_user("valid_token") + assert user == TEST_USER + assert user.username == "testuser" + assert user.roles == ["admin", "user"] + +def test_get_current_user_invalid_token(): + with pytest.raises(HTTPException) as exc: + get_current_user("invalid_token") + assert exc.value.status_code == 401 + +# Test require_roles decorator +@pytest.mark.asyncio +async def test_require_roles_success(): + # Create test user with required role + user = CognitoUser( + username="testuser", + email="test@example.com", + roles=["admin"], + groups=[] + ) + + result = await mock_protected_endpoint(user=user) + assert result == {"message": "Success", "user": "testuser"} + +@pytest.mark.asyncio +async def test_require_roles_missing_role(): + # Create test user without required role + user = CognitoUser( + username="testuser", + email="test@example.com", + roles=["user"], + groups=[] + ) + + with pytest.raises(HTTPException) as exc: + await mock_protected_endpoint(user=user) + assert exc.value.status_code == 403 + assert exc.value.detail == "You do not have the required roles to access this endpoint." + +@pytest.mark.asyncio +async def test_require_roles_no_roles(): + # Create test user with no roles + user = CognitoUser( + username="testuser", + email="test@example.com", + roles=[], + groups=[] + ) + + with pytest.raises(HTTPException) as exc: + await mock_protected_endpoint(user=user) + assert exc.value.status_code == 403 + +@pytest.mark.asyncio +async def test_require_roles_multiple_roles(): + # Test requiring multiple roles + @require_roles("admin", "super_user") + async def mock_multi_role_endpoint(user: CognitoUser = Depends(get_current_user)): + return {"message": "Success"} + + # User with all required roles + user_with_roles = CognitoUser( + username="testuser", + email="test@example.com", + roles=["admin", "super_user", "user"], + groups=[] + ) + result = await mock_multi_role_endpoint(user=user_with_roles) + assert result == {"message": "Success"} + + # User missing one required role + user_missing_role = CognitoUser( + username="testuser", + email="test@example.com", + roles=["admin", "user"], + groups=[] + ) + with pytest.raises(HTTPException) as exc: + await mock_multi_role_endpoint(user=user_missing_role) + assert exc.value.status_code == 403 + +@pytest.mark.asyncio +async def test_oauth2_scheme_configuration(): + # Verify that we have a properly configured OAuth2PasswordBearer instance + assert isinstance(oauth2_scheme, OAuth2PasswordBearer) + + # Create a mock request with no Authorization header + mock_request = Request(scope={ + 'type': 'http', + 'headers': [], + 'method': 'GET', + 'scheme': 'http', + 'path': '/', + 'query_string': b'', + 'client': ('127.0.0.1', 8000) + }) + + # Test that the scheme raises 401 when no token is provided + with pytest.raises(HTTPException) as exc: + await oauth2_scheme(mock_request) + assert exc.value.status_code == 401 + assert exc.value.detail == "Not authenticated" + +def test_mock_auth_import(monkeypatch): + # Save original env var value + original_value = os.environ.get("MOCK_AUTH") + + try: + # Set MOCK_AUTH to true + monkeypatch.setenv("MOCK_AUTH", "true") + + # Reload the dependencies module to trigger the import condition + import app.auth.dependencies + importlib.reload(app.auth.dependencies) + + # Verify that mock_get_user_from_token was imported + from app.auth.dependencies import get_user_from_token + assert get_user_from_token.__module__ == 'app.auth.mock_auth' + + finally: + # Restore original env var + if original_value is None: + monkeypatch.delenv("MOCK_AUTH", raising=False) + else: + monkeypatch.setenv("MOCK_AUTH", original_value) + + # Reload again to restore original state + importlib.reload(app.auth.dependencies) \ No newline at end of file diff --git a/tests/auth/test_mock_auth.py b/tests/auth/test_mock_auth.py new file mode 100644 index 0000000..a1bfb27 --- /dev/null +++ b/tests/auth/test_mock_auth.py @@ -0,0 +1,36 @@ +import pytest +from fastapi import HTTPException +from app.auth.mock_auth import mock_get_user_from_token, mock_initiate_auth +from app.models.auth import CognitoUser + +def test_mock_get_user_from_token_success(): + """Test successful token validation returns expected user""" + user = mock_get_user_from_token("testuser") + assert isinstance(user, CognitoUser) + assert user.username == "testuser" + assert user.roles == ["admin"] + +def test_mock_get_user_from_token_invalid(): + """Test invalid token raises expected exception""" + with pytest.raises(HTTPException) as exc_info: + mock_get_user_from_token("invalid_token") + + assert exc_info.value.status_code == 401 + assert exc_info.value.detail == "Invalid mock token - use 'testuser'" + +def test_mock_initiate_auth(): + """Test mock authentication returns expected token response""" + result = mock_initiate_auth("any_user", "any_password") + + assert isinstance(result, dict) + assert result["AccessToken"] == "testuser" + assert result["ExpiresIn"] == 3600 + assert result["TokenType"] == "Bearer" + +def test_mock_initiate_auth_different_credentials(): + """Test mock authentication works with any credentials""" + result1 = mock_initiate_auth("user1", "pass1") + result2 = mock_initiate_auth("user2", "pass2") + + # Should return same mock token regardless of credentials + assert result1 == result2 \ No newline at end of file