Files
syndarix/backend/tests/api/test_auth_endpoints.py
Felipe Cardoso defa33975f Add comprehensive test coverage for email service, password reset endpoints, and soft delete functionality
- Introduced unit tests for `EmailService` covering `ConsoleEmailBackend` and `SMTPEmailBackend`.
- Added tests for password reset request and confirmation endpoints, including edge cases and error handling.
- Implemented soft delete CRUD tests to validate `deleted_at` behavior and data exclusion in queries.
- Enhanced API tests for email functionality and user management workflows.
2025-10-30 17:18:25 +01:00

349 lines
11 KiB
Python

# tests/api/test_auth_endpoints.py
"""
Tests for authentication endpoints.
"""
import pytest
from unittest.mock import patch, MagicMock
from fastapi import status
from app.models.user import User
from app.schemas.users import UserCreate
# Disable rate limiting for tests
@pytest.fixture(autouse=True)
def disable_rate_limit():
"""Disable rate limiting for all tests in this module."""
with patch('app.api.routes.auth.limiter.enabled', False):
yield
class TestRegisterEndpoint:
"""Tests for POST /auth/register endpoint."""
def test_register_success(self, client, test_db):
"""Test successful user registration."""
response = client.post(
"/api/v1/auth/register",
json={
"email": "newuser@example.com",
"password": "SecurePassword123",
"first_name": "New",
"last_name": "User"
}
)
assert response.status_code == status.HTTP_201_CREATED
data = response.json()
assert data["email"] == "newuser@example.com"
assert data["first_name"] == "New"
assert "password" not in data
def test_register_duplicate_email(self, client, test_user):
"""Test registering with existing email."""
response = client.post(
"/api/v1/auth/register",
json={
"email": test_user.email,
"password": "SecurePassword123",
"first_name": "Duplicate",
"last_name": "User"
}
)
assert response.status_code == status.HTTP_409_CONFLICT
data = response.json()
assert data["success"] is False
def test_register_weak_password(self, client):
"""Test registration with weak password."""
response = client.post(
"/api/v1/auth/register",
json={
"email": "weakpass@example.com",
"password": "weak",
"first_name": "Weak",
"last_name": "Pass"
}
)
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
def test_register_unexpected_error(self, client, test_db):
"""Test registration with unexpected error."""
with patch('app.services.auth_service.AuthService.create_user') as mock_create:
mock_create.side_effect = Exception("Unexpected error")
response = client.post(
"/api/v1/auth/register",
json={
"email": "error@example.com",
"password": "SecurePassword123",
"first_name": "Error",
"last_name": "User"
}
)
assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
class TestLoginEndpoint:
"""Tests for POST /auth/login endpoint."""
def test_login_success(self, client, test_user):
"""Test successful login."""
response = client.post(
"/api/v1/auth/login",
json={
"email": test_user.email,
"password": "TestPassword123"
}
)
assert response.status_code == status.HTTP_200_OK
data = response.json()
assert "access_token" in data
assert "refresh_token" in data
assert data["token_type"] == "bearer"
def test_login_wrong_password(self, client, test_user):
"""Test login with wrong password."""
response = client.post(
"/api/v1/auth/login",
json={
"email": test_user.email,
"password": "WrongPassword123"
}
)
assert response.status_code == status.HTTP_401_UNAUTHORIZED
def test_login_nonexistent_user(self, client):
"""Test login with non-existent email."""
response = client.post(
"/api/v1/auth/login",
json={
"email": "nonexistent@example.com",
"password": "Password123"
}
)
assert response.status_code == status.HTTP_401_UNAUTHORIZED
def test_login_inactive_user(self, client, test_user, test_db):
"""Test login with inactive user."""
test_user.is_active = False
test_db.add(test_user)
test_db.commit()
response = client.post(
"/api/v1/auth/login",
json={
"email": test_user.email,
"password": "TestPassword123"
}
)
assert response.status_code == status.HTTP_401_UNAUTHORIZED
def test_login_unexpected_error(self, client, test_user):
"""Test login with unexpected error."""
with patch('app.services.auth_service.AuthService.authenticate_user') as mock_auth:
mock_auth.side_effect = Exception("Database error")
response = client.post(
"/api/v1/auth/login",
json={
"email": test_user.email,
"password": "TestPassword123"
}
)
assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
class TestOAuthLoginEndpoint:
"""Tests for POST /auth/login/oauth endpoint."""
def test_oauth_login_success(self, client, test_user):
"""Test successful OAuth login."""
response = client.post(
"/api/v1/auth/login/oauth",
data={
"username": test_user.email,
"password": "TestPassword123"
}
)
assert response.status_code == status.HTTP_200_OK
data = response.json()
assert "access_token" in data
assert "refresh_token" in data
def test_oauth_login_wrong_credentials(self, client, test_user):
"""Test OAuth login with wrong credentials."""
response = client.post(
"/api/v1/auth/login/oauth",
data={
"username": test_user.email,
"password": "WrongPassword"
}
)
assert response.status_code == status.HTTP_401_UNAUTHORIZED
def test_oauth_login_inactive_user(self, client, test_user, test_db):
"""Test OAuth login with inactive user."""
test_user.is_active = False
test_db.add(test_user)
test_db.commit()
response = client.post(
"/api/v1/auth/login/oauth",
data={
"username": test_user.email,
"password": "TestPassword123"
}
)
assert response.status_code == status.HTTP_401_UNAUTHORIZED
def test_oauth_login_unexpected_error(self, client, test_user):
"""Test OAuth login with unexpected error."""
with patch('app.services.auth_service.AuthService.authenticate_user') as mock_auth:
mock_auth.side_effect = Exception("Unexpected error")
response = client.post(
"/api/v1/auth/login/oauth",
data={
"username": test_user.email,
"password": "TestPassword123"
}
)
assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
class TestRefreshTokenEndpoint:
"""Tests for POST /auth/refresh endpoint."""
def test_refresh_token_success(self, client, test_user):
"""Test successful token refresh."""
# First, login to get a refresh token
login_response = client.post(
"/api/v1/auth/login",
json={
"email": test_user.email,
"password": "TestPassword123"
}
)
refresh_token = login_response.json()["refresh_token"]
# Now refresh the token
response = client.post(
"/api/v1/auth/refresh",
json={"refresh_token": refresh_token}
)
assert response.status_code == status.HTTP_200_OK
data = response.json()
assert "access_token" in data
assert "refresh_token" in data
def test_refresh_token_expired(self, client):
"""Test refresh with expired token."""
from app.core.auth import TokenExpiredError
with patch('app.services.auth_service.AuthService.refresh_tokens') as mock_refresh:
mock_refresh.side_effect = TokenExpiredError("Token expired")
response = client.post(
"/api/v1/auth/refresh",
json={"refresh_token": "some_token"}
)
assert response.status_code == status.HTTP_401_UNAUTHORIZED
def test_refresh_token_invalid(self, client):
"""Test refresh with invalid token."""
response = client.post(
"/api/v1/auth/refresh",
json={"refresh_token": "invalid_token"}
)
assert response.status_code == status.HTTP_401_UNAUTHORIZED
def test_refresh_token_unexpected_error(self, client, test_user):
"""Test refresh with unexpected error."""
# Get a valid refresh token first
login_response = client.post(
"/api/v1/auth/login",
json={
"email": test_user.email,
"password": "TestPassword123"
}
)
refresh_token = login_response.json()["refresh_token"]
with patch('app.services.auth_service.AuthService.refresh_tokens') as mock_refresh:
mock_refresh.side_effect = Exception("Unexpected error")
response = client.post(
"/api/v1/auth/refresh",
json={"refresh_token": refresh_token}
)
assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
class TestGetCurrentUserEndpoint:
"""Tests for GET /auth/me endpoint."""
def test_get_current_user_success(self, client, test_user):
"""Test getting current user info."""
# First, login to get an access token
login_response = client.post(
"/api/v1/auth/login",
json={
"email": test_user.email,
"password": "TestPassword123"
}
)
access_token = login_response.json()["access_token"]
# Get current user info
response = client.get(
"/api/v1/auth/me",
headers={"Authorization": f"Bearer {access_token}"}
)
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
def test_get_current_user_no_token(self, client):
"""Test getting current user without token."""
response = client.get("/api/v1/auth/me")
assert response.status_code == status.HTTP_401_UNAUTHORIZED
def test_get_current_user_invalid_token(self, client):
"""Test getting current user with invalid token."""
response = client.get(
"/api/v1/auth/me",
headers={"Authorization": "Bearer invalid_token"}
)
assert response.status_code == status.HTTP_401_UNAUTHORIZED
def test_get_current_user_expired_token(self, client):
"""Test getting current user with expired token."""
# Use a clearly invalid/malformed token
response = client.get(
"/api/v1/auth/me",
headers={"Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.invalid"}
)
assert response.status_code == status.HTTP_401_UNAUTHORIZED