mirror of
https://github.com/xlorepdarkhelm/numinar-coding-project.git
synced 2026-08-25 00:33:45 -04:00
Added tests for rooms endpoints.
Signed-off-by: Cliff Hill <xlorep@darkhelm.org>
This commit is contained in:
@@ -38,9 +38,3 @@ app.include_router(bookings.router)
|
||||
app.include_router(invitees.router)
|
||||
|
||||
setup_logging(app)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
"""Root endpoint to verify the service is running."""
|
||||
return {"message": "Welcome to the Numinar Coding Project Backend!"}
|
||||
|
||||
@@ -41,7 +41,7 @@ class Room(Base):
|
||||
|
||||
__tablename__ = "rooms"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
name: Mapped[str] = mapped_column(index=True, unique=True)
|
||||
location: Mapped[str]
|
||||
equipment: Mapped[str]
|
||||
|
||||
@@ -56,7 +56,7 @@ async def read_room(room_id: int, session: DBSession):
|
||||
room = await get_room(session, room_id)
|
||||
logger.info(f"Successfully retrieved room with id: {room_id}")
|
||||
return room
|
||||
except NoResultFound as e:
|
||||
except (NoResultFound, ValueError) as e:
|
||||
logger.warning(f"Room with id {room_id} not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
@@ -113,7 +113,7 @@ async def update_existing_room(
|
||||
updated_room = await update_room(session, room_id, **room_params)
|
||||
logger.info(f"Successfully updated room with id: {room_id}")
|
||||
return updated_room
|
||||
except NoResultFound as e:
|
||||
except (NoResultFound, ValueError) as e:
|
||||
logger.warning(f"Room with id {room_id} not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
@@ -141,7 +141,7 @@ async def delete_existing_room(room_id: int, session: DBSession):
|
||||
try:
|
||||
await delete_room(session, room_id)
|
||||
logger.info(f"Successfully deleted room with id: {room_id}")
|
||||
except NoResultFound as e:
|
||||
except (NoResultFound, ValueError) as e:
|
||||
logger.warning(f"Room with id {room_id} not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
|
||||
@@ -7,6 +7,7 @@ from pydantic import ConfigDict
|
||||
class RoomBase(BaseModel):
|
||||
"""Base schema for Room."""
|
||||
|
||||
id: int
|
||||
name: str
|
||||
location: str
|
||||
equipment: str
|
||||
@@ -22,6 +23,7 @@ class RoomCreate(RoomBase):
|
||||
class RoomUpdate(BaseModel):
|
||||
"""Schema for updating an existing Room."""
|
||||
|
||||
id: int | None = None
|
||||
name: str | None = None
|
||||
location: str | None = None
|
||||
equipment: str | None = None
|
||||
@@ -31,8 +33,6 @@ class RoomUpdate(BaseModel):
|
||||
|
||||
|
||||
class RoomResponse(RoomBase):
|
||||
"""Response schema for Room, including ID."""
|
||||
|
||||
id: int
|
||||
"""Response schema for Room."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
@@ -4,8 +4,11 @@ from unittest.mock import AsyncMock
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from backend.main import app
|
||||
|
||||
|
||||
type MockLogger = MagicMock
|
||||
|
||||
@@ -14,3 +17,9 @@ type MockLogger = MagicMock
|
||||
def async_session() -> AsyncSession:
|
||||
"""Fixture to provide a mocked AsyncSession for database interactions."""
|
||||
return AsyncMock(spec=AsyncSession)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client() -> TestClient:
|
||||
"""Fixture to provide a FastAPI TestClient for testing endpoints."""
|
||||
return TestClient(app)
|
||||
|
||||
264
backend/tests/routers/test_rooms.py
Normal file
264
backend/tests/routers/test_rooms.py
Normal file
@@ -0,0 +1,264 @@
|
||||
"""Unit tests for the backend.routers.rooms module."""
|
||||
|
||||
import logging
|
||||
from collections.abc import Generator
|
||||
from typing import Any
|
||||
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 backend.models import Room
|
||||
from backend.services.rooms import RoomList
|
||||
|
||||
from ..conftest import MockLogger
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_logger() -> Generator[MockLogger, None, None]:
|
||||
"""Fixture to mock the logger used in the rooms service."""
|
||||
mock_logger_instance = MagicMock(spec=logging.Logger)
|
||||
with patch("backend.routers.rooms.logger", mock_logger_instance):
|
||||
yield mock_logger_instance
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_rooms() -> RoomList:
|
||||
"""Fixture to provide sample Room objects for testing."""
|
||||
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.mark.asyncio
|
||||
async def test_get_rooms_success(
|
||||
client: TestClient, sample_rooms: RoomList, mock_logger: MockLogger
|
||||
) -> None:
|
||||
"""Test successful retrieval of all rooms via GET /rooms/.
|
||||
|
||||
Verifies that the endpoint returns the expected list of rooms with a 200 status.
|
||||
"""
|
||||
with patch("backend.routers.rooms.get_rooms", new=AsyncMock()) as mock_get_rooms:
|
||||
mock_get_rooms.return_value = sample_rooms
|
||||
|
||||
response = client.get("/rooms/")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Room 1",
|
||||
"location": "Building A",
|
||||
"equipment": "Projector",
|
||||
"capacity": 10,
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Room 2",
|
||||
"location": "Building B",
|
||||
"equipment": "Whiteboard",
|
||||
"capacity": 15,
|
||||
},
|
||||
]
|
||||
mock_get_rooms.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_rooms_empty(client: TestClient, mock_logger: MockLogger) -> None:
|
||||
"""Test retrieval of rooms when none exist via GET /rooms/.
|
||||
|
||||
Verifies that the endpoint returns an empty list with a 200 status.
|
||||
"""
|
||||
with patch("backend.routers.rooms.get_rooms", new=AsyncMock()) as mock_get_rooms:
|
||||
mock_get_rooms.return_value = []
|
||||
|
||||
response = client.get("/rooms/")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == []
|
||||
mock_get_rooms.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_room_success(client: TestClient, mock_logger: MockLogger) -> None:
|
||||
"""Test successful retrieval of a room by ID via GET /rooms/{room_id}.
|
||||
|
||||
Verifies that the endpoint returns the correct room with a 200 status.
|
||||
"""
|
||||
room_id = 1
|
||||
room = Room(
|
||||
id=room_id,
|
||||
name="Room 1",
|
||||
location="Building A",
|
||||
equipment="Projector",
|
||||
capacity=10,
|
||||
)
|
||||
with patch("backend.routers.rooms.get_room", new=AsyncMock()) as mock_get_room:
|
||||
mock_get_room.return_value = room
|
||||
|
||||
response = client.get(f"/rooms/{room_id}")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"id": room_id,
|
||||
"name": "Room 1",
|
||||
"location": "Building A",
|
||||
"equipment": "Projector",
|
||||
"capacity": 10,
|
||||
}
|
||||
mock_get_room.assert_called_once_with(ANY, room_id)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_room_not_found(client: TestClient, mock_logger: MockLogger) -> None:
|
||||
"""Test handling of non-existent room in GET /rooms/{room_id}.
|
||||
|
||||
Verifies that the endpoint returns a 404 status when the room is not found.
|
||||
"""
|
||||
room_id = 999
|
||||
with patch("backend.routers.rooms.get_room", new=AsyncMock()) as mock_get_room:
|
||||
mock_get_room.side_effect = NoResultFound(f"Room with id {room_id} not found")
|
||||
|
||||
response = client.get(f"/rooms/{room_id}")
|
||||
|
||||
assert response.status_code == 404
|
||||
assert "detail" in response.json()
|
||||
mock_get_room.assert_called_once_with(ANY, room_id)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_new_room_success(client: TestClient, mock_logger: MockLogger) -> None:
|
||||
"""Test successful creation of a new room via POST /rooms/.
|
||||
|
||||
Verifies that the endpoint returns the created room with a 201 status.
|
||||
"""
|
||||
room_data: dict[str, Any] = {
|
||||
"id": 3,
|
||||
"name": "Room 3",
|
||||
"location": "Building C",
|
||||
"equipment": "TV",
|
||||
"capacity": 20,
|
||||
}
|
||||
created_room = Room(**room_data)
|
||||
with patch("backend.routers.rooms.new_room", new=AsyncMock()) as mock_new_room:
|
||||
mock_new_room.return_value = created_room
|
||||
|
||||
response = client.post("/rooms/", json=room_data)
|
||||
|
||||
assert response.status_code == 201
|
||||
assert response.json() == room_data
|
||||
mock_new_room.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_room_success(client: TestClient, mock_logger: MockLogger) -> None:
|
||||
"""Test successful update of a room via PUT /rooms/{room_id}.
|
||||
|
||||
Verifies that the endpoint returns the updated room with a 200 status.
|
||||
"""
|
||||
room_id = 1
|
||||
update_data: dict[str, Any] = {
|
||||
"name": "Updated Room",
|
||||
"location": "Building D",
|
||||
"equipment": "Monitor",
|
||||
"capacity": 25,
|
||||
}
|
||||
updated_room = Room(id=room_id, **update_data)
|
||||
with patch(
|
||||
"backend.routers.rooms.update_room", new=AsyncMock()
|
||||
) as mock_update_room:
|
||||
mock_update_room.return_value = updated_room
|
||||
|
||||
response = client.put(f"/rooms/{room_id}", json=update_data)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"id": room_id,
|
||||
"name": "Updated Room",
|
||||
"location": "Building D",
|
||||
"equipment": "Monitor",
|
||||
"capacity": 25,
|
||||
}
|
||||
mock_update_room.assert_called_once_with(ANY, room_id, **update_data)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_room_not_found(
|
||||
client: TestClient, mock_logger: MockLogger
|
||||
) -> None:
|
||||
"""Test handling of non-existent room in PUT /rooms/{room_id}.
|
||||
|
||||
Verifies that the endpoint returns a 404 status when the room is not found.
|
||||
"""
|
||||
room_id = 999
|
||||
update_data: dict[str, Any] = {
|
||||
"name": "Updated Room",
|
||||
"location": "Building D",
|
||||
"equipment": "Monitor",
|
||||
"capacity": 25,
|
||||
}
|
||||
with patch(
|
||||
"backend.routers.rooms.update_room", new=AsyncMock()
|
||||
) as mock_update_room:
|
||||
mock_update_room.side_effect = ValueError()
|
||||
|
||||
response = client.put(f"/rooms/{room_id}", json=update_data)
|
||||
|
||||
assert response.status_code == 404
|
||||
assert "detail" in response.json()
|
||||
mock_update_room.assert_called_once_with(ANY, room_id, **update_data)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_room_success(client: TestClient, mock_logger: MockLogger) -> None:
|
||||
"""Test successful deletion of a room via DELETE /rooms/{room_id}.
|
||||
|
||||
Verifies that the endpoint returns a 204 status on successful deletion.
|
||||
"""
|
||||
room_id = 1
|
||||
with patch(
|
||||
"backend.routers.rooms.delete_room", new=AsyncMock()
|
||||
) as mock_delete_room:
|
||||
response = client.delete(f"/rooms/{room_id}")
|
||||
|
||||
assert response.status_code == 204
|
||||
assert response.text == ""
|
||||
mock_delete_room.assert_called_once_with(ANY, room_id)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_room_not_found(
|
||||
client: TestClient, mock_logger: MockLogger
|
||||
) -> None:
|
||||
"""Test handling of non-existent room in DELETE /rooms/{room_id}.
|
||||
|
||||
Verifies that the endpoint returns a 404 status when the room is not found.
|
||||
"""
|
||||
room_id = 999
|
||||
with patch(
|
||||
"backend.routers.rooms.delete_room", new=AsyncMock()
|
||||
) as mock_delete_room:
|
||||
mock_delete_room.side_effect = ValueError()
|
||||
|
||||
response = client.delete(f"/rooms/{room_id}")
|
||||
|
||||
assert response.status_code == 404
|
||||
assert "detail" in response.json()
|
||||
mock_delete_room.assert_called_once_with(ANY, room_id)
|
||||
@@ -12,7 +12,6 @@ from fastapi.testclient import TestClient
|
||||
from sqlalchemy.exc import NoResultFound
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
|
||||
from backend.main import app
|
||||
from backend.models import User
|
||||
from backend.services.users import UserList
|
||||
|
||||
@@ -36,12 +35,6 @@ def sample_users() -> UserList:
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client() -> TestClient:
|
||||
"""Fixture to provide a FastAPI TestClient for testing endpoints."""
|
||||
return TestClient(app)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_read_users_success(
|
||||
client: TestClient, sample_users: UserList, mock_logger: MockLogger
|
||||
@@ -88,12 +81,12 @@ async def test_read_users_database_error(
|
||||
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("Database error")
|
||||
mock_get_users.side_effect = SQLAlchemyError()
|
||||
|
||||
response = client.get("/users/")
|
||||
|
||||
assert response.status_code == 500
|
||||
assert response.json() == {"detail": "Database error occurred"}
|
||||
assert "detail" in response.json()
|
||||
mock_get_users.assert_called_once()
|
||||
|
||||
|
||||
@@ -138,12 +131,12 @@ async def test_read_user_not_found(
|
||||
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(f"User with email {email} not found")
|
||||
mock_get_user.side_effect = NoResultFound()
|
||||
|
||||
response = client.get(f"/users/{email}")
|
||||
|
||||
assert response.status_code == 404
|
||||
assert response.json() == {"detail": f"User with email {email} not found"}
|
||||
assert "detail" in response.json()
|
||||
mock_get_user.assert_called_once_with(ANY, email)
|
||||
|
||||
|
||||
@@ -157,10 +150,10 @@ async def test_read_user_database_error(
|
||||
"""
|
||||
email = "user1@example.com"
|
||||
with patch("backend.routers.users.get_user", new=AsyncMock()) as mock_get_user:
|
||||
mock_get_user.side_effect = SQLAlchemyError("Database error")
|
||||
mock_get_user.side_effect = SQLAlchemyError()
|
||||
|
||||
response = client.get(f"/users/{email}")
|
||||
|
||||
assert response.status_code == 500
|
||||
assert response.json() == {"detail": "Database error occurred"}
|
||||
assert "detail" in response.json()
|
||||
mock_get_user.assert_called_once_with(ANY, email)
|
||||
|
||||
Reference in New Issue
Block a user