Linted and formatted all files

This commit is contained in:
2025-05-28 21:52:39 -05:00
parent e46f13930d
commit 02913c7385
31 changed files with 1264 additions and 766 deletions

View File

@@ -1,5 +1,6 @@
from unittest.mock import MagicMock, patch
import pytest
from unittest.mock import patch, MagicMock
from fastapi import HTTPException, status
# Test constants
@@ -7,12 +8,15 @@ TEST_CLIENT_ID = "test_client_id"
TEST_CLIENT_SECRET = "test_client_secret"
# Patch constants before importing the module
with patch("app.utils.constants.COGNITO_CLIENT_ID", TEST_CLIENT_ID), \
patch("app.utils.constants.COGNITO_CLIENT_SECRET", TEST_CLIENT_SECRET):
from app.auth.cognito import initiate_auth, get_user_from_token
with (
patch("app.utils.constants.COGNITO_CLIENT_ID", TEST_CLIENT_ID),
patch("app.utils.constants.COGNITO_CLIENT_SECRET", TEST_CLIENT_SECRET),
):
from app.auth.cognito import get_user_from_token, initiate_auth
from app.models.auth import CognitoUser
from app.utils.constants import USER_ROLE_ATTRIBUTE
@pytest.fixture(autouse=True)
def mock_cognito_client():
with patch("app.auth.cognito.cognito_client") as mock_client:
@@ -26,13 +30,14 @@ def mock_cognito_client():
)
yield mock_client
def test_initiate_auth_success(mock_cognito_client):
# Mock successful authentication response
mock_cognito_client.initiate_auth.return_value = {
"AuthenticationResult": {
"AccessToken": "mock_access_token",
"IdToken": "mock_id_token",
"RefreshToken": "mock_refresh_token"
"RefreshToken": "mock_refresh_token",
}
}
@@ -40,104 +45,125 @@ def test_initiate_auth_success(mock_cognito_client):
assert result == {
"AccessToken": "mock_access_token",
"IdToken": "mock_id_token",
"RefreshToken": "mock_refresh_token"
"RefreshToken": "mock_refresh_token",
}
def test_initiate_auth_with_secret_hash(mock_cognito_client):
with patch("app.auth.cognito.calculate_secret_hash", return_value="mocked_secret_hash") as mock_hash:
with patch(
"app.auth.cognito.calculate_secret_hash", return_value="mocked_secret_hash"
) as mock_hash:
mock_cognito_client.initiate_auth.return_value = {
"AuthenticationResult": {"AccessToken": "token"}
}
result = initiate_auth("test_user", "test_pass")
initiate_auth("test_user", "test_pass")
# Verify calculate_secret_hash was called
mock_hash.assert_called_once_with("test_user", TEST_CLIENT_ID, TEST_CLIENT_SECRET)
mock_hash.assert_called_once_with(
"test_user", TEST_CLIENT_ID, TEST_CLIENT_SECRET
)
# Verify SECRET_HASH was included in auth params
call_args = mock_cognito_client.initiate_auth.call_args[1]
assert "SECRET_HASH" in call_args["AuthParameters"]
assert call_args["AuthParameters"]["SECRET_HASH"] == "mocked_secret_hash"
def test_initiate_auth_not_authorized(mock_cognito_client):
mock_cognito_client.initiate_auth.side_effect = mock_cognito_client.exceptions.NotAuthorizedException()
mock_cognito_client.initiate_auth.side_effect = (
mock_cognito_client.exceptions.NotAuthorizedException()
)
with pytest.raises(HTTPException) as exc_info:
initiate_auth("invalid_user", "wrong_pass")
assert exc_info.value.status_code == status.HTTP_401_UNAUTHORIZED
assert exc_info.value.detail == "Invalid username or password"
def test_initiate_auth_user_not_found(mock_cognito_client):
mock_cognito_client.initiate_auth.side_effect = mock_cognito_client.exceptions.UserNotFoundException()
mock_cognito_client.initiate_auth.side_effect = (
mock_cognito_client.exceptions.UserNotFoundException()
)
with pytest.raises(HTTPException) as exc_info:
initiate_auth("nonexistent_user", "any_pass")
assert exc_info.value.status_code == status.HTTP_404_NOT_FOUND
assert exc_info.value.detail == "User not found"
def test_initiate_auth_generic_error(mock_cognito_client):
mock_cognito_client.initiate_auth.side_effect = Exception("Some error")
with pytest.raises(HTTPException) as exc_info:
initiate_auth("test_user", "test_pass")
assert exc_info.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
assert "An error occurred during authentication" in exc_info.value.detail
def test_get_user_from_token_success(mock_cognito_client):
mock_response = {
"Username": "test_user",
"UserAttributes": [
{"Name": "sub", "Value": "123"},
{"Name": USER_ROLE_ATTRIBUTE, "Value": "admin,user"}
]
{"Name": USER_ROLE_ATTRIBUTE, "Value": "admin,user"},
],
}
mock_cognito_client.get_user.return_value = mock_response
result = get_user_from_token("valid_token")
assert isinstance(result, CognitoUser)
assert result.username == "test_user"
assert set(result.roles) == {"admin", "user"}
def test_get_user_from_token_no_roles(mock_cognito_client):
mock_response = {
"Username": "test_user",
"UserAttributes": [{"Name": "sub", "Value": "123"}]
"UserAttributes": [{"Name": "sub", "Value": "123"}],
}
mock_cognito_client.get_user.return_value = mock_response
result = get_user_from_token("valid_token")
assert isinstance(result, CognitoUser)
assert result.username == "test_user"
assert result.roles == []
def test_get_user_from_token_invalid_token(mock_cognito_client):
mock_cognito_client.get_user.side_effect = mock_cognito_client.exceptions.NotAuthorizedException()
mock_cognito_client.get_user.side_effect = (
mock_cognito_client.exceptions.NotAuthorizedException()
)
with pytest.raises(HTTPException) as exc_info:
get_user_from_token("invalid_token")
assert exc_info.value.status_code == status.HTTP_401_UNAUTHORIZED
assert exc_info.value.detail == "Invalid or expired token."
def test_get_user_from_token_user_not_found(mock_cognito_client):
mock_cognito_client.get_user.side_effect = mock_cognito_client.exceptions.UserNotFoundException()
mock_cognito_client.get_user.side_effect = (
mock_cognito_client.exceptions.UserNotFoundException()
)
with pytest.raises(HTTPException) as exc_info:
get_user_from_token("token_for_nonexistent_user")
assert exc_info.value.status_code == status.HTTP_401_UNAUTHORIZED
assert exc_info.value.detail == "User not found or invalid token."
def test_get_user_from_token_generic_error(mock_cognito_client):
mock_cognito_client.get_user.side_effect = Exception("Some error")
with pytest.raises(HTTPException) as exc_info:
get_user_from_token("test_token")
assert exc_info.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
assert "Token verification failed" in exc_info.value.detail
assert "Token verification failed" in exc_info.value.detail

