From 8016593336cee9d2675ed3ee322af98524b1526e Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 13 Apr 2025 23:35:34 +0300 Subject: [PATCH] test(business): Add tests for promo code detail endpoint. - Validate successful retrieval and update of promo codes. - Verify error responses for 400, 401, 403, and 404 scenarios. - Standardize naming conventions for all business promo code tests. --- promo_code/business/tests/promocodes/base.py | 8 + .../promocodes/operations/test_create.py | 2 +- .../promocodes/operations/test_detail.py | 279 ++++++++++++++++++ .../tests/promocodes/operations/test_list.py | 4 +- .../validations/test_create_validation.py | 2 +- .../validations/test_detail_validation.py | 245 +++++++++++++++ .../validations/test_list_validation.py | 2 +- 7 files changed, 537 insertions(+), 5 deletions(-) create mode 100644 promo_code/business/tests/promocodes/operations/test_detail.py create mode 100644 promo_code/business/tests/promocodes/validations/test_detail_validation.py diff --git a/promo_code/business/tests/promocodes/base.py b/promo_code/business/tests/promocodes/base.py index e03a996..e990287 100644 --- a/promo_code/business/tests/promocodes/base.py +++ b/promo_code/business/tests/promocodes/base.py @@ -1,5 +1,6 @@ import django.urls import rest_framework +import rest_framework.status import rest_framework.test import business.models @@ -57,6 +58,13 @@ def setUpTestData(cls): ) cls.company2_token = response2.data['access'] + @classmethod + def promo_detail_url(cls, promo_id): + return django.urls.reverse( + 'api-business:promo-detail', + kwargs={'id': promo_id}, + ) + def tearDown(self): business.models.Company.objects.all().delete() business.models.Promo.objects.all().delete() diff --git a/promo_code/business/tests/promocodes/operations/test_create.py b/promo_code/business/tests/promocodes/operations/test_create.py index 84461cc..e817950 100644 --- a/promo_code/business/tests/promocodes/operations/test_create.py +++ b/promo_code/business/tests/promocodes/operations/test_create.py @@ -4,7 +4,7 @@ import business.tests.promocodes.base -class TestSuccessfulPromoCreation( +class TestPromoCreate( business.tests.promocodes.base.BasePromoTestCase, ): def setUp(self): diff --git a/promo_code/business/tests/promocodes/operations/test_detail.py b/promo_code/business/tests/promocodes/operations/test_detail.py new file mode 100644 index 0000000..d319704 --- /dev/null +++ b/promo_code/business/tests/promocodes/operations/test_detail.py @@ -0,0 +1,279 @@ +import django.urls +import rest_framework.status +import rest_framework.test + +import business.tests.promocodes.base + + +class TestPromoDetail(business.tests.promocodes.base.BasePromoTestCase): + @classmethod + def setUpTestData(cls): + super().setUpTestData() + client = rest_framework.test.APIClient() + + client.credentials(HTTP_AUTHORIZATION='Bearer ' + cls.company1_token) + promo1_data = { + 'description': 'Increased 10% cashback for new bank clients!', + 'image_url': 'https://cdn2.thecatapi.com/images/3lo.jpg', + 'target': {}, + 'max_count': 10, + 'active_from': '2025-01-10', + 'mode': 'COMMON', + 'promo_common': 'sale-10', + } + response1 = client.post( + cls.promo_create_url, + promo1_data, + format='json', + ) + cls.promo1_id = response1.data['id'] + + client.credentials(HTTP_AUTHORIZATION='Bearer ' + cls.company2_token) + promo2_data = { + 'description': 'We gift a globe when order exceeds 30000!', + 'target': {'age_from': 28, 'age_until': 50, 'country': 'us'}, + 'max_count': 1, + 'active_until': '2025-01-10', + 'mode': 'UNIQUE', + 'promo_unique': ['only_youuuu', 'not_only_you'], + } + response2 = client.post( + cls.promo_create_url, + promo2_data, + format='json', + ) + cls.promo2_id = response2.data['id'] + + def test_get_promo_company1(self): + + promo_detail_url = django.urls.reverse( + 'api-business:promo-detail', + kwargs={'id': self.__class__.promo1_id}, + ) + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company1_token, + ) + response = self.client.get(promo_detail_url) + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) + expected = { + 'description': 'Increased 10% cashback for new bank clients!', + 'image_url': 'https://cdn2.thecatapi.com/images/3lo.jpg', + 'target': {}, + 'max_count': 10, + 'active_from': '2025-01-10', + 'mode': 'COMMON', + 'promo_common': 'sale-10', + 'promo_id': str(self.promo1_id), + 'company_name': self.company1_data['name'], + 'like_count': 0, + 'used_count': 0, + } + for key, value in expected.items(): + self.assertEqual(response.data.get(key), value) + + def test_get_promo_company2(self): + promo_detail_url = self.promo_detail_url(self.__class__.promo2_id) + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company2_token, + ) + response = self.client.get(promo_detail_url) + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) + expected = { + 'description': 'We gift a globe when order exceeds 30000!', + 'target': {'age_from': 28, 'age_until': 50, 'country': 'us'}, + 'max_count': 1, + 'active_until': '2025-01-10', + 'mode': 'UNIQUE', + 'promo_unique': ['only_youuuu', 'not_only_you'], + 'promo_id': str(self.promo2_id), + 'company_name': self.company2_data['name'], + 'like_count': 0, + 'used_count': 0, + } + for key, value in expected.items(): + self.assertEqual(response.data.get(key), value) + + def test_patch_description_image_company1(self): + promo_detail_url = self.promo_detail_url(self.__class__.promo1_id) + data = { + 'description': '100% Cashback', + 'image_url': 'https://doesitexist.com/', + } + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company1_token, + ) + response = self.client.patch(promo_detail_url, data, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) + self.assertEqual(response.data.get('description'), '100% Cashback') + self.assertEqual( + response.data.get('image_url'), + 'https://doesitexist.com/', + ) + self.assertEqual(response.data.get('target'), {}) + + def test_patch_active_from_company1(self): + promo_detail_url = self.promo_detail_url(self.__class__.promo1_id) + data = {'active_from': '2023-12-20'} + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company1_token, + ) + response = self.client.patch(promo_detail_url, data, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) + self.assertEqual(response.data.get('active_from'), '2023-12-20') + + def test_patch_partial_target_company1(self): + promo_detail_url = self.promo_detail_url(self.__class__.promo1_id) + data = {'target': {'country': 'fr', 'age_from': 28}} + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company1_token, + ) + response = self.client.patch(promo_detail_url, data, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) + self.assertEqual( + response.data.get('target'), + {'country': 'fr', 'age_from': 28}, + ) + + def test_patch_replace_target_company1(self): + promo_detail_url = self.promo_detail_url(self.__class__.promo1_id) + data = { + 'target': {'country': 'Us', 'categories': ['ios', 'footballer']}, + } + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company1_token, + ) + response = self.client.patch(promo_detail_url, data, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) + self.assertEqual( + response.data.get('target'), + {'country': 'Us', 'categories': ['ios', 'footballer']}, + ) + + def test_patch_active_until_company1(self): + promo_detail_url = self.promo_detail_url(self.__class__.promo1_id) + data = { + 'active_until': '2050-10-08', + 'target': {'country': 'Us', 'categories': ['ios', 'footballer']}, + } + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company1_token, + ) + response = self.client.patch(promo_detail_url, data, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) + self.assertEqual( + response.data.get('target'), + {'country': 'Us', 'categories': ['ios', 'footballer']}, + ) + self.assertEqual(response.data.get('active_until'), '2050-10-08') + + def test_patch_clear_target_company1(self): + promo_detail_url = self.promo_detail_url(self.__class__.promo1_id) + data = {'target': {}} + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company1_token, + ) + response = self.client.patch(promo_detail_url, data, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) + self.assertEqual(response.data.get('target'), {}) + + def test_patch_increase_max_count_company1(self): + promo_detail_url = self.promo_detail_url(self.__class__.promo1_id) + data = {'max_count': 20} + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company1_token, + ) + response = self.client.patch(promo_detail_url, data, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) + self.assertEqual(response.data.get('max_count'), 20) + + def test_patch_decrease_max_count_company1(self): + promo_detail_url = self.promo_detail_url(self.__class__.promo1_id) + data = {'max_count': 4} + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company1_token, + ) + response = self.client.patch(promo_detail_url, data, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) + self.assertEqual(response.data.get('max_count'), 4) + + def test_final_get_promo_company1(self): + promo_detail_url = self.promo_detail_url(self.__class__.promo1_id) + data = { + 'description': '100% Cashback', + 'image_url': 'https://doesitexist.com/', + 'active_from': '2023-12-20', + 'active_until': '2050-10-08', + 'target': {}, + 'max_count': 4, + } + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company1_token, + ) + response = self.client.patch(promo_detail_url, data, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) + + response = self.client.get(self.promo_list_url) + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) + data = response.data + + promo_detail_url = django.urls.reverse( + 'api-business:promo-detail', + kwargs={'id': self.__class__.promo1_id}, + ) + + response = self.client.get(promo_detail_url) + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) + expected = { + 'description': '100% Cashback', + 'image_url': 'https://doesitexist.com/', + 'target': {}, + 'max_count': 4, + 'active_from': '2023-12-20', + 'mode': 'COMMON', + 'promo_common': 'sale-10', + 'promo_id': str(self.__class__.promo1_id), + 'company_name': self.company1_data['name'], + 'like_count': 0, + 'used_count': 0, + } + for key, value in expected.items(): + self.assertEqual(response.data.get(key), value) diff --git a/promo_code/business/tests/promocodes/operations/test_list.py b/promo_code/business/tests/promocodes/operations/test_list.py index cf919c2..540b68e 100644 --- a/promo_code/business/tests/promocodes/operations/test_list.py +++ b/promo_code/business/tests/promocodes/operations/test_list.py @@ -6,7 +6,7 @@ import business.tests.promocodes.base -class TestPromoEndpoint( +class TestPromoList( business.tests.promocodes.base.BasePromoTestCase, ): def _create_additional_promo(self): @@ -30,7 +30,7 @@ def _create_additional_promo(self): @classmethod def setUpTestData(cls): - business.tests.promocodes.base.BasePromoCreateTestCase.setUpTestData() + business.tests.promocodes.base.BasePromoTestCase.setUpTestData() cls.promo1_data = { 'description': 'Increased cashback 10% for new bank customers!', diff --git a/promo_code/business/tests/promocodes/validations/test_create_validation.py b/promo_code/business/tests/promocodes/validations/test_create_validation.py index dcdd0ce..4e47934 100644 --- a/promo_code/business/tests/promocodes/validations/test_create_validation.py +++ b/promo_code/business/tests/promocodes/validations/test_create_validation.py @@ -4,7 +4,7 @@ import business.tests.promocodes.base -class TestPromoCodeCreation( +class TestPromoCreate( business.tests.promocodes.base.BasePromoTestCase, ): diff --git a/promo_code/business/tests/promocodes/validations/test_detail_validation.py b/promo_code/business/tests/promocodes/validations/test_detail_validation.py new file mode 100644 index 0000000..94e4485 --- /dev/null +++ b/promo_code/business/tests/promocodes/validations/test_detail_validation.py @@ -0,0 +1,245 @@ +import parameterized +import rest_framework.status + +import business.tests.promocodes.base + + +class TestPromoDetail(business.tests.promocodes.base.BasePromoTestCase): + + @classmethod + def setUpClass(cls): + super().setUpClass() + + cls.common_payload = { + 'description': 'Complimentary Auto Giveaway', + 'target': {}, + 'max_count': 10, + 'mode': 'COMMON', + 'promo_common': 'sale-10', + } + cls.unique_payload = { + 'description': 'Complimentary Pudge Skin on Registration!', + 'target': {}, + 'max_count': 1, + 'mode': 'UNIQUE', + 'active_from': '2030-08-08', + 'promo_unique': ['dota-arena', 'coda-core', 'warcraft3'], + } + + def create_promo(self, token, payload): + self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + token) + response = self.client.post( + self.promo_create_url, + payload, + format='json', + ) + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_201_CREATED, + ) + return response.data['id'] + + def test_create_promo_company1(self): + promo_id = self.create_promo(self.company1_token, self.common_payload) + self.assertTrue(promo_id) + + def test_create_promo_company2(self): + promo_id = self.create_promo(self.company2_token, self.unique_payload) + self.assertTrue(promo_id) + + def test_old_token_invalid_after_reauthentication(self): + promo_id = self.create_promo(self.company1_token, self.common_payload) + old_token = self.company1_token + + self.client.credentials() + signin_payload = { + 'email': self.company1_data['email'], + 'password': self.company1_data['password'], + } + response = self.client.post( + self.signin_url, + signin_payload, + format='json', + ) + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) + self.company1_token = response.data['access'] + + self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + old_token) + url = self.promo_detail_url(promo_id) + response = self.client.get(url, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_401_UNAUTHORIZED, + ) + + def test_edit_nonexistent_promo(self): + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company1_token, + ) + non_existent_uuid = '550e8400-e29b-41d4-a716-446655440000' + url = self.promo_detail_url(non_existent_uuid) + patch_payload = {'description': '100% Cashback'} + response = self.client.patch(url, patch_payload, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_404_NOT_FOUND, + ) + + def test_edit_foreign_promo(self): + promo_id = self.create_promo(self.company2_token, self.unique_payload) + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company1_token, + ) + url = self.promo_detail_url(promo_id) + patch_payload = {'description': '100% Cashback'} + response = self.client.patch(url, patch_payload, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_403_FORBIDDEN, + ) + + def test_edit_promo_without_authentication(self): + promo_id = self.create_promo(self.company2_token, self.unique_payload) + url = self.promo_detail_url(promo_id) + patch_payload = {'description': '100% Cashback'} + self.client.credentials() + response = self.client.patch(url, patch_payload, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_401_UNAUTHORIZED, + ) + + def test_edit_invalid_short_description(self): + promo_id = self.create_promo(self.company2_token, self.unique_payload) + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company2_token, + ) + url = self.promo_detail_url(promo_id) + patch_payload = {'description': 'qqall'} + response = self.client.patch(url, patch_payload, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_400_BAD_REQUEST, + ) + + @parameterized.parameterized.expand( + [ + ( + 'non_string', + {'description': 'Bonus 10000%!', 'image_url': False}, + ), + ( + 'incomplete_url', + {'description': 'Bonus 10000%!', 'image_url': 'https://'}, + ), + ( + 'malformed_url', + {'description': 'Bonus 10000%!', 'image_url': 'notalink'}, + ), + ('incorrect_type', {'image_url': 'jpeg'}), + ], + ) + def test_edit_invalid_image_urls(self, _, patch_payload): + promo_id = self.create_promo(self.company2_token, self.unique_payload) + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company2_token, + ) + url = self.promo_detail_url(promo_id) + response = self.client.patch(url, patch_payload, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_400_BAD_REQUEST, + ) + + @parameterized.parameterized.expand( + [ + ( + 'age_range_invalid', + { + 'description': 'Bonus 10000%!', + 'target': {'age_from': 19, 'age_until': 17}, + }, + ), + ( + 'incomplete_target', + {'description': 'Bonus 10000%!', 'target': {'country': 'USA'}}, + ), + ( + 'empty_category', + { + 'description': 'Bonus 10000%!', + 'target': {'categories': ['']}, + }, + ), + ], + ) + def test_edit_invalid_target(self, _, patch_payload): + promo_id = self.create_promo(self.company2_token, self.unique_payload) + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company2_token, + ) + url = self.promo_detail_url(promo_id) + response = self.client.patch(url, patch_payload, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_400_BAD_REQUEST, + ) + + def test_edit_invalid_max_count_for_unique(self): + promo_id = self.create_promo(self.company2_token, self.unique_payload) + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company2_token, + ) + url = self.promo_detail_url(promo_id) + patch_payload = { + 'max_count': 10, + } + response = self.client.patch(url, patch_payload, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_400_BAD_REQUEST, + ) + + def test_edit_negative_max_count(self): + promo_id = self.create_promo(self.company1_token, self.common_payload) + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company1_token, + ) + url = self.promo_detail_url(promo_id) + patch_payload = {'max_count': -10} + response = self.client.patch(url, patch_payload, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_400_BAD_REQUEST, + ) + + def test_edit_invalid_active_from(self): + promo_id = self.create_promo(self.company2_token, self.unique_payload) + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company2_token, + ) + url = self.promo_detail_url(promo_id) + patch_payload = {'active_from': '2024-12-28 12:00:00'} + response = self.client.patch(url, patch_payload, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_400_BAD_REQUEST, + ) + + def test_get_promo_verify_fields_unchanged(self): + promo_id = self.create_promo(self.company2_token, self.unique_payload) + self.client.credentials( + HTTP_AUTHORIZATION='Bearer ' + self.company2_token, + ) + url = self.promo_detail_url(promo_id) + response = self.client.get(url, format='json') + self.assertEqual( + response.status_code, + rest_framework.status.HTTP_200_OK, + ) + expected_data = self.unique_payload.copy() + for key, expected in expected_data.items(): + self.assertEqual(response.data.get(key), expected) diff --git a/promo_code/business/tests/promocodes/validations/test_list_validation.py b/promo_code/business/tests/promocodes/validations/test_list_validation.py index b0dc43b..bd0320f 100644 --- a/promo_code/business/tests/promocodes/validations/test_list_validation.py +++ b/promo_code/business/tests/promocodes/validations/test_list_validation.py @@ -5,7 +5,7 @@ import business.tests.promocodes.base -class TestPromoCodeList( +class TestPromoList( business.tests.promocodes.base.BasePromoTestCase, ): def setUp(self):