diff --git a/.coverage b/.coverage index 54b2bef..c5c9742 100644 Binary files a/.coverage and b/.coverage differ diff --git a/account/tests.py b/account/tests.py index a58f7a3..9b8af76 100644 --- a/account/tests.py +++ b/account/tests.py @@ -20,11 +20,10 @@ class UserLoginTestCase(TestCase): :return: """ self.client = APIClient() - self.user = User.objects.create_superuser( - username='admin@example.com', - email='admin@example.com', - password='admin@1234' - ) + self.user_email = 'user@example.com' + self.user = User.objects.create_superuser(username=self.user_email, email=self.user_email) + self.user.set_password('user@1234') + self.user.save() def test_admin_login_success(self): """ @@ -33,8 +32,8 @@ class UserLoginTestCase(TestCase): """ url = reverse('account:admin-login') data = { - 'email': 'admin@example.com', - 'password': 'admin@1234', + 'email': self.user_email, + 'password': 'user@1234', } response = self.client.post(url, data, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -49,8 +48,8 @@ class UserLoginTestCase(TestCase): """ url = reverse('account:admin-login') data = { - 'email': 'admin@example.com', - 'password': 'admin@1235', + 'email': self.user_email, + 'password': 'user@1235', } response = self.client.post(url, data, format='json') self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) diff --git a/celerybeat-schedule b/celerybeat-schedule index be892ed..7638e52 100644 Binary files a/celerybeat-schedule and b/celerybeat-schedule differ diff --git a/coverage-reports/coverage.xml b/coverage-reports/coverage.xml index dbf7607..d63768a 100644 --- a/coverage-reports/coverage.xml +++ b/coverage-reports/coverage.xml @@ -1,5 +1,5 @@ - + @@ -29,7 +29,7 @@ - + @@ -509,24 +509,27 @@ - - - + + + - - - - + - - + + - + + + + + + + @@ -566,7 +569,7 @@ - + @@ -673,11 +676,11 @@ - + - + - + @@ -1421,7 +1424,7 @@ - + @@ -1867,13 +1870,13 @@ - + - - + + @@ -2108,66 +2111,67 @@ - - - - + + + + + - - + + - - - - - - + + + + + + - - + + - - + + - - + + - - - - - + + + + + - - + + - - + + - + - - - - + + + + - - + + @@ -2179,43 +2183,42 @@ - - + + - + - - + + - - + + - + - + - + - - + + - - + + - - + + - @@ -2416,13 +2419,22 @@ + + + + + + + + + - + @@ -3006,6 +3018,7 @@ + @@ -3045,7 +3058,7 @@ - + @@ -3139,401 +3152,411 @@ - - - - + + + + + - + - - - - - - - - - - - - - - + + + + + + + - + - - - - - - - + - + - - + - - - - - - + + + + + + + + + + + + - + + + - - - - + + + + - - - - + + + + + + + + + + + - - - + + + - - - - - - - - - - - - - + + + + + + - - + + + - - - - - - - - - - - - - - + + + + + + + + + + + - + + + + + - - - - - - - - - - + + + + + + + + - + + - - - + + - - - - - - - - - + + + + + + + - + - - - - - - + + + + + + + - - - - - - - - - - + + + + + + + + + + + - - - + + + - - - - - - - - - - + + + + + + + + + + + - - + + + - - - + + + - - - - + + + - - - + + - - - - - + - - - - + + + + + + + + + + - + - - - - - - + + + + + - - + - - + + - - - + + + - - - - + + + + - + + + - - - - + + + - - - - + + + - - - - - + + + + + + - - - - - + + + + + - - - - + + - - - - - - - - - + + + + + + + + + + + + + - - - - + + + - - + + - - - - + - - - + + + - - - + + + + + - + - - - - - + + - - - - + + + + + + - + + + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + @@ -3837,7 +3860,7 @@ - + @@ -3953,7 +3976,7 @@ - + @@ -4016,37 +4039,34 @@ - + - - - - - + + + + + - - - + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + @@ -4318,7 +4338,7 @@ - + @@ -4606,7 +4626,7 @@ - + @@ -4625,40 +4645,40 @@ - - - - + + + + - - + + - - - - - - - - + + + + + + + + - - - - - - + + + + + + - + - - - + + + @@ -4667,18 +4687,18 @@ - - - - + + + + - - - - + + + + - - + + @@ -4843,120 +4863,120 @@ - - - + - - - - - - - - - - - + - - - - - + - - - - + + - - + - + - - - - - - - - + + - - - - + - - + - - + - - + - - - + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -4964,27 +4984,108 @@ - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - - + + + + + + + + + + + + + + + + + + + + + + + - + @@ -5216,7 +5317,7 @@ - + @@ -5231,26 +5332,26 @@ - - - - - + + + + + - - - - + + + + - - - - - - - + + + + + + + diff --git a/notifications/tests.py b/notifications/tests.py index fd087b9..925d5a1 100644 --- a/notifications/tests.py +++ b/notifications/tests.py @@ -1,6 +1,94 @@ """ notification test file """ -from django.test import TestCase +# third party imports +from fcm_django.models import FCMDevice -# Create your tests here. +# django imports +from django.urls import reverse +from rest_framework import status + +# local imports +from account.serializers import GuardianSerializer +from notifications.models import Notification +from web_admin.tests.test_set_up import AnalyticsSetUp + + +class NotificationTestCase(AnalyticsSetUp): + """ + test notification + """ + def setUp(self) -> None: + """ + test data up + :return: + """ + super(NotificationTestCase, self).setUp() + + # notification create + self.notification = Notification.objects.create(notification_to=self.user) + + # to get guardian/user auth token + self.guardian_data = GuardianSerializer( + self.guardian, context={'user_type': 2} + ).data + self.auth_token = self.guardian_data['auth_token'] + + # api header + self.header = { + 'HTTP_AUTHORIZATION': f'Bearer {self.auth_token}', + 'Content-Type': "Application/json" + } + + def test_notification_list(self): + """ + test notification list + :return: + """ + + url = reverse('notifications:notifications-list') + response = self.client.get(url, **self.header) + self.assertEqual(response.status_code, status.HTTP_200_OK) + # Assuming only one notification exists in the database + self.assertEqual(Notification.objects.filter(notification_to=self.user).count(), 1) + + def test_fcm_register(self): + """ + test fcm register + :return: + """ + url = reverse('notifications:notifications-device') + data = { + 'registration_id': 'registration_id', + 'device_id': 'device_id', + 'type': 'ios' + } + response = self.client.post(url, data, **self.header) + self.assertEqual(response.status_code, status.HTTP_200_OK) + # device created for user + self.assertEqual(FCMDevice.objects.count(), 1) + + def test_send_test_notification(self): + """ + test send test notification + :return: + """ + url = reverse('notifications:notifications-list') + response = self.client.get(url, **self.header) + self.assertEqual(response.status_code, status.HTTP_200_OK) + # Assuming only one notification exists in the database + self.assertEqual(Notification.objects.filter(notification_to=self.user).count(), 1) + + def test_mark_as_read(self): + """ + test mark as read + :return: + """ + url = reverse('notifications:notifications-mark-as-read') + data = { + 'id': [self.notification.id] + } + response = self.client.patch(url, data, **self.header) + self.notification.refresh_from_db() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(self.notification.is_read, True) diff --git a/web_admin/tests/test_analytics.py b/web_admin/tests/test_analytics.py new file mode 100644 index 0000000..81ff9b0 --- /dev/null +++ b/web_admin/tests/test_analytics.py @@ -0,0 +1,93 @@ +""" +web admin test analytics file +""" +# django imports +from django.urls import reverse +from rest_framework import status + +# local imports +from web_admin.tests.test_set_up import AnalyticsSetUp + + +class AnalyticsViewSetTestCase(AnalyticsSetUp): + """ + test cases for analytics, users count, new sign-ups, + assign tasks report, junior leaderboard, export excel + """ + def setUp(self) -> None: + """ + test data set up + :return: + """ + super(AnalyticsViewSetTestCase, self).setUp() + + def test_total_sign_up_count(self): + """ + test total sign up count + :return: + """ + self.client.force_authenticate(self.admin_user) + url = reverse('web_admin:analytics-users-count') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + # Assuming four users exists in the database + self.assertEqual(response.data['data']['total_users'], 4) + # Assuming two guardians exists in the database + self.assertEqual(response.data['data']['total_guardians'], 2) + # Assuming two juniors exists in the database + self.assertEqual(response.data['data']['total_juniors'], 2) + + def test_new_user_sign_ups(self): + """ + test new user sign-ups + :return: + """ + self.client.force_authenticate(self.admin_user) + url = reverse('web_admin:analytics-new-signups') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + # Assuming four users exists in the database + self.assertEqual(response.data['data'][0]['signups'], 4) + + def test_assign_tasks_report(self): + """ + test assign tasks report + :return: + """ + self.client.force_authenticate(self.admin_user) + url = reverse('web_admin:analytics-assign-tasks') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + # Assuming two completed tasks exists in the database + self.assertEqual(response.data['data']['task_completed'], 2) + # Assuming two pending tasks exists in the database + self.assertEqual(response.data['data']['task_pending'], 2) + # Assuming two in progress tasks exists in the database + self.assertEqual(response.data['data']['task_in_progress'], 2) + # Assuming two requested tasks exists in the database + self.assertEqual(response.data['data']['task_requested'], 2) + # Assuming two rejected tasks exists in the database + self.assertEqual(response.data['data']['task_rejected'], 2) + # Assuming two expired tasks exists in the database + self.assertEqual(response.data['data']['task_expired'], 2) + + def test_junior_leaderboard(self): + """ + test junior leaderboard + :return: + """ + self.client.force_authenticate(self.admin_user) + url = reverse('web_admin:analytics-junior-leaderboard') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_export_excel(self): + """ + test export excel + :return: + """ + self.client.force_authenticate(self.admin_user) + url = reverse('web_admin:analytics-export-excel') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertURLEqual(response.data['data'], self.export_excel_url) diff --git a/web_admin/tests/test_article.py b/web_admin/tests/test_article.py index 728907c..f77b26b 100644 --- a/web_admin/tests/test_article.py +++ b/web_admin/tests/test_article.py @@ -36,7 +36,7 @@ class ArticleViewSetTestCase(ArticleTestSetUp): test article create with default card_image :return: """ - url = reverse('web_admin:article-list') + url = reverse(self.article_list_url) response = self.client.post(url, self.article_data_with_default_card_image, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) # Check that a new article was created @@ -48,7 +48,7 @@ class ArticleViewSetTestCase(ArticleTestSetUp): :return: """ self.client.force_authenticate(user=self.admin_user) - url = reverse('web_admin:article-list') + url = reverse(self.article_list_url) response = self.client.post(url, self.article_data_with_base64_card_image, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) # Check that a new article was created @@ -60,13 +60,13 @@ class ArticleViewSetTestCase(ArticleTestSetUp): :return: """ self.client.force_authenticate(user=self.admin_user) - url = reverse('web_admin:article-detail', kwargs={'pk': self.article.id}) + url = reverse(self.article_detail_url, kwargs={'pk': self.article.id}) response = self.client.put(url, self.article_update_data, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) self.article.refresh_from_db() self.assertEqual(self.article.title, self.article_update_data['title']) self.assertEqual(self.article.article_cards.count(), 1) - self.assertEqual(self.article.article_survey.count(), 5) + self.assertEqual(self.article.article_survey.count(), 6) self.assertEqual(self.article.article_survey.first().options.count(), 3) def test_articles_list(self): @@ -74,7 +74,7 @@ class ArticleViewSetTestCase(ArticleTestSetUp): test articles list :return: """ - url = reverse('web_admin:article-list') + url = reverse(self.article_list_url) response = self.client.get(url) self.assertEqual(response.status_code, status.HTTP_200_OK) # Assuming only one article exists in the database @@ -85,7 +85,7 @@ class ArticleViewSetTestCase(ArticleTestSetUp): test article retrieve :return: """ - url = reverse('web_admin:article-detail', kwargs={'pk': self.article.id}) + url = reverse(self.article_detail_url, kwargs={'pk': self.article.id}) response = self.client.get(url) self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -94,7 +94,7 @@ class ArticleViewSetTestCase(ArticleTestSetUp): test article delete :return: """ - url = reverse('web_admin:article-detail', kwargs={'pk': self.article.id}) + url = reverse(self.article_detail_url, kwargs={'pk': self.article.id}) response = self.client.delete(url) self.article.refresh_from_db() self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -105,7 +105,7 @@ class ArticleViewSetTestCase(ArticleTestSetUp): test article create with invalid data :return: """ - url = reverse('web_admin:article-list') + url = reverse(self.article_list_url) # Missing article_cards invalid_data = { "title": "Invalid Article", diff --git a/web_admin/tests/test_auth.py b/web_admin/tests/test_auth.py new file mode 100644 index 0000000..fa16ced --- /dev/null +++ b/web_admin/tests/test_auth.py @@ -0,0 +1,160 @@ +""" +web admin test auth file +""" +from datetime import datetime +from django.utils import timezone +from django.urls import reverse +from django.contrib.auth import get_user_model + +from rest_framework.test import APITestCase, APIClient +from rest_framework import status + +from account.models import UserEmailOtp +from base.constants import USER_TYPE +from guardian.tasks import generate_otp +from web_admin.tests.test_set_up import BaseSetUp + +User = get_user_model() + + +class AdminOTPTestCase(BaseSetUp): + """ + test case to send otp to admin email + """ + + def setUp(self): + """ + inherit data here + :return: + """ + super(AdminOTPTestCase, self).setUp() + self.url = reverse('web_admin:admin-otp') + + def test_admin_otp_for_valid_email(self): + """ + test admin otp for valid email + :return: + """ + data = { + 'email': self.admin_email + } + response = self.client.post(self.url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(UserEmailOtp.objects.count(), 1) + + def test_admin_otp_for_invalid_email(self): + """ + test admin otp for invalid email + :return: + """ + data = { + 'email': 'notadmin@example.com' + } + response = self.client.post(self.url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + +class AdminVerifyOTPTestCase(BaseSetUp): + """ + test case to verify otp for admin email + """ + + def setUp(self): + """ + inherit data here + :return: + """ + super(AdminVerifyOTPTestCase, self).setUp() + self.verification_code = generate_otp() + expiry = timezone.now() + timezone.timedelta(days=1) + self.user_email_otp = UserEmailOtp.objects.create(email=self.admin_email, + otp=self.verification_code, + expired_at=expiry, + user_type=dict(USER_TYPE).get('3'), + ) + self.url = reverse('web_admin:admin-verify-otp') + + def test_admin_verify_otp_with_valid_otp(self): + """ + test admin verify otp with valid otp + :return: + """ + + data = { + 'email': self.admin_email, + "otp": self.verification_code + } + + response = self.client.post(self.url, data) + self.user_email_otp.refresh_from_db() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(self.user_email_otp.is_verified, True) + + def test_admin_verify_otp_with_invalid_otp(self): + """ + test admin verify otp with invalid otp + :return: + """ + data = { + 'email': self.admin_email, + "otp": generate_otp() + } + + response = self.client.post(self.url, data) + self.user_email_otp.refresh_from_db() + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(self.user_email_otp.is_verified, False) + + +class AdminCreateNewPassword(BaseSetUp): + """ + test case to create new password for admin email + """ + + def setUp(self): + """ + inherit data here + :return: + """ + super(AdminCreateNewPassword, self).setUp() + self.verification_code = generate_otp() + expiry = timezone.now() + timezone.timedelta(days=1) + self.user_email_otp = UserEmailOtp.objects.create(email=self.admin_email, + otp=self.verification_code, + expired_at=expiry, + user_type=dict(USER_TYPE).get('3'), + ) + self.url = reverse('web_admin:admin-create-password') + + def test_admin_create_new_password_after_verification(self): + """ + test admin create new password + :return: + """ + self.user_email_otp.is_verified = True + self.user_email_otp.save() + + data = { + 'email': self.admin_email, + "new_password": "New@1234", + "confirm_password": "New@1234" + } + + response = self.client.post(self.url, data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(UserEmailOtp.objects.count(), 0) + + def test_admin_create_new_password_without_verification(self): + """ + test admin create new password + :return: + """ + data = { + 'email': self.admin_email, + "new_password": "Some@1234", + "confirm_password": "Some@1234" + } + + response = self.client.post(self.url, data) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(UserEmailOtp.objects.count(), 1) diff --git a/web_admin/tests/test_set_up.py b/web_admin/tests/test_set_up.py index aa6a664..df522cd 100644 --- a/web_admin/tests/test_set_up.py +++ b/web_admin/tests/test_set_up.py @@ -4,10 +4,13 @@ web_admin test set up file # django imports from django.test import TestCase from django.contrib.auth import get_user_model +from django.conf import settings from rest_framework.test import APITestCase from rest_framework.test import APIClient # local imports +from guardian.models import Guardian, JuniorTask +from junior.models import Junior, JuniorPoints from web_admin.models import Article, ArticleCard, ArticleSurvey, SurveyOption # user model @@ -102,80 +105,114 @@ base64_image = ("data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHCBIS "rOORsR8oQkxdazE9A8QostdSBuBFvlKI6PY6ibg0z4VCscJi9WK3WzRiwmRqGHY3vndi5Hnebangse2hrpSHBEAjhE6rXj" "GYdEC/arYms/HtkfAS7huhuDXXJmPEkn5whN4xrZ0NkYdPRpIPdLS0kG5QPdCEskKlYcIWHCEJUO3KFuUIQD3QhCB//Z") +export_excel_path = 'analytics/ZOD_Bank_Analytics.xlsx' +export_excel_url = f"https://{settings.ALIYUN_OSS_BUCKET_NAME}.{settings.ALIYUN_OSS_ENDPOINT}/{export_excel_path}" -class ArticleTestSetUp(APITestCase): + +class BaseSetUp(APITestCase): + """ + basic setup + """ + + def setUp(self) -> None: + """ + user data + :return: + """ + self.user_email = 'user@example.com' + self.admin_email = 'admin@example.com' + self.client = APIClient() + self.user = User.objects.create_user(username=self.user_email, email=self.user_email) + self.user.set_password('user@1234') + self.user.save() + self.admin_user = User.objects.create_user(username=self.admin_email, email=self.admin_email, + is_staff=True, is_superuser=True) + self.admin_user.set_password('admin@1234') + self.admin_user.save() + + +class ArticleTestSetUp(BaseSetUp): """ test cases data set up for article create, update """ + def setUp(self): """ set up data for test :return: """ - self.client = APIClient() - self.user = User.objects.create_user(username='user@example.com', password='user@1234') - self.admin_user = User.objects.create_user(username='admin@example.com', email='admin@example.com', - password='admin@1234', is_staff=True, is_superuser=True) + super(ArticleTestSetUp, self).setUp() self.article = Article.objects.create(title="Existing Article", description="Existing Description", is_published=True) - self.article_card = ArticleCard.objects.create(article=self.article, title="Card 1", - description="Card 1 Description") + self.article_card = ArticleCard.objects.create(article=self.article, title="Existing Card 1", + description="Existing Card 1 Description") self.article_survey = ArticleSurvey.objects.create(article=self.article, points=5, - question="Survey Question 1") - SurveyOption.objects.create(survey=self.article_survey, option="Option 1", is_answer=True) - SurveyOption.objects.create(survey=self.article_survey, option="Option 2", is_answer=False) + question="Existing Survey Question 1") + SurveyOption.objects.create(survey=self.article_survey, option="Existing Option 1", is_answer=True) + SurveyOption.objects.create(survey=self.article_survey, option="Existing Option 2", is_answer=False) + + # article api url used for get api + self.article_list_url = 'web_admin:article-list' + + # article api url used for post api + self.article_detail_url = 'web_admin:article-detail' + + # article card data with default card image + self.article_card_data_with_default_card_image = { + "title": "Card 1", + "description": "Card 1 Description", + "image_name": "card1.jpg", + "image_url": "https://example.com/card1.jpg" + } + + # article card data with base64 image + self.article_card_data_with_base64_image = { + "title": "Card base64", + "description": "Card base64 Description", + "image_name": "base64_image.jpg", + "image_url": base64_image + } + + # article survey option data + self.article_survey_option_data = [ + {"option": "Option 1", "is_answer": True}, + {"option": "Option 2", "is_answer": False} + ] + + # article survey data + self.article_survey_data = [ + { + "question": "Survey Question 1", + "options": self.article_survey_option_data + }, + { + "question": "Survey Question 2", + "options": self.article_survey_option_data + }, + { + "question": "Survey Question 3", + "options": self.article_survey_option_data + }, + { + "question": "Survey Question 4", + "options": self.article_survey_option_data + }, + { + "question": "Survey Question 5", + "options": self.article_survey_option_data + }, + ] # article data with default card image self.article_data_with_default_card_image = { "title": "Test Article", "description": "Test Description", "article_cards": [ - { - "title": "Card 1", - "description": "Card 1 Description", - "image_name": "card1.jpg", - "image_url": "https://example.com/updated_card1.jpg" - } + self.article_card_data_with_default_card_image ], # minimum 5 article survey needed - "article_survey": [ - { - "question": "Survey Question 1", - "options": [ - {"option": "Option 1", "is_answer": True}, - {"option": "Option 2", "is_answer": False} - ] - }, - { - "question": "Survey Question 2", - "options": [ - {"option": "Option 1", "is_answer": True}, - {"option": "Option 2", "is_answer": False} - ] - }, - { - "question": "Survey Question 3", - "options": [ - {"option": "Option 1", "is_answer": True}, - {"option": "Option 2", "is_answer": False} - ] - }, - { - "question": "Survey Question 4", - "options": [ - {"option": "Option 1", "is_answer": True}, - {"option": "Option 2", "is_answer": False} - ] - }, - { - "question": "Survey Question 5", - "options": [ - {"option": "Option 1", "is_answer": True}, - {"option": "Option 2", "is_answer": False} - ] - }, - ] + "article_survey": self.article_survey_data } # article data with base64 card image @@ -183,51 +220,10 @@ class ArticleTestSetUp(APITestCase): "title": "Test Article", "description": "Test Description", "article_cards": [ - { - "title": "Card 1", - "description": "Card 1 Description", - "image_name": "card1.jpg", - "image_url": base64_image - } + self.article_card_data_with_base64_image ], # minimum 5 article survey needed - "article_survey": [ - { - "question": "Survey Question 1", - "options": [ - {"option": "Option 1", "is_answer": True}, - {"option": "Option 2", "is_answer": False} - ] - }, - { - "question": "Survey Question 2", - "options": [ - {"option": "Option 1", "is_answer": True}, - {"option": "Option 2", "is_answer": False} - ] - }, - { - "question": "Survey Question 3", - "options": [ - {"option": "Option 1", "is_answer": True}, - {"option": "Option 2", "is_answer": False} - ] - }, - { - "question": "Survey Question 4", - "options": [ - {"option": "Option 1", "is_answer": True}, - {"option": "Option 2", "is_answer": False} - ] - }, - { - "question": "Survey Question 5", - "options": [ - {"option": "Option 1", "is_answer": True}, - {"option": "Option 2", "is_answer": False} - ] - } - ] + "article_survey": self.article_survey_data } # article update data @@ -247,55 +243,137 @@ class ArticleTestSetUp(APITestCase): ], # updated article survey "article_survey": [ - # updated article survey - { - "id": self.article_survey.id, - "question": "Updated Survey Question 1", - "options": [ - {"id": self.article_survey.options.first().id, - "option": "Updated Option 1", "is_answer": False}, - # New option - {"option": "Option 3", "is_answer": True} - ] - }, - # new article survey - { - "question": "Survey Question 2", - "options": [ - {"option": "Option 1", "is_answer": True}, - {"option": "Option 2", "is_answer": False} - ] - }, - # new article survey - { - "question": "Survey Question 3", - "options": [ - {"option": "Option 1", "is_answer": True}, - {"option": "Option 2", "is_answer": False} - ] - }, - # new article survey - { - "question": "Survey Question 4", - "options": [ - {"option": "Option 1", "is_answer": True}, - {"option": "Option 2", "is_answer": False} - ] - }, - # new article survey - { - "question": "Survey Question 5", - "options": [ - {"option": "Option 1", "is_answer": True}, - {"option": "Option 2", "is_answer": False} - ] - } - ] - } - # article card data with default card image - self.article_card_data_with_default_card_image = { - "title": "Card 1", - "description": "Card 1 Description", - "image_name": "card1.jpg", - "image_url": "https://example.com/card2.jpg" + # updated article survey + { + "id": self.article_survey.id, + "question": "Updated Survey Question 1", + "options": [ + {"id": self.article_survey.options.first().id, + "option": "Updated Option 1", "is_answer": False}, + # New option + {"option": "New Option 3", "is_answer": True} + ] + # added new articles + }] + self.article_survey_data } + + +class UserManagementSetUp(BaseSetUp): + """ + test cases for user management + """ + + def setUp(self) -> None: + """ + data setup + :return: + """ + super(UserManagementSetUp, self).setUp() + # guardian codes + self.guardian_code_1 = 'GRD123' + self.guardian_code_2 = 'GRD456' + + # guardian 1 + self.guardian = Guardian.objects.create(user=self.user, country_code=91, phone='8765876565', + country_name='India', gender=2, is_verified=True, + guardian_code=self.guardian_code_1) + + # user 2 + self.user_email_2 = 'user2@yopmail.com' + self.user_2 = User.objects.create_user(username=self.user_email_2, email=self.user_email_2) + self.user_2.set_password('user2@1234') + self.user_2.save() + + # guardian 2 + self.guardian_2 = Guardian.objects.create(user=self.user_2, country_code=92, phone='8765876575', + country_name='India', gender=1, is_verified=True, + guardian_code=self.guardian_code_2) + + # user 3 + self.user_email_3 = 'user3@yopmail.com' + self.user_3 = User.objects.create_user(username=self.user_email_3, email=self.user_email_3) + self.user_3.set_password('user3@1234') + self.user_3.save() + + # junior 1 + self.junior = Junior.objects.create(auth=self.user_3, country_code=91, phone='8765887643', + country_name='India', gender=2, is_verified=True, + guardian_code=[self.guardian_code_1]) + + # user 4 + self.user_email_4 = 'user4@yopmail.com' + self.user_4 = User.objects.create_user(username=self.user_email_4, email=self.user_email_4) + self.user_4.set_password('user4@1234') + self.user_4.save() + + # junior 2 + self.junior_2 = Junior.objects.create(auth=self.user_4, country_code=92, phone='8768763443', + country_name='India', gender=1, is_verified=True, + guardian_code=[self.guardian_code_2]) + + +class AnalyticsSetUp(UserManagementSetUp): + """ + test analytics + """ + def setUp(self) -> None: + """ + test data set up + :return: + """ + super(AnalyticsSetUp, self).setUp() + + # pending tasks + self.pending_task_1 = JuniorTask.objects.create(guardian=self.guardian, junior=self.junior, + task_name='Pending Task 1', task_status=1, + due_date='2024-09-12') + self.pending_task_2 = JuniorTask.objects.create(guardian=self.guardian_2, junior=self.junior_2, + task_name='Pending Task 2', task_status=1, + due_date='2024-09-12') + + # in progress tasks + self.in_progress_task_1 = JuniorTask.objects.create(guardian=self.guardian, junior=self.junior, + task_name='In progress Task 1', task_status=2, + due_date='2024-09-12') + self.in_progress_task_2 = JuniorTask.objects.create(guardian=self.guardian_2, junior=self.junior_2, + task_name='In progress Task 2', task_status=2, + due_date='2024-09-12') + + # rejected tasks + self.rejected_task_1 = JuniorTask.objects.create(guardian=self.guardian, junior=self.junior, + task_name='Rejected Task 1', task_status=3, + due_date='2024-09-12') + self.rejected_task_2 = JuniorTask.objects.create(guardian=self.guardian_2, junior=self.junior_2, + task_name='Rejected Task 2', task_status=3, + due_date='2024-09-12') + + # requested task + self.requested_task_1 = JuniorTask.objects.create(guardian=self.guardian, junior=self.junior, + task_name='Requested Task 1', task_status=4, + due_date='2024-09-12') + self.requested_task_2 = JuniorTask.objects.create(guardian=self.guardian_2, junior=self.junior_2, + task_name='Requested Task 2', task_status=4, + due_date='2024-09-12') + + # completed task + self.completed_task_1 = JuniorTask.objects.create(guardian=self.guardian, junior=self.junior, + task_name='Completed Task 1', task_status=5, + due_date='2024-09-12') + self.completed_task_2 = JuniorTask.objects.create(guardian=self.guardian_2, junior=self.junior_2, + task_name='Completed Task 2', task_status=5, + due_date='2024-09-12') + + # expired task + self.expired_task_1 = JuniorTask.objects.create(guardian=self.guardian, junior=self.junior, + task_name='Expired Task 1', task_status=6, + due_date='2024-09-11') + self.expired_task_2 = JuniorTask.objects.create(guardian=self.guardian_2, junior=self.junior_2, + task_name='Expired Task 2', task_status=6, + due_date='2024-09-11') + + # junior point table data + JuniorPoints.objects.create(junior=self.junior_2, total_points=50) + JuniorPoints.objects.create(junior=self.junior, total_points=40) + + # export excel url + self.export_excel_url = export_excel_url diff --git a/web_admin/tests/test_user_management.py b/web_admin/tests/test_user_management.py new file mode 100644 index 0000000..c2e7adb --- /dev/null +++ b/web_admin/tests/test_user_management.py @@ -0,0 +1,255 @@ +""" +web admin test user management file +""" +# django imports +from django.contrib.auth import get_user_model +from rest_framework import status + +# local imports +from base.constants import GUARDIAN, JUNIOR +from web_admin.tests.test_set_up import UserManagementSetUp + +# user model +User = get_user_model() + + +class UserManagementViewSetTestCase(UserManagementSetUp): + """ + test cases for user management + """ + def setUp(self) -> None: + super(UserManagementViewSetTestCase, self).setUp() + + self.update_data = { + 'email': 'user5@yopmail.com', + 'country_code': 93, + 'phone': '8765454235' + } + self.user_management_endpoint = "/api/v1/user-management" + + def test_user_management_list_all_users(self): + """ + test user management list all users + :return: + """ + # admin user authentication + self.client.force_authenticate(user=self.admin_user) + url = f"{self.user_management_endpoint}/" + response = self.client.get(url, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + # Assuming four user exists in the database + self.assertEqual(len(response.data['data']), 4) + + def test_user_management_list_guardians(self): + """ + test user management list guardians + :return: + """ + # admin user authentication + self.client.force_authenticate(user=self.admin_user) + url = f"{self.user_management_endpoint}/?user_type={GUARDIAN}" + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + # Assuming two guardians exists in the database + self.assertEqual(len(response.data['data']), 2) + + def test_user_management_list_juniors(self): + """ + test user management list juniors + :return: + """ + # admin user authentication + self.client.force_authenticate(user=self.admin_user) + url = f"{self.user_management_endpoint}/?user_type={JUNIOR}" + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + # Assuming two juniors exists in the database + self.assertEqual(len(response.data['data']), 2) + + def test_user_management_list_with_unauthorised_user(self): + """ + test user management list with unauthorised user + :return: + """ + # user unauthorised access + self.client.force_authenticate(user=self.user) + url = f"{self.user_management_endpoint}/" + response = self.client.get(url, format='json') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_user_management_retrieve_guardian(self): + """ + test user management retrieve guardian + :return: + """ + # admin user authentication + self.client.force_authenticate(user=self.admin_user) + url = f"{self.user_management_endpoint}/{self.user.id}/?user_type={GUARDIAN}" + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data['data']), 1) + + def test_user_management_retrieve_junior(self): + """ + test user management retrieve junior + :return: + """ + # admin user authentication + self.client.force_authenticate(user=self.admin_user) + url = f"{self.user_management_endpoint}/{self.user_3.id}/?user_type={JUNIOR}" + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data['data']), 1) + + def test_user_management_retrieve_without_user_type(self): + """ + test user management retrieve without user type + user status is mandatory + API will throw error + :return: + """ + # admin user authentication + self.client.force_authenticate(user=self.admin_user) + url = f"{self.user_management_endpoint}/{self.user.id}/" + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_user_management_update_guardian(self): + """ + test user management update guardian + :return: + """ + # admin user authentication + self.client.force_authenticate(user=self.admin_user) + url = f"{self.user_management_endpoint}/{self.user.id}/?user_type={GUARDIAN}" + response = self.client.patch(url, self.update_data, format='json',) + self.user.refresh_from_db() + self.guardian.refresh_from_db() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(self.user.email, self.update_data['email']) + self.assertEqual(self.guardian.country_code, self.update_data['country_code']) + self.assertEqual(self.guardian.phone, self.update_data['phone']) + + def test_user_management_update_guardian_with_existing_email(self): + """ + test user management update guardian with existing email + :return: + """ + # admin user authentication + self.client.force_authenticate(user=self.admin_user) + url = f"{self.user_management_endpoint}/{self.user.id}/?user_type={GUARDIAN}" + data = { + 'email': self.user_email_2 + } + response = self.client.patch(url, data, format='json',) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_user_management_update_guardian_with_existing_phone(self): + """ + test user management update guardian with existing phone + :return: + """ + # admin user authentication + self.client.force_authenticate(user=self.admin_user) + url = f"{self.user_management_endpoint}/{self.user.id}/?user_type={GUARDIAN}" + data = { + 'phone': self.guardian_2.phone + } + response = self.client.patch(url, data, format='json',) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_user_management_update_junior(self): + """ + test user management update junior + :return: + """ + # admin user authentication + self.client.force_authenticate(user=self.admin_user) + url = f"{self.user_management_endpoint}/{self.user_3.id}/?user_type={JUNIOR}" + response = self.client.patch(url, self.update_data, format='json',) + self.user_3.refresh_from_db() + self.junior.refresh_from_db() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(self.user_3.email, self.update_data['email']) + self.assertEqual(self.junior.country_code, self.update_data['country_code']) + self.assertEqual(self.junior.phone, self.update_data['phone']) + + def test_user_management_update_junior_with_existing_email(self): + """ + test user management update guardian with existing phone + :return: + """ + # admin user authentication + self.client.force_authenticate(user=self.admin_user) + url = f"{self.user_management_endpoint}/{self.user_3.id}/?user_type={JUNIOR}" + data = { + 'email': self.user_email_4 + } + response = self.client.patch(url, data, format='json',) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_user_management_update_junior_with_existing_phone(self): + """ + test user management update junior with existing phone + :return: + """ + # admin user authentication + self.client.force_authenticate(user=self.admin_user) + url = f"{self.user_management_endpoint}/{self.user_3.id}/?user_type={JUNIOR}" + data = { + 'phone': self.junior_2.phone + } + response = self.client.patch(url, data, format='json',) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_user_management_update_without_user_type(self): + """ + test user management update without user type + user status is mandatory + API will throw error + :return: + """ + # admin user authentication + self.client.force_authenticate(user=self.admin_user) + url = f"{self.user_management_endpoint}/{self.user_3.id}/" + response = self.client.patch(url, self.update_data, format='json',) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_user_management_change_status_guardian(self): + """ + test user management change status guardian + :return: + """ + # admin user authentication + self.client.force_authenticate(user=self.admin_user) + url = f"{self.user_management_endpoint}/{self.user.id}/change-status/?user_type={GUARDIAN}" + response = self.client.get(url) + self.guardian.refresh_from_db() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(self.guardian.is_active, False) + + def test_user_management_change_status_junior(self): + """ + test user management change status junior + :return: + """ + # admin user authentication + self.client.force_authenticate(user=self.admin_user) + url = f"{self.user_management_endpoint}/{self.user_3.id}/change-status/?user_type={JUNIOR}" + response = self.client.get(url) + self.junior.refresh_from_db() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(self.junior.is_active, False) + + def test_user_management_change_status_without_user_type(self): + """ + test user management change status without user type + user status is mandatory + API will throw error + :return: + """ + # admin user authentication + self.client.force_authenticate(user=self.admin_user) + url = f"{self.user_management_endpoint}/{self.user.id}/" + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) diff --git a/web_admin/urls.py b/web_admin/urls.py index 7f65fc8..4b0f480 100644 --- a/web_admin/urls.py +++ b/web_admin/urls.py @@ -18,7 +18,7 @@ router = routers.SimpleRouter() router.register('article', ArticleViewSet, basename='article') router.register('default-card-images', DefaultArticleCardImagesViewSet, basename='default-card-images') router.register('user-management', UserManagementViewSet, basename='user') -router.register('analytics', AnalyticsViewSet, basename='user-analytics') +router.register('analytics', AnalyticsViewSet, basename='analytics') router.register('article-list', ArticleListViewSet, basename='article-list') router.register('article-card-list', ArticleCardListViewSet, basename='article-card-list')