Remove legacy test files for auth, rate limiting, and users

- Deleted outdated backend test cases (`test_auth.py`, `test_rate_limiting.py`, `test_users.py`) to clean up deprecated test structure.
- These tests are now redundant with newer async test implementations and improved coverage.
This commit is contained in:
Felipe Cardoso
2025-11-01 00:02:17 +01:00
parent 31e2109278
commit f4be8b56f0
10 changed files with 285 additions and 1712 deletions

View File

@@ -1,401 +0,0 @@
# tests/api/routes/test_auth.py
import json
import uuid
from datetime import datetime, timezone
from unittest.mock import patch, MagicMock, Mock
import pytest
from fastapi import FastAPI, Depends
from fastapi.testclient import TestClient
from sqlalchemy.orm import Session
from app.api.routes.auth import router as auth_router
from app.api.routes.users import router as users_router
from app.core.auth import get_password_hash
from app.core.database import get_db
from app.models.user import User
from app.services.auth_service import AuthService, AuthenticationError
from app.core.auth import TokenExpiredError, TokenInvalidError
# Mock the get_db dependency
@pytest.fixture
def override_get_db(db_session):
"""Override get_db dependency for testing."""
return db_session
@pytest.fixture
def app(override_get_db):
"""Create a FastAPI test application with overridden dependencies."""
app = FastAPI()
app.include_router(auth_router, prefix="/auth", tags=["auth"])
app.include_router(users_router, prefix="/api/v1/users", tags=["users"])
# Override the get_db dependency
app.dependency_overrides[get_db] = lambda: override_get_db
return app
@pytest.fixture
def client(app):
"""Create a FastAPI test client."""
return TestClient(app)
class TestRegisterUser:
"""Tests for the register_user endpoint."""
def test_register_user_success(self, client, monkeypatch, db_session):
"""Test successful user registration."""
# Mock the service method with a valid complete User object
mock_user = User(
id=uuid.uuid4(),
email="newuser@example.com",
password_hash="hashed_password",
first_name="New",
last_name="User",
is_active=True,
is_superuser=False,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
# Use patch for mocking
with patch.object(AuthService, 'create_user', return_value=mock_user):
# Test request
response = client.post(
"/auth/register",
json={
"email": "newuser@example.com",
"password": "Password123",
"first_name": "New",
"last_name": "User"
}
)
# Assertions
assert response.status_code == 201
data = response.json()
assert data["email"] == "newuser@example.com"
assert data["first_name"] == "New"
assert data["last_name"] == "User"
assert "password" not in data
def test_register_user_duplicate_email(self, client, db_session):
"""Test registration with duplicate email."""
# Use patch for mocking with a side effect
with patch.object(AuthService, 'create_user',
side_effect=AuthenticationError("User with this email already exists")):
# Test request
response = client.post(
"/auth/register",
json={
"email": "existing@example.com",
"password": "Password123",
"first_name": "Existing",
"last_name": "User"
}
)
# Assertions
assert response.status_code == 409
assert "already exists" in response.json()["detail"]
class TestLogin:
"""Tests for the login endpoint."""
def test_login_success(self, client, mock_user, db_session):
"""Test successful login."""
# Ensure mock_user has required attributes
if not hasattr(mock_user, 'created_at') or mock_user.created_at is None:
mock_user.created_at = datetime.now(timezone.utc)
if not hasattr(mock_user, 'updated_at') or mock_user.updated_at is None:
mock_user.updated_at = datetime.now(timezone.utc)
# Create mock tokens
mock_tokens = MagicMock(
access_token="mock_access_token",
refresh_token="mock_refresh_token",
token_type="bearer"
)
# Use context managers for patching
with patch.object(AuthService, 'authenticate_user', return_value=mock_user), \
patch.object(AuthService, 'create_tokens', return_value=mock_tokens):
# Test request
response = client.post(
"/auth/login",
json={
"email": "user@example.com",
"password": "Password123"
}
)
# Assertions
assert response.status_code == 200
data = response.json()
assert data["access_token"] == "mock_access_token"
assert data["refresh_token"] == "mock_refresh_token"
assert data["token_type"] == "bearer"
def test_login_invalid_credentials_debug(self, client, app):
"""Improved test for login with invalid credentials."""
# Print response for debugging
from app.services.auth_service import AuthService
# Create a complete mock for AuthService
class MockAuthService:
@staticmethod
def authenticate_user(db, email, password):
print(f"Mock called with: {email}, {password}")
return None
# Replace the entire class with our mock
original_service = AuthService
try:
# Replace with our mock
import sys
sys.modules['app.services.auth_service'].AuthService = MockAuthService
# Make the request
response = client.post(
"/auth/login",
json={
"email": "user@example.com",
"password": "WrongPassword"
}
)
# Print response details for debugging
print(f"Response status: {response.status_code}")
print(f"Response body: {response.text}")
# Assertions
assert response.status_code == 401
assert "Invalid email or password" in response.json()["detail"]
finally:
# Restore original service
sys.modules['app.services.auth_service'].AuthService = original_service
def test_login_inactive_user(self, client, db_session):
"""Test login with inactive user."""
# Mock authentication to raise an error
with patch.object(AuthService, 'authenticate_user',
side_effect=AuthenticationError("User account is inactive")):
# Test request
response = client.post(
"/auth/login",
json={
"email": "inactive@example.com",
"password": "Password123"
}
)
# Assertions
assert response.status_code == 401
assert "inactive" in response.json()["detail"]
class TestRefreshToken:
"""Tests for the refresh_token endpoint."""
def test_refresh_token_success(self, client, db_session):
"""Test successful token refresh."""
from app.models.user import User
from app.core.auth import get_password_hash
import uuid
# Create a test user
test_user = User(
id=uuid.uuid4(),
email="refreshtest@example.com",
password_hash=get_password_hash("TestPassword123"),
first_name="Refresh",
last_name="Test",
is_active=True
)
db_session.add(test_user)
db_session.commit()
# Login to get real tokens with a session
login_response = client.post(
"/auth/login",
json={
"email": "refreshtest@example.com",
"password": "TestPassword123"
}
)
assert login_response.status_code == 200
tokens = login_response.json()
# Test refresh with real token
response = client.post(
"/auth/refresh",
json={
"refresh_token": tokens["refresh_token"]
}
)
# Assertions
assert response.status_code == 200
data = response.json()
assert "access_token" in data
assert "refresh_token" in data
assert data["token_type"] == "bearer"
def test_refresh_token_expired(self, client, db_session):
"""Test refresh with expired token."""
from app.api.routes import auth as auth_routes
# Mock decode_token to raise expired token error
with patch.object(auth_routes, 'decode_token',
side_effect=TokenExpiredError("Token expired")):
# Test request
response = client.post(
"/auth/refresh",
json={
"refresh_token": "expired_refresh_token"
}
)
# Assertions
assert response.status_code == 401
# Check if it's in the new error format or old detail format
response_data = response.json()
if "errors" in response_data:
assert "expired" in response_data["errors"][0]["message"].lower()
else:
assert "detail" in response_data
assert "expired" in response_data["detail"].lower()
def test_refresh_token_invalid(self, client, db_session):
"""Test refresh with invalid token."""
# Mock refresh to raise invalid token error
with patch.object(AuthService, 'refresh_tokens',
side_effect=TokenInvalidError("Invalid token")):
# Test request
response = client.post(
"/auth/refresh",
json={
"refresh_token": "invalid_refresh_token"
}
)
# Assertions
assert response.status_code == 401
assert "Invalid" in response.json()["detail"]
class TestChangePassword:
"""Tests for the change_password endpoint."""
def test_change_password_success(self, client, mock_user, db_session, app):
"""Test successful password change."""
# Ensure mock_user has required attributes
if not hasattr(mock_user, 'created_at') or mock_user.created_at is None:
mock_user.created_at = datetime.now(timezone.utc)
if not hasattr(mock_user, 'updated_at') or mock_user.updated_at is None:
mock_user.updated_at = datetime.now(timezone.utc)
# Override get_current_user dependency
from app.api.dependencies.auth import get_current_user
app.dependency_overrides[get_current_user] = lambda: mock_user
# Mock password change to return success
with patch.object(AuthService, 'change_password', return_value=True):
# Test request (new endpoint)
response = client.patch(
"/api/v1/users/me/password",
json={
"current_password": "OldPassword123",
"new_password": "NewPassword123"
}
)
# Assertions
assert response.status_code == 200
assert response.json()["success"] is True
assert "message" in response.json()
# Clean up override
if get_current_user in app.dependency_overrides:
del app.dependency_overrides[get_current_user]
def test_change_password_incorrect_current_password(self, client, mock_user, db_session, app):
"""Test change password with incorrect current password."""
# Ensure mock_user has required attributes
if not hasattr(mock_user, 'created_at') or mock_user.created_at is None:
mock_user.created_at = datetime.now(timezone.utc)
if not hasattr(mock_user, 'updated_at') or mock_user.updated_at is None:
mock_user.updated_at = datetime.now(timezone.utc)
# Override get_current_user dependency
from app.api.dependencies.auth import get_current_user
app.dependency_overrides[get_current_user] = lambda: mock_user
# Mock password change to raise error
with patch.object(AuthService, 'change_password',
side_effect=AuthenticationError("Current password is incorrect")):
# Test request (new endpoint)
response = client.patch(
"/api/v1/users/me/password",
json={
"current_password": "WrongPassword",
"new_password": "NewPassword123"
}
)
# Assertions - Now returns standardized error response
assert response.status_code == 403
# The response has standardized error format
data = response.json()
assert "detail" in data or "errors" in data
# Clean up override
if get_current_user in app.dependency_overrides:
del app.dependency_overrides[get_current_user]
class TestGetCurrentUserInfo:
"""Tests for the get_current_user_info endpoint."""
def test_get_current_user_info(self, client, mock_user, app):
"""Test getting current user info."""
# Ensure mock_user has required attributes
if not hasattr(mock_user, 'created_at') or mock_user.created_at is None:
mock_user.created_at = datetime.now(timezone.utc)
if not hasattr(mock_user, 'updated_at') or mock_user.updated_at is None:
mock_user.updated_at = datetime.now(timezone.utc)
# Override get_current_user dependency
from app.api.dependencies.auth import get_current_user
app.dependency_overrides[get_current_user] = lambda: mock_user
# Test request
response = client.get("/auth/me")
# Assertions
assert response.status_code == 200
data = response.json()
assert data["email"] == mock_user.email
assert data["first_name"] == mock_user.first_name
assert data["last_name"] == mock_user.last_name
assert "password" not in data
# Clean up override
if get_current_user in app.dependency_overrides:
del app.dependency_overrides[get_current_user]
def test_get_current_user_info_unauthorized(self, client):
"""Test getting user info without authentication."""
# Test request without authentication
response = client.get("/auth/me")
# Assertions
assert response.status_code == 401

View File

@@ -1,203 +0,0 @@
# tests/api/routes/test_rate_limiting.py
import os
import pytest
from fastapi import FastAPI, status
from fastapi.testclient import TestClient
from unittest.mock import patch, MagicMock
from app.api.routes.auth import router as auth_router, limiter
from app.api.routes.users import router as users_router
from app.core.database import get_db
# Skip all rate limiting tests when IS_TEST=True (rate limits are disabled in test mode)
pytestmark = pytest.mark.skipif(
os.getenv("IS_TEST", "False") == "True",
reason="Rate limits are disabled in test mode (RATE_MULTIPLIER=100)"
)
# Mock the get_db dependency
@pytest.fixture
def override_get_db():
"""Override get_db dependency for testing."""
mock_db = MagicMock()
return mock_db
@pytest.fixture
def app(override_get_db):
"""Create a FastAPI test application with rate limiting."""
from slowapi import _rate_limit_exceeded_handler
from slowapi.errors import RateLimitExceeded
app = FastAPI()
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
app.include_router(auth_router, prefix="/auth", tags=["auth"])
app.include_router(users_router, prefix="/api/v1/users", tags=["users"])
# Override the get_db dependency
app.dependency_overrides[get_db] = lambda: override_get_db
return app
@pytest.fixture
def client(app):
"""Create a FastAPI test client."""
return TestClient(app)
class TestRegisterRateLimiting:
"""Tests for rate limiting on /register endpoint"""
def test_register_rate_limit_blocks_over_limit(self, client):
"""Test that requests over rate limit are blocked"""
from app.services.auth_service import AuthService
from app.models.user import User
from datetime import datetime, timezone
import uuid
mock_user = User(
id=uuid.uuid4(),
email="test@example.com",
password_hash="hashed",
first_name="Test",
last_name="User",
is_active=True,
is_superuser=False,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
with patch.object(AuthService, 'create_user', return_value=mock_user):
user_data = {
"email": f"test{uuid.uuid4()}@example.com",
"password": "TestPassword123!",
"first_name": "Test",
"last_name": "User"
}
# Make 6 requests (limit is 5/minute)
responses = []
for i in range(6):
response = client.post("/auth/register", json=user_data)
responses.append(response)
# Last request should be rate limited
assert responses[-1].status_code == status.HTTP_429_TOO_MANY_REQUESTS
class TestLoginRateLimiting:
"""Tests for rate limiting on /login endpoint"""
def test_login_rate_limit_blocks_over_limit(self, client):
"""Test that login requests over rate limit are blocked"""
from app.services.auth_service import AuthService
with patch.object(AuthService, 'authenticate_user', return_value=None):
login_data = {
"email": "test@example.com",
"password": "wrong_password"
}
# Make 11 requests (limit is 10/minute)
responses = []
for i in range(11):
response = client.post("/auth/login", json=login_data)
responses.append(response)
# Last request should be rate limited
assert responses[-1].status_code == status.HTTP_429_TOO_MANY_REQUESTS
class TestRefreshTokenRateLimiting:
"""Tests for rate limiting on /refresh endpoint"""
def test_refresh_rate_limit_blocks_over_limit(self, client):
"""Test that refresh requests over rate limit are blocked"""
from app.services.auth_service import AuthService
from app.core.auth import TokenInvalidError
with patch.object(AuthService, 'refresh_tokens', side_effect=TokenInvalidError("Invalid")):
refresh_data = {
"refresh_token": "invalid_token"
}
# Make 31 requests (limit is 30/minute)
responses = []
for i in range(31):
response = client.post("/auth/refresh", json=refresh_data)
responses.append(response)
# Last request should be rate limited
assert responses[-1].status_code == status.HTTP_429_TOO_MANY_REQUESTS
class TestChangePasswordRateLimiting:
"""Tests for rate limiting on /change-password endpoint"""
def test_change_password_rate_limit_blocks_over_limit(self, client):
"""Test that change password requests over rate limit are blocked"""
from app.api.dependencies.auth import get_current_user
from app.models.user import User
from app.services.auth_service import AuthService, AuthenticationError
from datetime import datetime, timezone
import uuid
# Mock current user
mock_user = User(
id=uuid.uuid4(),
email="test@example.com",
password_hash="hashed",
first_name="Test",
last_name="User",
is_active=True,
is_superuser=False,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
# Override get_current_user dependency in the app
test_app = client.app
test_app.dependency_overrides[get_current_user] = lambda: mock_user
with patch.object(AuthService, 'change_password', side_effect=AuthenticationError("Invalid password")):
password_data = {
"current_password": "wrong_password",
"new_password": "NewPassword123!"
}
# Make 6 requests (limit is 5/minute) - using new endpoint
responses = []
for i in range(6):
response = client.patch("/api/v1/users/me/password", json=password_data)
responses.append(response)
# Last request should be rate limited
assert responses[-1].status_code == status.HTTP_429_TOO_MANY_REQUESTS
# Clean up override
test_app.dependency_overrides.clear()
class TestRateLimitErrorResponse:
"""Tests for rate limit error response format"""
def test_rate_limit_error_response_format(self, client):
"""Test that rate limit error has correct format"""
from app.services.auth_service import AuthService
with patch.object(AuthService, 'authenticate_user', return_value=None):
login_data = {
"email": "test@example.com",
"password": "password"
}
# Exceed rate limit
for i in range(11):
response = client.post("/auth/login", json=login_data)
# Check error response
assert response.status_code == status.HTTP_429_TOO_MANY_REQUESTS
assert "detail" in response.json() or "error" in response.json()

View File

@@ -1,487 +0,0 @@
# tests/api/routes/test_users.py
"""
Tests for user management endpoints.
"""
import uuid
from datetime import datetime, timezone
from unittest.mock import patch, MagicMock
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from sqlalchemy.orm import Session
from app.api.routes.users import router as users_router
from app.core.database import get_db
from app.models.user import User
from app.api.dependencies.auth import get_current_user, get_current_superuser
@pytest.fixture
def override_get_db(db_session):
"""Override get_db dependency for testing."""
return db_session
@pytest.fixture
def app(override_get_db):
"""Create a FastAPI test application."""
app = FastAPI()
app.include_router(users_router, prefix="/api/v1/users", tags=["users"])
# Override the get_db dependency
app.dependency_overrides[get_db] = lambda: override_get_db
return app
@pytest.fixture
def client(app):
"""Create a FastAPI test client."""
return TestClient(app)
@pytest.fixture
def regular_user():
"""Create a mock regular user."""
return User(
id=uuid.uuid4(),
email="regular@example.com",
password_hash="hashed_password",
first_name="Regular",
last_name="User",
is_active=True,
is_superuser=False,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
@pytest.fixture
def super_user():
"""Create a mock superuser."""
return User(
id=uuid.uuid4(),
email="admin@example.com",
password_hash="hashed_password",
first_name="Admin",
last_name="User",
is_active=True,
is_superuser=True,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
class TestListUsers:
"""Tests for the list_users endpoint."""
def test_list_users_as_superuser(self, client, app, super_user, regular_user, db_session):
"""Test that superusers can list all users."""
from app.crud.user import user as user_crud
# Override auth dependency
app.dependency_overrides[get_current_superuser] = lambda: super_user
# Mock user_crud to return test data
mock_users = [regular_user for _ in range(3)]
with patch.object(user_crud, 'get_multi_with_total', return_value=(mock_users, 3)):
response = client.get("/api/v1/users?page=1&limit=20")
assert response.status_code == 200
data = response.json()
assert "data" in data
assert "pagination" in data
assert len(data["data"]) == 3
assert data["pagination"]["total"] == 3
# Clean up
if get_current_superuser in app.dependency_overrides:
del app.dependency_overrides[get_current_superuser]
def test_list_users_pagination(self, client, app, super_user, regular_user, db_session):
"""Test pagination parameters for list users."""
from app.crud.user import user as user_crud
app.dependency_overrides[get_current_superuser] = lambda: super_user
# Mock user_crud
mock_users = [regular_user for _ in range(10)]
with patch.object(user_crud, 'get_multi_with_total', return_value=(mock_users[:5], 10)):
response = client.get("/api/v1/users?page=1&limit=5")
assert response.status_code == 200
data = response.json()
assert data["pagination"]["page"] == 1
assert data["pagination"]["page_size"] == 5
assert data["pagination"]["total"] == 10
assert data["pagination"]["total_pages"] == 2
# Clean up
if get_current_superuser in app.dependency_overrides:
del app.dependency_overrides[get_current_superuser]
class TestGetCurrentUserProfile:
"""Tests for the get_current_user_profile endpoint."""
def test_get_current_user_profile(self, client, app, regular_user):
"""Test getting current user's profile."""
app.dependency_overrides[get_current_user] = lambda: regular_user
response = client.get("/api/v1/users/me")
assert response.status_code == 200
data = response.json()
assert data["email"] == regular_user.email
assert data["first_name"] == regular_user.first_name
assert data["last_name"] == regular_user.last_name
assert "password" not in data
# Clean up
if get_current_user in app.dependency_overrides:
del app.dependency_overrides[get_current_user]
class TestUpdateCurrentUser:
"""Tests for the update_current_user endpoint."""
def test_update_current_user_success(self, client, app, regular_user, db_session):
"""Test successful profile update."""
from app.crud.user import user as user_crud
app.dependency_overrides[get_current_user] = lambda: regular_user
updated_user = User(
id=regular_user.id,
email=regular_user.email,
password_hash=regular_user.password_hash,
first_name="Updated",
last_name="Name",
is_active=True,
is_superuser=False,
created_at=regular_user.created_at,
updated_at=datetime.now(timezone.utc)
)
with patch.object(user_crud, 'update', return_value=updated_user):
response = client.patch(
"/api/v1/users/me",
json={"first_name": "Updated", "last_name": "Name"}
)
assert response.status_code == 200
data = response.json()
assert data["first_name"] == "Updated"
assert data["last_name"] == "Name"
# Clean up
if get_current_user in app.dependency_overrides:
del app.dependency_overrides[get_current_user]
def test_update_current_user_extra_fields_ignored(self, client, app, regular_user, db_session):
"""Test that extra fields like is_superuser are ignored by schema validation."""
from app.crud.user import user as user_crud
app.dependency_overrides[get_current_user] = lambda: regular_user
# Create updated user without is_superuser changed
updated_user = User(
id=regular_user.id,
email=regular_user.email,
password_hash=regular_user.password_hash,
first_name="Updated",
last_name=regular_user.last_name,
is_active=True,
is_superuser=False, # Should remain False
created_at=regular_user.created_at,
updated_at=datetime.now(timezone.utc)
)
with patch.object(user_crud, 'update', return_value=updated_user):
response = client.patch(
"/api/v1/users/me",
json={"first_name": "Updated", "is_superuser": True} # is_superuser will be ignored
)
# Request should succeed but is_superuser should be unchanged
assert response.status_code == 200
data = response.json()
assert data["is_superuser"] is False
# Clean up
if get_current_user in app.dependency_overrides:
del app.dependency_overrides[get_current_user]
class TestGetUserById:
"""Tests for the get_user_by_id endpoint."""
def test_get_own_profile(self, client, app, regular_user, db_session):
"""Test that users can get their own profile."""
from app.crud.user import user as user_crud
app.dependency_overrides[get_current_user] = lambda: regular_user
with patch.object(user_crud, 'get', return_value=regular_user):
response = client.get(f"/api/v1/users/{regular_user.id}")
assert response.status_code == 200
data = response.json()
assert data["email"] == regular_user.email
# Clean up
if get_current_user in app.dependency_overrides:
del app.dependency_overrides[get_current_user]
def test_get_other_user_as_regular_user(self, client, app, regular_user):
"""Test that regular users cannot view other users."""
app.dependency_overrides[get_current_user] = lambda: regular_user
other_user_id = uuid.uuid4()
response = client.get(f"/api/v1/users/{other_user_id}")
assert response.status_code == 403
# Clean up
if get_current_user in app.dependency_overrides:
del app.dependency_overrides[get_current_user]
def test_get_other_user_as_superuser(self, client, app, super_user, db_session):
"""Test that superusers can view any user."""
from app.crud.user import user as user_crud
app.dependency_overrides[get_current_user] = lambda: super_user
other_user = User(
id=uuid.uuid4(),
email="other@example.com",
password_hash="hashed",
first_name="Other",
last_name="User",
is_active=True,
is_superuser=False,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
with patch.object(user_crud, 'get', return_value=other_user):
response = client.get(f"/api/v1/users/{other_user.id}")
assert response.status_code == 200
data = response.json()
assert data["email"] == other_user.email
# Clean up
if get_current_user in app.dependency_overrides:
del app.dependency_overrides[get_current_user]
def test_get_nonexistent_user(self, client, app, super_user, db_session):
"""Test getting a user that doesn't exist."""
from app.crud.user import user as user_crud
app.dependency_overrides[get_current_user] = lambda: super_user
with patch.object(user_crud, 'get', return_value=None):
response = client.get(f"/api/v1/users/{uuid.uuid4()}")
assert response.status_code == 404
# Clean up
if get_current_user in app.dependency_overrides:
del app.dependency_overrides[get_current_user]
class TestUpdateUser:
"""Tests for the update_user endpoint."""
def test_update_own_profile(self, client, app, regular_user, db_session):
"""Test that users can update their own profile."""
from app.crud.user import user as user_crud
app.dependency_overrides[get_current_user] = lambda: regular_user
updated_user = User(
id=regular_user.id,
email=regular_user.email,
password_hash=regular_user.password_hash,
first_name="NewName",
last_name=regular_user.last_name,
is_active=True,
is_superuser=False,
created_at=regular_user.created_at,
updated_at=datetime.now(timezone.utc)
)
with patch.object(user_crud, 'get', return_value=regular_user), \
patch.object(user_crud, 'update', return_value=updated_user):
response = client.patch(
f"/api/v1/users/{regular_user.id}",
json={"first_name": "NewName"}
)
assert response.status_code == 200
data = response.json()
assert data["first_name"] == "NewName"
# Clean up
if get_current_user in app.dependency_overrides:
del app.dependency_overrides[get_current_user]
def test_update_other_user_as_regular_user(self, client, app, regular_user):
"""Test that regular users cannot update other users."""
app.dependency_overrides[get_current_user] = lambda: regular_user
other_user_id = uuid.uuid4()
response = client.patch(
f"/api/v1/users/{other_user_id}",
json={"first_name": "NewName"}
)
assert response.status_code == 403
# Clean up
if get_current_user in app.dependency_overrides:
del app.dependency_overrides[get_current_user]
def test_user_schema_ignores_extra_fields(self, client, app, regular_user, db_session):
"""Test that UserUpdate schema ignores extra fields like is_superuser."""
from app.crud.user import user as user_crud
app.dependency_overrides[get_current_user] = lambda: regular_user
# Updated user with is_superuser unchanged
updated_user = User(
id=regular_user.id,
email=regular_user.email,
password_hash=regular_user.password_hash,
first_name="Changed",
last_name=regular_user.last_name,
is_active=True,
is_superuser=False, # Should remain False
created_at=regular_user.created_at,
updated_at=datetime.now(timezone.utc)
)
with patch.object(user_crud, 'get', return_value=regular_user), \
patch.object(user_crud, 'update', return_value=updated_user):
response = client.patch(
f"/api/v1/users/{regular_user.id}",
json={"first_name": "Changed", "is_superuser": True} # is_superuser ignored
)
# Should succeed, extra field is ignored
assert response.status_code == 200
data = response.json()
assert data["is_superuser"] is False
# Clean up
if get_current_user in app.dependency_overrides:
del app.dependency_overrides[get_current_user]
def test_superuser_can_update_any_user(self, client, app, super_user, db_session):
"""Test that superusers can update any user."""
from app.crud.user import user as user_crud
app.dependency_overrides[get_current_user] = lambda: super_user
target_user = User(
id=uuid.uuid4(),
email="target@example.com",
password_hash="hashed",
first_name="Target",
last_name="User",
is_active=True,
is_superuser=False,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
updated_user = User(
id=target_user.id,
email=target_user.email,
password_hash=target_user.password_hash,
first_name="Updated",
last_name=target_user.last_name,
is_active=True,
is_superuser=False,
created_at=target_user.created_at,
updated_at=datetime.now(timezone.utc)
)
with patch.object(user_crud, 'get', return_value=target_user), \
patch.object(user_crud, 'update', return_value=updated_user):
response = client.patch(
f"/api/v1/users/{target_user.id}",
json={"first_name": "Updated"}
)
assert response.status_code == 200
data = response.json()
assert data["first_name"] == "Updated"
# Clean up
if get_current_user in app.dependency_overrides:
del app.dependency_overrides[get_current_user]
class TestDeleteUser:
"""Tests for the delete_user endpoint."""
def test_delete_user_as_superuser(self, client, app, super_user, db_session):
"""Test that superusers can delete users."""
from app.crud.user import user as user_crud
app.dependency_overrides[get_current_superuser] = lambda: super_user
target_user = User(
id=uuid.uuid4(),
email="target@example.com",
password_hash="hashed",
first_name="Target",
last_name="User",
is_active=True,
is_superuser=False,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
with patch.object(user_crud, 'get', return_value=target_user), \
patch.object(user_crud, 'remove', return_value=target_user):
response = client.delete(f"/api/v1/users/{target_user.id}")
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert "deleted successfully" in data["message"]
# Clean up
if get_current_superuser in app.dependency_overrides:
del app.dependency_overrides[get_current_superuser]
def test_delete_nonexistent_user(self, client, app, super_user, db_session):
"""Test deleting a user that doesn't exist."""
from app.crud.user import user as user_crud
app.dependency_overrides[get_current_superuser] = lambda: super_user
with patch.object(user_crud, 'get', return_value=None):
response = client.delete(f"/api/v1/users/{uuid.uuid4()}")
assert response.status_code == 404
# Clean up
if get_current_superuser in app.dependency_overrides:
del app.dependency_overrides[get_current_superuser]
def test_cannot_delete_self(self, client, app, super_user, db_session):
"""Test that users cannot delete their own account."""
app.dependency_overrides[get_current_superuser] = lambda: super_user
response = client.delete(f"/api/v1/users/{super_user.id}")
assert response.status_code == 403
# Clean up
if get_current_superuser in app.dependency_overrides:
del app.dependency_overrides[get_current_superuser]