feat: Space Booking System - MVP complet
Sistem web pentru rezervarea de birouri și săli de ședință cu flux de aprobare administrativă. Stack: FastAPI + Vue.js 3 + SQLite + TypeScript Features implementate: - Autentificare JWT + Self-registration cu email verification - CRUD Spații, Utilizatori, Settings (Admin) - Calendar interactiv (FullCalendar) cu drag-and-drop - Creare rezervări cu validare (durată, program, overlap, max/zi) - Rezervări recurente (săptămânal) - Admin: aprobare/respingere/anulare cereri - Admin: creare directă rezervări (bypass approval) - Admin: editare orice rezervare - User: editare/anulare rezervări proprii - Notificări in-app (bell icon + dropdown) - Notificări email (async SMTP cu BackgroundTasks) - Jurnal acțiuni administrative (audit log) - Rapoarte avansate (utilizare, top users, approval rate) - Șabloane rezervări (booking templates) - Atașamente fișiere (upload/download) - Conflict warnings (verificare disponibilitate real-time) - Integrare Google Calendar (OAuth2) - Suport timezone (UTC storage + user preference) - 225+ teste backend Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
179
backend/tests/test_notifications.py
Normal file
179
backend/tests/test_notifications.py
Normal file
@@ -0,0 +1,179 @@
|
||||
"""Tests for notifications API endpoints."""
|
||||
from datetime import datetime
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.models.notification import Notification
|
||||
from app.models.user import User
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_notification(db: Session, test_user: User) -> Notification:
|
||||
"""Create test notification."""
|
||||
notification = Notification(
|
||||
user_id=test_user.id,
|
||||
type="booking_created",
|
||||
title="Test Notification",
|
||||
message="This is a test notification",
|
||||
is_read=False,
|
||||
created_at=datetime.utcnow(),
|
||||
)
|
||||
db.add(notification)
|
||||
db.commit()
|
||||
db.refresh(notification)
|
||||
return notification
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_read_notification(db: Session, test_user: User) -> Notification:
|
||||
"""Create read test notification."""
|
||||
notification = Notification(
|
||||
user_id=test_user.id,
|
||||
type="booking_approved",
|
||||
title="Read Notification",
|
||||
message="This notification has been read",
|
||||
is_read=True,
|
||||
created_at=datetime.utcnow(),
|
||||
)
|
||||
db.add(notification)
|
||||
db.commit()
|
||||
db.refresh(notification)
|
||||
return notification
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def other_user_notification(db: Session, test_admin: User) -> Notification:
|
||||
"""Create notification for another user."""
|
||||
notification = Notification(
|
||||
user_id=test_admin.id,
|
||||
type="booking_created",
|
||||
title="Admin Notification",
|
||||
message="This belongs to admin",
|
||||
is_read=False,
|
||||
created_at=datetime.utcnow(),
|
||||
)
|
||||
db.add(notification)
|
||||
db.commit()
|
||||
db.refresh(notification)
|
||||
return notification
|
||||
|
||||
|
||||
def test_get_notifications_for_user(
|
||||
client: TestClient,
|
||||
auth_headers: dict[str, str],
|
||||
test_notification: Notification,
|
||||
test_read_notification: Notification,
|
||||
other_user_notification: Notification,
|
||||
) -> None:
|
||||
"""Test getting all notifications for current user."""
|
||||
response = client.get("/api/notifications", headers=auth_headers)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
|
||||
# Should only return notifications for current user
|
||||
assert len(data) == 2
|
||||
|
||||
# Should be ordered by created_at DESC (most recent first)
|
||||
notification_ids = [n["id"] for n in data]
|
||||
assert test_notification.id in notification_ids
|
||||
assert test_read_notification.id in notification_ids
|
||||
assert other_user_notification.id not in notification_ids
|
||||
|
||||
|
||||
def test_filter_unread_notifications(
|
||||
client: TestClient,
|
||||
auth_headers: dict[str, str],
|
||||
test_notification: Notification,
|
||||
test_read_notification: Notification,
|
||||
) -> None:
|
||||
"""Test filtering notifications by is_read status."""
|
||||
# Get only unread notifications
|
||||
response = client.get(
|
||||
"/api/notifications",
|
||||
headers=auth_headers,
|
||||
params={"is_read": False},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
|
||||
assert len(data) == 1
|
||||
assert data[0]["id"] == test_notification.id
|
||||
assert data[0]["is_read"] is False
|
||||
|
||||
# Get only read notifications
|
||||
response = client.get(
|
||||
"/api/notifications",
|
||||
headers=auth_headers,
|
||||
params={"is_read": True},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
|
||||
assert len(data) == 1
|
||||
assert data[0]["id"] == test_read_notification.id
|
||||
assert data[0]["is_read"] is True
|
||||
|
||||
|
||||
def test_mark_notification_as_read(
|
||||
client: TestClient,
|
||||
auth_headers: dict[str, str],
|
||||
test_notification: Notification,
|
||||
) -> None:
|
||||
"""Test marking a notification as read."""
|
||||
response = client.put(
|
||||
f"/api/notifications/{test_notification.id}/read",
|
||||
headers=auth_headers,
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
|
||||
assert data["id"] == test_notification.id
|
||||
assert data["is_read"] is True
|
||||
assert data["title"] == "Test Notification"
|
||||
assert data["message"] == "This is a test notification"
|
||||
|
||||
|
||||
def test_cannot_mark_others_notification(
|
||||
client: TestClient,
|
||||
auth_headers: dict[str, str],
|
||||
other_user_notification: Notification,
|
||||
) -> None:
|
||||
"""Test that users cannot mark other users' notifications as read."""
|
||||
response = client.put(
|
||||
f"/api/notifications/{other_user_notification.id}/read",
|
||||
headers=auth_headers,
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
data = response.json()
|
||||
assert "own notifications" in data["detail"].lower()
|
||||
|
||||
|
||||
def test_mark_nonexistent_notification(
|
||||
client: TestClient,
|
||||
auth_headers: dict[str, str],
|
||||
) -> None:
|
||||
"""Test marking a non-existent notification as read."""
|
||||
response = client.put(
|
||||
"/api/notifications/99999/read",
|
||||
headers=auth_headers,
|
||||
)
|
||||
|
||||
assert response.status_code == 404
|
||||
data = response.json()
|
||||
assert "not found" in data["detail"].lower()
|
||||
|
||||
|
||||
def test_get_notifications_without_auth(client: TestClient) -> None:
|
||||
"""Test that authentication is required for notifications endpoints."""
|
||||
response = client.get("/api/notifications")
|
||||
assert response.status_code == 403
|
||||
|
||||
response = client.put("/api/notifications/1/read")
|
||||
assert response.status_code == 403
|
||||
Reference in New Issue
Block a user