Files
conference-room-booking-system/backend/tests/services/test_users.py
Cliff Hill 4b4017d7c6 Fixing things.
Signed-off-by: Cliff Hill <xlorep@darkhelm.org>
2025-08-26 17:15:56 -04:00

187 lines
6.2 KiB
Python

"""Unit tests for the backend.services.users module."""
from unittest.mock import AsyncMock
from unittest.mock import MagicMock
import pytest
from sqlalchemy import select
from sqlalchemy.exc import NoResultFound
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncSession
from backend.models import User
from backend.models import UserList
from backend.services.users import get_user
from backend.services.users import get_users
@pytest.mark.asyncio
@pytest.mark.parametrize("mock_logger", ["backend.services.users"], indirect=True)
async def test_get_users_success(
async_session: AsyncSession, sample_users: UserList, mock_logger: MagicMock
) -> None:
"""Test successful retrieval of all users from the database.
Verifies that get_users returns the expected list of users and constructs
the correct SQLAlchemy query.
Args:
async_session: The asynchronous database session.
sample_users: The mocked list of users to return.
mock_logger: The mocked logger instance.
"""
mock_scalars_result = AsyncMock()
mock_scalars_result.all = MagicMock(return_value=sample_users)
mock_scalars = AsyncMock(return_value=mock_scalars_result)
async_session.scalars = mock_scalars # type: ignore [method-assign]
result: UserList = await get_users(async_session)
assert isinstance(result, list)
assert len(result) == 2
assert result == sample_users
async_session.scalars.assert_called_once()
assert async_session.scalars.call_args.args[0].compare(select(User))
@pytest.mark.asyncio
@pytest.mark.parametrize("mock_logger", ["backend.services.users"], indirect=True)
async def test_get_users_empty(
async_session: AsyncSession, mock_logger: MagicMock
) -> None:
"""Test retrieval of users when the database is empty.
Verifies that get_users returns an empty list when no users are found.
Args:
async_session: The asynchronous database session.
mock_logger: The mocked logger instance.
"""
mock_scalars_result = AsyncMock()
mock_scalars_result.all = MagicMock(return_value=[])
mock_scalars = AsyncMock(return_value=mock_scalars_result)
async_session.scalars = mock_scalars # type: ignore [method-assign]
result: UserList = await get_users(async_session)
assert isinstance(result, list)
assert len(result) == 0
async_session.scalars.assert_called_once()
assert async_session.scalars.call_args.args[0].compare(select(User))
@pytest.mark.asyncio
@pytest.mark.parametrize("mock_logger", ["backend.services.users"], indirect=True)
async def test_get_users_database_error(
async_session: AsyncSession, mock_logger: MagicMock
) -> None:
"""Test handling of database errors in get_users.
Verifies that get_users raises an exception on database failure.
Args:
async_session: The asynchronous database session.
mock_logger: The mocked logger instance.
"""
async_session.scalars = AsyncMock( # type: ignore [method-assign]
side_effect=SQLAlchemyError("Database error")
)
with pytest.raises(SQLAlchemyError):
await get_users(async_session)
async_session.scalars.assert_called_once()
@pytest.mark.asyncio
@pytest.mark.parametrize("mock_logger", ["backend.services.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_get_user_success(
async_session: AsyncSession, email: str, expected_name: str, mock_logger: MagicMock
) -> None:
"""Test successful retrieval of a user by email.
Verifies that get_user returns the correct user and constructs the correct query.
Args:
async_session: The asynchronous database session.
email: The email of the user to retrieve.
expected_name: The expected name of the user.
mock_logger: The mocked logger instance.
"""
user = User(email=email, name=expected_name)
async_session.scalar = AsyncMock(return_value=user) # type: ignore [method-assign]
result: User = await get_user(async_session, email)
assert result.email == email
assert result.name == expected_name
async_session.scalar.assert_called_once()
assert async_session.scalar.call_args.args[0].compare(
select(User).where(User.email == email)
)
@pytest.mark.asyncio
@pytest.mark.parametrize("mock_logger", ["backend.services.users"], indirect=True)
@pytest.mark.parametrize(
"email",
[
"nonexistent@example.com",
"invalid@domain.com",
],
ids=["nonexistent_email", "invalid_email"],
)
async def test_get_user_not_found(
async_session: AsyncSession, mock_logger: MagicMock, email: str
) -> None:
"""Test handling of non-existent user in get_user.
Verifies that get_user raises NoResultFound when the user is not found.
Args:
async_session: The asynchronous database session.
mock_logger: The mocked logger instance.
email: The email of the user to retrieve.
"""
async_session.scalar = AsyncMock(return_value=None) # type: ignore [method-assign]
with pytest.raises(NoResultFound):
await get_user(async_session, email)
async_session.scalar.assert_called_once()
assert async_session.scalar.call_args.args[0].compare(
select(User).where(User.email == email)
)
@pytest.mark.asyncio
@pytest.mark.parametrize("mock_logger", ["backend.services.users"], indirect=True)
async def test_get_user_database_error(
async_session: AsyncSession, mock_logger: MagicMock
) -> None:
"""Test handling of database errors in get_user.
Verifies that get_user raises an exception on database failure.
Args:
async_session: The asynchronous database session.
mock_logger: The mocked logger instance.
"""
email: str = "user1@example.com"
async_session.scalar = AsyncMock( # type: ignore [method-assign]
side_effect=SQLAlchemyError("Database error")
)
with pytest.raises(SQLAlchemyError):
await get_user(async_session, email)
async_session.scalar.assert_called_once()
assert async_session.scalar.call_args.args[0].compare(
select(User).where(User.email == email)
)