"""Test configuration and fixtures for the backend tests.""" import logging from collections.abc import Generator from datetime import datetime from unittest.mock import AsyncMock from unittest.mock import MagicMock from unittest.mock import patch import pytest from fastapi.testclient import TestClient from sqlalchemy.ext.asyncio import AsyncSession from backend.main import app from backend.models import Booking from backend.models import BookingList from backend.models import Invitee from backend.models import Room from backend.models import RoomList from backend.models import User from backend.models import UserList @pytest.fixture def async_session() -> AsyncSession: """Fixture to provide a mocked AsyncSession for database interactions. Returns: A mocked AsyncSession object for database interactions. """ return AsyncMock(spec=AsyncSession) @pytest.fixture def client() -> TestClient: """Fixture to provide a FastAPI TestClient for testing endpoints. Returns: A FastAPI TestClient instance for making HTTP requests. """ return TestClient(app) @pytest.fixture def mock_logger(request: pytest.FixtureRequest) -> Generator[MagicMock, None, None]: """Fixture to mock a logger for a specified module path. Args: request: Pytest request object, used to pass the module path for patching. Yields: A mocked logger instance for the specified module path. Usage: To use this fixture in a test module, parameterize it with the module path where the logger is defined. For example, to mock the logger in `backend.services.users`, use the `@pytest.mark.parametrize` decorator: .. code-block:: python @pytest.mark.parametrize("mock_logger", ["backend.services.users"], indirect=True) async def test_example(mock_logger): # Test code here, with mock_logger patched for backend.services.users The `indirect=True` argument ensures the fixture uses the provided module path. The fixture patches the logger at the specified path (e.g., `backend.services.users.logger`) and yields a `MagicMock` instance that can be used to verify logging behavior. """ # Default path if none provided, or use the parameterized path module_path = getattr(request, "param", "backend") + ".logger" mock_logger_instance = MagicMock(spec=logging.Logger) with patch(module_path, mock_logger_instance): yield mock_logger_instance @pytest.fixture def sample_users() -> UserList: """Fixture to provide sample User objects for testing. Returns: A list of sample User objects. """ return [ User(email="user1@example.com", name="User One"), User(email="user2@example.com", name="User Two"), ] @pytest.fixture def sample_rooms() -> RoomList: """Fixture to provide sample Room objects for testing. Returns: A list of sample Room objects. """ return [ Room( id=1, name="Room 1", location="Building A", equipment="Projector", capacity=10, ), Room( id=2, name="Room 2", location="Building B", equipment="Whiteboard", capacity=15, ), ] @pytest.fixture def sample_room(sample_rooms: RoomList) -> Room: """Fixture to provide a sample Room object for testing. Args: sample_rooms: The list of sample Room objects. Returns: A single sample Room object. """ return sample_rooms[0] @pytest.fixture def sample_bookings(sample_room: Room) -> BookingList: """Fixture to provide sample Booking objects for testing. Args: sample_room: The sample Room object to attach to bookings. Returns: A list of sample Booking objects with attached room relationships. """ booking1 = Booking( room_id=1, start_time=datetime.fromisoformat("2025-08-25T10:00:00"), end_time=datetime.fromisoformat("2025-08-25T11:00:00"), ) booking1.id = 1 # Manually set ID for testing purposes booking1.room = sample_room # Attach room relationship booking2 = Booking( room_id=1, start_time=datetime.fromisoformat("2025-08-25T12:00:00"), end_time=datetime.fromisoformat("2025-08-25T13:00:00"), ) booking2.id = 2 # Manually set ID for testing purposes booking2.room = sample_room # Attach room relationship return [booking1, booking2] @pytest.fixture def sample_booking(sample_bookings: BookingList) -> Booking: """Fixture to provide a sample Booking object for testing. Args: sample_bookings: The list of sample Booking objects. Returns: A single sample Booking object. """ return sample_bookings[0] @pytest.fixture def sample_invitees(sample_booking: Booking, sample_users: UserList) -> list[Invitee]: """Fixture to provide sample Invitee objects for testing. Args: sample_booking: The sample Booking object to attach to invitees. sample_users: The list of sample User objects to attach to invitees. Returns: A list of sample Invitee objects with attached booking and user relationships. """ invitee1 = Invitee(booking_id=1, user_email="user1@example.com") invitee1.id = 1 invitee1.booking = sample_booking invitee1.user = sample_users[0] invitee2 = Invitee(booking_id=1, user_email="user2@example.com") invitee2.id = 2 invitee2.booking = sample_booking invitee2.user = sample_users[1] return [invitee1, invitee2] @pytest.fixture def sample_invitee(sample_invitees: list[Invitee]) -> Invitee: """Fixture to provide a sample Invitee object for testing. Args: sample_invitees: The list of sample Invitee objects. Returns: A single sample Invitee object. """ return sample_invitees[0]