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:
Claude Agent
2026-02-09 17:51:29 +00:00
commit df4031d99c
113 changed files with 24491 additions and 0 deletions

View File

@@ -0,0 +1,127 @@
"""Tests for Google Calendar API endpoints."""
from unittest.mock import MagicMock, patch
import pytest
from fastapi.testclient import TestClient
from sqlalchemy.orm import Session
from app.main import app
from app.models.google_calendar_token import GoogleCalendarToken
from app.models.user import User
client = TestClient(app)
@pytest.fixture
def auth_headers(db: Session, test_user: User) -> dict[str, str]:
"""Get auth headers for test user."""
# Login to get token
response = client.post(
"/api/auth/login",
json={"email": test_user.email, "password": "testpassword"},
)
token = response.json()["access_token"]
return {"Authorization": f"Bearer {token}"}
def test_google_status_not_connected(auth_headers: dict[str, str]):
"""Test Google Calendar status when not connected."""
response = client.get("/api/integrations/google/status", headers=auth_headers)
assert response.status_code == 200
data = response.json()
assert data["connected"] is False
assert data["expires_at"] is None
def test_google_status_connected(
db: Session, test_user: User, auth_headers: dict[str, str]
):
"""Test Google Calendar status when connected."""
# Create token for user
token = GoogleCalendarToken(
user_id=test_user.id, # type: ignore[arg-type]
access_token="test_token",
refresh_token="test_refresh",
token_expiry=None,
)
db.add(token)
db.commit()
response = client.get("/api/integrations/google/status", headers=auth_headers)
assert response.status_code == 200
data = response.json()
assert data["connected"] is True
@patch("app.api.google_calendar.Flow")
def test_connect_google(mock_flow: MagicMock, auth_headers: dict[str, str]):
"""Test starting Google OAuth flow."""
# Setup mock
mock_flow_instance = MagicMock()
mock_flow_instance.authorization_url.return_value = (
"https://accounts.google.com/o/oauth2/auth?...",
"test_state",
)
mock_flow.from_client_config.return_value = mock_flow_instance
response = client.get("/api/integrations/google/connect", headers=auth_headers)
assert response.status_code == 200
data = response.json()
assert "authorization_url" in data
assert "state" in data
assert data["state"] == "test_state"
def test_disconnect_google_not_connected(auth_headers: dict[str, str]):
"""Test disconnecting when not connected."""
response = client.delete(
"/api/integrations/google/disconnect", headers=auth_headers
)
assert response.status_code == 200
assert response.json()["message"] == "Google Calendar disconnected"
def test_disconnect_google_success(
db: Session, test_user: User, auth_headers: dict[str, str]
):
"""Test successful Google Calendar disconnect."""
# Create token for user
token = GoogleCalendarToken(
user_id=test_user.id, # type: ignore[arg-type]
access_token="test_token",
refresh_token="test_refresh",
token_expiry=None,
)
db.add(token)
db.commit()
response = client.delete(
"/api/integrations/google/disconnect", headers=auth_headers
)
assert response.status_code == 200
assert response.json()["message"] == "Google Calendar disconnected"
# Verify token was deleted
token_in_db = (
db.query(GoogleCalendarToken)
.filter(GoogleCalendarToken.user_id == test_user.id)
.first()
)
assert token_in_db is None
def test_google_connect_requires_auth():
"""Test that Google Calendar endpoints require authentication."""
response = client.get("/api/integrations/google/connect")
assert response.status_code == 401
response = client.get("/api/integrations/google/status")
assert response.status_code == 401
response = client.delete("/api/integrations/google/disconnect")
assert response.status_code == 401