View File

@@ -1,9 +1,11 @@
import os
import pytest
import importlib
import os
import pytest
from fastapi import Depends, HTTPException, Request
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.auth.dependencies import get_current_user, oauth2_scheme, require_roles
from app.models.auth import CognitoUser
# Mock user for testing
@@ -11,24 +13,30 @@ TEST_USER = CognitoUser(
username="testuser",
email="test@example.com",
roles=["admin", "user"],
groups=["test_group"]
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)
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():
@@ -37,54 +45,53 @@ def test_get_current_user_success():
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=[]
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=[]
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."
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=[]
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
@@ -97,7 +104,7 @@ async def test_require_roles_multiple_roles():
username="testuser",
email="test@example.com",
roles=["admin", "super_user", "user"],
groups=[]
groups=[],
)
result = await mock_multi_role_endpoint(user=user_with_roles)
assert result == {"message": "Success"}
@@ -107,56 +114,62 @@ async def test_require_roles_multiple_roles():
username="testuser",
email="test@example.com",
roles=["admin", "user"],
groups=[]
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)
})
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'
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)
importlib.reload(app.auth.dependencies)

View File

@@ -1,8 +1,10 @@
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")
@@ -10,27 +12,30 @@ def test_mock_get_user_from_token_success():
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
assert result1 == result2