django-rest-api/accounts/tests.py

519 lines
17 KiB
Python

from django.test import TestCase
from django.contrib.auth import get_user_model
from django.urls import reverse
from rest_framework.test import APITestCase, APIClient
from rest_framework import status
from rest_framework.authtoken.models import Token
import json
User = get_user_model()
class UserRegistrationTestCase(APITestCase):
"""
Test cases for user registration functionality.
"""
def setUp(self):
self.client = APIClient()
self.registration_url = '/api/accounts/auth/registration/' # Back to dj-rest-auth
self.valid_user_data = {
'email': 'testuser@example.com',
'password1': 'testpassword123',
'password2': 'testpassword123',
}
def test_user_registration_success(self):
"""
Test successful user registration.
"""
response = self.client.post(
self.registration_url,
self.valid_user_data,
format='json'
)
# Debug: print response if test fails
if response.status_code != status.HTTP_201_CREATED:
print(f"Registration failed with status {response.status_code}")
print(f"Response data: {response.data}")
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertIn('key', response.data) # Token should be returned
# Verify user was created
user = User.objects.get(email=self.valid_user_data['email'])
self.assertTrue(user.is_active) # User is active since email verification is optional
def test_user_registration_password_mismatch(self):
"""
Test registration with mismatched passwords.
"""
invalid_data = self.valid_user_data.copy()
invalid_data['password2'] = 'differentpassword'
response = self.client.post(
self.registration_url,
invalid_data,
format='json'
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
# dj-rest-auth may return field errors; accept either
self.assertTrue('non_field_errors' in response.data or 'password2' in response.data)
def test_user_registration_duplicate_email(self):
"""
Test registration with duplicate email.
"""
# Create a user first with the same email
User.objects.create_user(
email='testuser@example.com',
password='password123'
)
response = self.client.post(
self.registration_url,
self.valid_user_data,
format='json'
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
# Check for either email or username error since they're the same
self.assertTrue('email' in response.data or 'username' in response.data)
def test_user_registration_missing_fields(self):
"""
Test registration with missing required fields.
"""
incomplete_data = {
'email': 'testuser@example.com',
'password1': 'testpassword123'
# Missing password2, first_name, last_name
}
response = self.client.post(
self.registration_url,
incomplete_data,
format='json'
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn('password2', response.data)
class UserLoginTestCase(APITestCase):
"""
Test cases for user login functionality.
"""
def setUp(self):
self.client = APIClient()
self.login_url = '/api/accounts/auth/login/'
self.logout_url = '/api/accounts/auth/logout/'
# Create an active user for testing
self.user = User.objects.create_user(
email='testuser@example.com',
password='testpassword123',
)
self.user.full_name = 'Test User'
self.user.preferred_name = 'Test'
self.user.save()
self.user_credentials = {
'email': 'testuser@example.com',
'password': 'testpassword123'
}
def test_user_login_success(self):
"""
Test successful user login.
"""
response = self.client.post(
self.login_url,
self.user_credentials,
format='json'
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('key', response.data) # Token should be returned
# Note: 'user' field might not be returned by default in dj-rest-auth
# Verify token was created
token = Token.objects.get(user=self.user)
self.assertEqual(response.data['key'], token.key)
def test_user_login_invalid_credentials(self):
"""
Test login with invalid credentials.
"""
invalid_credentials = {
'email': 'testuser@example.com',
'password': 'wrongpassword'
}
response = self.client.post(
self.login_url,
invalid_credentials,
format='json'
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn('non_field_errors', response.data)
def test_user_login_inactive_user(self):
"""
Test login with inactive user account.
"""
# Create inactive user
inactive_user = User.objects.create_user(
email='inactive@example.com',
password='testpassword123',
)
inactive_user.full_name = 'Inactive User'
inactive_user.is_active = False
inactive_user.save()
credentials = {
'email': 'inactive@example.com',
'password': 'testpassword123'
}
response = self.client.post(
self.login_url,
credentials,
format='json'
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_user_logout_success(self):
"""
Test successful user logout.
"""
# First login to get a token
login_response = self.client.post(
self.login_url,
self.user_credentials,
format='json'
)
self.assertEqual(login_response.status_code, status.HTTP_200_OK)
# Get token from response (might be 'key' or 'access_token')
token = login_response.data.get('key') or login_response.data.get('access_token')
self.assertIsNotNone(token, f"No token found in response: {login_response.data}")
# Set authentication header
self.client.credentials(HTTP_AUTHORIZATION=f'Token {token}')
# Logout
response = self.client.post(self.logout_url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Verify token was deleted
self.assertFalse(Token.objects.filter(key=token).exists())
def test_user_logout_without_authentication(self):
"""
Test logout without authentication.
"""
response = self.client.post(self.logout_url)
# dj-rest-auth might return 400 instead of 401 for unauthenticated logout
self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST, status.HTTP_401_UNAUTHORIZED])
class TokenAuthenticationTestCase(APITestCase):
"""
Test cases for token-based authentication.
"""
def setUp(self):
self.client = APIClient()
self.user = User.objects.create_user(
email='testuser@example.com',
password='testpassword123',
)
self.user.full_name = 'Test User'
self.user.save()
self.token = Token.objects.create(user=self.user)
self.user_detail_url = '/api/accounts/auth/user/'
def test_authenticated_request_with_valid_token(self):
"""
Test authenticated request with valid token.
"""
self.client.credentials(HTTP_AUTHORIZATION=f'Token {self.token.key}')
response = self.client.get(self.user_detail_url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['email'], self.user.email)
self.assertEqual(response.data['first_name'], self.user.first_name)
def test_authenticated_request_with_invalid_token(self):
"""
Test authenticated request with invalid token.
"""
self.client.credentials(HTTP_AUTHORIZATION='Token invalidtoken123')
response = self.client.get(self.user_detail_url)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_authenticated_request_without_token(self):
"""
Test authenticated request without token.
"""
response = self.client.get(self.user_detail_url)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_update_user_details_with_authentication(self):
"""
Test updating user details with valid authentication.
"""
self.client.credentials(HTTP_AUTHORIZATION=f'Token {self.token.key}')
update_data = {
'first_name': 'Updated',
'last_name': 'Name'
}
response = self.client.patch(
self.user_detail_url,
update_data,
format='json'
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['first_name'], 'Updated')
self.assertEqual(response.data['last_name'], 'Name')
# Verify database was updated
self.user.refresh_from_db()
self.assertEqual(self.user.first_name, 'Updated')
self.assertEqual(self.user.last_name, 'Name')
class UserProfileViewTestCase(APITestCase):
"""
Test cases for custom user profile views.
"""
def setUp(self):
self.client = APIClient()
self.user = User.objects.create_user(
email='testuser@example.com',
password='testpassword123',
)
self.user.full_name = 'Test User'
self.user.save()
self.token = Token.objects.create(user=self.user)
self.profile_url = reverse('accounts:user-profile')
self.stats_url = reverse('accounts:user-stats')
self.change_password_url = reverse('accounts:change-password')
def test_get_user_profile_authenticated(self):
"""
Test retrieving user profile with authentication.
"""
self.client.credentials(HTTP_AUTHORIZATION=f'Token {self.token.key}')
response = self.client.get(self.profile_url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['email'], self.user.email)
self.assertEqual(response.data['full_name'], 'Test User')
self.assertIn('date_joined', response.data)
def test_get_user_profile_unauthenticated(self):
"""
Test retrieving user profile without authentication.
"""
response = self.client.get(self.profile_url)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_get_user_stats_authenticated(self):
"""
Test retrieving user stats with authentication.
"""
self.client.credentials(HTTP_AUTHORIZATION=f'Token {self.token.key}')
response = self.client.get(self.stats_url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['user_id'], self.user.pk)
self.assertEqual(response.data['email'], self.user.email)
self.assertEqual(response.data['is_active'], True)
def test_change_password_success(self):
"""
Test successful password change.
"""
self.client.credentials(HTTP_AUTHORIZATION=f'Token {self.token.key}')
password_data = {
'old_password': 'testpassword123',
'new_password1': 'newpassword456',
'new_password2': 'newpassword456'
}
response = self.client.post(
self.change_password_url,
password_data,
format='json'
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('message', response.data)
# Verify password was changed
self.user.refresh_from_db()
self.assertTrue(self.user.check_password('newpassword456'))
def test_change_password_wrong_old_password(self):
"""
Test password change with wrong old password.
"""
self.client.credentials(HTTP_AUTHORIZATION=f'Token {self.token.key}')
password_data = {
'old_password': 'wrongpassword',
'new_password1': 'newpassword456',
'new_password2': 'newpassword456'
}
response = self.client.post(
self.change_password_url,
password_data,
format='json'
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn('old_password', response.data)
def test_change_password_mismatch(self):
"""
Test password change with mismatched new passwords.
"""
self.client.credentials(HTTP_AUTHORIZATION=f'Token {self.token.key}')
password_data = {
'old_password': 'testpassword123',
'new_password1': 'newpassword456',
'new_password2': 'differentpassword'
}
response = self.client.post(
self.change_password_url,
password_data,
format='json'
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn('non_field_errors', response.data)
class SerializerTestCase(APITestCase):
"""
Test cases for custom serializers.
"""
def setUp(self):
self.user = User.objects.create_user(
email='testuser@example.com',
password='testpassword123',
)
self.user.full_name = 'Test User'
self.user.save()
def test_custom_register_serializer_validation(self):
"""
Test custom registration serializer validation.
"""
from .api.serializers import CustomRegisterSerializer
valid_data = {
'email': 'newuser@example.com',
'password1': 'newpassword123',
'password2': 'newpassword123',
'full_name': 'New User',
'preferred_name': 'New'
}
serializer = CustomRegisterSerializer(data=valid_data)
self.assertTrue(serializer.is_valid())
cleaned_data = serializer.get_cleaned_data()
self.assertEqual(cleaned_data['first_name'], 'New')
self.assertEqual(cleaned_data['last_name'], 'User')
def test_user_details_serializer_email_validation(self):
"""
Test user details serializer email uniqueness validation.
"""
from .api.serializers import UserDetailsSerializer
from django.test import RequestFactory
from rest_framework.request import Request
# Create another user with different email
other_user = User.objects.create_user(
email='other@example.com',
password='password123'
)
# Create a mock request
factory = RequestFactory()
request = factory.get('/')
request.user = other_user
drf_request = Request(request)
# Try to update with existing email
serializer = UserDetailsSerializer(
instance=other_user,
data={'email': self.user.email},
context={'request': drf_request},
partial=True
)
self.assertFalse(serializer.is_valid())
self.assertIn('email', serializer.errors)
def test_change_password_serializer_validation(self):
"""
Test change password serializer validation using the actual API endpoint.
"""
from rest_framework.authtoken.models import Token
# Create a token for the user
token = Token.objects.create(user=self.user)
# Test with correct old password via API
self.client.credentials(HTTP_AUTHORIZATION=f'Token {token.key}')
valid_data = {
'old_password': 'testpassword123',
'new_password1': 'newpassword456',
'new_password2': 'newpassword456'
}
response = self.client.post('/api/accounts/change-password/', valid_data, format='json')
self.assertEqual(response.status_code, 200)
# Verify password was changed
self.user.refresh_from_db()
self.assertTrue(self.user.check_password('newpassword456'))
# Test with incorrect old password
invalid_data = {
'old_password': 'wrongpassword',
'new_password1': 'anotherpassword789',
'new_password2': 'anotherpassword789'
}
response = self.client.post('/api/accounts/change-password/', invalid_data, format='json')
self.assertEqual(response.status_code, 400)
self.assertIn('old_password', response.data)