from django.test import TestCase from django.contrib.auth import get_user_model from rest_framework.status import HTTP_200_OK, HTTP_201_CREATED, HTTP_400_BAD_REQUEST, HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN from rest_framework.test import APIClient User = get_user_model() class UserLoginActionTests(TestCase): def setUp(self): self.client = APIClient() self.user_data = { 'email_address': 'testuser@example.com', 'password': 'testpass123', 'first_name': 'Test', 'last_name': 'User', 'date_of_birth': '1990-01-01' } self.user = User.objects.create_user(**self.user_data) def test_login_successful(self): response = self.client.post('/api/user/login/', { 'email_address': 'testuser@example.com', 'password': 'testpass123' }) self.assertEqual(response.status_code, HTTP_200_OK) data = response.json() self.assertTrue(data['success']) self.assertEqual(data['message'], 'Login successful') self.assertIn('user', data) self.assertEqual(data['user']['email_address'], 'testuser@example.com') def test_login_missing_email(self): response = self.client.post('/api/user/login/', { 'password': 'testpass123' }) self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST) data = response.json() self.assertIn('error', data) def test_login_missing_password(self): response = self.client.post('/api/user/login/', { 'email_address': 'testuser@example.com' }) self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST) data = response.json() self.assertIn('error', data) def test_login_invalid_credentials(self): response = self.client.post('/api/user/login/', { 'email_address': 'testuser@example.com', 'password': 'wrongpassword' }) self.assertEqual(response.status_code, HTTP_401_UNAUTHORIZED) def test_login_nonexistent_user(self): response = self.client.post('/api/user/login/', { 'email_address': 'nonexistent@example.com', 'password': 'testpass123' }) self.assertEqual(response.status_code, HTTP_401_UNAUTHORIZED) def test_login_session_created(self): response = self.client.post('/api/user/login/', { 'email_address': 'testuser@example.com', 'password': 'testpass123' }) self.assertEqual(response.status_code, HTTP_200_OK) self.assertIn('sessionid', self.client.cookies) def test_login_inactive_user(self): self.user.is_active = False self.user.save() response = self.client.post('/api/user/login/', { 'email_address': 'testuser@example.com', 'password': 'testpass123' }) self.assertEqual(response.status_code, HTTP_401_UNAUTHORIZED) def test_login_case_insensitive_email(self): response = self.client.post('/api/user/login/', { 'email_address': 'testuser@EXAMPLE.COM', 'password': 'testpass123' }) self.assertEqual(response.status_code, HTTP_200_OK) class UserLogoutActionTests(TestCase): def setUp(self): self.client = APIClient() self.user = User.objects.create_user( email_address='testuser@example.com', password='testpass123', first_name='Test', last_name='User' ) def test_logout_successful(self): self.client.post('/api/user/login/', { 'email_address': 'testuser@example.com', 'password': 'testpass123' }) response = self.client.post('/api/user/logout/') self.assertEqual(response.status_code, HTTP_200_OK) data = response.json() self.assertTrue(data['success']) def test_logout_without_login(self): response = self.client.post('/api/user/logout/') self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) def test_session_destroyed_after_logout(self): self.client.post('/api/user/login/', { 'email_address': 'testuser@example.com', 'password': 'testpass123' }) self.client.post('/api/user/logout/') response = self.client.get('/api/user/me/') self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) class UserMeActionTests(TestCase): def setUp(self): self.client = APIClient() self.user = User.objects.create_user( email_address='testuser@example.com', password='testpass123', first_name='Test', last_name='User' ) def test_me_authenticated(self): self.client.post('/api/user/login/', { 'email_address': 'testuser@example.com', 'password': 'testpass123' }) response = self.client.get('/api/user/me/') self.assertEqual(response.status_code, HTTP_200_OK) data = response.json() self.assertTrue(data['success']) self.assertEqual(data['email_address'], 'testuser@example.com') def test_me_unauthenticated(self): response = self.client.get('/api/user/me/') self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) def test_me_returns_correct_user_data(self): self.client.post('/api/user/login/', { 'email_address': 'testuser@example.com', 'password': 'testpass123' }) response = self.client.get('/api/user/me/') data = response.json() expected_fields = {'id', 'uuid', 'email_address', 'first_name', 'last_name'} self.assertTrue(expected_fields.issubset(set(data.keys()))) class UserSessionActionTests(TestCase): def setUp(self): self.client = APIClient() self.user = User.objects.create_user( email_address='testuser@example.com', password='testpass123', first_name='Test', last_name='User' ) def test_session_authenticated(self): self.client.post('/api/user/login/', { 'email_address': 'testuser@example.com', 'password': 'testpass123' }) response = self.client.get('/api/user/session/') self.assertEqual(response.status_code, HTTP_200_OK) data = response.json() self.assertTrue(data['isAuthenticated']) def test_session_unauthenticated(self): response = self.client.get('/api/user/session/') self.assertEqual(response.status_code, HTTP_200_OK) data = response.json() self.assertFalse(data['isAuthenticated']) def test_session_staff_status(self): self.client.post('/api/user/login/', { 'email_address': 'testuser@example.com', 'password': 'testpass123' }) response = self.client.get('/api/user/session/') data = response.json() self.assertIn('isStaff', data) self.assertFalse(data['isStaff']) def test_session_unauthenticated_no_staff(self): response = self.client.get('/api/user/session/') data = response.json() self.assertFalse(data['isAuthenticated']) class UserSignupActionTests(TestCase): def setUp(self): self.client = APIClient() def test_signup_successful(self): response = self.client.post('/api/user/signup/', { 'email_address': 'newuser@example.com', 'password': 'newpass123', 'confirm_password': 'newpass123', 'first_name': 'New', 'last_name': 'User', 'date_of_birth': '1995-05-05' }) self.assertEqual(response.status_code, HTTP_201_CREATED) data = response.json() self.assertTrue(data['success']) self.assertIn('User account created successfully', data['detail']) self.assertTrue(User.objects.filter(email_address='newuser@example.com').exists()) def test_signup_email_exists(self): User.objects.create_user( email_address='existing@example.com', password='pass', first_name='Existing', last_name='User' ) response = self.client.post('/api/user/signup/', { 'email_address': 'existing@example.com', 'password': 'newpass123', 'confirm_password': 'newpass123', 'first_name': 'New', 'last_name': 'User' }) self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST) data = response.json() self.assertFalse(data['success']) self.assertIn('Email address already exists', data['detail']) def test_signup_missing_first_name(self): response = self.client.post('/api/user/signup/', { 'email_address': 'newuser2@example.com', 'password': 'newpass123', 'confirm_password': 'newpass123', 'last_name': 'User' }) self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST) data = response.json() self.assertFalse(data['success']) def test_signup_missing_last_name(self): response = self.client.post('/api/user/signup/', { 'email_address': 'newuser3@example.com', 'password': 'newpass123', 'confirm_password': 'newpass123', 'first_name': 'New' }) self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST) data = response.json() self.assertFalse(data['success']) def test_signup_passwords_mismatch(self): response = self.client.post('/api/user/signup/', { 'email_address': 'newuser4@example.com', 'password': 'newpass123', 'confirm_password': 'differentpass', 'first_name': 'New', 'last_name': 'User' }) self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST) data = response.json() self.assertIn('Passwords do not match', data['detail']) def test_signup_missing_email(self): response = self.client.post('/api/user/signup/', { 'password': 'newpass123', 'confirm_password': 'newpass123', 'first_name': 'New', 'last_name': 'User' }) self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST) def test_signup_missing_password(self): response = self.client.post('/api/user/signup/', { 'email_address': 'newuser@example.com', 'confirm_password': 'newpass123', 'first_name': 'New', 'last_name': 'User' }) self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST) def test_signup_empty_data(self): response = self.client.post('/api/user/signup/', {}) self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST) def test_signup_case_insensitive_email(self): response = self.client.post('/api/user/signup/', { 'email_address': 'NewUser@EXAMPLE.COM', 'password': 'newpass123', 'confirm_password': 'newpass123', 'first_name': 'New', 'last_name': 'User' }) self.assertEqual(response.status_code, HTTP_201_CREATED) user = User.objects.get(email_address='NewUser@example.com') self.assertEqual(user.email_address, 'NewUser@example.com') def test_signup_duplicate_case_insensitive(self): User.objects.create_user( email_address='test@example.com', password='pass', first_name='Test', last_name='User' ) response = self.client.post('/api/user/signup/', { 'password': 'newpass123', 'confirm_password': 'newpass123', 'first_name': 'New', 'last_name': 'User' }) self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST) class UserChangePasswordActionTests(TestCase): def setUp(self): self.client = APIClient() self.user = User.objects.create_user( email_address='testuser@example.com', password='testpass123', first_name='Test', last_name='User' ) def test_change_password_successful(self): self.client.post('/api/user/login/', { 'email_address': 'testuser@example.com', 'password': 'testpass123' }) response = self.client.post('/api/user/change_password/', { 'old_password': 'testpass123', 'password': 'newpass456', 'confirm_password': 'newpass456' }) self.assertEqual(response.status_code, HTTP_200_OK) data = response.json() self.assertTrue(data['success']) self.user.refresh_from_db() self.assertTrue(self.user.check_password('newpass456')) def test_change_password_wrong_old_password(self): self.client.post('/api/user/login/', { 'email_address': 'testuser@example.com', 'password': 'testpass123' }) response = self.client.post('/api/user/change_password/', { 'old_password': 'wrongoldpass', 'password': 'newpass456', 'confirm_password': 'newpass456' }) self.assertEqual(response.status_code, HTTP_401_UNAUTHORIZED) data = response.json() self.assertFalse(data['success']) def test_change_password_mismatch(self): self.client.post('/api/user/login/', { 'email_address': 'testuser@example.com', 'password': 'testpass123' }) response = self.client.post('/api/user/change_password/', { 'old_password': 'testpass123', 'password': 'newpass456', 'confirm_password': 'differentpass' }) self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST) data = response.json() self.assertIn('Passwords do not match', data['detail']) def test_change_password_missing_old_password(self): self.client.post('/api/user/login/', { 'email_address': 'testuser@example.com', 'password': 'testpass123' }) response = self.client.post('/api/user/change_password/', { 'password': 'newpass456', 'confirm_password': 'newpass456' }) self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST) data = response.json() self.assertIn('old_password', data['detail']) def test_change_password_missing_new_password(self): self.client.post('/api/user/login/', { 'email_address': 'testuser@example.com', 'password': 'testpass123' }) response = self.client.post('/api/user/change_password/', { 'old_password': 'testpass123', 'confirm_password': 'newpass456' }) self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST) def test_change_password_unauthenticated(self): response = self.client.post('/api/user/change_password/', { 'old_password': 'testpass123', 'password': 'newpass456', 'confirm_password': 'newpass456' }) self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) def test_change_password_empty_old_password(self): self.client.post('/api/user/login/', { 'email_address': 'testuser@example.com', 'password': 'testpass123' }) response = self.client.post('/api/user/change_password/', { 'old_password': '', 'password': 'newpass456', 'confirm_password': 'newpass456' }) self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST) def test_can_login_with_new_password_after_change(self): self.client.post('/api/user/login/', { 'email_address': 'testuser@example.com', 'password': 'testpass123' }) self.client.post('/api/user/change_password/', { 'old_password': 'testpass123', 'password': 'brandnewpass789', 'confirm_password': 'brandnewpass789' }) self.client.logout() response = self.client.post('/api/user/login/', { 'email_address': 'testuser@example.com', 'password': 'brandnewpass789' }) self.assertEqual(response.status_code, HTTP_200_OK) class UserEdgeCaseTests(TestCase): def setUp(self): self.client = APIClient() self.user = User.objects.create_user( email_address='edgecase@example.com', password='testpass123', first_name='Edge', last_name='Case' ) def test_login_with_whitespace_email(self): response = self.client.post('/api/user/login/', { 'email_address': ' testuser@example.com ', 'password': 'testpass123' }) self.assertEqual(response.status_code, HTTP_401_UNAUTHORIZED) def test_signup_with_very_long_name(self): long_name = 'A' * 255 response = self.client.post('/api/user/signup/', { 'email_address': 'longname@example.com', 'password': 'newpass123', 'confirm_password': 'newpass123', 'first_name': long_name, 'last_name': long_name }) self.assertEqual(response.status_code, HTTP_201_CREATED) def test_signup_with_too_long_name(self): too_long_name = 'A' * 256 response = self.client.post('/api/user/signup/', { 'email_address': 'verylongname@example.com', 'password': 'newpass123', 'confirm_password': 'newpass123', 'first_name': too_long_name, 'last_name': 'User' }) self.assertIn(response.status_code, [HTTP_400_BAD_REQUEST, HTTP_201_CREATED]) def test_signup_with_special_characters_in_name(self): response = self.client.post('/api/user/signup/', { 'email_address': 'special@example.com', 'password': 'newpass123', 'confirm_password': 'newpass123', 'first_name': 'José', 'last_name': "O'Brien-Smith" }) self.assertEqual(response.status_code, HTTP_201_CREATED) def test_change_password_same_as_old(self): self.client.post('/api/user/login/', { 'email_address': 'edgecase@example.com', 'password': 'testpass123' }) response = self.client.post('/api/user/change_password/', { 'old_password': 'testpass123', 'password': 'testpass123', 'confirm_password': 'testpass123' }) self.assertEqual(response.status_code, HTTP_200_OK) def test_signup_missing_confirm_password_field(self): response = self.client.post('/api/user/signup/', { 'email_address': 'missingconfirm@example.com', 'password': 'newpass123', 'first_name': 'Missing', 'last_name': 'Confirm' }) self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST) def test_login_multiple_times_same_session(self): response1 = self.client.post('/api/user/login/', { 'email_address': 'edgecase@example.com', 'password': 'testpass123' }) session_id_1 = self.client.cookies.get('sessionid') me1 = self.client.get('/api/user/me/') self.assertEqual(me1.status_code, HTTP_200_OK) response2 = self.client.post('/api/user/login/', { 'email_address': 'edgecase@example.com', 'password': 'testpass123' }) session_id_2 = self.client.cookies.get('sessionid') self.assertEqual(response1.status_code, HTTP_200_OK) self.assertEqual(response2.status_code, HTTP_200_OK) def test_staff_user_login_shows_staff_status(self): staff_user = User.objects.create_user( email_address='staff@example.com', password='staffpass', first_name='Staff', last_name='User', is_staff=True ) response = self.client.post('/api/user/login/', { 'email_address': 'staff@example.com', 'password': 'staffpass' }) self.assertEqual(response.status_code, HTTP_200_OK) data = response.json() self.assertIn('user', data) def test_session_status_after_explicit_logout(self): self.client.post('/api/user/login/', { 'email_address': 'edgecase@example.com', 'password': 'testpass123' }) self.client.post('/api/user/logout/') response = self.client.get('/api/user/session/') data = response.json() self.assertFalse(data['isAuthenticated']) def test_signup_with_null_optional_fields(self): response = self.client.post('/api/user/signup/', { 'email_address': 'optional@example.com', 'password': 'newpass123', 'confirm_password': 'newpass123', 'first_name': 'Optional', 'last_name': 'Fields' }) self.assertEqual(response.status_code, HTTP_201_CREATED) def test_change_password_with_missing_confirm_password(self): self.client.post('/api/user/login/', { 'email_address': 'edgecase@example.com', 'password': 'testpass123' }) response = self.client.post('/api/user/change_password/', { 'old_password': 'testpass123', 'password': 'newpass456' }) self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST) def test_login_and_logout_sequence(self): resp1 = self.client.post('/api/user/login/', { 'email_address': 'edgecase@example.com', 'password': 'testpass123' }) self.assertEqual(resp1.status_code, HTTP_200_OK) me1 = self.client.get('/api/user/me/') self.assertEqual(me1.status_code, HTTP_200_OK) logout_resp = self.client.post('/api/user/logout/') self.assertEqual(logout_resp.status_code, HTTP_200_OK) me2 = self.client.get('/api/user/me/') self.assertEqual(me2.status_code, HTTP_403_FORBIDDEN) resp2 = self.client.post('/api/user/login/', { 'email_address': 'edgecase@example.com', 'password': 'testpass123' }) self.assertEqual(resp2.status_code, HTTP_200_OK) me3 = self.client.get('/api/user/me/') self.assertEqual(me3.status_code, HTTP_200_OK) def test_invalid_email_format(self): response = self.client.post('/api/user/signup/', { 'email_address': 'not-an-email', 'password': 'newpass123', 'confirm_password': 'newpass123', 'first_name': 'Invalid', 'last_name': 'Email' }) self.assertIn(response.status_code, [HTTP_400_BAD_REQUEST, HTTP_201_CREATED]) def test_empty_password_signup(self): response = self.client.post('/api/user/signup/', { 'email_address': 'emptypass@example.com', 'password': '', 'confirm_password': '', 'first_name': 'Empty', 'last_name': 'Pass' }) self.assertIn(response.status_code, [HTTP_400_BAD_REQUEST, HTTP_201_CREATED]) def test_role_preserved_after_login(self): user = User.objects.create_user( email_address='manager@example.com', password='managerpass', first_name='Manager', last_name='User', is_manager=True ) response = self.client.post('/api/user/login/', { 'email_address': 'manager@example.com', 'password': 'managerpass' }) self.assertEqual(response.status_code, HTTP_200_OK) data = response.json() self.assertIn('user', data) self.assertEqual(data['user']['email_address'], 'manager@example.com') self.assertTrue(data['user']['is_manager'])