"""
Integration tests for user authentication and profile management flows.
Tests multiple components working together: models, services, views, database.
Run with: python manage.py test users.test_integration
"""
import json
from datetime import timedelta
from django.test import Client, TestCase
from django.utils import timezone
from users.models import User
[docs]
class AuthenticationFlowIntegrationTests(TestCase):
"""Test complete authentication flows from registration to login"""
[docs]
def setUp(self):
self.client = Client()
[docs]
def test_complete_registration_and_login_flow(self):
"""
Integration test: Register -> Auto-verify (test mode) -> Login -> Access profile
Tests: services.py, models.py, views.py, authentication backend
"""
# Step 1: Register via API
response = self.client.post('/api/users/register/',
data=json.dumps({
'email': 'newuser@ucla.edu',
'password': 'securepassword12345678',
'role': 'MENTEE'
}),
content_type='application/json'
)
self.assertEqual(response.status_code, 201) # Created
data = response.json()
self.assertEqual(data['email'], 'newuser@ucla.edu')
# Verify user was created in database
user = User.objects.get(email='newuser@ucla.edu')
self.assertTrue(user.is_verified) # Auto-verified in test mode
# Step 2: Login via API
response = self.client.post('/api/users/login/',
data=json.dumps({
'email': 'newuser@ucla.edu',
'password': 'securepassword12345678'
}),
content_type='application/json'
)
self.assertEqual(response.status_code, 200) # OK
# Step 3: Access protected endpoint
response = self.client.get('/api/users/me/')
self.assertEqual(response.status_code, 200)
profile_data = response.json()
self.assertEqual(profile_data['email'], 'newuser@ucla.edu')
self.assertEqual(profile_data['role'], 'MENTEE')
[docs]
def test_unverified_user_cannot_login(self):
"""
Integration test: Register -> Don't verify -> Try login -> Blocked
Tests: Email verification enforcement across services and views
"""
# Create unverified user directly (bypass auto-verify)
user = User.objects.create_user(
email='unverified@ucla.edu',
password='password12345678',
role='MENTEE'
)
user.is_verified = False # user not verified
user.save()
# Attempt login
response = self.client.post('/api/users/login/',
data=json.dumps({
'email': 'unverified@ucla.edu',
'password': 'password12345678'
}),
content_type='application/json'
)
self.assertEqual(response.status_code, 403) # Forbidden (b/c not verified)
self.assertIn('not verified', response.json()['error'].lower())
[docs]
class ProfileManagementIntegrationTests(TestCase):
"""Test profile CRUD operations across multiple layers"""
[docs]
def setUp(self):
self.client = Client()
self.user = User.objects.create_user(
email='profile_test@ucla.edu',
password='password12345678',
role='MENTEE'
)
self.user.is_verified = True # user verified
self.user.save()
# Login
self.client.post('/api/users/login/',
data=json.dumps({
'email': 'profile_test@ucla.edu',
'password': 'password12345678'
}),
content_type='application/json'
)
[docs]
def test_complete_profile_update_flow(self):
"""
Integration test: Login -> Update profile -> Verify changes -> Check matching eligibility
Tests: Authentication, profile updates, scoring triggers
"""
# Update profile via API
response = self.client.put('/api/users/profile/update/',
data=json.dumps({
'firstName': 'John',
'lastName': 'Bruin',
'major': ['Computer Science'],
'year': 1,
'hobbies': 'Coding, hiking'
}),
content_type='application/json'
)
self.assertEqual(response.status_code, 200)
# Verify changes persisted in database
self.user.refresh_from_db()
self.assertEqual(self.user.firstName, 'John')
self.assertEqual(self.user.lastName, 'Bruin')
self.assertEqual(self.user.major, ['Computer Science'])
self.assertEqual(self.user.year, 1)
# Verify profile is now complete
response = self.client.get('/api/users/me/')
data = response.json()
self.assertTrue(data.get('profile_complete'))
[docs]
def test_security_role_change_blocked_via_api(self):
"""
Integration test: Attempt ADMIN escalation via API -> Verify blocked
Tests: End-to-end security from API to database
"""
# Attempt to become admin via profile update
response = self.client.put('/api/users/profile/update/',
data=json.dumps({
'firstName': 'Hacker',
'role': 'ADMIN'
}),
content_type='application/json'
)
# Update should succeed for firstName but block role
self.assertEqual(response.status_code, 200)
# Verify in database
self.user.refresh_from_db()
self.assertEqual(self.user.firstName, 'Hacker')
self.assertNotEqual(self.user.role, 'ADMIN')
self.assertEqual(self.user.role, 'MENTEE')
[docs]
def test_security_role_change_blocked_via_api_2(self):
"""
Integration test: Attempt ADMIN escalation via API -> Verify blocked
Tests: End-to-end security from API to database
"""
# Attempt to become admin via complete survey
response = self.client.post('/api/users/survey/complete/',
data=json.dumps({
'firstName': 'Hacker',
'role': 'ADMIN'
}),
content_type='application/json'
)
# Update should succeed for firstName but block role
self.assertEqual(response.status_code, 200)
# Verify in database
self.user.refresh_from_db()
self.assertEqual(self.user.firstName, 'Hacker')
self.assertNotEqual(self.user.role, 'ADMIN')
self.assertEqual(self.user.role, 'MENTEE')
[docs]
class AccountDeletionIntegrationTests(TestCase):
"""Test complete account deletion workflows"""
[docs]
def setUp(self):
self.client = Client()
self.user = User.objects.create_user(
email='delete_me@ucla.edu',
password='password12345678',
role='MENTEE'
)
self.user.is_verified = True # user verified
self.user.save()
self.client.post('/api/users/login/',
data=json.dumps({
'email': 'delete_me@ucla.edu',
'password': 'password12345678'
}),
content_type='application/json'
)
[docs]
def test_deletion_request_and_cancellation_flow(self):
"""
Integration test: Request deletion -> Cancel -> Verify restored
Tests: Deletion service, API, database state management
"""
# Login FIRST (ensure we have a session)
login_response = self.client.post('/api/users/login/',
data=json.dumps({
'email': 'delete_me@ucla.edu',
'password': 'password12345678'
}),
content_type='application/json'
)
self.assertEqual(login_response.status_code, 200)
# Request deletion WITH CONFIRMATION
delete_response = self.client.post('/api/users/delete-account/',
data=json.dumps({
'confirmation': 'delete_me@ucla.edu' # confirmation needed
}),
content_type='application/json'
)
self.assertEqual(delete_response.status_code, 200)
# Verify deletion flags set
self.user.refresh_from_db()
self.assertTrue(self.user.is_deleted)
self.assertIsNotNone(self.user.permanent_deletion_date)
# Cancel deletion
cancel_response = self.client.post('/api/users/cancel-deletion/',
content_type='application/json'
)
self.assertEqual(cancel_response.status_code, 200)
# Verify restoration
self.user.refresh_from_db()
self.assertFalse(self.user.is_deleted)
self.assertIsNone(self.user.permanent_deletion_date)
[docs]
def test_expired_account_cleanup_and_reregistration(self):
"""
Integration test: Delete account -> Expire -> Garbage collect -> Re-register
Tests: Deletion lifecycle, cleanup service, registration validation
"""
# Request deletion
self.user.request_deletion()
# Manually expire (by subtracting 2 hours)
self.user.permanent_deletion_date = timezone.now() - timedelta(hours=2)
self.user.save()
# Run garbage collection
deleted_count = User.permanently_delete_expired_accounts()
self.assertEqual(deleted_count, 1)
# Verify hard deletion
with self.assertRaises(User.DoesNotExist):
User.objects.get(email='delete_me@ucla.edu')
# Register again with same email
response = self.client.post('/api/users/register/',
data=json.dumps({
'email': 'delete_me@ucla.edu',
'password': 'newpassword12345',
'role': 'MENTOR' # Different role
}),
content_type='application/json'
)
self.assertEqual(response.status_code, 201)
# Verify new user created
new_user = User.objects.get(email='delete_me@ucla.edu')
self.assertEqual(new_user.role, 'MENTOR')
[docs]
class RoleChangeIntegrationTests(TestCase):
"""Test role changes and their cascading effects"""
[docs]
def setUp(self):
self.client = Client()
# Create mentor and mentee
self.mentor = User.objects.create_user(
email='mentor@ucla.edu',
password='password12345678',
role='MENTOR'
)
self.mentor.is_verified = True
self.mentor.save()
self.mentee = User.objects.create_user(
email='mentee@ucla.edu',
password='password12345678',
role='MENTEE'
)
self.mentee.is_verified = True
self.mentee.save()
# Set up match
self.mentor.add_mentee(self.mentee.email)
self.mentee.matchedMentorEmail = self.mentor.email
self.mentee.isMatched = True
self.mentee.save()
[docs]
def test_role_change_cleans_up_relationships(self):
"""
Integration test: Create match -> Change role -> Verify cleanup
Tests: Matching system, role change service, database cascades
"""
# Login as mentee
login_response = self.client.post('/api/users/login/',
data=json.dumps({
'email': 'mentee@ucla.edu',
'password': 'password12345678'
}),
content_type='application/json'
)
self.assertEqual(login_response.status_code, 200)
# Change role via API
response = self.client.post('/api/users/change-role/',
data=json.dumps({'new_role': 'MENTOR'}),
content_type='application/json'
)
self.assertEqual(response.status_code, 200)
# Verify mentee's new state
self.mentee.refresh_from_db()
self.assertEqual(self.mentee.role, 'MENTOR') # new role is MENTOR
self.assertFalse(self.mentee.isMatched)
self.assertEqual(self.mentee.matchedMentorEmail, '')
# Verify mentor's mentee list updated
self.mentor.refresh_from_db()
# Make sure the ex-mentee's email is not in the mentor's current_mentees list
# (even if that list is None)
self.assertNotIn(self.mentee.email, self.mentor.current_mentees or [])