"""Test report endpoints.""" from datetime import datetime import pytest from fastapi.testclient import TestClient from sqlalchemy.orm import Session from app.models.booking import Booking from app.models.space import Space from app.models.user import User @pytest.fixture def test_spaces(db: Session) -> list[Space]: """Create multiple test spaces.""" spaces = [ Space( name="Conference Room A", type="sala", capacity=10, description="Test room A", is_active=True, ), Space( name="Office B", type="birou", capacity=2, description="Test office B", is_active=True, ), ] for space in spaces: db.add(space) db.commit() for space in spaces: db.refresh(space) return spaces @pytest.fixture def test_users(db: Session, test_user: User) -> list[User]: """Create multiple test users.""" from app.core.security import get_password_hash user2 = User( email="user2@example.com", full_name="User Two", hashed_password=get_password_hash("password"), role="user", is_active=True, ) db.add(user2) db.commit() db.refresh(user2) return [test_user, user2] @pytest.fixture def test_bookings( db: Session, test_users: list[User], test_spaces: list[Space] ) -> list[Booking]: """Create multiple test bookings with various statuses.""" bookings = [ # User 1, Space 1, approved Booking( user_id=test_users[0].id, space_id=test_spaces[0].id, title="Meeting 1", description="Test", start_datetime=datetime(2024, 3, 15, 10, 0), end_datetime=datetime(2024, 3, 15, 12, 0), # 2 hours status="approved", ), # User 1, Space 1, pending Booking( user_id=test_users[0].id, space_id=test_spaces[0].id, title="Meeting 2", description="Test", start_datetime=datetime(2024, 3, 16, 10, 0), end_datetime=datetime(2024, 3, 16, 11, 0), # 1 hour status="pending", ), # User 1, Space 2, rejected Booking( user_id=test_users[0].id, space_id=test_spaces[1].id, title="Meeting 3", description="Test", start_datetime=datetime(2024, 3, 17, 10, 0), end_datetime=datetime(2024, 3, 17, 13, 0), # 3 hours status="rejected", rejection_reason="Conflict", ), # User 2, Space 1, approved Booking( user_id=test_users[1].id, space_id=test_spaces[0].id, title="Meeting 4", description="Test", start_datetime=datetime(2024, 3, 18, 10, 0), end_datetime=datetime(2024, 3, 18, 14, 0), # 4 hours status="approved", ), # User 2, Space 1, canceled Booking( user_id=test_users[1].id, space_id=test_spaces[0].id, title="Meeting 5", description="Test", start_datetime=datetime(2024, 3, 19, 10, 0), end_datetime=datetime(2024, 3, 19, 11, 30), # 1.5 hours status="canceled", cancellation_reason="Not needed", ), ] for booking in bookings: db.add(booking) db.commit() for booking in bookings: db.refresh(booking) return bookings def test_usage_report( client: TestClient, admin_headers: dict[str, str], test_bookings: list[Booking], ) -> None: """Test usage report generation.""" response = client.get("/api/admin/reports/usage", headers=admin_headers) assert response.status_code == 200 data = response.json() assert "items" in data assert "total_bookings" in data assert "date_range" in data # Should have 2 spaces assert len(data["items"]) == 2 # Check Conference Room A conf_room = next((i for i in data["items"] if i["space_name"] == "Conference Room A"), None) assert conf_room is not None assert conf_room["total_bookings"] == 4 # 4 bookings in this space assert conf_room["approved_bookings"] == 2 assert conf_room["pending_bookings"] == 1 assert conf_room["rejected_bookings"] == 0 assert conf_room["canceled_bookings"] == 1 assert conf_room["total_hours"] == 8.5 # 2 + 1 + 4 + 1.5 # Check Office B office = next((i for i in data["items"] if i["space_name"] == "Office B"), None) assert office is not None assert office["total_bookings"] == 1 assert office["rejected_bookings"] == 1 assert office["total_hours"] == 3.0 # Total bookings across all spaces assert data["total_bookings"] == 5 def test_usage_report_with_date_filter( client: TestClient, admin_headers: dict[str, str], test_bookings: list[Booking], ) -> None: """Test usage report with date range filter.""" # Filter for March 15-16 only response = client.get( "/api/admin/reports/usage", headers=admin_headers, params={"start_date": "2024-03-15", "end_date": "2024-03-16"}, ) assert response.status_code == 200 data = response.json() assert len(data["items"]) == 1 # Only Conference Room A assert data["total_bookings"] == 2 # Meeting 1 and 2 def test_usage_report_with_space_filter( client: TestClient, admin_headers: dict[str, str], test_bookings: list[Booking], test_spaces: list[Space], ) -> None: """Test usage report with space filter.""" response = client.get( "/api/admin/reports/usage", headers=admin_headers, params={"space_id": test_spaces[0].id}, ) assert response.status_code == 200 data = response.json() assert len(data["items"]) == 1 assert data["items"][0]["space_name"] == "Conference Room A" def test_top_users_report( client: TestClient, admin_headers: dict[str, str], test_bookings: list[Booking], test_users: list[User], ) -> None: """Test top users report.""" response = client.get("/api/admin/reports/top-users", headers=admin_headers) assert response.status_code == 200 data = response.json() assert "items" in data assert "date_range" in data # Should have 2 users assert len(data["items"]) == 2 # Top user should be test_user with 3 bookings assert data["items"][0]["user_email"] == test_users[0].email assert data["items"][0]["total_bookings"] == 3 assert data["items"][0]["approved_bookings"] == 1 assert data["items"][0]["total_hours"] == 6.0 # 2 + 1 + 3 # Second user with 2 bookings assert data["items"][1]["user_email"] == test_users[1].email assert data["items"][1]["total_bookings"] == 2 assert data["items"][1]["approved_bookings"] == 1 assert data["items"][1]["total_hours"] == 5.5 # 4 + 1.5 def test_top_users_report_with_limit( client: TestClient, admin_headers: dict[str, str], test_bookings: list[Booking], ) -> None: """Test top users report with limit.""" response = client.get( "/api/admin/reports/top-users", headers=admin_headers, params={"limit": 1}, ) assert response.status_code == 200 data = response.json() assert len(data["items"]) == 1 # Only top user def test_approval_rate_report( client: TestClient, admin_headers: dict[str, str], test_bookings: list[Booking], ) -> None: """Test approval rate report.""" response = client.get("/api/admin/reports/approval-rate", headers=admin_headers) assert response.status_code == 200 data = response.json() assert data["total_requests"] == 5 assert data["approved"] == 2 assert data["rejected"] == 1 assert data["pending"] == 1 assert data["canceled"] == 1 # Approval rate = 2 / (2 + 1) = 66.67% assert data["approval_rate"] == 66.67 # Rejection rate = 1 / (2 + 1) = 33.33% assert data["rejection_rate"] == 33.33 def test_reports_require_admin( client: TestClient, auth_headers: dict[str, str] ) -> None: """Test that regular users cannot access reports.""" endpoints = [ "/api/admin/reports/usage", "/api/admin/reports/top-users", "/api/admin/reports/approval-rate", ] for endpoint in endpoints: response = client.get(endpoint, headers=auth_headers) assert response.status_code == 403 assert response.json()["detail"] == "Not enough permissions" def test_reports_require_auth(client: TestClient) -> None: """Test that reports require authentication.""" endpoints = [ "/api/admin/reports/usage", "/api/admin/reports/top-users", "/api/admin/reports/approval-rate", ] for endpoint in endpoints: response = client.get(endpoint) assert response.status_code == 403