diff --git a/promo_code/business/tests/__init__.py b/promo_code/business/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/promo_code/business/tests/auth/__init__.py b/promo_code/business/tests/auth/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/promo_code/business/tests/auth/base.py b/promo_code/business/tests/auth/base.py new file mode 100644 index 0000000..3f32203 --- /dev/null +++ b/promo_code/business/tests/auth/base.py @@ -0,0 +1,23 @@ +import business.models +import django.urls +import rest_framework +import rest_framework.status +import rest_framework.test + + +class BaseBusinessAuthTestCase(rest_framework.test.APITestCase): + @classmethod + def setUpTestData(cls): + super().setUpTestData() + cls.client = rest_framework.test.APIClient() + cls.signup_url = django.urls.reverse('api-business:company-sign-up') + cls.signin_url = django.urls.reverse('api-business:company-sign-in') + cls.protected_url = django.urls.reverse('api-core:protected') + cls.valid_data = { + 'name': 'Digital Marketing Solutions Inc.', + 'email': 'testcompany@example.com', + 'password': 'SecurePass123!', + } + + def tearDown(self): + business.models.Company.objects.all().delete() diff --git a/promo_code/business/tests/auth/test_authentication.py b/promo_code/business/tests/auth/test_authentication.py new file mode 100644 index 0000000..46d43ae --- /dev/null +++ b/promo_code/business/tests/auth/test_authentication.py @@ -0,0 +1,33 @@ +import business.models +import business.tests.auth.base +import rest_framework.status +import rest_framework.test + + +class AuthenticationTests(business.tests.auth.base.BaseBusinessAuthTestCase): + def test_signin_success(self): + registration_data = {**self.valid_data, 'email': 'unique@company.com'} + business.models.Company.objects.create_company( + name=registration_data['name'], + email=registration_data['email'], + password=registration_data['password'], + ) + self.assertTrue( + business.models.Company.objects.filter( + email=registration_data['email'], + ).exists(), + ) + + response = self.client.post( + self.signin_url, + { + 'email': registration_data['email'], + 'password': registration_data['password'], + }, + format='json', + ) + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) + self.assertIn('access', response.data) diff --git a/promo_code/business/tests/auth/test_registration.py b/promo_code/business/tests/auth/test_registration.py new file mode 100644 index 0000000..44e87c1 --- /dev/null +++ b/promo_code/business/tests/auth/test_registration.py @@ -0,0 +1,25 @@ +import business.models +import business.tests.auth.base +import rest_framework.status +import rest_framework.test + + +class TestCompanyRegistration( + business.tests.auth.base.BaseBusinessAuthTestCase, +): + def test_registration_success(self): + registration_data = {**self.valid_data, 'email': 'unique@company.com'} + response = self.client.post( + self.signup_url, + registration_data, + format='json', + ) + self.assertTrue( + business.models.Company.objects.filter( + email=registration_data['email'], + ).exists(), + ) + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) diff --git a/promo_code/business/tests/auth/test_tokens.py b/promo_code/business/tests/auth/test_tokens.py new file mode 100644 index 0000000..b2ab66a --- /dev/null +++ b/promo_code/business/tests/auth/test_tokens.py @@ -0,0 +1,84 @@ +import business.models +import business.tests.auth.base +import rest_framework.status +import rest_framework.test + + +class JWTTests(business.tests.auth.base.BaseBusinessAuthTestCase): + def setUp(self): + super().setUp() + business.models.Company.objects.create_company( + name='Digital Marketing Solutions Inc.', + email='testcompany@example.com', + password='SuperStrongPassword2000!', + ) + + self.user_data = { + 'email': 'testcompany@example.com', + 'password': 'SuperStrongPassword2000!', + } + + def test_access_protected_view_with_valid_token(self): + response = self.client.post( + self.signin_url, + self.user_data, + format='json', + ) + + token = response.data['access'] + + self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + token) + response = self.client.get(self.protected_url) + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) + self.assertEqual(response.data['status'], 'request was permitted') + + def test_registration_token_invalid_after_login(self): + data = { + 'email': 'test@example.com', + 'password': 'StrongPass123!cd', + 'name': 'Digital Marketing Solutions Inc.', + } + response = self.client.post( + self.signup_url, + data, + format='json', + ) + reg_access_token = response.data['access'] + + self.client.credentials( + HTTP_AUTHORIZATION=f'Bearer {reg_access_token}', + ) + response = self.client.get(self.protected_url) + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) + + login_data = {'email': data['email'], 'password': data['password']} + response = self.client.post( + self.signin_url, + login_data, + format='json', + ) + login_access_token = response.data['access'] + + self.client.credentials( + HTTP_AUTHORIZATION=f'Bearer {reg_access_token}', + ) + response = self.client.get(self.protected_url) + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_401_UNAUTHORIZED, + ) + + self.client.credentials( + HTTP_AUTHORIZATION=f'Bearer {login_access_token}', + ) + response = self.client.get(self.protected_url) + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) diff --git a/promo_code/business/tests/auth/test_validation.py b/promo_code/business/tests/auth/test_validation.py new file mode 100644 index 0000000..e8a37af --- /dev/null +++ b/promo_code/business/tests/auth/test_validation.py @@ -0,0 +1,118 @@ +import business.models +import business.tests.auth.base +import parameterized +import rest_framework.status +import rest_framework.test + + +class InvalidCompanyRegistrationTestCase( + business.tests.auth.base.BaseBusinessAuthTestCase, +): + def test_duplicate_email_registration(self): + business.models.Company.objects.create_company( + name=self.valid_data['name'], + email=self.valid_data['email'], + password=self.valid_data['password'], + ) + self.assertTrue( + business.models.Company.objects.filter( + email=self.valid_data['email'], + ).exists(), + ) + + response = self.client.post( + self.signup_url, + self.valid_data, + format='json', + ) + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_409_CONFLICT, + ) + + @parameterized.parameterized.expand( + [ + ('short_password_1', 'easypwd'), + ('short_password_2', 'Ar1@!$'), + ('no_digits', 'PasswordWithoutDigits'), + ('no_special_chars', 'PasswordWithoutSpecial1'), + ('common_phrase', 'whereismymoney777'), + ('missing_uppercase', 'lowercase123$'), + ('missing_lowercase', 'UPPERCASE123$'), + ('non_ascii', 'Päss123$!AAd'), + ('emoji', '😎werY!!*Dj3sd'), + ], + ) + def test_invalid_password_cases(self, _, invalid_password): + test_data = {**self.valid_data, 'password': invalid_password} + response = self.client.post(self.signup_url, test_data, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_400_BAD_REQUEST, + f'Failed for password: {invalid_password}', + ) + + @parameterized.parameterized.expand( + [ + ('domain_missing_dot', 'a@b'), + ('domain_missing_dot', 'test@dom'), + ('missing_local_part', '@domain.com'), + ('missing_at_symbol', 'missing.at.sign'), + ('multiple_at_symbols', 'double@@at.com'), + ], + ) + def test_invalid_email_cases(self, _, invalid_email): + test_data = {**self.valid_data, 'email': invalid_email} + response = self.client.post(self.signup_url, test_data, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_400_BAD_REQUEST, + f'Failed for email: {invalid_email}', + ) + + def test_short_company_name(self): + test_data = {**self.valid_data, 'name': 'A'} + response = self.client.post(self.signup_url, test_data, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_400_BAD_REQUEST, + ) + + +class InvalidCompanyAuthenticationTestCase( + business.tests.auth.base.BaseBusinessAuthTestCase, +): + @parameterized.parameterized.expand( + [ + ('missing_password', {'email': 'valid@example.com'}, 'password'), + ('missing_email', {'password': 'any'}, 'email'), + ('empty_data', {}, ['email', 'password']), + ], + ) + def test_missing_required_fields(self, case_name, data, expected_fields): + response = self.client.post(self.signin_url, data, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_400_BAD_REQUEST, + ) + + def test_signin_invalid_password(self): + business.models.Company.objects.create_company( + email=self.valid_data['email'], + name=self.valid_data['name'], + password=self.valid_data['password'], + ) + + data = { + 'email': self.valid_data['email'], + 'password': 'SuperInvalidPassword2000!', + } + response = self.client.post( + self.signin_url, + data, + format='json', + ) + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_401_UNAUTHORIZED, + ) diff --git a/promo_code/user/tests/auth/base.py b/promo_code/user/tests/auth/base.py index d8d58f8..fc13f54 100644 --- a/promo_code/user/tests/auth/base.py +++ b/promo_code/user/tests/auth/base.py @@ -5,7 +5,7 @@ import user.models -class BaseAuthTestCase(rest_framework.test.APITestCase): +class BaseUserAuthTestCase(rest_framework.test.APITestCase): @classmethod def setUpTestData(cls): super().setUpTestData() diff --git a/promo_code/user/tests/auth/test_authentication.py b/promo_code/user/tests/auth/test_authentication.py index 5e71bca..e545ff7 100644 --- a/promo_code/user/tests/auth/test_authentication.py +++ b/promo_code/user/tests/auth/test_authentication.py @@ -5,7 +5,7 @@ import user.tests.auth.base -class AuthenticationTests(user.tests.auth.base.BaseAuthTestCase): +class UserAuthenticationTests(user.tests.auth.base.BaseUserAuthTestCase): def test_signin_success(self): user.models.User.objects.create_user( email='minecraft.digger@gmail.com', diff --git a/promo_code/user/tests/auth/test_registration.py b/promo_code/user/tests/auth/test_registration.py index 07783c9..1759ce2 100644 --- a/promo_code/user/tests/auth/test_registration.py +++ b/promo_code/user/tests/auth/test_registration.py @@ -5,7 +5,7 @@ import user.tests.auth.base -class RegistrationTests(user.tests.auth.base.BaseAuthTestCase): +class UserRegistrationTests(user.tests.auth.base.BaseUserAuthTestCase): def test_registration_success(self): valid_data = { 'name': 'Emma', diff --git a/promo_code/user/tests/auth/test_tokens.py b/promo_code/user/tests/auth/test_tokens.py index c4031ea..d3fb812 100644 --- a/promo_code/user/tests/auth/test_tokens.py +++ b/promo_code/user/tests/auth/test_tokens.py @@ -6,7 +6,7 @@ import user.tests.auth.base -class JWTTests(user.tests.auth.base.BaseAuthTestCase): +class JWTTests(user.tests.auth.base.BaseUserAuthTestCase): def setUp(self): super().setUp() user.models.User.objects.create_user( @@ -33,7 +33,10 @@ def test_access_protected_view_with_valid_token(self): self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + token) response = self.client.get(self.protected_url) - self.assertEqual(response.status_code, 200) + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) self.assertEqual(response.data['status'], 'request was permitted') def test_registration_token_invalid_after_login(self): @@ -55,7 +58,10 @@ def test_registration_token_invalid_after_login(self): HTTP_AUTHORIZATION=f'Bearer {reg_access_token}', ) response = self.client.get(self.protected_url) - self.assertEqual(response.status_code, 200) + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) login_data = {'email': data['email'], 'password': data['password']} response = self.client.post( @@ -69,13 +75,19 @@ def test_registration_token_invalid_after_login(self): HTTP_AUTHORIZATION=f'Bearer {reg_access_token}', ) response = self.client.get(self.protected_url) - self.assertEqual(response.status_code, 401) + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_401_UNAUTHORIZED, + ) self.client.credentials( HTTP_AUTHORIZATION=f'Bearer {login_access_token}', ) response = self.client.get(self.protected_url) - self.assertEqual(response.status_code, 200) + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) def test_refresh_token_invalidation_after_new_login(self): first_login_response = self.client.post( diff --git a/promo_code/user/tests/auth/test_validation.py b/promo_code/user/tests/auth/test_validation.py index a52f742..4f357ec 100644 --- a/promo_code/user/tests/auth/test_validation.py +++ b/promo_code/user/tests/auth/test_validation.py @@ -6,7 +6,9 @@ import user.tests.auth.base -class RegistrationTestCase(user.tests.auth.base.BaseAuthTestCase): +class InvalidUserRegistrationTestCase( + user.tests.auth.base.BaseUserAuthTestCase, +): def test_email_duplication(self): valid_data = { 'name': 'Emma', @@ -42,24 +44,6 @@ def test_email_duplication(self): rest_framework.status.HTTP_409_CONFLICT, ) - def test_invalid_email_format(self): - data = { - 'name': 'Emma', - 'surname': 'Thompson', - 'email': 'dota.fan', - 'password': 'SuperStrongPassword2000!', - 'other': {'age': 23, 'country': 'us'}, - } - response = self.client.post( - self.signup_url, - data, - format='json', - ) - self.assertEqual( - response.status_code, - rest_framework.status.HTTP_400_BAD_REQUEST, - ) - @parameterized.parameterized.expand( [ ('common_phrase', 'whereismymoney777'), @@ -292,7 +276,9 @@ def test_empty_surname_field(self): ) -class AuthenticationTestCase(user.tests.auth.base.BaseAuthTestCase): +class InvalidUserAuthenticationTestCase( + user.tests.auth.base.BaseUserAuthTestCase, +): @parameterized.parameterized.expand( [ ('missing_password', {'email': 'valid@example.com'}, 'password'),