mirror of
https://github.com/xlorepdarkhelm/numinar-coding-project.git
synced 2026-08-25 00:33:45 -04:00
@@ -10,11 +10,10 @@ from pydantic import EmailStr
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
|
||||
from backend.dependencies.db import DBSession
|
||||
from backend.models import User
|
||||
from backend.models import UserList
|
||||
from backend.models import Invitee
|
||||
from backend.models import InviteeList
|
||||
from backend.schemas.invitees import InviteeCreate
|
||||
from backend.schemas.invitees import InviteeResponse
|
||||
from backend.schemas.users import UserResponse
|
||||
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
|
||||
@@ -25,8 +24,8 @@ logger = logging.getLogger(__name__)
|
||||
router = APIRouter(prefix="/invitees", tags=["invitees"])
|
||||
|
||||
|
||||
@router.get("/booking/{booking_id}", response_model=List[UserResponse])
|
||||
async def read_invitees_for_booking(booking_id: int, session: DBSession) -> UserList:
|
||||
@router.get("/booking/{booking_id}", response_model=List[InviteeResponse])
|
||||
async def read_invitees_for_booking(booking_id: int, session: DBSession) -> InviteeList:
|
||||
"""Retrieve all invitees for a specific booking.
|
||||
|
||||
Args:
|
||||
@@ -71,7 +70,7 @@ async def read_invitees_for_booking(booking_id: int, session: DBSession) -> User
|
||||
)
|
||||
async def add_invitee(
|
||||
booking_id: int, invitee: InviteeCreate, session: DBSession
|
||||
) -> User:
|
||||
) -> Invitee:
|
||||
"""Add an invitee to a booking.
|
||||
|
||||
Args:
|
||||
|
||||
@@ -7,7 +7,6 @@ including relationships to bookings and users.
|
||||
from pydantic import BaseModel
|
||||
from pydantic import ConfigDict
|
||||
|
||||
from backend.schemas.bookings import BookingResponse
|
||||
from backend.schemas.users import UserResponse
|
||||
|
||||
|
||||
@@ -52,12 +51,10 @@ class InviteeResponse(InviteeBase):
|
||||
(inherited from InviteeBase).
|
||||
user_email: The email address of the user associated with the invitee
|
||||
(inherited from InviteeBase).
|
||||
booking: The BookingResponse object containing details of the associated booking.
|
||||
user: The UserResponse object containing details of the associated user.
|
||||
"""
|
||||
|
||||
id: int
|
||||
booking: BookingResponse
|
||||
user: UserResponse
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
@@ -12,14 +12,15 @@ from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from backend.models import Invitee
|
||||
from backend.models import User
|
||||
from backend.models import UserList
|
||||
from backend.models import InviteeList
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def get_invitees_for_booking(session: AsyncSession, booking_id: int) -> UserList:
|
||||
async def get_invitees_for_booking(
|
||||
session: AsyncSession, booking_id: int
|
||||
) -> InviteeList:
|
||||
"""Retrieve all invitees for a specific booking.
|
||||
|
||||
Args:
|
||||
@@ -27,16 +28,16 @@ async def get_invitees_for_booking(session: AsyncSession, booking_id: int) -> Us
|
||||
booking_id: The ID of the booking to retrieve invitees for.
|
||||
|
||||
Returns:
|
||||
A list of User objects associated with the booking as invitees.
|
||||
A list of Invitee objects associated with the booking as invitees.
|
||||
|
||||
Raises:
|
||||
Exception: Any database error encountered during the query is logged and re-raised.
|
||||
"""
|
||||
logger.debug(f"Entering get_invitees_for_booking with booking_id: {booking_id}")
|
||||
try:
|
||||
stmt = select(Invitee.user).where(Invitee.booking_id == booking_id)
|
||||
stmt = select(Invitee).where(Invitee.booking_id == booking_id)
|
||||
result = await session.scalars(stmt)
|
||||
invitees = cast(UserList, result.all())
|
||||
invitees = cast(InviteeList, result.all())
|
||||
logger.info(
|
||||
f"Successfully retrieved {len(invitees)} invitees for booking_id: {booking_id}"
|
||||
)
|
||||
@@ -52,7 +53,7 @@ async def get_invitees_for_booking(session: AsyncSession, booking_id: int) -> Us
|
||||
|
||||
async def add_invitee_to_booking(
|
||||
session: AsyncSession, booking_id: int, user_email: str
|
||||
) -> User:
|
||||
) -> Invitee:
|
||||
"""Add an invitee to a booking.
|
||||
|
||||
Args:
|
||||
@@ -76,7 +77,7 @@ async def add_invitee_to_booking(
|
||||
logger.info(
|
||||
f"Successfully added invitee with email {user_email} to booking_id: {booking_id}"
|
||||
)
|
||||
return invitee.user
|
||||
return invitee
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Failed to add invitee with email {user_email} to"
|
||||
|
||||
@@ -186,3 +186,16 @@ def sample_invitees(sample_booking: Booking, sample_users: UserList) -> list[Inv
|
||||
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]
|
||||
|
||||
@@ -12,13 +12,16 @@ from sqlalchemy.exc import SQLAlchemyError
|
||||
|
||||
from backend.models import Booking
|
||||
from backend.models import Invitee
|
||||
from backend.services.invitees import UserList
|
||||
from backend.models import InviteeList
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("mock_logger", ["backend.routers.invitees"], indirect=True)
|
||||
async def test_get_invitees_for_booking_success(
|
||||
client: TestClient, sample_users: UserList, mock_logger: MagicMock
|
||||
client: TestClient,
|
||||
sample_booking: Booking,
|
||||
sample_invitees: InviteeList,
|
||||
mock_logger: MagicMock,
|
||||
) -> None:
|
||||
"""Test successful retrieval of all invitees for a booking.
|
||||
|
||||
@@ -27,23 +30,28 @@ async def test_get_invitees_for_booking_success(
|
||||
|
||||
Args:
|
||||
client: The FastAPI test client for making HTTP requests.
|
||||
sample_users: The mocked list of users to return.
|
||||
sample_booking: The mocked booking object.
|
||||
sample_invitees: The mocked list of invitees to return.
|
||||
mock_logger: The mocked logger instance.
|
||||
"""
|
||||
booking_id = 1
|
||||
with patch(
|
||||
"backend.routers.invitees.get_invitees_for_booking", new=AsyncMock()
|
||||
) as mock_get_invitees:
|
||||
mock_get_invitees.return_value = sample_users
|
||||
mock_get_invitees.return_value = sample_invitees
|
||||
|
||||
response = client.get(f"/invitees/booking/{booking_id}")
|
||||
response = client.get(f"/invitees/booking/{sample_booking.id}")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == [
|
||||
{"email": "user1@example.com", "name": "User One"},
|
||||
{"email": "user2@example.com", "name": "User Two"},
|
||||
{
|
||||
"id": invitee.id,
|
||||
"booking_id": invitee.booking_id,
|
||||
"user_email": invitee.user_email,
|
||||
"user": {"name": invitee.user.name, "email": invitee.user.email},
|
||||
}
|
||||
for invitee in sample_invitees
|
||||
]
|
||||
mock_get_invitees.assert_called_once_with(ANY, booking_id)
|
||||
mock_get_invitees.assert_called_once_with(ANY, sample_booking.id)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -103,8 +111,7 @@ async def test_get_invitees_for_booking_database_error(
|
||||
@pytest.mark.parametrize("mock_logger", ["backend.routers.invitees"], indirect=True)
|
||||
async def test_add_invitee_success(
|
||||
client: TestClient,
|
||||
sample_booking: Booking,
|
||||
sample_users: UserList,
|
||||
sample_invitee: Invitee,
|
||||
mock_logger: MagicMock,
|
||||
) -> None:
|
||||
"""Test successful addition of an invitee to a booking.
|
||||
@@ -114,44 +121,28 @@ async def test_add_invitee_success(
|
||||
|
||||
Args:
|
||||
client: The FastAPI test client for making HTTP requests.
|
||||
sample_booking: The mocked booking object.
|
||||
sample_users: The mocked list of users.
|
||||
sample_invitee: The mocked invitee object.
|
||||
mock_logger: The mocked logger instance.
|
||||
"""
|
||||
booking_id = 1
|
||||
invitee_data: dict[str, Any] = {
|
||||
"booking_id": booking_id,
|
||||
"user_email": "user1@example.com",
|
||||
"booking_id": sample_invitee.booking_id,
|
||||
"user_email": sample_invitee.user_email,
|
||||
}
|
||||
created_invitee = Invitee(**invitee_data)
|
||||
created_invitee.id = 1
|
||||
created_invitee.booking = sample_booking
|
||||
created_invitee.user = sample_users[0]
|
||||
with patch(
|
||||
"backend.routers.invitees.add_invitee_to_booking", new=AsyncMock()
|
||||
) as mock_add_invitee:
|
||||
mock_add_invitee.return_value = created_invitee
|
||||
mock_add_invitee.return_value = sample_invitee
|
||||
|
||||
response = client.post(f"/invitees/booking/{booking_id}", json=invitee_data)
|
||||
response = client.post(
|
||||
f"/invitees/booking/{sample_invitee.booking_id}", json=invitee_data
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
assert response.json() == {
|
||||
"id": 1,
|
||||
"booking_id": booking_id,
|
||||
"user_email": "user1@example.com",
|
||||
"booking": {
|
||||
"id": 1,
|
||||
"room_id": 1,
|
||||
"start_time": "2025-08-25T10:00:00",
|
||||
"end_time": "2025-08-25T11:00:00",
|
||||
},
|
||||
"user": {
|
||||
"email": "user1@example.com",
|
||||
"name": "User One",
|
||||
},
|
||||
}
|
||||
result = response.json()
|
||||
assert result["booking_id"] == sample_invitee.booking_id
|
||||
assert result["user_email"] == sample_invitee.user_email
|
||||
mock_add_invitee.assert_called_once_with(
|
||||
ANY, booking_id, invitee_data["user_email"]
|
||||
ANY, sample_invitee.booking_id, sample_invitee.user_email
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from backend.models import Booking
|
||||
from backend.services.bookings import BookingList
|
||||
from backend.models import BookingList
|
||||
from backend.services.bookings import delete_booking
|
||||
from backend.services.bookings import get_booking
|
||||
from backend.services.bookings import get_bookings_for_room
|
||||
|
||||
@@ -10,7 +10,7 @@ from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from backend.models import Invitee
|
||||
from backend.services.invitees import UserList
|
||||
from backend.models import InviteeList
|
||||
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
|
||||
@@ -19,7 +19,7 @@ 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
|
||||
async_session: AsyncSession, sample_invitees: InviteeList, mock_logger: MagicMock
|
||||
) -> None:
|
||||
"""Test successful retrieval of all invitees for a booking.
|
||||
|
||||
@@ -37,14 +37,14 @@ async def test_get_invitees_for_booking_success(
|
||||
mock_scalars = AsyncMock(return_value=mock_scalars_result)
|
||||
async_session.scalars = mock_scalars # type: ignore [method-assign]
|
||||
|
||||
result: UserList = await get_invitees_for_booking(async_session, booking_id)
|
||||
result: InviteeList = 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)
|
||||
select(Invitee).where(Invitee.booking_id == booking_id)
|
||||
)
|
||||
|
||||
|
||||
@@ -67,13 +67,13 @@ async def test_get_invitees_for_booking_empty(
|
||||
mock_scalars = AsyncMock(return_value=mock_scalars_result)
|
||||
async_session.scalars = mock_scalars # type: ignore [method-assign]
|
||||
|
||||
result: UserList = await get_invitees_for_booking(async_session, booking_id)
|
||||
result: InviteeList = 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)
|
||||
select(Invitee).where(Invitee.booking_id == booking_id)
|
||||
)
|
||||
|
||||
|
||||
@@ -102,18 +102,9 @@ async def test_get_invitees_for_booking_database_error(
|
||||
|
||||
@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,
|
||||
sample_invitee: Invitee,
|
||||
mock_logger: MagicMock,
|
||||
) -> None:
|
||||
"""Test successful addition of an invitee to a booking.
|
||||
@@ -123,22 +114,19 @@ async def test_add_invitee_to_booking_success(
|
||||
|
||||
Args:
|
||||
async_session: The asynchronous database session.
|
||||
booking_id: The ID of the booking to add the invitee to.
|
||||
user_email: The email of the user to add as an invitee.
|
||||
sample_invitee: The mocked invitee object.
|
||||
mock_logger: The mocked logger instance.
|
||||
"""
|
||||
invitee = Invitee(booking_id=booking_id, user_email=user_email)
|
||||
async_session.add = MagicMock() # type: ignore [method-assign]
|
||||
async_session.commit = AsyncMock() # type: ignore [method-assign]
|
||||
|
||||
result: Invitee = await add_invitee_to_booking(
|
||||
async_session, booking_id, user_email
|
||||
async_session, sample_invitee.booking_id, sample_invitee.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)
|
||||
assert result.booking_id == sample_invitee.booking_id
|
||||
assert result.user_email == sample_invitee.user_email
|
||||
async_session.add.assert_called_once()
|
||||
async_session.commit.assert_called_once()
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from backend.models import Room
|
||||
from backend.services.rooms import RoomList
|
||||
from backend.models import RoomList
|
||||
from backend.services.rooms import delete_room
|
||||
from backend.services.rooms import get_room
|
||||
from backend.services.rooms import get_rooms
|
||||
|
||||
@@ -10,7 +10,7 @@ from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from backend.models import User
|
||||
from backend.services.users import UserList
|
||||
from backend.models import UserList
|
||||
from backend.services.users import get_user
|
||||
from backend.services.users import get_users
|
||||
|
||||
|
||||
Reference in New Issue
Block a user