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>
180 lines
5.0 KiB
Python
180 lines
5.0 KiB
Python
"""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
|