Public Access
mirror of
https://github.com/xlorepdarkhelm/numinar-coding-project.git
synced 2026-09-10 18:22:42 -04:00
Added tests for the users router.
Signed-off-by: Cliff Hill <xlorep@darkhelm.org>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
"""Tests for the routers package."""
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user