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>
242 lines
7.5 KiB
Python
242 lines
7.5 KiB
Python
"""Tests for settings endpoints."""
|
|
from fastapi.testclient import TestClient
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.models.settings import Settings
|
|
from app.models.user import User
|
|
|
|
|
|
def test_get_settings_admin(
|
|
client: TestClient,
|
|
db: Session,
|
|
admin_headers: dict[str, str],
|
|
) -> None:
|
|
"""Test GET /api/admin/settings returns settings for admin."""
|
|
# Create default settings
|
|
settings = Settings(
|
|
id=1,
|
|
min_duration_minutes=30,
|
|
max_duration_minutes=480,
|
|
working_hours_start=8,
|
|
working_hours_end=20,
|
|
max_bookings_per_day_per_user=3,
|
|
min_hours_before_cancel=2,
|
|
)
|
|
db.add(settings)
|
|
db.commit()
|
|
|
|
response = client.get("/api/admin/settings", headers=admin_headers)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["min_duration_minutes"] == 30
|
|
assert data["max_duration_minutes"] == 480
|
|
assert data["working_hours_start"] == 8
|
|
assert data["working_hours_end"] == 20
|
|
assert data["max_bookings_per_day_per_user"] == 3
|
|
assert data["min_hours_before_cancel"] == 2
|
|
|
|
|
|
def test_get_settings_creates_default_if_not_exist(
|
|
client: TestClient,
|
|
db: Session,
|
|
admin_headers: dict[str, str],
|
|
) -> None:
|
|
"""Test GET /api/admin/settings creates default settings if not exist."""
|
|
response = client.get("/api/admin/settings", headers=admin_headers)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["min_duration_minutes"] == 30
|
|
assert data["max_duration_minutes"] == 480
|
|
|
|
# Verify it was created in DB
|
|
settings = db.query(Settings).filter(Settings.id == 1).first()
|
|
assert settings is not None
|
|
assert settings.min_duration_minutes == 30
|
|
|
|
|
|
def test_get_settings_non_admin_forbidden(
|
|
client: TestClient,
|
|
auth_headers: dict[str, str],
|
|
) -> None:
|
|
"""Test GET /api/admin/settings forbidden for non-admin."""
|
|
response = client.get("/api/admin/settings", headers=auth_headers)
|
|
assert response.status_code == 403
|
|
|
|
|
|
def test_update_settings_admin(
|
|
client: TestClient,
|
|
db: Session,
|
|
admin_headers: dict[str, str],
|
|
) -> None:
|
|
"""Test PUT /api/admin/settings updates settings for admin."""
|
|
# Create default settings
|
|
settings = Settings(
|
|
id=1,
|
|
min_duration_minutes=30,
|
|
max_duration_minutes=480,
|
|
working_hours_start=8,
|
|
working_hours_end=20,
|
|
max_bookings_per_day_per_user=3,
|
|
min_hours_before_cancel=2,
|
|
)
|
|
db.add(settings)
|
|
db.commit()
|
|
|
|
# Update settings
|
|
update_data = {
|
|
"min_duration_minutes": 60,
|
|
"max_duration_minutes": 600,
|
|
"working_hours_start": 9,
|
|
"working_hours_end": 18,
|
|
"max_bookings_per_day_per_user": 5,
|
|
"min_hours_before_cancel": 4,
|
|
}
|
|
response = client.put("/api/admin/settings", headers=admin_headers, json=update_data)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["min_duration_minutes"] == 60
|
|
assert data["max_duration_minutes"] == 600
|
|
assert data["working_hours_start"] == 9
|
|
assert data["working_hours_end"] == 18
|
|
assert data["max_bookings_per_day_per_user"] == 5
|
|
assert data["min_hours_before_cancel"] == 4
|
|
|
|
# Verify update in DB
|
|
db.refresh(settings)
|
|
assert settings.min_duration_minutes == 60
|
|
assert settings.max_bookings_per_day_per_user == 5
|
|
|
|
|
|
def test_update_settings_validation_min_max_duration(
|
|
client: TestClient,
|
|
db: Session,
|
|
admin_headers: dict[str, str],
|
|
) -> None:
|
|
"""Test PUT /api/admin/settings validates min <= max duration."""
|
|
# Create default settings
|
|
settings = Settings(id=1)
|
|
db.add(settings)
|
|
db.commit()
|
|
|
|
# Try to set min > max (but within Pydantic range)
|
|
update_data = {
|
|
"min_duration_minutes": 400,
|
|
"max_duration_minutes": 100,
|
|
"working_hours_start": 8,
|
|
"working_hours_end": 20,
|
|
"max_bookings_per_day_per_user": 3,
|
|
"min_hours_before_cancel": 2,
|
|
}
|
|
response = client.put("/api/admin/settings", headers=admin_headers, json=update_data)
|
|
assert response.status_code == 400
|
|
assert "duration" in response.json()["detail"].lower()
|
|
|
|
|
|
def test_update_settings_validation_working_hours(
|
|
client: TestClient,
|
|
db: Session,
|
|
admin_headers: dict[str, str],
|
|
) -> None:
|
|
"""Test PUT /api/admin/settings validates start < end hours."""
|
|
# Create default settings
|
|
settings = Settings(id=1)
|
|
db.add(settings)
|
|
db.commit()
|
|
|
|
# Try to set start >= end
|
|
update_data = {
|
|
"min_duration_minutes": 30,
|
|
"max_duration_minutes": 480,
|
|
"working_hours_start": 20,
|
|
"working_hours_end": 8,
|
|
"max_bookings_per_day_per_user": 3,
|
|
"min_hours_before_cancel": 2,
|
|
}
|
|
response = client.put("/api/admin/settings", headers=admin_headers, json=update_data)
|
|
assert response.status_code == 400
|
|
assert "working hours" in response.json()["detail"].lower()
|
|
|
|
|
|
def test_update_settings_non_admin_forbidden(
|
|
client: TestClient,
|
|
auth_headers: dict[str, str],
|
|
) -> None:
|
|
"""Test PUT /api/admin/settings forbidden for non-admin."""
|
|
update_data = {
|
|
"min_duration_minutes": 60,
|
|
"max_duration_minutes": 600,
|
|
"working_hours_start": 9,
|
|
"working_hours_end": 18,
|
|
"max_bookings_per_day_per_user": 5,
|
|
"min_hours_before_cancel": 4,
|
|
}
|
|
response = client.put("/api/admin/settings", headers=auth_headers, json=update_data)
|
|
assert response.status_code == 403
|
|
|
|
|
|
# ===== Audit Log Integration Tests =====
|
|
|
|
|
|
def test_settings_update_creates_audit_log(
|
|
client: TestClient,
|
|
admin_token: str,
|
|
test_admin: User,
|
|
db: Session,
|
|
) -> None:
|
|
"""Test that updating settings creates an audit log entry with changed fields."""
|
|
from app.models.audit_log import AuditLog
|
|
|
|
# Create default settings
|
|
settings = Settings(
|
|
id=1,
|
|
min_duration_minutes=30,
|
|
max_duration_minutes=480,
|
|
working_hours_start=8,
|
|
working_hours_end=20,
|
|
max_bookings_per_day_per_user=3,
|
|
min_hours_before_cancel=2,
|
|
)
|
|
db.add(settings)
|
|
db.commit()
|
|
|
|
# Update settings
|
|
update_data = {
|
|
"min_duration_minutes": 60,
|
|
"max_duration_minutes": 600,
|
|
"working_hours_start": 9,
|
|
"working_hours_end": 18,
|
|
"max_bookings_per_day_per_user": 5,
|
|
"min_hours_before_cancel": 4,
|
|
}
|
|
response = client.put(
|
|
"/api/admin/settings",
|
|
headers={"Authorization": f"Bearer {admin_token}"},
|
|
json=update_data
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
|
|
# Check audit log was created
|
|
audit = db.query(AuditLog).filter(
|
|
AuditLog.action == "settings_updated",
|
|
AuditLog.target_id == 1
|
|
).first()
|
|
|
|
assert audit is not None
|
|
assert audit.target_type == "settings"
|
|
assert audit.user_id == test_admin.id
|
|
|
|
# Check that changed fields are tracked with old and new values
|
|
changed_fields = audit.details["changed_fields"]
|
|
assert "min_duration_minutes" in changed_fields
|
|
assert changed_fields["min_duration_minutes"]["old"] == 30
|
|
assert changed_fields["min_duration_minutes"]["new"] == 60
|
|
assert "max_duration_minutes" in changed_fields
|
|
assert changed_fields["max_duration_minutes"]["old"] == 480
|
|
assert changed_fields["max_duration_minutes"]["new"] == 600
|
|
assert "working_hours_start" in changed_fields
|
|
assert "working_hours_end" in changed_fields
|
|
assert "max_bookings_per_day_per_user" in changed_fields
|
|
assert "min_hours_before_cancel" in changed_fields
|
|
assert len(changed_fields) == 6 # All 6 fields changed
|