Convert password reset and auth dependencies tests to async

- Refactored all `password reset` and `auth dependency` tests to utilize async patterns for compatibility with async database sessions.
- Enhanced test fixtures with `pytest-asyncio` to support asynchronous database operations.
- Improved user handling with async context management for `test_user` and `async_mock_user`.
- Introduced `await` syntax for route calls, token generation, and database transactions in test cases.
This commit is contained in:
Felipe Cardoso
2025-10-31 22:31:01 +01:00
parent 8a7a3b9521
commit 92a8699479
32 changed files with 708 additions and 437 deletions

0
backend/tests/api/dependencies/__init__.py Normal file → Executable file
View File

242
backend/tests/api/dependencies/test_auth_dependencies.py Normal file → Executable file
View File

@@ -1,5 +1,6 @@
# tests/api/dependencies/test_auth_dependencies.py
import pytest
import pytest_asyncio
import uuid
from unittest.mock import patch
from fastapi import HTTPException
@@ -10,7 +11,8 @@ from app.api.dependencies.auth import (
get_current_superuser,
get_optional_current_user
)
from app.core.auth import TokenExpiredError, TokenInvalidError
from app.core.auth import TokenExpiredError, TokenInvalidError, get_password_hash
from app.models.user import User
@pytest.fixture
@@ -19,79 +21,119 @@ def mock_token():
return "mock.jwt.token"
@pytest_asyncio.fixture
async def async_mock_user(async_test_db):
"""Async fixture to create and return a mock User instance."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
mock_user = User(
id=uuid.uuid4(),
email="mockuser@example.com",
password_hash=get_password_hash("mockhashedpassword"),
first_name="Mock",
last_name="User",
phone_number="1234567890",
is_active=True,
is_superuser=False,
preferences=None,
)
session.add(mock_user)
await session.commit()
await session.refresh(mock_user)
return mock_user
class TestGetCurrentUser:
"""Tests for get_current_user dependency"""
def test_get_current_user_success(self, db_session, mock_user, mock_token):
@pytest.mark.asyncio
async def test_get_current_user_success(self, async_test_db, async_mock_user, mock_token):
"""Test successfully getting the current user"""
# Mock get_token_data to return user_id that matches our mock_user
with patch('app.api.dependencies.auth.get_token_data') as mock_get_data:
mock_get_data.return_value.user_id = mock_user.id
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
# Mock get_token_data to return user_id that matches our mock_user
with patch('app.api.dependencies.auth.get_token_data') as mock_get_data:
mock_get_data.return_value.user_id = async_mock_user.id
# Call the dependency
user = get_current_user(db=db_session, token=mock_token)
# Call the dependency
user = await get_current_user(db=session, token=mock_token)
# Verify the correct user was returned
assert user.id == mock_user.id
assert user.email == mock_user.email
# Verify the correct user was returned
assert user.id == async_mock_user.id
assert user.email == async_mock_user.email
def test_get_current_user_nonexistent(self, db_session, mock_token):
@pytest.mark.asyncio
async def test_get_current_user_nonexistent(self, async_test_db, mock_token):
"""Test when the token contains a user ID that doesn't exist"""
# Mock get_token_data to return a non-existent user ID
nonexistent_id = uuid.UUID("11111111-1111-1111-1111-111111111111")
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
# Mock get_token_data to return a non-existent user ID
nonexistent_id = uuid.UUID("11111111-1111-1111-1111-111111111111")
with patch('app.api.dependencies.auth.get_token_data') as mock_get_data:
mock_get_data.return_value.user_id = nonexistent_id
with patch('app.api.dependencies.auth.get_token_data') as mock_get_data:
mock_get_data.return_value.user_id = nonexistent_id
# Should raise HTTPException with 404 status
with pytest.raises(HTTPException) as exc_info:
get_current_user(db=db_session, token=mock_token)
# Should raise HTTPException with 404 status
with pytest.raises(HTTPException) as exc_info:
await get_current_user(db=session, token=mock_token)
assert exc_info.value.status_code == 404
assert "User not found" in exc_info.value.detail
assert exc_info.value.status_code == 404
assert "User not found" in exc_info.value.detail
def test_get_current_user_inactive(self, db_session, mock_user, mock_token):
@pytest.mark.asyncio
async def test_get_current_user_inactive(self, async_test_db, async_mock_user, mock_token):
"""Test when the user is inactive"""
# Make the user inactive
mock_user.is_active = False
db_session.commit()
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
# Get the user in this session and make it inactive
from sqlalchemy import select
result = await session.execute(select(User).where(User.id == async_mock_user.id))
user_in_session = result.scalar_one_or_none()
user_in_session.is_active = False
await session.commit()
# Mock get_token_data
with patch('app.api.dependencies.auth.get_token_data') as mock_get_data:
mock_get_data.return_value.user_id = mock_user.id
# Mock get_token_data
with patch('app.api.dependencies.auth.get_token_data') as mock_get_data:
mock_get_data.return_value.user_id = async_mock_user.id
# Should raise HTTPException with 403 status
with pytest.raises(HTTPException) as exc_info:
get_current_user(db=db_session, token=mock_token)
# Should raise HTTPException with 403 status
with pytest.raises(HTTPException) as exc_info:
await get_current_user(db=session, token=mock_token)
assert exc_info.value.status_code == 403
assert "Inactive user" in exc_info.value.detail
assert exc_info.value.status_code == 403
assert "Inactive user" in exc_info.value.detail
def test_get_current_user_expired_token(self, db_session, mock_token):
@pytest.mark.asyncio
async def test_get_current_user_expired_token(self, async_test_db, mock_token):
"""Test with an expired token"""
# Mock get_token_data to raise TokenExpiredError
with patch('app.api.dependencies.auth.get_token_data') as mock_get_data:
mock_get_data.side_effect = TokenExpiredError("Token expired")
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
# Mock get_token_data to raise TokenExpiredError
with patch('app.api.dependencies.auth.get_token_data') as mock_get_data:
mock_get_data.side_effect = TokenExpiredError("Token expired")
# Should raise HTTPException with 401 status
with pytest.raises(HTTPException) as exc_info:
get_current_user(db=db_session, token=mock_token)
# Should raise HTTPException with 401 status
with pytest.raises(HTTPException) as exc_info:
await get_current_user(db=session, token=mock_token)
assert exc_info.value.status_code == 401
assert "Token expired" in exc_info.value.detail
assert exc_info.value.status_code == 401
assert "Token expired" in exc_info.value.detail
def test_get_current_user_invalid_token(self, db_session, mock_token):
@pytest.mark.asyncio
async def test_get_current_user_invalid_token(self, async_test_db, mock_token):
"""Test with an invalid token"""
# Mock get_token_data to raise TokenInvalidError
with patch('app.api.dependencies.auth.get_token_data') as mock_get_data:
mock_get_data.side_effect = TokenInvalidError("Invalid token")
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
# Mock get_token_data to raise TokenInvalidError
with patch('app.api.dependencies.auth.get_token_data') as mock_get_data:
mock_get_data.side_effect = TokenInvalidError("Invalid token")
# Should raise HTTPException with 401 status
with pytest.raises(HTTPException) as exc_info:
get_current_user(db=db_session, token=mock_token)
# Should raise HTTPException with 401 status
with pytest.raises(HTTPException) as exc_info:
await get_current_user(db=session, token=mock_token)
assert exc_info.value.status_code == 401
assert "Could not validate credentials" in exc_info.value.detail
assert exc_info.value.status_code == 401
assert "Could not validate credentials" in exc_info.value.detail
class TestGetCurrentActiveUser:
@@ -151,63 +193,81 @@ class TestGetCurrentSuperuser:
class TestGetOptionalCurrentUser:
"""Tests for get_optional_current_user dependency"""
def test_get_optional_current_user_with_token(self, db_session, mock_user, mock_token):
@pytest.mark.asyncio
async def test_get_optional_current_user_with_token(self, async_test_db, async_mock_user, mock_token):
"""Test getting optional user with a valid token"""
# Mock get_token_data
with patch('app.api.dependencies.auth.get_token_data') as mock_get_data:
mock_get_data.return_value.user_id = mock_user.id
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
# Mock get_token_data
with patch('app.api.dependencies.auth.get_token_data') as mock_get_data:
mock_get_data.return_value.user_id = async_mock_user.id
# Call the dependency
user = get_optional_current_user(db=db_session, token=mock_token)
# Call the dependency
user = await get_optional_current_user(db=session, token=mock_token)
# Should return the correct user
assert user is not None
assert user.id == mock_user.id
# Should return the correct user
assert user is not None
assert user.id == async_mock_user.id
def test_get_optional_current_user_no_token(self, db_session):
@pytest.mark.asyncio
async def test_get_optional_current_user_no_token(self, async_test_db):
"""Test getting optional user with no token"""
# Call the dependency with no token
user = get_optional_current_user(db=db_session, token=None)
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
# Call the dependency with no token
user = await get_optional_current_user(db=session, token=None)
# Should return None
assert user is None
# Should return None
assert user is None
def test_get_optional_current_user_invalid_token(self, db_session, mock_token):
@pytest.mark.asyncio
async def test_get_optional_current_user_invalid_token(self, async_test_db, mock_token):
"""Test getting optional user with an invalid token"""
# Mock get_token_data to raise TokenInvalidError
with patch('app.api.dependencies.auth.get_token_data') as mock_get_data:
mock_get_data.side_effect = TokenInvalidError("Invalid token")
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
# Mock get_token_data to raise TokenInvalidError
with patch('app.api.dependencies.auth.get_token_data') as mock_get_data:
mock_get_data.side_effect = TokenInvalidError("Invalid token")
# Call the dependency
user = get_optional_current_user(db=db_session, token=mock_token)
# Call the dependency
user = await get_optional_current_user(db=session, token=mock_token)
# Should return None, not raise an exception
assert user is None
# Should return None, not raise an exception
assert user is None
def test_get_optional_current_user_expired_token(self, db_session, mock_token):
@pytest.mark.asyncio
async def test_get_optional_current_user_expired_token(self, async_test_db, mock_token):
"""Test getting optional user with an expired token"""
# Mock get_token_data to raise TokenExpiredError
with patch('app.api.dependencies.auth.get_token_data') as mock_get_data:
mock_get_data.side_effect = TokenExpiredError("Token expired")
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
# Mock get_token_data to raise TokenExpiredError
with patch('app.api.dependencies.auth.get_token_data') as mock_get_data:
mock_get_data.side_effect = TokenExpiredError("Token expired")
# Call the dependency
user = get_optional_current_user(db=db_session, token=mock_token)
# Call the dependency
user = await get_optional_current_user(db=session, token=mock_token)
# Should return None, not raise an exception
assert user is None
# Should return None, not raise an exception
assert user is None
def test_get_optional_current_user_inactive(self, db_session, mock_user, mock_token):
@pytest.mark.asyncio
async def test_get_optional_current_user_inactive(self, async_test_db, async_mock_user, mock_token):
"""Test getting optional user when user is inactive"""
# Make the user inactive
mock_user.is_active = False
db_session.commit()
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
# Get the user in this session and make it inactive
from sqlalchemy import select
result = await session.execute(select(User).where(User.id == async_mock_user.id))
user_in_session = result.scalar_one_or_none()
user_in_session.is_active = False
await session.commit()
# Mock get_token_data
with patch('app.api.dependencies.auth.get_token_data') as mock_get_data:
mock_get_data.return_value.user_id = mock_user.id
# Mock get_token_data
with patch('app.api.dependencies.auth.get_token_data') as mock_get_data:
mock_get_data.return_value.user_id = async_mock_user.id
# Call the dependency
user = get_optional_current_user(db=db_session, token=mock_token)
# Call the dependency
user = await get_optional_current_user(db=session, token=mock_token)
# Should return None for inactive users
assert user is None
# Should return None for inactive users
assert user is None