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>
252 lines
7.7 KiB
Python
252 lines
7.7 KiB
Python
"""Tests for user management endpoints."""
|
|
from fastapi.testclient import TestClient
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.models.user import User
|
|
|
|
|
|
def test_get_current_user(client: TestClient, test_user: User, auth_headers: dict) -> None:
|
|
"""Test getting current user info."""
|
|
response = client.get("/api/users/me", headers=auth_headers)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["email"] == "test@example.com"
|
|
assert data["role"] == "user"
|
|
|
|
|
|
def test_list_users_admin(
|
|
client: TestClient, test_user: User, admin_headers: dict
|
|
) -> None:
|
|
"""Test listing all users as admin."""
|
|
response = client.get("/api/admin/users", headers=admin_headers)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert isinstance(data, list)
|
|
assert len(data) >= 2 # test_user + admin_user
|
|
|
|
|
|
def test_list_users_unauthorized(client: TestClient, auth_headers: dict) -> None:
|
|
"""Test listing users as non-admin (should fail)."""
|
|
response = client.get("/api/admin/users", headers=auth_headers)
|
|
assert response.status_code == 403
|
|
|
|
|
|
def test_list_users_filter_by_role(client: TestClient, admin_headers: dict) -> None:
|
|
"""Test filtering users by role."""
|
|
response = client.get("/api/admin/users?role=admin", headers=admin_headers)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert all(user["role"] == "admin" for user in data)
|
|
|
|
|
|
def test_create_user_admin(client: TestClient, admin_headers: dict) -> None:
|
|
"""Test creating a new user as admin."""
|
|
response = client.post(
|
|
"/api/admin/users",
|
|
headers=admin_headers,
|
|
json={
|
|
"email": "newuser@example.com",
|
|
"full_name": "New User",
|
|
"password": "newpassword",
|
|
"role": "user",
|
|
"organization": "Test Org",
|
|
},
|
|
)
|
|
assert response.status_code == 201
|
|
data = response.json()
|
|
assert data["email"] == "newuser@example.com"
|
|
assert data["full_name"] == "New User"
|
|
assert data["role"] == "user"
|
|
assert data["organization"] == "Test Org"
|
|
assert data["is_active"] is True
|
|
|
|
|
|
def test_create_user_duplicate_email(
|
|
client: TestClient, test_user: User, admin_headers: dict
|
|
) -> None:
|
|
"""Test creating user with duplicate email."""
|
|
response = client.post(
|
|
"/api/admin/users",
|
|
headers=admin_headers,
|
|
json={
|
|
"email": "test@example.com", # Already exists
|
|
"full_name": "Duplicate User",
|
|
"password": "password",
|
|
"role": "user",
|
|
},
|
|
)
|
|
assert response.status_code == 400
|
|
assert "already exists" in response.json()["detail"]
|
|
|
|
|
|
def test_create_user_invalid_role(client: TestClient, admin_headers: dict) -> None:
|
|
"""Test creating user with invalid role."""
|
|
response = client.post(
|
|
"/api/admin/users",
|
|
headers=admin_headers,
|
|
json={
|
|
"email": "invalid@example.com",
|
|
"full_name": "Invalid User",
|
|
"password": "password",
|
|
"role": "superadmin", # Invalid role
|
|
},
|
|
)
|
|
assert response.status_code == 400
|
|
assert "admin" in response.json()["detail"].lower()
|
|
|
|
|
|
def test_update_user_admin(client: TestClient, test_user: User, admin_headers: dict) -> None:
|
|
"""Test updating user as admin."""
|
|
response = client.put(
|
|
f"/api/admin/users/{test_user.id}",
|
|
headers=admin_headers,
|
|
json={
|
|
"full_name": "Updated Name",
|
|
"organization": "Updated Org",
|
|
},
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["full_name"] == "Updated Name"
|
|
assert data["organization"] == "Updated Org"
|
|
assert data["email"] == "test@example.com" # Should remain unchanged
|
|
|
|
|
|
def test_update_user_not_found(client: TestClient, admin_headers: dict) -> None:
|
|
"""Test updating non-existent user."""
|
|
response = client.put(
|
|
"/api/admin/users/99999",
|
|
headers=admin_headers,
|
|
json={"full_name": "Updated"},
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
|
|
def test_update_user_status(client: TestClient, test_user: User, admin_headers: dict) -> None:
|
|
"""Test deactivating user."""
|
|
response = client.patch(
|
|
f"/api/admin/users/{test_user.id}/status",
|
|
headers=admin_headers,
|
|
json={"is_active": False},
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["is_active"] is False
|
|
|
|
# Verify user cannot login
|
|
login_response = client.post(
|
|
"/api/auth/login",
|
|
json={"email": "test@example.com", "password": "testpassword"},
|
|
)
|
|
assert login_response.status_code == 403
|
|
|
|
|
|
def test_reset_password(
|
|
client: TestClient, test_user: User, admin_headers: dict, db: Session
|
|
) -> None:
|
|
"""Test resetting user password."""
|
|
response = client.post(
|
|
f"/api/admin/users/{test_user.id}/reset-password",
|
|
headers=admin_headers,
|
|
json={"new_password": "newpassword123"},
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
# Verify new password works
|
|
login_response = client.post(
|
|
"/api/auth/login",
|
|
json={"email": "test@example.com", "password": "newpassword123"},
|
|
)
|
|
assert login_response.status_code == 200
|
|
|
|
# Verify old password doesn't work
|
|
old_login_response = client.post(
|
|
"/api/auth/login",
|
|
json={"email": "test@example.com", "password": "testpassword"},
|
|
)
|
|
assert old_login_response.status_code == 401
|
|
|
|
|
|
def test_reset_password_not_found(client: TestClient, admin_headers: dict) -> None:
|
|
"""Test resetting password for non-existent user."""
|
|
response = client.post(
|
|
"/api/admin/users/99999/reset-password",
|
|
headers=admin_headers,
|
|
json={"new_password": "newpassword"},
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
|
|
# ===== Audit Log Integration Tests =====
|
|
|
|
|
|
def test_user_creation_creates_audit_log(
|
|
client: TestClient,
|
|
admin_token: str,
|
|
test_admin: User,
|
|
db: Session,
|
|
) -> None:
|
|
"""Test that creating a user creates an audit log entry."""
|
|
from app.models.audit_log import AuditLog
|
|
|
|
response = client.post(
|
|
"/api/admin/users",
|
|
headers={"Authorization": f"Bearer {admin_token}"},
|
|
json={
|
|
"email": "newuser@example.com",
|
|
"full_name": "New User",
|
|
"password": "newpassword",
|
|
"role": "user",
|
|
"organization": "Test Org",
|
|
},
|
|
)
|
|
|
|
assert response.status_code == 201
|
|
user_id = response.json()["id"]
|
|
|
|
# Check audit log was created
|
|
audit = db.query(AuditLog).filter(
|
|
AuditLog.action == "user_created",
|
|
AuditLog.target_id == user_id
|
|
).first()
|
|
|
|
assert audit is not None
|
|
assert audit.target_type == "user"
|
|
assert audit.user_id == test_admin.id
|
|
assert audit.details == {"email": "newuser@example.com", "role": "user"}
|
|
|
|
|
|
def test_user_update_creates_audit_log(
|
|
client: TestClient,
|
|
admin_token: str,
|
|
test_admin: User,
|
|
test_user: User,
|
|
db: Session,
|
|
) -> None:
|
|
"""Test that updating a user creates an audit log entry."""
|
|
from app.models.audit_log import AuditLog
|
|
|
|
response = client.put(
|
|
f"/api/admin/users/{test_user.id}",
|
|
headers={"Authorization": f"Bearer {admin_token}"},
|
|
json={
|
|
"full_name": "Updated Name",
|
|
"role": "admin",
|
|
},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
|
|
# Check audit log was created
|
|
audit = db.query(AuditLog).filter(
|
|
AuditLog.action == "user_updated",
|
|
AuditLog.target_id == test_user.id
|
|
).first()
|
|
|
|
assert audit is not None
|
|
assert audit.target_type == "user"
|
|
assert audit.user_id == test_admin.id
|
|
# Should track changed fields
|
|
assert "full_name" in audit.details["updated_fields"]
|
|
assert "role" in audit.details["updated_fields"]
|