Added tests for the users router.

Signed-off-by: Cliff Hill <xlorep@darkhelm.org>
This commit is contained in:
2025-08-24 16:37:21 -04:00
parent 793baad224
commit 2f47b389b6
3 changed files with 182 additions and 1 deletions
+15 -1
View File
@@ -4,6 +4,7 @@ from fastapi import APIRouter
from fastapi import HTTPException
from pydantic import EmailStr
from sqlalchemy.exc import NoResultFound
from sqlalchemy.exc import SQLAlchemyError
from backend.dependencies.db import DBSession
from backend.schemas.users import UserResponse
@@ -17,7 +18,14 @@ router = APIRouter(prefix="/users", tags=["users"])
@router.get("/", response_model=list[UserResponse])
async def read_users(session: DBSession):
"""Endpoint to retrieve all users."""
users = await get_users(session)
try:
users = await get_users(session)
except SQLAlchemyError as e:
raise HTTPException(status_code=500, detail="Database error occurred") from e
except Exception as e:
raise HTTPException(
status_code=500, detail="An unexpected error occurred"
) from e
return users
@@ -30,4 +38,10 @@ async def read_user(email: EmailStr, session: DBSession):
raise HTTPException(
status_code=404, detail=f"User with email {email} not found"
) from e
except SQLAlchemyError as e:
raise HTTPException(status_code=500, detail="Database error occurred") from e
except Exception as e:
raise HTTPException(
status_code=500, detail="An unexpected error occurred"
) from e
return user
+1
View File
@@ -0,0 +1 @@
"""Tests for the routers package."""
+166
View File
@@ -0,0 +1,166 @@
"""Unit tests for the backend.routers.users module."""
import logging
from collections.abc import Generator
from unittest.mock import ANY
from unittest.mock import AsyncMock
from unittest.mock import MagicMock
from unittest.mock import patch
import pytest
from fastapi.testclient import TestClient
from sqlalchemy.exc import NoResultFound
from sqlalchemy.exc import SQLAlchemyError
from backend.main import app
from backend.models import User
from backend.services.users import UserList
from ..conftest import MockLogger
@pytest.fixture(autouse=True)
def mock_logger() -> Generator[MockLogger, None, None]:
"""Fixture to mock the logger used in the users service."""
mock_logger_instance = MagicMock(spec=logging.Logger)
with patch("backend.services.users.logger", mock_logger_instance):
yield mock_logger_instance
@pytest.fixture
def sample_users() -> UserList:
"""Fixture to provide sample User objects for testing."""
return [
User(email="user1@example.com", name="User One"),
User(email="user2@example.com", name="User Two"),
]
@pytest.fixture
def client() -> TestClient:
"""Fixture to provide a FastAPI TestClient for testing endpoints."""
return TestClient(app)
@pytest.mark.asyncio
async def test_read_users_success(
client: TestClient, sample_users: UserList, mock_logger: MockLogger
) -> None:
"""Test successful retrieval of all users via GET /users/.
Verifies that the endpoint returns the expected list of users with a 200 status.
"""
with patch("backend.routers.users.get_users", new=AsyncMock()) as mock_get_users:
mock_get_users.return_value = sample_users
response = client.get("/users/")
assert response.status_code == 200
assert response.json() == [
{"email": "user1@example.com", "name": "User One"},
{"email": "user2@example.com", "name": "User Two"},
]
mock_get_users.assert_called_once()
@pytest.mark.asyncio
async def test_read_users_empty(client: TestClient, mock_logger: MockLogger) -> None:
"""Test retrieval of users when none exist via GET /users/.
Verifies that the endpoint returns an empty list with a 200 status.
"""
with patch("backend.routers.users.get_users", new=AsyncMock()) as mock_get_users:
mock_get_users.return_value = []
response = client.get("/users/")
assert response.status_code == 200
assert response.json() == []
mock_get_users.assert_called_once()
@pytest.mark.asyncio
async def test_read_users_database_error(
client: TestClient, mock_logger: MockLogger
) -> None:
"""Test handling of database errors in GET /users/.
Verifies that the endpoint returns a 500 status on database failure.
"""
with patch("backend.routers.users.get_users", new=AsyncMock()) as mock_get_users:
mock_get_users.side_effect = SQLAlchemyError("Database error")
response = client.get("/users/")
assert response.status_code == 500
assert response.json() == {"detail": "Database error occurred"}
mock_get_users.assert_called_once()
@pytest.mark.asyncio
@pytest.mark.parametrize(
"email, expected_name",
[
("user1@example.com", "User One"),
("user2@example.com", "User Two"),
],
ids=["user1", "user2"],
)
async def test_read_user_success(
client: TestClient, email: str, expected_name: str, mock_logger: MockLogger
) -> None:
"""Test successful retrieval of a user by email via GET /users/{email}.
Verifies that the endpoint returns the correct user with a 200 status.
"""
user = User(email=email, name=expected_name)
with patch("backend.routers.users.get_user", new=AsyncMock()) as mock_get_user:
mock_get_user.return_value = user
response = client.get(f"/users/{email}")
assert response.status_code == 200
assert response.json() == {"email": email, "name": expected_name}
mock_get_user.assert_called_once_with(ANY, email)
@pytest.mark.asyncio
@pytest.mark.parametrize(
"email",
["nonexistent@example.com", "invalid@domain.com"],
ids=["nonexistent_email", "invalid_email"],
)
async def test_read_user_not_found(
client: TestClient, email: str, mock_logger: MockLogger
) -> None:
"""Test handling of non-existent user in GET /users/{email}.
Verifies that the endpoint returns a 404 status when the user is not found.
"""
with patch("backend.routers.users.get_user", new=AsyncMock()) as mock_get_user:
mock_get_user.side_effect = NoResultFound(f"User with email {email} not found")
response = client.get(f"/users/{email}")
assert response.status_code == 404
assert response.json() == {"detail": f"User with email {email} not found"}
mock_get_user.assert_called_once_with(ANY, email)
@pytest.mark.asyncio
async def test_read_user_database_error(
client: TestClient, mock_logger: MockLogger
) -> None:
"""Test handling of database errors in GET /users/{email}.
Verifies that the endpoint returns a 500 status on database failure.
"""
email = "user1@example.com"
with patch("backend.routers.users.get_user", new=AsyncMock()) as mock_get_user:
mock_get_user.side_effect = SQLAlchemyError("Database error")
response = client.get(f"/users/{email}")
assert response.status_code == 500
assert response.json() == {"detail": "Database error occurred"}
mock_get_user.assert_called_once_with(ANY, email)