Files
conference-room-booking-system/backend/tests/services/test_invitees.py
2025-08-25 21:38:31 -04:00

205 lines
7.2 KiB
Python

"""Unit tests for the backend.services.invitees module."""
from unittest.mock import AsyncMock
from unittest.mock import MagicMock
import pytest
from sqlalchemy import delete
from sqlalchemy import select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncSession
from backend.models import Invitee
from backend.services.invitees import UserList
from backend.services.invitees import add_invitee_to_booking
from backend.services.invitees import get_invitees_for_booking
from backend.services.invitees import remove_invitee_from_booking
@pytest.mark.asyncio
@pytest.mark.parametrize("mock_logger", ["backend.services.invitees"], indirect=True)
async def test_get_invitees_for_booking_success(
async_session: AsyncSession, sample_invitees: UserList, mock_logger: MagicMock
) -> None:
"""Test successful retrieval of all invitees for a booking.
Verifies that get_invitees_for_booking returns the expected list of users and constructs
the correct SQLAlchemy query.
"""
booking_id = 1
mock_scalars_result = AsyncMock()
mock_scalars_result.all = MagicMock(return_value=sample_invitees)
mock_scalars = AsyncMock(return_value=mock_scalars_result)
async_session.scalars = mock_scalars
result: UserList = await get_invitees_for_booking(async_session, booking_id)
assert isinstance(result, list)
assert len(result) == 2
assert result == sample_invitees
async_session.scalars.assert_called_once()
assert async_session.scalars.call_args.args[0].compare(
select(Invitee.user).where(Invitee.booking_id == booking_id)
)
@pytest.mark.asyncio
@pytest.mark.parametrize("mock_logger", ["backend.services.invitees"], indirect=True)
async def test_get_invitees_for_booking_empty(
async_session: AsyncSession, mock_logger: MagicMock
) -> None:
"""Test retrieval of invitees when none exist for the booking.
Verifies that get_invitees_for_booking returns an empty list when no invitees are found.
"""
booking_id = 1
mock_scalars_result = AsyncMock()
mock_scalars_result.all = MagicMock(return_value=[])
mock_scalars = AsyncMock(return_value=mock_scalars_result)
async_session.scalars = mock_scalars
result: UserList = await get_invitees_for_booking(async_session, booking_id)
assert isinstance(result, list)
assert len(result) == 0
async_session.scalars.assert_called_once()
assert async_session.scalars.call_args.args[0].compare(
select(Invitee.user).where(Invitee.booking_id == booking_id)
)
@pytest.mark.asyncio
@pytest.mark.parametrize("mock_logger", ["backend.services.invitees"], indirect=True)
async def test_get_invitees_for_booking_database_error(
async_session: AsyncSession, mock_logger: MagicMock
) -> None:
"""Test handling of database errors in get_invitees_for_booking.
Verifies that get_invitees_for_booking raises an exception on database failure.
"""
booking_id = 1
async_session.scalars = AsyncMock(side_effect=SQLAlchemyError("Database error"))
with pytest.raises(SQLAlchemyError):
await get_invitees_for_booking(async_session, booking_id)
async_session.scalars.assert_called_once()
@pytest.mark.asyncio
@pytest.mark.parametrize("mock_logger", ["backend.services.invitees"], indirect=True)
@pytest.mark.parametrize(
"booking_id, user_email",
[
(1, "user1@example.com"),
(2, "user2@example.com"),
],
ids=["invitee1", "invitee2"],
)
async def test_add_invitee_to_booking_success(
async_session: AsyncSession,
booking_id: int,
user_email: str,
mock_logger: MagicMock,
) -> None:
"""Test successful addition of an invitee to a booking.
Verifies that add_invitee_to_booking adds the invitee, commits the session,
and returns the invitee.
"""
invitee = Invitee(booking_id=booking_id, user_email=user_email)
async_session.add = MagicMock()
async_session.commit = AsyncMock()
result: Invitee = await add_invitee_to_booking(
async_session, booking_id, user_email
)
assert result == invitee
assert result.booking_id == booking_id
assert result.user_email == user_email
async_session.add.assert_called_once_with(invitee)
async_session.commit.assert_called_once()
@pytest.mark.asyncio
@pytest.mark.parametrize("mock_logger", ["backend.services.invitees"], indirect=True)
async def test_add_invitee_to_booking_database_error(
async_session: AsyncSession, mock_logger: MagicMock
) -> None:
"""Test handling of database errors in add_invitee_to_booking.
Verifies that add_invitee_to_booking rolls back the session on database failure.
"""
booking_id = 1
user_email = "user1@example.com"
invitee = Invitee(booking_id=booking_id, user_email=user_email)
async_session.add = MagicMock()
async_session.commit = AsyncMock(side_effect=SQLAlchemyError("Database error"))
async_session.rollback = AsyncMock()
with pytest.raises(SQLAlchemyError):
await add_invitee_to_booking(async_session, booking_id, user_email)
async_session.add.assert_called_once_with(invitee)
async_session.commit.assert_called_once()
async_session.rollback.assert_called_once()
@pytest.mark.asyncio
@pytest.mark.parametrize("mock_logger", ["backend.services.invitees"], indirect=True)
@pytest.mark.parametrize(
"booking_id, user_email",
[
(1, "user1@example.com"),
(2, "user2@example.com"),
],
ids=["invitee1", "invitee2"],
)
async def test_remove_invitee_from_booking_success(
async_session: AsyncSession,
booking_id: int,
user_email: str,
mock_logger: MagicMock,
) -> None:
"""Test successful removal of an invitee from a booking.
Verifies that remove_invitee_from_booking deletes the invitee and commits the session.
"""
mock_execute_result = MagicMock(rowcount=1)
async_session.execute = AsyncMock(return_value=mock_execute_result)
async_session.commit = AsyncMock()
await remove_invitee_from_booking(async_session, booking_id, user_email)
async_session.execute.assert_called_once()
assert async_session.execute.call_args.args[0].compare(
delete(Invitee).where(
Invitee.booking_id == booking_id, Invitee.user_email == user_email
)
)
async_session.commit.assert_called_once()
@pytest.mark.asyncio
@pytest.mark.parametrize("mock_logger", ["backend.services.invitees"], indirect=True)
async def test_remove_invitee_from_booking_database_error(
async_session: AsyncSession, mock_logger: MagicMock
) -> None:
"""Test handling of database errors in remove_invitee_from_booking.
Verifies that remove_invitee_from_booking rolls back the session on database failure.
"""
booking_id = 1
user_email = "user1@example.com"
async_session.execute = AsyncMock(side_effect=SQLAlchemyError("Database error"))
async_session.rollback = AsyncMock()
with pytest.raises(SQLAlchemyError):
await remove_invitee_from_booking(async_session, booking_id, user_email)
async_session.execute.assert_called_once()
assert async_session.execute.call_args.args[0].compare(
delete(Invitee).where(
Invitee.booking_id == booking_id, Invitee.user_email == user_email
)
)
async_session.rollback.assert_called_once()