forked from cardosofelipe/fast-next-template
Convert password reset and auth dependencies tests to async
- Refactored all `password reset` and `auth dependency` tests to utilize async patterns for compatibility with async database sessions. - Enhanced test fixtures with `pytest-asyncio` to support asynchronous database operations. - Improved user handling with async context management for `test_user` and `async_mock_user`. - Introduced `await` syntax for route calls, token generation, and database transactions in test cases.
This commit is contained in:
254
backend/tests/api/test_user_routes.py
Normal file → Executable file
254
backend/tests/api/test_user_routes.py
Normal file → Executable file
@@ -4,10 +4,13 @@ Comprehensive tests for user management endpoints.
|
||||
These tests focus on finding potential bugs, not just coverage.
|
||||
"""
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
from unittest.mock import patch
|
||||
from fastapi import status
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import select
|
||||
from app.models.user import User
|
||||
from app.models.user import User
|
||||
from app.schemas.users import UserUpdate
|
||||
|
||||
@@ -21,9 +24,9 @@ def disable_rate_limit():
|
||||
yield
|
||||
|
||||
|
||||
def get_auth_headers(client, email, password):
|
||||
async def get_auth_headers(client, email, password):
|
||||
"""Helper to get authentication headers."""
|
||||
response = client.post(
|
||||
response = await client.post(
|
||||
"/api/v1/auth/login",
|
||||
json={"email": email, "password": password}
|
||||
)
|
||||
@@ -34,11 +37,12 @@ def get_auth_headers(client, email, password):
|
||||
class TestListUsers:
|
||||
"""Tests for GET /users endpoint."""
|
||||
|
||||
def test_list_users_as_superuser(self, client, test_superuser):
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_users_as_superuser(self, client, async_test_superuser):
|
||||
"""Test listing users as superuser."""
|
||||
headers = get_auth_headers(client, test_superuser.email, "SuperPassword123")
|
||||
headers = await get_auth_headers(client, async_test_superuser.email, "SuperPassword123")
|
||||
|
||||
response = client.get("/api/v1/users", headers=headers)
|
||||
response = await client.get("/api/v1/users", headers=headers)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
@@ -46,15 +50,17 @@ class TestListUsers:
|
||||
assert "pagination" in data
|
||||
assert isinstance(data["data"], list)
|
||||
|
||||
def test_list_users_as_regular_user(self, client, test_user):
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_users_as_regular_user(self, client, async_test_user):
|
||||
"""Test that regular users cannot list users."""
|
||||
headers = get_auth_headers(client, test_user.email, "TestPassword123")
|
||||
headers = await get_auth_headers(client, async_test_user.email, "TestPassword123")
|
||||
|
||||
response = client.get("/api/v1/users", headers=headers)
|
||||
response = await client.get("/api/v1/users", headers=headers)
|
||||
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
|
||||
def test_list_users_pagination(self, client, test_superuser, test_db):
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_users_pagination(self, client, async_test_superuser, test_db):
|
||||
"""Test pagination works correctly."""
|
||||
# Create multiple users
|
||||
for i in range(15):
|
||||
@@ -68,17 +74,18 @@ class TestListUsers:
|
||||
test_db.add(user)
|
||||
test_db.commit()
|
||||
|
||||
headers = get_auth_headers(client, test_superuser.email, "SuperPassword123")
|
||||
headers = await get_auth_headers(client, async_test_superuser.email, "SuperPassword123")
|
||||
|
||||
# Get first page
|
||||
response = client.get("/api/v1/users?page=1&limit=5", headers=headers)
|
||||
response = await client.get("/api/v1/users?page=1&limit=5", headers=headers)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert len(data["data"]) == 5
|
||||
assert data["pagination"]["page"] == 1
|
||||
assert data["pagination"]["total"] >= 15
|
||||
|
||||
def test_list_users_filter_active(self, client, test_superuser, test_db):
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_users_filter_active(self, client, async_test_superuser, test_db):
|
||||
"""Test filtering by active status."""
|
||||
# Create active and inactive users
|
||||
active_user = User(
|
||||
@@ -98,35 +105,37 @@ class TestListUsers:
|
||||
test_db.add_all([active_user, inactive_user])
|
||||
test_db.commit()
|
||||
|
||||
headers = get_auth_headers(client, test_superuser.email, "SuperPassword123")
|
||||
headers = await get_auth_headers(client, async_test_superuser.email, "SuperPassword123")
|
||||
|
||||
# Filter for active users
|
||||
response = client.get("/api/v1/users?is_active=true", headers=headers)
|
||||
response = await client.get("/api/v1/users?is_active=true", headers=headers)
|
||||
data = response.json()
|
||||
emails = [u["email"] for u in data["data"]]
|
||||
assert "activefilter@example.com" in emails
|
||||
assert "inactivefilter@example.com" not in emails
|
||||
|
||||
# Filter for inactive users
|
||||
response = client.get("/api/v1/users?is_active=false", headers=headers)
|
||||
response = await client.get("/api/v1/users?is_active=false", headers=headers)
|
||||
data = response.json()
|
||||
emails = [u["email"] for u in data["data"]]
|
||||
assert "inactivefilter@example.com" in emails
|
||||
assert "activefilter@example.com" not in emails
|
||||
|
||||
def test_list_users_sort_by_email(self, client, test_superuser):
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_users_sort_by_email(self, client, async_test_superuser):
|
||||
"""Test sorting users by email."""
|
||||
headers = get_auth_headers(client, test_superuser.email, "SuperPassword123")
|
||||
headers = await get_auth_headers(client, async_test_superuser.email, "SuperPassword123")
|
||||
|
||||
response = client.get("/api/v1/users?sort_by=email&sort_order=asc", headers=headers)
|
||||
response = await client.get("/api/v1/users?sort_by=email&sort_order=asc", headers=headers)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
emails = [u["email"] for u in data["data"]]
|
||||
assert emails == sorted(emails)
|
||||
|
||||
def test_list_users_no_auth(self, client):
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_users_no_auth(self, client):
|
||||
"""Test that unauthenticated requests are rejected."""
|
||||
response = client.get("/api/v1/users")
|
||||
response = await client.get("/api/v1/users")
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
|
||||
# Note: Removed test_list_users_unexpected_error because mocking at CRUD level
|
||||
@@ -136,31 +145,34 @@ class TestListUsers:
|
||||
class TestGetCurrentUserProfile:
|
||||
"""Tests for GET /users/me endpoint."""
|
||||
|
||||
def test_get_own_profile(self, client, test_user):
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_own_profile(self, client, async_test_user):
|
||||
"""Test getting own profile."""
|
||||
headers = get_auth_headers(client, test_user.email, "TestPassword123")
|
||||
headers = await get_auth_headers(client, async_test_user.email, "TestPassword123")
|
||||
|
||||
response = client.get("/api/v1/users/me", headers=headers)
|
||||
response = await client.get("/api/v1/users/me", headers=headers)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert data["email"] == test_user.email
|
||||
assert data["first_name"] == test_user.first_name
|
||||
assert data["email"] == async_test_user.email
|
||||
assert data["first_name"] == async_test_user.first_name
|
||||
|
||||
def test_get_profile_no_auth(self, client):
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_profile_no_auth(self, client):
|
||||
"""Test that unauthenticated requests are rejected."""
|
||||
response = client.get("/api/v1/users/me")
|
||||
response = await client.get("/api/v1/users/me")
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
|
||||
|
||||
class TestUpdateCurrentUser:
|
||||
"""Tests for PATCH /users/me endpoint."""
|
||||
|
||||
def test_update_own_profile(self, client, test_user, test_db):
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_own_profile(self, client, async_test_user, test_db):
|
||||
"""Test updating own profile."""
|
||||
headers = get_auth_headers(client, test_user.email, "TestPassword123")
|
||||
headers = await get_auth_headers(client, async_test_user.email, "TestPassword123")
|
||||
|
||||
response = client.patch(
|
||||
response = await client.patch(
|
||||
"/api/v1/users/me",
|
||||
headers=headers,
|
||||
json={"first_name": "Updated", "last_name": "Name"}
|
||||
@@ -172,14 +184,15 @@ class TestUpdateCurrentUser:
|
||||
assert data["last_name"] == "Name"
|
||||
|
||||
# Verify in database
|
||||
test_db.refresh(test_user)
|
||||
assert test_user.first_name == "Updated"
|
||||
test_db.refresh(async_test_user)
|
||||
assert async_test_user.first_name == "Updated"
|
||||
|
||||
def test_update_profile_phone_number(self, client, test_user, test_db):
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_profile_phone_number(self, client, async_test_user, test_db):
|
||||
"""Test updating phone number with validation."""
|
||||
headers = get_auth_headers(client, test_user.email, "TestPassword123")
|
||||
headers = await get_auth_headers(client, async_test_user.email, "TestPassword123")
|
||||
|
||||
response = client.patch(
|
||||
response = await client.patch(
|
||||
"/api/v1/users/me",
|
||||
headers=headers,
|
||||
json={"phone_number": "+19876543210"}
|
||||
@@ -189,11 +202,12 @@ class TestUpdateCurrentUser:
|
||||
data = response.json()
|
||||
assert data["phone_number"] == "+19876543210"
|
||||
|
||||
def test_update_profile_invalid_phone(self, client, test_user):
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_profile_invalid_phone(self, client, async_test_user):
|
||||
"""Test that invalid phone numbers are rejected."""
|
||||
headers = get_auth_headers(client, test_user.email, "TestPassword123")
|
||||
headers = await get_auth_headers(client, async_test_user.email, "TestPassword123")
|
||||
|
||||
response = client.patch(
|
||||
response = await client.patch(
|
||||
"/api/v1/users/me",
|
||||
headers=headers,
|
||||
json={"phone_number": "invalid"}
|
||||
@@ -201,13 +215,14 @@ class TestUpdateCurrentUser:
|
||||
|
||||
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
|
||||
|
||||
def test_cannot_elevate_to_superuser(self, client, test_user):
|
||||
@pytest.mark.asyncio
|
||||
async def test_cannot_elevate_to_superuser(self, client, async_test_user):
|
||||
"""Test that users cannot make themselves superuser."""
|
||||
headers = get_auth_headers(client, test_user.email, "TestPassword123")
|
||||
headers = await get_auth_headers(client, async_test_user.email, "TestPassword123")
|
||||
|
||||
# Note: is_superuser is not in UserUpdate schema, but the endpoint checks for it
|
||||
# This tests that even if someone tries to send it, it's rejected
|
||||
response = client.patch(
|
||||
response = await client.patch(
|
||||
"/api/v1/users/me",
|
||||
headers=headers,
|
||||
json={"first_name": "Test", "is_superuser": True}
|
||||
@@ -220,9 +235,10 @@ class TestUpdateCurrentUser:
|
||||
# Verify user is still not a superuser
|
||||
assert data["is_superuser"] is False
|
||||
|
||||
def test_update_profile_no_auth(self, client):
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_profile_no_auth(self, client):
|
||||
"""Test that unauthenticated requests are rejected."""
|
||||
response = client.patch(
|
||||
response = await client.patch(
|
||||
"/api/v1/users/me",
|
||||
json={"first_name": "Hacker"}
|
||||
)
|
||||
@@ -234,17 +250,19 @@ class TestUpdateCurrentUser:
|
||||
class TestGetUserById:
|
||||
"""Tests for GET /users/{user_id} endpoint."""
|
||||
|
||||
def test_get_own_profile_by_id(self, client, test_user):
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_own_profile_by_id(self, client, async_test_user):
|
||||
"""Test getting own profile by ID."""
|
||||
headers = get_auth_headers(client, test_user.email, "TestPassword123")
|
||||
headers = await get_auth_headers(client, async_test_user.email, "TestPassword123")
|
||||
|
||||
response = client.get(f"/api/v1/users/{test_user.id}", headers=headers)
|
||||
response = await client.get(f"/api/v1/users/{async_test_user.id}", headers=headers)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert data["email"] == test_user.email
|
||||
assert data["email"] == async_test_user.email
|
||||
|
||||
def test_get_other_user_as_regular_user(self, client, test_user, test_db):
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_other_user_as_regular_user(self, client, async_test_user, test_db):
|
||||
"""Test that regular users cannot view other profiles."""
|
||||
# Create another user
|
||||
other_user = User(
|
||||
@@ -258,36 +276,39 @@ class TestGetUserById:
|
||||
test_db.commit()
|
||||
test_db.refresh(other_user)
|
||||
|
||||
headers = get_auth_headers(client, test_user.email, "TestPassword123")
|
||||
headers = await get_auth_headers(client, async_test_user.email, "TestPassword123")
|
||||
|
||||
response = client.get(f"/api/v1/users/{other_user.id}", headers=headers)
|
||||
response = await client.get(f"/api/v1/users/{other_user.id}", headers=headers)
|
||||
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
|
||||
def test_get_other_user_as_superuser(self, client, test_superuser, test_user):
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_other_user_as_superuser(self, client, async_test_superuser, async_test_user):
|
||||
"""Test that superusers can view other profiles."""
|
||||
headers = get_auth_headers(client, test_superuser.email, "SuperPassword123")
|
||||
headers = await get_auth_headers(client, async_test_superuser.email, "SuperPassword123")
|
||||
|
||||
response = client.get(f"/api/v1/users/{test_user.id}", headers=headers)
|
||||
response = await client.get(f"/api/v1/users/{async_test_user.id}", headers=headers)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert data["email"] == test_user.email
|
||||
assert data["email"] == async_test_user.email
|
||||
|
||||
def test_get_nonexistent_user(self, client, test_superuser):
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_nonexistent_user(self, client, async_test_superuser):
|
||||
"""Test getting non-existent user."""
|
||||
headers = get_auth_headers(client, test_superuser.email, "SuperPassword123")
|
||||
headers = await get_auth_headers(client, async_test_superuser.email, "SuperPassword123")
|
||||
fake_id = uuid.uuid4()
|
||||
|
||||
response = client.get(f"/api/v1/users/{fake_id}", headers=headers)
|
||||
response = await client.get(f"/api/v1/users/{fake_id}", headers=headers)
|
||||
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
|
||||
def test_get_user_invalid_uuid(self, client, test_superuser):
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_user_invalid_uuid(self, client, async_test_superuser):
|
||||
"""Test getting user with invalid UUID format."""
|
||||
headers = get_auth_headers(client, test_superuser.email, "SuperPassword123")
|
||||
headers = await get_auth_headers(client, async_test_superuser.email, "SuperPassword123")
|
||||
|
||||
response = client.get("/api/v1/users/not-a-uuid", headers=headers)
|
||||
response = await client.get("/api/v1/users/not-a-uuid", headers=headers)
|
||||
|
||||
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
|
||||
|
||||
@@ -295,12 +316,13 @@ class TestGetUserById:
|
||||
class TestUpdateUserById:
|
||||
"""Tests for PATCH /users/{user_id} endpoint."""
|
||||
|
||||
def test_update_own_profile_by_id(self, client, test_user, test_db):
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_own_profile_by_id(self, client, async_test_user, test_db):
|
||||
"""Test updating own profile by ID."""
|
||||
headers = get_auth_headers(client, test_user.email, "TestPassword123")
|
||||
headers = await get_auth_headers(client, async_test_user.email, "TestPassword123")
|
||||
|
||||
response = client.patch(
|
||||
f"/api/v1/users/{test_user.id}",
|
||||
response = await client.patch(
|
||||
f"/api/v1/users/{async_test_user.id}",
|
||||
headers=headers,
|
||||
json={"first_name": "SelfUpdated"}
|
||||
)
|
||||
@@ -309,7 +331,8 @@ class TestUpdateUserById:
|
||||
data = response.json()
|
||||
assert data["first_name"] == "SelfUpdated"
|
||||
|
||||
def test_update_other_user_as_regular_user(self, client, test_user, test_db):
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_other_user_as_regular_user(self, client, async_test_user, test_db):
|
||||
"""Test that regular users cannot update other profiles."""
|
||||
# Create another user
|
||||
other_user = User(
|
||||
@@ -323,9 +346,9 @@ class TestUpdateUserById:
|
||||
test_db.commit()
|
||||
test_db.refresh(other_user)
|
||||
|
||||
headers = get_auth_headers(client, test_user.email, "TestPassword123")
|
||||
headers = await get_auth_headers(client, async_test_user.email, "TestPassword123")
|
||||
|
||||
response = client.patch(
|
||||
response = await client.patch(
|
||||
f"/api/v1/users/{other_user.id}",
|
||||
headers=headers,
|
||||
json={"first_name": "Hacked"}
|
||||
@@ -337,12 +360,13 @@ class TestUpdateUserById:
|
||||
test_db.refresh(other_user)
|
||||
assert other_user.first_name == "Other"
|
||||
|
||||
def test_update_other_user_as_superuser(self, client, test_superuser, test_user, test_db):
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_other_user_as_superuser(self, client, async_test_superuser, async_test_user, test_db):
|
||||
"""Test that superusers can update other profiles."""
|
||||
headers = get_auth_headers(client, test_superuser.email, "SuperPassword123")
|
||||
headers = await get_auth_headers(client, async_test_superuser.email, "SuperPassword123")
|
||||
|
||||
response = client.patch(
|
||||
f"/api/v1/users/{test_user.id}",
|
||||
response = await client.patch(
|
||||
f"/api/v1/users/{async_test_user.id}",
|
||||
headers=headers,
|
||||
json={"first_name": "AdminUpdated"}
|
||||
)
|
||||
@@ -351,14 +375,15 @@ class TestUpdateUserById:
|
||||
data = response.json()
|
||||
assert data["first_name"] == "AdminUpdated"
|
||||
|
||||
def test_regular_user_cannot_modify_superuser_status(self, client, test_user):
|
||||
@pytest.mark.asyncio
|
||||
async def test_regular_user_cannot_modify_superuser_status(self, client, async_test_user):
|
||||
"""Test that regular users cannot change superuser status even if they try."""
|
||||
headers = get_auth_headers(client, test_user.email, "TestPassword123")
|
||||
headers = await get_auth_headers(client, async_test_user.email, "TestPassword123")
|
||||
|
||||
# is_superuser not in UserUpdate schema, so it gets ignored by Pydantic
|
||||
# Just verify the user stays the same
|
||||
response = client.patch(
|
||||
f"/api/v1/users/{test_user.id}",
|
||||
response = await client.patch(
|
||||
f"/api/v1/users/{async_test_user.id}",
|
||||
headers=headers,
|
||||
json={"first_name": "Test"}
|
||||
)
|
||||
@@ -367,12 +392,13 @@ class TestUpdateUserById:
|
||||
data = response.json()
|
||||
assert data["is_superuser"] is False
|
||||
|
||||
def test_superuser_can_update_users(self, client, test_superuser, test_user, test_db):
|
||||
@pytest.mark.asyncio
|
||||
async def test_superuser_can_update_users(self, client, async_test_superuser, async_test_user, test_db):
|
||||
"""Test that superusers can update other users."""
|
||||
headers = get_auth_headers(client, test_superuser.email, "SuperPassword123")
|
||||
headers = await get_auth_headers(client, async_test_superuser.email, "SuperPassword123")
|
||||
|
||||
response = client.patch(
|
||||
f"/api/v1/users/{test_user.id}",
|
||||
response = await client.patch(
|
||||
f"/api/v1/users/{async_test_user.id}",
|
||||
headers=headers,
|
||||
json={"first_name": "AdminChanged", "is_active": False}
|
||||
)
|
||||
@@ -382,12 +408,13 @@ class TestUpdateUserById:
|
||||
assert data["first_name"] == "AdminChanged"
|
||||
assert data["is_active"] is False
|
||||
|
||||
def test_update_nonexistent_user(self, client, test_superuser):
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_nonexistent_user(self, client, async_test_superuser):
|
||||
"""Test updating non-existent user."""
|
||||
headers = get_auth_headers(client, test_superuser.email, "SuperPassword123")
|
||||
headers = await get_auth_headers(client, async_test_superuser.email, "SuperPassword123")
|
||||
fake_id = uuid.uuid4()
|
||||
|
||||
response = client.patch(
|
||||
response = await client.patch(
|
||||
f"/api/v1/users/{fake_id}",
|
||||
headers=headers,
|
||||
json={"first_name": "Ghost"}
|
||||
@@ -401,11 +428,12 @@ class TestUpdateUserById:
|
||||
class TestChangePassword:
|
||||
"""Tests for PATCH /users/me/password endpoint."""
|
||||
|
||||
def test_change_password_success(self, client, test_user, test_db):
|
||||
@pytest.mark.asyncio
|
||||
async def test_change_password_success(self, client, async_test_user, test_db):
|
||||
"""Test successful password change."""
|
||||
headers = get_auth_headers(client, test_user.email, "TestPassword123")
|
||||
headers = await get_auth_headers(client, async_test_user.email, "TestPassword123")
|
||||
|
||||
response = client.patch(
|
||||
response = await client.patch(
|
||||
"/api/v1/users/me/password",
|
||||
headers=headers,
|
||||
json={
|
||||
@@ -419,20 +447,21 @@ class TestChangePassword:
|
||||
assert data["success"] is True
|
||||
|
||||
# Verify can login with new password
|
||||
login_response = client.post(
|
||||
login_response = await client.post(
|
||||
"/api/v1/auth/login",
|
||||
json={
|
||||
"email": test_user.email,
|
||||
"email": async_test_user.email,
|
||||
"password": "NewPassword123"
|
||||
}
|
||||
)
|
||||
assert login_response.status_code == status.HTTP_200_OK
|
||||
|
||||
def test_change_password_wrong_current(self, client, test_user):
|
||||
@pytest.mark.asyncio
|
||||
async def test_change_password_wrong_current(self, client, async_test_user):
|
||||
"""Test that wrong current password is rejected."""
|
||||
headers = get_auth_headers(client, test_user.email, "TestPassword123")
|
||||
headers = await get_auth_headers(client, async_test_user.email, "TestPassword123")
|
||||
|
||||
response = client.patch(
|
||||
response = await client.patch(
|
||||
"/api/v1/users/me/password",
|
||||
headers=headers,
|
||||
json={
|
||||
@@ -443,11 +472,12 @@ class TestChangePassword:
|
||||
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
|
||||
def test_change_password_weak_new_password(self, client, test_user):
|
||||
@pytest.mark.asyncio
|
||||
async def test_change_password_weak_new_password(self, client, async_test_user):
|
||||
"""Test that weak new passwords are rejected."""
|
||||
headers = get_auth_headers(client, test_user.email, "TestPassword123")
|
||||
headers = await get_auth_headers(client, async_test_user.email, "TestPassword123")
|
||||
|
||||
response = client.patch(
|
||||
response = await client.patch(
|
||||
"/api/v1/users/me/password",
|
||||
headers=headers,
|
||||
json={
|
||||
@@ -458,9 +488,10 @@ class TestChangePassword:
|
||||
|
||||
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
|
||||
|
||||
def test_change_password_no_auth(self, client):
|
||||
@pytest.mark.asyncio
|
||||
async def test_change_password_no_auth(self, client):
|
||||
"""Test that unauthenticated requests are rejected."""
|
||||
response = client.patch(
|
||||
response = await client.patch(
|
||||
"/api/v1/users/me/password",
|
||||
json={
|
||||
"current_password": "TestPassword123",
|
||||
@@ -475,7 +506,8 @@ class TestChangePassword:
|
||||
class TestDeleteUser:
|
||||
"""Tests for DELETE /users/{user_id} endpoint."""
|
||||
|
||||
def test_delete_user_as_superuser(self, client, test_superuser, test_db):
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_user_as_superuser(self, client, async_test_superuser, test_db):
|
||||
"""Test deleting a user as superuser."""
|
||||
# Create a user to delete
|
||||
user_to_delete = User(
|
||||
@@ -489,9 +521,9 @@ class TestDeleteUser:
|
||||
test_db.commit()
|
||||
test_db.refresh(user_to_delete)
|
||||
|
||||
headers = get_auth_headers(client, test_superuser.email, "SuperPassword123")
|
||||
headers = await get_auth_headers(client, async_test_superuser.email, "SuperPassword123")
|
||||
|
||||
response = client.delete(f"/api/v1/users/{user_to_delete.id}", headers=headers)
|
||||
response = await client.delete(f"/api/v1/users/{user_to_delete.id}", headers=headers)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
@@ -501,15 +533,17 @@ class TestDeleteUser:
|
||||
test_db.refresh(user_to_delete)
|
||||
assert user_to_delete.deleted_at is not None
|
||||
|
||||
def test_cannot_delete_self(self, client, test_superuser):
|
||||
@pytest.mark.asyncio
|
||||
async def test_cannot_delete_self(self, client, async_test_superuser):
|
||||
"""Test that users cannot delete their own account."""
|
||||
headers = get_auth_headers(client, test_superuser.email, "SuperPassword123")
|
||||
headers = await get_auth_headers(client, async_test_superuser.email, "SuperPassword123")
|
||||
|
||||
response = client.delete(f"/api/v1/users/{test_superuser.id}", headers=headers)
|
||||
response = await client.delete(f"/api/v1/users/{async_test_superuser.id}", headers=headers)
|
||||
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
|
||||
def test_delete_user_as_regular_user(self, client, test_user, test_db):
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_user_as_regular_user(self, client, async_test_user, test_db):
|
||||
"""Test that regular users cannot delete users."""
|
||||
# Create another user
|
||||
other_user = User(
|
||||
@@ -523,24 +557,26 @@ class TestDeleteUser:
|
||||
test_db.commit()
|
||||
test_db.refresh(other_user)
|
||||
|
||||
headers = get_auth_headers(client, test_user.email, "TestPassword123")
|
||||
headers = await get_auth_headers(client, async_test_user.email, "TestPassword123")
|
||||
|
||||
response = client.delete(f"/api/v1/users/{other_user.id}", headers=headers)
|
||||
response = await client.delete(f"/api/v1/users/{other_user.id}", headers=headers)
|
||||
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
|
||||
def test_delete_nonexistent_user(self, client, test_superuser):
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_nonexistent_user(self, client, async_test_superuser):
|
||||
"""Test deleting non-existent user."""
|
||||
headers = get_auth_headers(client, test_superuser.email, "SuperPassword123")
|
||||
headers = await get_auth_headers(client, async_test_superuser.email, "SuperPassword123")
|
||||
fake_id = uuid.uuid4()
|
||||
|
||||
response = client.delete(f"/api/v1/users/{fake_id}", headers=headers)
|
||||
response = await client.delete(f"/api/v1/users/{fake_id}", headers=headers)
|
||||
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
|
||||
def test_delete_user_no_auth(self, client, test_user):
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_user_no_auth(self, client, async_test_user):
|
||||
"""Test that unauthenticated requests are rejected."""
|
||||
response = client.delete(f"/api/v1/users/{test_user.id}")
|
||||
response = await client.delete(f"/api/v1/users/{async_test_user.id}")
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
|
||||
# Note: Removed test_delete_user_unexpected_error - see comment above
|
||||
|
||||
Reference in New Issue
Block a user