"""Unit tests for the backend.routers.users module.""" from typing import Any import pytest import sqlalchemy.exc from httpx import AsyncClient from sqlalchemy.exc import SQLAlchemyError from backend.dependencies.users import get_users_service from backend.main import app from backend.models import User from backend.types import UserData # Useful constants for users router tests USERS_SUCCESS_STATUS = 200 USERS_DB_ERROR_STATUS = 500 USERS_NOT_FOUND_STATUS = 404 USERS_DB_ERROR_DETAIL = "Database error" USERS_NOT_FOUND_DETAIL = "User not found" USERS_UNEXPECTED_ERROR_DETAIL = "An unexpected error occurred" @pytest.mark.asyncio @pytest.mark.parametrize("has_users", [True, False], ids=["users_exist", "users_empty"]) async def test_read_users_parametrized( client: AsyncClient, sample_users: list[User], sample_users_data: list[UserData], has_users: bool, ) -> None: """Test GET /users/ for success and empty list cases. Args: client: The FastAPI test client for making HTTP requests. sample_users: The list of sample User objects. sample_users_data: The expected response data for users. has_users: Whether to return users or an empty list. Asserts: - Response status code is USERS_SUCCESS_STATUS. - Response JSON matches expected user data or empty list. """ async def mock_get_users(*args: Any, **kwargs: Any) -> list[User]: return sample_users if has_users else [] app.dependency_overrides[get_users_service] = lambda: mock_get_users response = await client.get("/users/") assert response.status_code == USERS_SUCCESS_STATUS expected = sample_users_data if has_users else [] assert response.json() == expected @pytest.mark.asyncio async def test_read_users_database_error( client: AsyncClient, ) -> None: """Test GET /users/ for database error. Args: client: The FastAPI test client for making HTTP requests. Asserts: - Response status code is USERS_DB_ERROR_STATUS. - Response JSON contains 'detail'. """ async def mock_get_users(*args: Any, **kwargs: Any) -> None: raise SQLAlchemyError() app.dependency_overrides[get_users_service] = lambda: mock_get_users response = await client.get("/users/") assert response.status_code == USERS_DB_ERROR_STATUS assert "detail" in response.json() # Parametrized error path tests for GET /users/ @pytest.mark.asyncio @pytest.mark.parametrize( "exception_to_raise,expected_status,expected_detail", [ ( sqlalchemy.exc.NoResultFound(), USERS_NOT_FOUND_STATUS, USERS_NOT_FOUND_DETAIL, ), (Exception("fail"), USERS_DB_ERROR_STATUS, USERS_UNEXPECTED_ERROR_DETAIL), ], ids=["no_result_found", "unexpected_error"], ) async def test_read_users_error_paths( client: AsyncClient, exception_to_raise: Exception, expected_status: int, expected_detail: str, ) -> None: """Test GET /users/ for error paths: NoResultFound (404) and generic exception (500). Args: client: The FastAPI test client for making HTTP requests. exception_to_raise: The exception to raise in the service dependency. expected_status: The expected HTTP status code. expected_detail: The expected error detail message. Asserts: - Response status code matches expected_status. - Response JSON 'detail' matches expected_detail. """ async def mock_get_users(*args: Any, **kwargs: Any) -> None: raise exception_to_raise app.dependency_overrides[get_users_service] = lambda: mock_get_users response = await client.get("/users/") assert response.status_code == expected_status assert response.json()["detail"] == expected_detail