Files
conference-room-booking-system/backend/tests/routers/test_users.py
2025-08-24 16:37:21 -04:00

167 lines
5.4 KiB
Python

"""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)