Source code for users.test_integration

"""
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 [])