"""Unit tests for the backend.routers.users module.""" 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.models import User from backend.services.users import UserList @pytest.mark.asyncio @pytest.mark.parametrize("mock_logger", ["backend.routers.users"], indirect=True) async def test_read_users_success( client: TestClient, sample_users: UserList, mock_logger: MagicMock ) -> 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 @pytest.mark.parametrize("mock_logger", ["backend.routers.users"], indirect=True) async def test_read_users_empty(client: TestClient, mock_logger: MagicMock) -> 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 @pytest.mark.parametrize("mock_logger", ["backend.routers.users"], indirect=True) async def test_read_users_database_error( client: TestClient, mock_logger: MagicMock ) -> 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() response = client.get("/users/") assert response.status_code == 500 assert "detail" in response.json() mock_get_users.assert_called_once() @pytest.mark.asyncio @pytest.mark.parametrize("mock_logger", ["backend.routers.users"], indirect=True) @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: MagicMock ) -> 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("mock_logger", ["backend.routers.users"], indirect=True) @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: MagicMock ) -> 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() response = client.get(f"/users/{email}") assert response.status_code == 404 assert "detail" in response.json() mock_get_user.assert_called_once_with(ANY, email) @pytest.mark.asyncio @pytest.mark.parametrize("mock_logger", ["backend.routers.users"], indirect=True) async def test_read_user_database_error( client: TestClient, mock_logger: MagicMock ) -> 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() response = client.get(f"/users/{email}") assert response.status_code == 500 assert "detail" in response.json() mock_get_user.assert_called_once_with(ANY, email)