From 03c75cdacb17d5ab7bcce589f66ae5a1c2fff006 Mon Sep 17 00:00:00 2001 From: mehmeteminipekdal Date: Tue, 19 Nov 2024 05:08:02 +0300 Subject: [PATCH 01/14] CommentModelTest, QuestionModelTest and VoteModelTest tests are added to test.py and for our questionmodeltest, __str__ method is added to models.py for django to use title of question while converting question object to a string. --- .gitignore | 3 +- django_project_491/django_app/models.py | 3 + django_project_491/django_app/test.py | 179 +++++++++++++++++++++++- 3 files changed, 183 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 7433049c..2b08267c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ keygen-apk/ buroute-web/web_env wiki_database_API/db_env koduyorum-web/node_modules -.venv \ No newline at end of file +.venv +venv \ No newline at end of file diff --git a/django_project_491/django_app/models.py b/django_project_491/django_app/models.py index a3eb756b..a79bb34d 100644 --- a/django_project_491/django_app/models.py +++ b/django_project_491/django_app/models.py @@ -81,6 +81,9 @@ class Question(models.Model): author = models.ForeignKey('User', on_delete=models.CASCADE, related_name='questions') + def __str__(self): + return self.title + def run_snippet(self): # TODO result = run_code(self.code_snippet, self.language_id) outs = result['stdout'].split('\n') diff --git a/django_project_491/django_app/test.py b/django_project_491/django_app/test.py index 2afd564e..3306ef82 100644 --- a/django_project_491/django_app/test.py +++ b/django_project_491/django_app/test.py @@ -2,7 +2,9 @@ from django.test import TestCase from .Utils.utils import run_code from .views.utilization_views import wiki_result, wiki_search -from .models import User +from django.utils import timezone +from .models import User, Comment, Question, Comment_Vote, Question_Vote, UserType, VoteType + from django.urls import reverse # class TestRunCode(TestCase): @@ -121,3 +123,178 @@ def test_post_sample_code(self): response_json = response.json() self.assertIn('stdout', response_json) self.assertTrue(response_json['stdout'].startswith('Hello, World!')) + +class CommentModelTest(TestCase): + def setUp(self): + # Create a test user + self.user = User.objects.create_user( + username='testuser', + email='test@example.com', + password='testpassword' + ) + + # Create a test question + self.question = Question.objects.create( + title='Sample Question', + language='Python', + details='How to test models in Django?', + code_snippet='print("Test")', + author=self.user + ) + + # Create a test comment + self.comment = Comment.objects.create( + details='This is a test comment', + code_snippet='print("Test comment")', + language_id=71, + question=self.question, + author=self.user + ) + + def test_comment_creation(self): + # Test if comment is created successfully + self.assertEqual(self.comment.details, 'This is a test comment') + self.assertEqual(self.comment.language_id, 71) + self.assertEqual(self.comment.question.title, 'Sample Question') + self.assertEqual(self.comment.author.username, 'testuser') + + def test_run_snippet(self): + # Test if the `run_snippet` method returns correct output + result = self.comment.run_snippet() + self.assertIn('Test comment', result) # Ensure the output matches the snippet's print statement + +class QuestionModelTest(TestCase): + def setUp(self): + # Create a test user + self.user = User.objects.create_user( + username='testuser', + email='test@example.com', + password='testpassword' + ) + + # Create a test question + self.question = Question.objects.create( + title='Sample Question', + language='Python', + language_id=71, # Language ID for Python + details='How to test models in Django?', + code_snippet='print("Test")', + tags=['Django', 'Testing'], + topic='Django Testing', + author=self.user + ) + + def test_question_creation(self): + # Test if question is created successfully + self.assertEqual(self.question.title, 'Sample Question') + self.assertEqual(self.question.language, 'Python') + self.assertEqual(self.question.language_id, 71) + self.assertEqual(self.question.details, 'How to test models in Django?') + self.assertEqual(self.question.code_snippet, 'print("Test")') + self.assertEqual(self.question.tags, ['Django', 'Testing']) + self.assertEqual(self.question.topic, 'Django Testing') + self.assertEqual(self.question.author.username, 'testuser') + + def test_question_snippet_execution(self): + # Test if the `run_snippet` method works + result = self.question.run_snippet() + self.assertIn('Test', result) # Ensure the output matches the snippet's print statement + + def test_mark_as_answered(self): + # Test if the `mark_as_answered` method works + self.question.mark_as_answered() + self.question.refresh_from_db() # Refresh the instance + self.assertTrue(self.question.answered) + + def test_question_upvotes(self): + # Test the upvotes field + self.assertEqual(self.question.upvotes, 0) # Initially, upvotes should be 0 + self.question.upvotes += 1 + self.question.save() + self.assertEqual(self.question.upvotes, 1) # After incrementing, upvotes should be 1 + + def test_reported_by_relationship(self): + # Test the ManyToMany relationship with `reported_by` + self.assertEqual(self.question.reported_by.count(), 0) # Initially, no reports + self.question.reported_by.add(self.user) + self.question.save() + self.assertEqual(self.question.reported_by.count(), 1) # After adding, it should be 1 + self.assertEqual(self.question.reported_by.first().username, 'testuser') # Ensure the user is added + + def test_question_str_method(self): + # Test the string representation of the question + question_str = str(self.question) + self.assertIn(self.question.title, question_str) # The title should be in the string representation + + def test_question_created_at(self): + # Test if the `created_at` field is automatically set + self.assertTrue(self.question.created_at) + self.assertTrue(isinstance(self.question.created_at, timezone.datetime)) + + def test_question_topic(self): + # Test if the `topic` field is correctly saved + self.assertEqual(self.question.topic, 'Django Testing') + + def test_question_tags(self): + # Test if the `tags` field is correctly saved + self.assertEqual(self.question.tags, ['Django', 'Testing']) + + + +class VoteModelTest(TestCase): + def setUp(self): + # Create a test user + self.user = User.objects.create_user( + username='testuser', + email='test@example.com', + password='testpassword' + ) + + # Create a test question + self.question = Question.objects.create( + title='Sample Question', + language='Python', + details='How to test models in Django?', + code_snippet='print("Test")', + author=self.user + ) + + # Create a test comment + self.comment = Comment.objects.create( + details='This is a test comment', + code_snippet='print("Test comment")', + language_id=71, + question=self.question, + author=self.user + ) + + # Create a test vote + self.vote = Question_Vote.objects.create( + vote_type=VoteType.UPVOTE.value, + user=self.user, + question=self.question + ) + + def test_question_vote_creation(self): + # Test if the vote is created correctly + self.assertEqual(self.vote.vote_type, 'upvote') + self.assertEqual(self.vote.user.username, 'testuser') + self.assertEqual(self.vote.question.title, 'Sample Question') + + def test_comment_vote_creation(self): + # Test if the comment vote is created correctly + comment_vote = Comment_Vote.objects.create( + vote_type=VoteType.DOWNVOTE.value, + user=self.user, + comment=self.comment + ) + self.assertEqual(comment_vote.vote_type, 'downvote') + self.assertEqual(comment_vote.user.username, 'testuser') + self.assertEqual(comment_vote.comment.details, 'This is a test comment') + + def test_vote_str_method(self): + # Test the string representation of the vote + vote_str = str(self.vote) + self.assertIn(self.user.username, vote_str) + self.assertIn(self.vote.vote_type, vote_str) + self.assertIn(self.question.title, vote_str) From 4caf7d70fb6f3c993788a23c8b2cb3e9142955b7 Mon Sep 17 00:00:00 2001 From: mehmeteminipekdal Date: Tue, 19 Nov 2024 08:29:44 +0300 Subject: [PATCH 02/14] user_profile_by_id is added to urls --- django_project_491/django_app/urls.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/django_project_491/django_app/urls.py b/django_project_491/django_app/urls.py index b7d37c10..5aa28030 100644 --- a/django_project_491/django_app/urls.py +++ b/django_project_491/django_app/urls.py @@ -13,6 +13,7 @@ path('login/', user_views.login_user, name='login'), path('signup/', user_views.signup, name='signup'), + path('user_profile_by_id//', user_views.get_user_profile_by_id, name='user_profile_by_id'), path('get_user_profile_by_username//', user_views.get_user_profile_by_username, name='get_user_profile'), path('edit_user_profile//', user_views.edit_user_profile, name='edit_user_profile'), path('delete_user_profile//', user_views.delete_user_profile, name='delete_user_profile'), @@ -21,6 +22,7 @@ path('upload-profile-pic/', user_views.upload_profile_pic, name='upload_profile_pic'), path('reset_password/', user_views.reset_password_request, name='reset_password'), path('reset_password///', user_views.reset_password_view, name='reset_password'), + path('interested_languages/', user_views.add_interested_languages_for_a_user, name='interested_languages'), path('get_top_five_contributors/', user_views.list_most_contributed_five_person, name='get_top_five_contributors'), @@ -52,11 +54,10 @@ path('run_code//', utilization_views.run_code_of_question_or_comment, name='run_code'), - path('list_questions/', question_views.list_questions_by_language, name='list_questions'), # path('run_code/', utilization_views.run_code_view, name='run_code'), path('code_execute/', utilization_views.post_sample_code, name='code_execute'), - path('interested_languages/', user_views.add_interested_languages_for_a_user, name='interested_languages'), + path('specific_feed//', question_views.list_questions_according_to_the_user, name='specific_feed'), path('question_of_the_day/', question_views.question_of_the_day, name='question_of_the_day'), path('daily_question/', question_views.question_of_the_day, name='wiki_search'), From 6a5aaf8497c55d190ea997457ae7049be6408045 Mon Sep 17 00:00:00 2001 From: mehmeteminipekdal Date: Tue, 19 Nov 2024 08:40:55 +0300 Subject: [PATCH 03/14] url name changed --- django_project_491/django_app/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_project_491/django_app/urls.py b/django_project_491/django_app/urls.py index 5aa28030..d6d067b9 100644 --- a/django_project_491/django_app/urls.py +++ b/django_project_491/django_app/urls.py @@ -14,7 +14,7 @@ path('login/', user_views.login_user, name='login'), path('signup/', user_views.signup, name='signup'), path('user_profile_by_id//', user_views.get_user_profile_by_id, name='user_profile_by_id'), - path('get_user_profile_by_username//', user_views.get_user_profile_by_username, name='get_user_profile'), + path('get_user_profile_by_username//', user_views.get_user_profile_by_username, name='get_user_profile_by_username'), path('edit_user_profile//', user_views.edit_user_profile, name='edit_user_profile'), path('delete_user_profile//', user_views.delete_user_profile, name='delete_user_profile'), path('logout/', user_views.logout_user, name='logout'), From b1494a760d5cccbacfe6e5f8207bf68bbbc840cf Mon Sep 17 00:00:00 2001 From: mehmeteminipekdal Date: Tue, 19 Nov 2024 08:46:46 +0300 Subject: [PATCH 04/14] In order to maintain further tests for user_views, two extra fields are added. If the changes will cause a major effect; models.py and two migrations added will be reverted. --- .../migrations/0006_user_is_superuser.py | 18 ++ .../0007_user_is_active_user_is_staff.py | 23 +++ django_project_491/django_app/models.py | 35 ++-- django_project_491/django_app/test.py | 189 +++++++++++++++++- 4 files changed, 244 insertions(+), 21 deletions(-) create mode 100644 django_project_491/django_app/migrations/0006_user_is_superuser.py create mode 100644 django_project_491/django_app/migrations/0007_user_is_active_user_is_staff.py diff --git a/django_project_491/django_app/migrations/0006_user_is_superuser.py b/django_project_491/django_app/migrations/0006_user_is_superuser.py new file mode 100644 index 00000000..7dbcad90 --- /dev/null +++ b/django_project_491/django_app/migrations/0006_user_is_superuser.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.2 on 2024-11-19 03:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_app', '0005_comment_language'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='is_superuser', + field=models.BooleanField(default=False), + ), + ] diff --git a/django_project_491/django_app/migrations/0007_user_is_active_user_is_staff.py b/django_project_491/django_app/migrations/0007_user_is_active_user_is_staff.py new file mode 100644 index 00000000..cff65833 --- /dev/null +++ b/django_project_491/django_app/migrations/0007_user_is_active_user_is_staff.py @@ -0,0 +1,23 @@ +# Generated by Django 5.1.2 on 2024-11-19 04:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_app', '0006_user_is_superuser'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='is_active', + field=models.BooleanField(default=True), + ), + migrations.AddField( + model_name='user', + name='is_staff', + field=models.BooleanField(default=False), + ), + ] diff --git a/django_project_491/django_app/models.py b/django_project_491/django_app/models.py index a79bb34d..bb6fc9dd 100644 --- a/django_project_491/django_app/models.py +++ b/django_project_491/django_app/models.py @@ -101,7 +101,7 @@ def save(self, *args, **kwargs): class UserManager(BaseUserManager): - def create_user(self, username, email, password=None, userType: UserType = UserType.USER): + def create_user(self, username, email, password=None, userType: UserType = UserType.USER, **extra_fields): if not email: raise ValueError("Users must have an email address") if not username: @@ -109,22 +109,16 @@ def create_user(self, username, email, password=None, userType: UserType = UserT if not password: raise ValueError("Users must have a password") - user: User = self.model( - email=self.normalize_email(email), - username=username, - userType=userType - ) + email = self.normalize_email(email) + user = self.model(username=username, email=email, userType=userType, **extra_fields) user.set_password(password) user.save(using=self._db) return user - def create_superuser(self, username: str, email: str, password: str = None): - return self.create_user( - username=username, - email=email, - password=password, - userType=UserType.ADMIN - ) + def create_superuser(self, username: str, email: str, password: str = None, userType: UserType = UserType.ADMIN, **extra_fields): + extra_fields.setdefault('is_staff', True) + extra_fields.setdefault('is_superuser', True) # Ensure is_superuser is set + return self.create_user(username, email, password, userType=userType, **extra_fields) class User(AbstractBaseUser): @@ -144,10 +138,14 @@ class User(AbstractBaseUser): # Relationships bookmarks = models.ManyToManyField('Question', related_name='bookmarked_by', blank=True) + is_active = models.BooleanField(default=True) + is_staff = models.BooleanField(default=False) + is_superuser = models.BooleanField(default=False) + objects = UserManager() - USERNAME_FIELD = 'username' - REQUIRED_FIELDS = ['email'] + USERNAME_FIELD = 'email' + REQUIRED_FIELDS = ['username', 'userType'] def __str__(self): return self.username @@ -162,10 +160,6 @@ def has_module_perms(self, app_label): # 2/3 added because of my custom userTyp return True return False - @property - def is_staff(self): # 3/3 added because of my custom userType - return self.userType == UserType.ADMIN - def get_question_details(self): return [{ 'id': question._id, @@ -226,4 +220,5 @@ def check_and_promote(self): self.userType = UserType.USER self.save() - return self.userType \ No newline at end of file + return self.userType + \ No newline at end of file diff --git a/django_project_491/django_app/test.py b/django_project_491/django_app/test.py index 3306ef82..1074593e 100644 --- a/django_project_491/django_app/test.py +++ b/django_project_491/django_app/test.py @@ -1,9 +1,15 @@ import json -from django.test import TestCase +from django.test import TestCase, Client from .Utils.utils import run_code from .views.utilization_views import wiki_result, wiki_search from django.utils import timezone from .models import User, Comment, Question, Comment_Vote, Question_Vote, UserType, VoteType +from datetime import timedelta +from rest_framework.test import APITestCase +from rest_framework import status +from django.contrib.auth import get_user_model +from rest_framework_simplejwt.tokens import RefreshToken + from django.urls import reverse @@ -298,3 +304,184 @@ def test_vote_str_method(self): self.assertIn(self.user.username, vote_str) self.assertIn(self.vote.vote_type, vote_str) self.assertIn(self.question.title, vote_str) + +class UserModelTest(TestCase): + + def setUp(self): + self.username = 'testuser' + self.password = 'testpassword123' + self.email = 'testuser@example.com' + self.user = get_user_model().objects.create_user( + username=self.username, + email=self.email, + password=self.password + ) + + def test_user_creation(self): + """Test that the user is created correctly.""" + user = self.user + self.assertEqual(user.username, self.username) + self.assertEqual(user.email, self.email) + self.assertTrue(user.check_password(self.password)) + self.assertFalse(user.is_superuser) + self.assertFalse(user.is_staff) + + def test_user_type_default(self): + """Test that the default user type is 'USER'.""" + user = self.user + self.assertEqual(user.userType, UserType.USER) # Compare enum members + + def test_user_str_method(self): + """Test the __str__ method of the User model.""" + user = self.user + self.assertEqual(str(user), self.username) + + def test_check_and_promote(self): + """Test that the user promotion logic works.""" + user = self.user + # Before promotion + self.assertEqual(user.userType, UserType.USER) # Compare enum members + + # Simulate enough points for promotion + user.calculate_total_points = lambda: 150 # Simulate enough points + user.check_and_promote() + self.assertEqual(user.userType, UserType.SUPER_USER) # Compare enum members + + # Simulate losing points for demotion + user.calculate_total_points = lambda: 50 # Simulate low points + user.check_and_promote() + self.assertEqual(user.userType, UserType.USER) # Compare enum members + +class UserViewsTest(TestCase): + + def setUp(self): + self.username = 'testuser' + self.password = 'testpassword123' + self.email = 'testuser@example.com' + self.user = get_user_model().objects.create_user( + username=self.username, + email=self.email, + password=self.password + ) + self.admin = get_user_model().objects.create_superuser( + username='admin', + email='admin@example.com', + password='adminpassword123' + ) + self.client = self.client # Django's test client for making requests + + def test_get_user_profile_by_username(self): + """Test retrieving user profile by username.""" + url = reverse('get_user_profile_by_username', args=[self.username]) + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIn('user', response.json()) + self.assertEqual(response.json()['user']['username'], self.username) + + def test_get_user_profile_by_id(self): + """Test retrieving user profile by user ID.""" + url = reverse('user_profile_by_id', args=[self.user.pk]) + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.json()['user']['username'], self.username) + +# def test_edit_user_profile(self): +# """Test editing user profile.""" +# url = reverse('edit_user_profile', args=[self.user.pk]) +# self.client.login(username=self.username, password=self.password) +# data = { +# 'username': 'newusername', +# 'email': 'newemail@example.com', +# 'bio': 'Updated bio' +# } +# response = self.client.post(url, json.dumps(data), content_type="application/json") +# self.assertEqual(response.status_code, status.HTTP_200_OK) +# self.user.refresh_from_db() +# self.assertEqual(self.user.username, 'newusername') +# self.assertEqual(self.user.email, 'newemail@example.com') + +# def test_delete_user_profile(self): +# """Test deleting user profile.""" +# url = reverse('delete_user_profile', args=[self.user.pk]) +# self.client.login(username=self.username, password=self.password) +# response = self.client.delete(url) +# self.assertEqual(response.status_code, status.HTTP_200_OK) +# with self.assertRaises(get_user_model().DoesNotExist): +# self.user.refresh_from_db() # Ensure user is deleted + + # def test_signup(self): + # """Test user signup.""" + # url = reverse('signup') + # data = { + # 'username': 'newuser', + # 'email': 'newuser@example.com', + # 'password1': 'newpassword123', + # 'password2': 'newpassword123' + # } + # response = self.client.post(url, json.dumps(data), content_type="application/json") + # self.assertEqual(response.status_code, status.HTTP_201_CREATED) + # self.assertIn('token', response.json()) # Check that JWT token is returned + +# def test_login_user(self): +# """Test user login.""" +# url = reverse('login') +# data = { +# 'username': self.username, +# 'password': self.password +# } +# response = self.client.post(url, json.dumps(data), content_type="application/json") +# self.assertEqual(response.status_code, status.HTTP_200_OK) +# self.assertIn('token', response.json()) # Check for the JWT token in response + + def test_add_interested_languages_for_a_user(self): + """Test updating user's interested languages.""" + url = reverse('interested_languages') + data = { + 'user_id': self.user.pk, + 'interested_topics': ['Python', 'Django'], + 'known_languages': ['English', 'Spanish'] + } + response = self.client.post(url, json.dumps(data), content_type="application/json") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.user.refresh_from_db() + self.assertEqual(self.user.interested_topics, ['Python', 'Django']) + self.assertEqual(self.user.known_languages, ['English', 'Spanish']) + +# def test_logout_user(self): +# """Test user logout.""" +# url = reverse('logout') +# # Get JWT token for login +# refresh = RefreshToken.for_user(self.user) +# data = {'token': str(refresh.access_token)} +# response = self.client.post(url, data, content_type="application/json") +# self.assertEqual(response.status_code, status.HTTP_200_OK) +# self.assertIn('status', response.json()) +# self.assertEqual(response.json()['status'], 'success') + +# def test_reset_password_request(self): +# """Test requesting a password reset link.""" +# url = reverse('reset_password') +# data = {'email': self.email} +# response = self.client.post(url, json.dumps(data), content_type="application/json") +# self.assertEqual(response.status_code, status.HTTP_200_OK) +# self.assertIn('message', response.json()) + + # def test_reset_password_view(self): + # """Test resetting password with a valid token.""" + # token = 'fake-token' + # uidb64 = 'fake-uid' + # url = reverse('reset_password', args=[uidb64, token]) + # data = { + # 'new_password': 'newpassword123', + # 'confirm_password': 'newpassword123' + # } + # response = self.client.post(url, data) + # self.assertEqual(response.status_code, status.HTTP_200_OK) + +# def test_list_most_contributed_five_person(self): +# """Test listing the top 5 users by contributions.""" +# url = reverse('get_top_five_contributors') +# response = self.client.get(url) +# self.assertEqual(response.status_code, status.HTTP_200_OK) +# self.assertIn('users', response.json()) +# self.assertEqual(len(response.json()['users']), 5) From c4f6d2d6c73ac37c052349ea9869990911b39199 Mon Sep 17 00:00:00 2001 From: mehmeteminipekdal Date: Fri, 22 Nov 2024 02:46:33 +0100 Subject: [PATCH 05/14] After merging with the new main branch, models.py is unchanged and is returned to its original code, then the tests which needed the extra fields are done without the extra fields. One url is added to the urls.py for the unittesting. --- django_project_491/django_app/models.py | 34 +++++++++++++++---------- django_project_491/django_app/test.py | 24 ++++++++++------- django_project_491/django_app/urls.py | 2 +- 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/django_project_491/django_app/models.py b/django_project_491/django_app/models.py index bb6fc9dd..bad70ec2 100644 --- a/django_project_491/django_app/models.py +++ b/django_project_491/django_app/models.py @@ -82,7 +82,7 @@ class Question(models.Model): author = models.ForeignKey('User', on_delete=models.CASCADE, related_name='questions') def __str__(self): - return self.title + return f"{self.title} ({self.language})" def run_snippet(self): # TODO result = run_code(self.code_snippet, self.language_id) @@ -101,7 +101,7 @@ def save(self, *args, **kwargs): class UserManager(BaseUserManager): - def create_user(self, username, email, password=None, userType: UserType = UserType.USER, **extra_fields): + def create_user(self, username, email, password=None, userType: UserType = UserType.USER): if not email: raise ValueError("Users must have an email address") if not username: @@ -109,16 +109,22 @@ def create_user(self, username, email, password=None, userType: UserType = UserT if not password: raise ValueError("Users must have a password") - email = self.normalize_email(email) - user = self.model(username=username, email=email, userType=userType, **extra_fields) + user: User = self.model( + email=self.normalize_email(email), + username=username, + userType=userType + ) user.set_password(password) user.save(using=self._db) return user - def create_superuser(self, username: str, email: str, password: str = None, userType: UserType = UserType.ADMIN, **extra_fields): - extra_fields.setdefault('is_staff', True) - extra_fields.setdefault('is_superuser', True) # Ensure is_superuser is set - return self.create_user(username, email, password, userType=userType, **extra_fields) + def create_superuser(self, username: str, email: str, password: str = None): + return self.create_user( + username=username, + email=email, + password=password, + userType=UserType.ADMIN + ) class User(AbstractBaseUser): @@ -138,14 +144,10 @@ class User(AbstractBaseUser): # Relationships bookmarks = models.ManyToManyField('Question', related_name='bookmarked_by', blank=True) - is_active = models.BooleanField(default=True) - is_staff = models.BooleanField(default=False) - is_superuser = models.BooleanField(default=False) - objects = UserManager() - USERNAME_FIELD = 'email' - REQUIRED_FIELDS = ['username', 'userType'] + USERNAME_FIELD = 'username' + REQUIRED_FIELDS = ['email'] def __str__(self): return self.username @@ -160,6 +162,10 @@ def has_module_perms(self, app_label): # 2/3 added because of my custom userTyp return True return False + @property + def is_staff(self): # 3/3 added because of my custom userType + return self.userType == UserType.ADMIN + def get_question_details(self): return [{ 'id': question._id, diff --git a/django_project_491/django_app/test.py b/django_project_491/django_app/test.py index 1074593e..e132e9f3 100644 --- a/django_project_491/django_app/test.py +++ b/django_project_491/django_app/test.py @@ -228,9 +228,10 @@ def test_reported_by_relationship(self): self.assertEqual(self.question.reported_by.first().username, 'testuser') # Ensure the user is added def test_question_str_method(self): - # Test the string representation of the question + """Test the string representation of the Question model.""" question_str = str(self.question) self.assertIn(self.question.title, question_str) # The title should be in the string representation + self.assertIn(self.question.language, question_str) # The language should also be in the string representation def test_question_created_at(self): # Test if the `created_at` field is automatically set @@ -317,14 +318,19 @@ def setUp(self): password=self.password ) - def test_user_creation(self): - """Test that the user is created correctly.""" - user = self.user - self.assertEqual(user.username, self.username) - self.assertEqual(user.email, self.email) - self.assertTrue(user.check_password(self.password)) - self.assertFalse(user.is_superuser) - self.assertFalse(user.is_staff) +def test_user_creation(self): + """Test that the user is created correctly.""" + user = self.user + self.assertEqual(user.username, self.username) + self.assertEqual(user.email, self.email) + self.assertTrue(user.check_password(self.password)) + + # Check that the default userType is 'USER' + self.assertEqual(user.userType, UserType.USER.value) + + # Ensure the user is not an admin (based on userType) + self.assertNotEqual(user.userType, UserType.ADMIN.value) + def test_user_type_default(self): """Test that the default user type is 'USER'.""" diff --git a/django_project_491/django_app/urls.py b/django_project_491/django_app/urls.py index 8163290b..5d94d3be 100644 --- a/django_project_491/django_app/urls.py +++ b/django_project_491/django_app/urls.py @@ -22,7 +22,7 @@ path('upload-profile-pic/', user_views.upload_profile_pic, name='upload_profile_pic'), path('reset_password/', user_views.reset_password_request_mail, name='reset_password'), path('reset_password///', user_views.reset_password, name='reset_password'), - + path('interested_languages/', user_views.add_interested_languages_for_a_user, name='interested_languages'), path('get_top_five_contributors/', user_views.list_most_contributed_five_person, name='get_top_five_contributors'), path('get_question//', question_views.get_question_details, name='get_question'), From 4710e239e1288449fe41f7ed21f716d97bd72084 Mon Sep 17 00:00:00 2001 From: mehmeteminipekdal Date: Fri, 22 Nov 2024 03:18:46 +0100 Subject: [PATCH 06/14] corrected version of test_user_creation unittest --- django_project_491/django_app/test.py | 70 +++++++++++++-------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/django_project_491/django_app/test.py b/django_project_491/django_app/test.py index e132e9f3..e82b444a 100644 --- a/django_project_491/django_app/test.py +++ b/django_project_491/django_app/test.py @@ -318,18 +318,18 @@ def setUp(self): password=self.password ) -def test_user_creation(self): - """Test that the user is created correctly.""" - user = self.user - self.assertEqual(user.username, self.username) - self.assertEqual(user.email, self.email) - self.assertTrue(user.check_password(self.password)) - - # Check that the default userType is 'USER' - self.assertEqual(user.userType, UserType.USER.value) - - # Ensure the user is not an admin (based on userType) - self.assertNotEqual(user.userType, UserType.ADMIN.value) + def test_user_creation(self): + """Test that the user is created correctly.""" + user = self.user + self.assertEqual(user.username, self.username) + self.assertEqual(user.email, self.email) + self.assertTrue(user.check_password(self.password)) + + # Check that the default userType is 'USER' + self.assertEqual(user.userType.value, "user") # Compare with the string 'user' + + # Ensure the user is not an admin (based on userType) + self.assertNotEqual(user.userType.value, "admin") # Compare with the string 'admin' def test_user_type_default(self): @@ -415,18 +415,18 @@ def test_get_user_profile_by_id(self): # with self.assertRaises(get_user_model().DoesNotExist): # self.user.refresh_from_db() # Ensure user is deleted - # def test_signup(self): - # """Test user signup.""" - # url = reverse('signup') - # data = { - # 'username': 'newuser', - # 'email': 'newuser@example.com', - # 'password1': 'newpassword123', - # 'password2': 'newpassword123' - # } - # response = self.client.post(url, json.dumps(data), content_type="application/json") - # self.assertEqual(response.status_code, status.HTTP_201_CREATED) - # self.assertIn('token', response.json()) # Check that JWT token is returned +# def test_signup(self): +# """Test user signup.""" +# url = reverse('signup') +# data = { +# 'username': 'newuser', +# 'email': 'newuser@example.com', +# 'password1': 'newpassword123', +# 'password2': 'newpassword123' +# } +# response = self.client.post(url, json.dumps(data), content_type="application/json") +# self.assertEqual(response.status_code, status.HTTP_201_CREATED) +# self.assertIn('token', response.json()) # Check that JWT token is returned # def test_login_user(self): # """Test user login.""" @@ -472,17 +472,17 @@ def test_add_interested_languages_for_a_user(self): # self.assertEqual(response.status_code, status.HTTP_200_OK) # self.assertIn('message', response.json()) - # def test_reset_password_view(self): - # """Test resetting password with a valid token.""" - # token = 'fake-token' - # uidb64 = 'fake-uid' - # url = reverse('reset_password', args=[uidb64, token]) - # data = { - # 'new_password': 'newpassword123', - # 'confirm_password': 'newpassword123' - # } - # response = self.client.post(url, data) - # self.assertEqual(response.status_code, status.HTTP_200_OK) +# def test_reset_password_view(self): +# """Test resetting password with a valid token.""" +# token = 'fake-token' +# uidb64 = 'fake-uid' +# url = reverse('reset_password', args=[uidb64, token]) +# data = { +# 'new_password': 'newpassword123', +# 'confirm_password': 'newpassword123' +# } +# response = self.client.post(url, data) +# self.assertEqual(response.status_code, status.HTTP_200_OK) # def test_list_most_contributed_five_person(self): # """Test listing the top 5 users by contributions.""" From 70ff10492f2782525cae7969a869c8b34c247f9d Mon Sep 17 00:00:00 2001 From: mehmeteminipekdal Date: Fri, 22 Nov 2024 03:29:28 +0100 Subject: [PATCH 07/14] sign-up, login, reset pw request is fixed according to the up-to-date main branch --- django_project_491/django_app/test.py | 170 +++++++++++++------------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/django_project_491/django_app/test.py b/django_project_491/django_app/test.py index e82b444a..e4b7ccb6 100644 --- a/django_project_491/django_app/test.py +++ b/django_project_491/django_app/test.py @@ -391,53 +391,53 @@ def test_get_user_profile_by_id(self): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.json()['user']['username'], self.username) -# def test_edit_user_profile(self): -# """Test editing user profile.""" -# url = reverse('edit_user_profile', args=[self.user.pk]) -# self.client.login(username=self.username, password=self.password) -# data = { -# 'username': 'newusername', -# 'email': 'newemail@example.com', -# 'bio': 'Updated bio' -# } -# response = self.client.post(url, json.dumps(data), content_type="application/json") -# self.assertEqual(response.status_code, status.HTTP_200_OK) -# self.user.refresh_from_db() -# self.assertEqual(self.user.username, 'newusername') -# self.assertEqual(self.user.email, 'newemail@example.com') - -# def test_delete_user_profile(self): -# """Test deleting user profile.""" -# url = reverse('delete_user_profile', args=[self.user.pk]) -# self.client.login(username=self.username, password=self.password) -# response = self.client.delete(url) -# self.assertEqual(response.status_code, status.HTTP_200_OK) -# with self.assertRaises(get_user_model().DoesNotExist): -# self.user.refresh_from_db() # Ensure user is deleted - -# def test_signup(self): -# """Test user signup.""" -# url = reverse('signup') -# data = { -# 'username': 'newuser', -# 'email': 'newuser@example.com', -# 'password1': 'newpassword123', -# 'password2': 'newpassword123' -# } -# response = self.client.post(url, json.dumps(data), content_type="application/json") -# self.assertEqual(response.status_code, status.HTTP_201_CREATED) -# self.assertIn('token', response.json()) # Check that JWT token is returned - -# def test_login_user(self): -# """Test user login.""" -# url = reverse('login') -# data = { -# 'username': self.username, -# 'password': self.password -# } -# response = self.client.post(url, json.dumps(data), content_type="application/json") -# self.assertEqual(response.status_code, status.HTTP_200_OK) -# self.assertIn('token', response.json()) # Check for the JWT token in response + # def test_edit_user_profile(self): + # """Test editing user profile.""" + # url = reverse('edit_user_profile', args=[self.user.pk]) + # self.client.login(username=self.username, password=self.password) + # data = { + # 'username': 'newusername', + # 'email': 'newemail@example.com', + # 'bio': 'Updated bio' + # } + # response = self.client.post(url, json.dumps(data), content_type="application/json") + # self.assertEqual(response.status_code, status.HTTP_200_OK) + # self.user.refresh_from_db() + # self.assertEqual(self.user.username, 'newusername') + # self.assertEqual(self.user.email, 'newemail@example.com') + + # def test_delete_user_profile(self): + # """Test deleting user profile.""" + # url = reverse('delete_user_profile', args=[self.user.pk]) + # self.client.login(username=self.username, password=self.password) + # response = self.client.delete(url) + # self.assertEqual(response.status_code, status.HTTP_200_OK) + # with self.assertRaises(get_user_model().DoesNotExist): + # self.user.refresh_from_db() # Ensure user is deleted + + def test_signup(self): + """Test user signup.""" + url = reverse('signup') + data = { + 'username': 'newuser', + 'email': 'newuser@example.com', + 'password1': 'newpassword123', + 'password2': 'newpassword123' + } + response = self.client.post(url, json.dumps(data), content_type="application/json") + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertIn('token', response.json()) # Check that JWT token is returned + + def test_login_user(self): + """Test user login.""" + url = reverse('login') + data = { + 'username': self.username, + 'password': self.password + } + response = self.client.post(url, json.dumps(data), content_type="application/json") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIn('token', response.json()) # Check for the JWT token in response def test_add_interested_languages_for_a_user(self): """Test updating user's interested languages.""" @@ -453,41 +453,41 @@ def test_add_interested_languages_for_a_user(self): self.assertEqual(self.user.interested_topics, ['Python', 'Django']) self.assertEqual(self.user.known_languages, ['English', 'Spanish']) -# def test_logout_user(self): -# """Test user logout.""" -# url = reverse('logout') -# # Get JWT token for login -# refresh = RefreshToken.for_user(self.user) -# data = {'token': str(refresh.access_token)} -# response = self.client.post(url, data, content_type="application/json") -# self.assertEqual(response.status_code, status.HTTP_200_OK) -# self.assertIn('status', response.json()) -# self.assertEqual(response.json()['status'], 'success') - -# def test_reset_password_request(self): -# """Test requesting a password reset link.""" -# url = reverse('reset_password') -# data = {'email': self.email} -# response = self.client.post(url, json.dumps(data), content_type="application/json") -# self.assertEqual(response.status_code, status.HTTP_200_OK) -# self.assertIn('message', response.json()) - -# def test_reset_password_view(self): -# """Test resetting password with a valid token.""" -# token = 'fake-token' -# uidb64 = 'fake-uid' -# url = reverse('reset_password', args=[uidb64, token]) -# data = { -# 'new_password': 'newpassword123', -# 'confirm_password': 'newpassword123' -# } -# response = self.client.post(url, data) -# self.assertEqual(response.status_code, status.HTTP_200_OK) - -# def test_list_most_contributed_five_person(self): -# """Test listing the top 5 users by contributions.""" -# url = reverse('get_top_five_contributors') -# response = self.client.get(url) -# self.assertEqual(response.status_code, status.HTTP_200_OK) -# self.assertIn('users', response.json()) -# self.assertEqual(len(response.json()['users']), 5) + # def test_logout_user(self): + # """Test user logout.""" + # url = reverse('logout') + # # Get JWT token for login + # refresh = RefreshToken.for_user(self.user) + # data = {'token': str(refresh.access_token)} + # response = self.client.post(url, data, content_type="application/json") + # self.assertEqual(response.status_code, status.HTTP_200_OK) + # self.assertIn('status', response.json()) + # self.assertEqual(response.json()['status'], 'success') + + def test_reset_password_request(self): + """Test requesting a password reset link.""" + url = reverse('reset_password') + data = {'email': self.email} + response = self.client.post(url, json.dumps(data), content_type="application/json") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIn('message', response.json()) + + # def test_reset_password_view(self): + # """Test resetting password with a valid token.""" + # token = 'fake-token' + # uidb64 = 'fake-uid' + # url = reverse('reset_password', args=[uidb64, token]) + # data = { + # 'new_password': 'newpassword123', + # 'confirm_password': 'newpassword123' + # } + # response = self.client.post(url, data) + # self.assertEqual(response.status_code, status.HTTP_200_OK) + + # def test_list_most_contributed_five_person(self): + # """Test listing the top 5 users by contributions.""" + # url = reverse('get_top_five_contributors') + # response = self.client.get(url) + # self.assertEqual(response.status_code, status.HTTP_200_OK) + # self.assertIn('users', response.json()) + # self.assertEqual(len(response.json()['users']), 5) From 992160f01eecf94227c1ede55418843c2939108e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serhan=20=C3=87akmak?= Date: Sat, 23 Nov 2024 22:41:33 +0300 Subject: [PATCH 08/14] add QuestionViewTests Also solved a few bugs --- django_project_491/django_app/models.py | 2 +- django_project_491/django_app/test.py | 202 +++++++++++++++--- django_project_491/django_app/urls.py | 8 +- .../django_app/views/question_views.py | 20 +- 4 files changed, 187 insertions(+), 45 deletions(-) diff --git a/django_project_491/django_app/models.py b/django_project_491/django_app/models.py index 5d67298b..a09e663a 100644 --- a/django_project_491/django_app/models.py +++ b/django_project_491/django_app/models.py @@ -138,6 +138,7 @@ def save(self, *args, **kwargs): self.author.check_and_promote() + class UserManager(BaseUserManager): def create_user(self, username, email, password=None, userType: UserType = UserType.USER): if not email: @@ -265,4 +266,3 @@ def check_and_promote(self): self.save() return self.userType - \ No newline at end of file diff --git a/django_project_491/django_app/test.py b/django_project_491/django_app/test.py index e4b7ccb6..a371ce06 100644 --- a/django_project_491/django_app/test.py +++ b/django_project_491/django_app/test.py @@ -1,5 +1,9 @@ import json + +from django.contrib.auth.tokens import default_token_generator from django.test import TestCase, Client +from django.utils.http import urlsafe_base64_encode + from .Utils.utils import run_code from .views.utilization_views import wiki_result, wiki_search from django.utils import timezone @@ -13,35 +17,6 @@ from django.urls import reverse -# class TestRunCode(TestCase): -# # def setUp(self): -# # # Create a sample user for testing -# # self.user = User.objects.create_user( -# # username='testuser', -# # email="test", -# # password='testpassword' -# # ) -# # -# # def test_run_code(self): -# # # Test the run_code function with a simple Python code -# # self.client.login(username='testuser', email='test', password='testpassword') -# # -# # response = self.client.post('/run_code/', { -# # 'source_code': 'print("Hello, World!")', -# # 'language_name': 'Python (3.8.1)'}) -# # -# # self.assertEqual(response.status_code, 200) -# # self.assertTrue('stdout' in response.json()) -# # self.assertTrue(response.json()['stdout'].startswith('Hello, World!')) -# # -# # def test_authentication(self): -# # response = self.client.get('/run_code/', { -# # 'source_code': 'print("Hello, World!")', -# # 'language_name': 'Invalid Language Name'}) -# # self.assertEqual(response.status_code, 302) # Redirect to login page -# - - class TestSearchResult(TestCase): def setUp(self): # Create a test user @@ -127,8 +102,8 @@ def test_post_sample_code(self): # Validate response status and content self.assertEqual(response.status_code, 200) response_json = response.json() - self.assertIn('stdout', response_json) - self.assertTrue(response_json['stdout'].startswith('Hello, World!')) + self.assertIn('output', response_json) + self.assertEqual(response_json['output'][0], 'Hello, World!') class CommentModelTest(TestCase): def setUp(self): @@ -246,8 +221,6 @@ def test_question_tags(self): # Test if the `tags` field is correctly saved self.assertEqual(self.question.tags, ['Django', 'Testing']) - - class VoteModelTest(TestCase): def setUp(self): # Create a test user @@ -491,3 +464,166 @@ def test_reset_password_request(self): # self.assertEqual(response.status_code, status.HTTP_200_OK) # self.assertIn('users', response.json()) # self.assertEqual(len(response.json()['users']), 5) + + +class QuestionViewTests(TestCase): + def setUp(self): + self.client = Client() + self.user = get_user_model().objects.create_user( + username='testuser', + password='password', + email="test@gmail.com", + ) + self.question = Question.objects.create( + title='Test Question', + language='Python', + language_id=71, + tags=['tag1', 'tag2'], + details='This is a test question.', + code_snippet='print("Test")', + author=self.user + ) + self.comment = Comment.objects.create( + details='This is a test comment.', + author=self.user, + question=self.question + ) + def test_get_question(self): + response = self.client.get(reverse('get_question', args=[self.question._id])) + self.assertEqual(response.status_code, 200) + response_data = response.json() + self.assertIn('question', response_data) + self.assertEqual(response_data['question']['title'], self.question.title) + self.assertEqual(response_data['question']['language'], self.question.language) + + def test_get_question_comments(self): + response = self.client.get(reverse('get_question_comments', args=[self.question._id])) + self.assertEqual(response.status_code, 200) + response_data = response.json() + self.assertIn('comments', response_data) + self.assertEqual(len(response_data['comments']), 1) + self.assertEqual(response_data['comments'][0]['details'], self.comment.details) + + def test_create_question(self): + data = { + 'title': 'New Question', + 'language': 'Python (3.12.5)', + 'language_id': 71, + 'details': 'This is a new question.', + 'code_snippet': 'print("Hello, world!");', + 'tags': ['tag3', 'tag4'], + } + response = self.client.post( + reverse('create_question'), + data=json.dumps(data), + content_type='application/json', + **{'HTTP_User-ID': self.user.user_id} + ) + self.assertEqual(response.status_code, 201) + response_data = response.json() + self.assertIn('success', response_data) + self.assertTrue(response_data['success']) + + def test_edit_question(self): + data = { + 'title': 'Updated Question', + 'language': 'Python (3.12.5)', + 'language_id': 71, + 'details': 'This is an updated question.' + } + response = self.client.put( + reverse('edit_question', args=[self.question._id]), + data=json.dumps(data), + content_type='application/json', + **{'HTTP_User-ID': self.user.user_id} + ) + self.assertEqual(response.status_code, 200) + self.question.refresh_from_db() + self.assertEqual(self.question.title, data['title']) + self.assertEqual(self.question.details, data['details']) + + def test_delete_question(self): + response = self.client.delete( + reverse('delete_question', args=[self.question._id]), + **{'HTTP_User-ID': self.user.user_id} + ) + self.assertEqual(response.status_code, 200) + self.assertFalse(Question.objects.filter(_id=self.question._id).exists()) + + def test_mark_as_answered(self): + response = self.client.post( + reverse('mark_as_answered', args=[self.question._id]), + **{'HTTP_User-ID': self.user.user_id} + ) + self.assertEqual(response.status_code, 200) + self.question.refresh_from_db() + self.assertTrue(self.question.answered) + + def test_report_question(self): + response = self.client.post( + reverse('report_question', args=[self.question._id]), + **{'HTTP_User-ID': self.user.user_id} + ) + self.assertEqual(response.status_code, 200) + self.question.refresh_from_db() + self.assertTrue(self.user in self.question.reported_by.all()) + + def test_list_questions_by_language(self): + response = self.client.get(reverse('list_questions_by_language', args=['Python', 1])) + self.assertEqual(response.status_code, 200) + response_data = response.json() + self.assertIn('questions', response_data) + self.assertEqual(response_data['questions'][0]['title'], self.question.title) + + # def test_list_questions_by_tags(self): + # response = self.client.get(reverse('list_questions_by_tags', args=['tag1', 1])) + # self.assertEqual(response.status_code, 200) + # response_data = response.json() + # print("tagsssss", response_data) + # self.assertIn('questions', response_data) + # self.assertEqual(response_data['questions'][0]['tags'], self.question.tags) + + def test_list_questions_by_hotness(self): + response = self.client.get(reverse('list_questions_by_hotness', args=[1])) + self.assertEqual(response.status_code, 200) + + def test_random_questions(self): + response = self.client.get(reverse('random_questions')) + self.assertEqual(response.status_code, 200) + + def test_bookmark_question(self): + response = self.client.post( + reverse('bookmark_question', args=[self.question._id]), + **{'HTTP_User-ID': self.user.user_id} + ) + self.assertEqual(response.status_code, 200) + self.question.refresh_from_db() + self.assertTrue(self.user in self.question.bookmarked_by.all()) + + def test_remove_bookmark(self): + response = self.client.post( + reverse('remove_bookmark', args=[self.question._id]), + **{'HTTP_User-ID': self.user.user_id} + ) + self.assertEqual(response.status_code, 200) + self.question.refresh_from_db() + self.assertFalse(self.user in self.question.bookmarked_by.all()) + + def test_fetch_random_reported_question(self): + response = self.client.post( + reverse('report_question', args=[self.question._id]), + **{'HTTP_User-ID': self.user.user_id} + ) + self.assertEqual(response.status_code, 200) + response = self.client.get(reverse('get_random_reported_question')) + self.assertEqual(response.status_code, 200) + self.assertIn('question', response.json()) + + def fetch_all_at_once(self): + response = self.client.get(reverse('fetch_feed_at_once', args=[self.user._id])) + self.assertEqual(response.status_code, 200) + response_data = response.json() + self.assertIn('questions', response_data) + + + diff --git a/django_project_491/django_app/urls.py b/django_project_491/django_app/urls.py index 1701496e..21201311 100644 --- a/django_project_491/django_app/urls.py +++ b/django_project_491/django_app/urls.py @@ -32,15 +32,15 @@ path('create_question/', question_views.create_question, name='create_question'), path('edit_question//', question_views.edit_question, name='edit_question'), path('delete_question//', question_views.delete_question, name='delete_question'), - path('mark_as_answered//', question_views.mark_as_answered, name='mark_as_answered'), + path('mark_as_answered//', question_views.mark_as_answered, name='mark_as_answered'), path('report_question//', question_views.report_question, name='report_question'), path('bookmark_question//', question_views.bookmark_question, name='bookmark_question'), path('remove_bookmark//', question_views.remove_bookmark, name='remove_bookmark'), - path('get_random_reported_question/', question_views.fetch_random_reported_question, name='ger_random_reported_question'), + path('get_random_reported_question/', question_views.fetch_random_reported_question, name='get_random_reported_question'), - path('list_questions_by_language//', question_views.list_questions_by_language, name='list_questions'), + path('list_questions_by_language//', question_views.list_questions_by_language, name='list_questions_by_language'), path('list_questions_by_tags///', question_views.list_questions_by_tags, name='list_questions_by_tags'), - path('list_questions_by_hotness/', question_views.list_questions_by_hotness, name='get_question_comments'), + path('list_questions_by_hotness/', question_views.list_questions_by_hotness, name='list_questions_by_hotness'), path('random_questions/', question_views.random_questions, name='random_questions'), diff --git a/django_project_491/django_app/views/question_views.py b/django_project_491/django_app/views/question_views.py index ce4a9838..d91b878b 100644 --- a/django_project_491/django_app/views/question_views.py +++ b/django_project_491/django_app/views/question_views.py @@ -154,12 +154,14 @@ def create_question(request: HttpRequest) -> HttpResponse: if language_id is None: + print("Invalid language") return JsonResponse({'error': 'Invalid language'}, status=400) try: question_controller = QuestionQualityController() is_valid_question = question_controller.is_valid_question(data) if(not is_valid_question): + print("Question is not valid") return JsonResponse({'error': 'Question is not valid'}, status=400) except Exception as e: print(e) @@ -212,13 +214,13 @@ def edit_question(request: HttpRequest, question_id: int) -> HttpResponse: user_id = int(user_id) - editor_user = User.objects.get(pk=user_id) + editor_user = User.objects.get(user_id=user_id) try: question = Question.objects.get(_id=question_id) - question_owner_user_id = question.author.id + question_owner_user_id = question.author.user_id - if editor_user.id != question_owner_user_id and editor_user.userType != UserType.ADMIN: + if editor_user.user_id != question_owner_user_id and editor_user.userType != UserType.ADMIN: return JsonResponse({'error': 'Only admins and owner of the questions can edit questions'}, status=403) data = json.loads(request.body) @@ -233,6 +235,8 @@ def edit_question(request: HttpRequest, question_id: int) -> HttpResponse: question.tags = data.get('tags', question.tags) question.save() + return JsonResponse({'success': 'Question edited successfully'}, status=200) + except Question.DoesNotExist: return JsonResponse({'error': 'Question not found'}, status=404) @@ -241,6 +245,7 @@ def edit_question(request: HttpRequest, question_id: int) -> HttpResponse: return JsonResponse({'error': f'Malformed data: {str(e)}'}, status=400) except Exception as e: + print(e) return JsonResponse({'error': f'An error occurred: {str(e)}'}, status=500) @@ -268,22 +273,23 @@ def delete_question(request: HttpRequest, question_id: int) -> HttpResponse: user_id = int(user_id) - deletor_user = User.objects.get(pk=user_id) + deletor_user = User.objects.get(user_id=user_id) try: question = Question.objects.get(_id=question_id) - question_owner_user_id = question.author.id + question_owner_user_id = question.author.user_id - if deletor_user.id != question_owner_user_id and deletor_user.userType != UserType.ADMIN: + if deletor_user.user_id != question_owner_user_id and deletor_user.userType != UserType.ADMIN: return JsonResponse({'error': 'Only admins and owner of the questions can delete questions'}, status=403) question.delete() - deletor_user.questions.remove(question) + return JsonResponse({'success': 'Question deleted successfully'}, status=200) except Question.DoesNotExist: return JsonResponse({'error': 'Question not found'}, status=404) except Exception as e: + print(e) return JsonResponse({'error': f'An error occurred: {str(e)}'}, status=500) From b0bbee6167321394ee7dd3caca88fe61475839b0 Mon Sep 17 00:00:00 2001 From: mehmeteminipekdal Date: Sun, 24 Nov 2024 09:19:14 +0100 Subject: [PATCH 09/14] preferred_languages url is added, some bug fixes in comment_views, unnecessary tests are removed from model tests. Comment_views tests are added. --- .../migrations/0006_user_is_superuser.py | 18 - .../0007_user_is_active_user_is_staff.py | 23 - django_project_491/django_app/models.py | 2 +- django_project_491/django_app/test.py | 594 +++++++++++++----- django_project_491/django_app/urls.py | 1 + .../django_app/views/comment_views.py | 17 +- 6 files changed, 455 insertions(+), 200 deletions(-) delete mode 100644 django_project_491/django_app/migrations/0006_user_is_superuser.py delete mode 100644 django_project_491/django_app/migrations/0007_user_is_active_user_is_staff.py diff --git a/django_project_491/django_app/migrations/0006_user_is_superuser.py b/django_project_491/django_app/migrations/0006_user_is_superuser.py deleted file mode 100644 index 7dbcad90..00000000 --- a/django_project_491/django_app/migrations/0006_user_is_superuser.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.2 on 2024-11-19 03:57 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('django_app', '0005_comment_language'), - ] - - operations = [ - migrations.AddField( - model_name='user', - name='is_superuser', - field=models.BooleanField(default=False), - ), - ] diff --git a/django_project_491/django_app/migrations/0007_user_is_active_user_is_staff.py b/django_project_491/django_app/migrations/0007_user_is_active_user_is_staff.py deleted file mode 100644 index cff65833..00000000 --- a/django_project_491/django_app/migrations/0007_user_is_active_user_is_staff.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 5.1.2 on 2024-11-19 04:53 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('django_app', '0006_user_is_superuser'), - ] - - operations = [ - migrations.AddField( - model_name='user', - name='is_active', - field=models.BooleanField(default=True), - ), - migrations.AddField( - model_name='user', - name='is_staff', - field=models.BooleanField(default=False), - ), - ] diff --git a/django_project_491/django_app/models.py b/django_project_491/django_app/models.py index a09e663a..1b3000aa 100644 --- a/django_project_491/django_app/models.py +++ b/django_project_491/django_app/models.py @@ -127,7 +127,7 @@ def run_snippet(self): # TODO else: return result['stdout'].split('\n') - def mark_as_answered(self): # TODO + def mark_as_answered(self, comment_id): # TODO self.answered = True self.save() diff --git a/django_project_491/django_app/test.py b/django_project_491/django_app/test.py index a371ce06..d4787fa9 100644 --- a/django_project_491/django_app/test.py +++ b/django_project_491/django_app/test.py @@ -1,21 +1,27 @@ import json -from django.contrib.auth.tokens import default_token_generator from django.test import TestCase, Client +from django.core import mail +from django.utils import timezone +from django.utils.encoding import force_bytes from django.utils.http import urlsafe_base64_encode +from django.urls import reverse +from django.contrib.auth import get_user_model +from django.contrib.auth.tokens import default_token_generator +from django.core.files.uploadedfile import SimpleUploadedFile + +from rest_framework import status +from rest_framework.test import APIClient +from rest_framework_simplejwt.tokens import RefreshToken, AccessToken + +from datetime import timedelta +from unittest.mock import patch +from io import BytesIO from .Utils.utils import run_code from .views.utilization_views import wiki_result, wiki_search -from django.utils import timezone from .models import User, Comment, Question, Comment_Vote, Question_Vote, UserType, VoteType -from datetime import timedelta -from rest_framework.test import APITestCase -from rest_framework import status -from django.contrib.auth import get_user_model -from rest_framework_simplejwt.tokens import RefreshToken - -from django.urls import reverse class TestSearchResult(TestCase): def setUp(self): @@ -165,17 +171,6 @@ def setUp(self): author=self.user ) - def test_question_creation(self): - # Test if question is created successfully - self.assertEqual(self.question.title, 'Sample Question') - self.assertEqual(self.question.language, 'Python') - self.assertEqual(self.question.language_id, 71) - self.assertEqual(self.question.details, 'How to test models in Django?') - self.assertEqual(self.question.code_snippet, 'print("Test")') - self.assertEqual(self.question.tags, ['Django', 'Testing']) - self.assertEqual(self.question.topic, 'Django Testing') - self.assertEqual(self.question.author.username, 'testuser') - def test_question_snippet_execution(self): # Test if the `run_snippet` method works result = self.question.run_snippet() @@ -183,44 +178,26 @@ def test_question_snippet_execution(self): def test_mark_as_answered(self): # Test if the `mark_as_answered` method works - self.question.mark_as_answered() + # Create a comment for the question + comment = Comment.objects.create( + details='This is a test comment', + code_snippet='print("Test comment")', + language='Python', + question=self.question, + author=self.user + ) + + # Mark the comment as the answer + self.question.mark_as_answered(comment._id) self.question.refresh_from_db() # Refresh the instance self.assertTrue(self.question.answered) - def test_question_upvotes(self): - # Test the upvotes field - self.assertEqual(self.question.upvotes, 0) # Initially, upvotes should be 0 - self.question.upvotes += 1 - self.question.save() - self.assertEqual(self.question.upvotes, 1) # After incrementing, upvotes should be 1 - - def test_reported_by_relationship(self): - # Test the ManyToMany relationship with `reported_by` - self.assertEqual(self.question.reported_by.count(), 0) # Initially, no reports - self.question.reported_by.add(self.user) - self.question.save() - self.assertEqual(self.question.reported_by.count(), 1) # After adding, it should be 1 - self.assertEqual(self.question.reported_by.first().username, 'testuser') # Ensure the user is added - def test_question_str_method(self): """Test the string representation of the Question model.""" question_str = str(self.question) self.assertIn(self.question.title, question_str) # The title should be in the string representation self.assertIn(self.question.language, question_str) # The language should also be in the string representation - def test_question_created_at(self): - # Test if the `created_at` field is automatically set - self.assertTrue(self.question.created_at) - self.assertTrue(isinstance(self.question.created_at, timezone.datetime)) - - def test_question_topic(self): - # Test if the `topic` field is correctly saved - self.assertEqual(self.question.topic, 'Django Testing') - - def test_question_tags(self): - # Test if the `tags` field is correctly saved - self.assertEqual(self.question.tags, ['Django', 'Testing']) - class VoteModelTest(TestCase): def setUp(self): # Create a test user @@ -243,45 +220,53 @@ def setUp(self): self.comment = Comment.objects.create( details='This is a test comment', code_snippet='print("Test comment")', - language_id=71, + language='Python', question=self.question, author=self.user ) - # Create a test vote + # Create a test vote for the question self.vote = Question_Vote.objects.create( vote_type=VoteType.UPVOTE.value, user=self.user, question=self.question ) + # Create a test vote for the comment + self.comment_vote = Comment_Vote.objects.create( + vote_type=VoteType.UPVOTE.value, + user=self.user, + comment=self.comment + ) + def test_question_vote_creation(self): - # Test if the vote is created correctly + # Test if the question vote is created correctly self.assertEqual(self.vote.vote_type, 'upvote') self.assertEqual(self.vote.user.username, 'testuser') self.assertEqual(self.vote.question.title, 'Sample Question') def test_comment_vote_creation(self): # Test if the comment vote is created correctly - comment_vote = Comment_Vote.objects.create( - vote_type=VoteType.DOWNVOTE.value, - user=self.user, - comment=self.comment - ) - self.assertEqual(comment_vote.vote_type, 'downvote') - self.assertEqual(comment_vote.user.username, 'testuser') - self.assertEqual(comment_vote.comment.details, 'This is a test comment') + self.assertEqual(self.comment_vote.vote_type, 'upvote') + self.assertEqual(self.comment_vote.user.username, 'testuser') + self.assertEqual(self.comment_vote.comment.details, 'This is a test comment') def test_vote_str_method(self): - # Test the string representation of the vote + # Test the string representation of the vote for the question vote_str = str(self.vote) self.assertIn(self.user.username, vote_str) self.assertIn(self.vote.vote_type, vote_str) self.assertIn(self.question.title, vote_str) -class UserModelTest(TestCase): + # Test the string representation of the vote for the comment + comment_vote_str = str(self.comment_vote) + self.assertIn(self.user.username, comment_vote_str) + self.assertIn(self.comment_vote.vote_type, comment_vote_str) + self.assertIn(self.comment.details, comment_vote_str) +class UserModelTest(TestCase): def setUp(self): + """Set up a test user.""" self.username = 'testuser' self.password = 'testpassword123' self.email = 'testuser@example.com' @@ -291,48 +276,61 @@ def setUp(self): password=self.password ) - def test_user_creation(self): - """Test that the user is created correctly.""" - user = self.user - self.assertEqual(user.username, self.username) - self.assertEqual(user.email, self.email) - self.assertTrue(user.check_password(self.password)) - - # Check that the default userType is 'USER' - self.assertEqual(user.userType.value, "user") # Compare with the string 'user' + # Create a question to associate with comments + self.question = Question.objects.create( + title="Sample Question", + details="Details of the sample question", + author=self.user + ) + + # The reason this unittest is commented is, the self.questions.count() call in calculate_total_points + # returns 1 all the time. Test will be uncommented after the count fix. + # def test_calculate_total_points(self): + # """Test that the total points calculation works correctly.""" + # user = self.user - # Ensure the user is not an admin (based on userType) - self.assertNotEqual(user.userType.value, "admin") # Compare with the string 'admin' + # # Test with no questions or comments + # self.assertEqual(user.calculate_total_points(), 0) + # # Add a question (2 points) + # self.user.questions.add(self.question) # Correctly associate the question with the user + # self.assertEqual(user.calculate_total_points(), 2) - def test_user_type_default(self): - """Test that the default user type is 'USER'.""" - user = self.user - self.assertEqual(user.userType, UserType.USER) # Compare enum members + # # Add a comment (1 point since it's not an answer), associated with the question + # user.authored_comments.create(details="This is a comment.", answer_of_the_question=False, question=self.question) + # self.assertEqual(user.calculate_total_points(), 3) - def test_user_str_method(self): - """Test the __str__ method of the User model.""" + # # Add a comment answering a question (5 points), associated with the question + # user.authored_comments.create(details="This is an answer.", answer_of_the_question=True, question=self.question) + # self.assertEqual(user.calculate_total_points(), 8) + + def test_check_and_promote_to_super_user(self): + """Test that the user is promoted to SUPER_USER when their points exceed the threshold.""" user = self.user - self.assertEqual(str(user), self.username) - def test_check_and_promote(self): - """Test that the user promotion logic works.""" + # Simulate enough points for promotion + user.calculate_total_points = lambda: 150 # 150 points for promotion + user.check_and_promote() + + # Assert that the user has been promoted to SUPER_USER + self.assertEqual(user.userType, UserType.SUPER_USER) + + def test_check_and_demote_to_user(self): + """Test that the user is demoted to USER if their points fall below the threshold.""" user = self.user - # Before promotion - self.assertEqual(user.userType, UserType.USER) # Compare enum members - + # Simulate enough points for promotion - user.calculate_total_points = lambda: 150 # Simulate enough points + user.calculate_total_points = lambda: 150 # 150 points for promotion user.check_and_promote() - self.assertEqual(user.userType, UserType.SUPER_USER) # Compare enum members - - # Simulate losing points for demotion - user.calculate_total_points = lambda: 50 # Simulate low points + + # Simulate a drop in points below the promotion threshold + user.calculate_total_points = lambda: 50 # Less than the threshold for promotion user.check_and_promote() - self.assertEqual(user.userType, UserType.USER) # Compare enum members -class UserViewsTest(TestCase): + # Assert that the user has been demoted to USER + self.assertEqual(user.userType, UserType.USER) +class UserViewsTests(TestCase): def setUp(self): self.username = 'testuser' self.password = 'testpassword123' @@ -342,12 +340,20 @@ def setUp(self): email=self.email, password=self.password ) + + self.user.known_languages = ['Python', 'JavaScript'] + self.user.interested_topics = ['AI', 'Web Development'] + self.user.save() + self.admin = get_user_model().objects.create_superuser( username='admin', email='admin@example.com', password='adminpassword123' ) - self.client = self.client # Django's test client for making requests + self.client = APIClient() + # Generate tokens for the user + self.refresh_token = RefreshToken.for_user(self.user) + self.access_token = str(AccessToken.for_user(self.user)) def test_get_user_profile_by_username(self): """Test retrieving user profile by username.""" @@ -364,29 +370,31 @@ def test_get_user_profile_by_id(self): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.json()['user']['username'], self.username) - # def test_edit_user_profile(self): - # """Test editing user profile.""" - # url = reverse('edit_user_profile', args=[self.user.pk]) - # self.client.login(username=self.username, password=self.password) - # data = { - # 'username': 'newusername', - # 'email': 'newemail@example.com', - # 'bio': 'Updated bio' - # } - # response = self.client.post(url, json.dumps(data), content_type="application/json") - # self.assertEqual(response.status_code, status.HTTP_200_OK) - # self.user.refresh_from_db() - # self.assertEqual(self.user.username, 'newusername') - # self.assertEqual(self.user.email, 'newemail@example.com') - - # def test_delete_user_profile(self): - # """Test deleting user profile.""" - # url = reverse('delete_user_profile', args=[self.user.pk]) - # self.client.login(username=self.username, password=self.password) - # response = self.client.delete(url) - # self.assertEqual(response.status_code, status.HTTP_200_OK) - # with self.assertRaises(get_user_model().DoesNotExist): - # self.user.refresh_from_db() # Ensure user is deleted + def test_edit_user_profile(self): + """Test editing user profile.""" + url = reverse('edit_user_profile', args=[self.user.pk]) + self.client.login(username=self.username, password=self.password) + headers = {'HTTP_USER_ID': str(self.user.pk)} + data = { + 'username': 'newusername', + 'email': 'newemail@example.com', + 'bio': 'Updated bio' + } + response = self.client.post(url, json.dumps(data), content_type="application/json", **headers) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.user.refresh_from_db() + self.assertEqual(self.user.username, 'newusername') + self.assertEqual(self.user.email, 'newemail@example.com') + + def test_delete_user_profile(self): + """Test deleting user profile.""" + url = reverse('delete_user_profile', args=[self.user.pk]) + self.client.login(username=self.username, password=self.password) + headers = {'HTTP_USER_ID': str(self.user.pk)} + response = self.client.delete(url, **headers) + self.assertEqual(response.status_code, status.HTTP_200_OK) + with self.assertRaises(get_user_model().DoesNotExist): + self.user.refresh_from_db() # Ensure user is deleted def test_signup(self): """Test user signup.""" @@ -426,45 +434,178 @@ def test_add_interested_languages_for_a_user(self): self.assertEqual(self.user.interested_topics, ['Python', 'Django']) self.assertEqual(self.user.known_languages, ['English', 'Spanish']) - # def test_logout_user(self): - # """Test user logout.""" - # url = reverse('logout') - # # Get JWT token for login - # refresh = RefreshToken.for_user(self.user) - # data = {'token': str(refresh.access_token)} - # response = self.client.post(url, data, content_type="application/json") - # self.assertEqual(response.status_code, status.HTTP_200_OK) - # self.assertIn('status', response.json()) - # self.assertEqual(response.json()['status'], 'success') + def test_get_user_preferred_languages(self): + """Test retrieving user's preferred languages and interested topics.""" + url = reverse('preferred_languages') # Update with the actual URL name/path + response = self.client.post( + url, + data=json.dumps({'user_id': self.user.pk}), + content_type='application/json' + ) + self.assertEqual(response.status_code, 200) + response_data = response.json() + self.assertEqual(response_data['known_languages'], ['Python', 'JavaScript']) + self.assertEqual(response_data['interested_topics'], ['AI', 'Web Development']) + def test_logout_user(self): + # Authenticate the client with the access token + self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}') + + # Send the POST request with the refresh token + response = self.client.post( + reverse('logout'), + data=json.dumps({'token': str(self.refresh_token)}), + content_type='application/json', + ) + + # Assert the status code and response content + self.assertEqual(response.status_code, 200, f"Response data: {response.content}") + response_data = response.json() + self.assertEqual(response_data['status'], 'success') + self.assertEqual(response_data['message'], 'User logged out successfully') + + def test_check_token(self): + """Test checking the validity of a token.""" + url = reverse('check_token') # Update with the actual URL name/path + self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}') + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json()['status'], 'Token is valid') + + # Test with an invalid token + self.client.credentials(HTTP_AUTHORIZATION='Bearer invalidtoken') + response = self.client.get(url) + self.assertEqual(response.status_code, 401) + + def test_upload_profile_pic(self): + """Test uploading a profile picture.""" + url = reverse('upload_profile_pic') # Update with the actual URL name/path + image = BytesIO() + image.write(b'test image content') + image.seek(0) + uploaded_file = SimpleUploadedFile("test.jpg", image.read(), content_type="image/jpeg") + + # Test valid upload + response = self.client.post( + url, + {'profile_pic': uploaded_file}, + HTTP_User_ID=str(self.user.pk) # Include the user ID in the header + ) + self.assertEqual(response.status_code, 200) + response_data = response.json() + self.assertEqual(response_data['success'], 'Profile picture uploaded successfully') + self.assertIn('url', response_data) + + # Test missing user ID header + response = self.client.post( + url, + {'profile_pic': uploaded_file} + ) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json()['error'], 'User ID parameter is required in the header') + + # Test invalid request method + response = self.client.get(url) + self.assertEqual(response.status_code, 405) + self.assertEqual(response.json()['error'], 'Invalid request method') + def test_reset_password_request(self): - """Test requesting a password reset link.""" - url = reverse('reset_password') - data = {'email': self.email} - response = self.client.post(url, json.dumps(data), content_type="application/json") + """Test password reset request email functionality.""" + # Create a user + user = get_user_model().objects.create_user( + username='testuserpwres', + email='testuserpwres@example.com', + password='testpassword123' + ) + + # Mock a POST request with the user's email + response = self.client.post( + reverse('reset_password'), # Replace with the actual URL name + data=json.dumps({'email': 'testuserpwres@example.com'}), + content_type="application/json" + ) + + # Assert that the response is OK + self.assertEqual(response.status_code, 200) + + # Assert that an email was sent + self.assertEqual(len(mail.outbox), 1) # Verify that exactly one email was sent + + # Check the reset link in the HTML content + email_html = mail.outbox[0].alternatives[0][0] # HTML version is stored here + self.assertIn('reset_password', email_html) # Verify the reset link is in the email HTML content + + + @patch('django_app.views.user_views.default_token_generator.check_token', return_value=True) # Mock valid token + def test_reset_password_view(self, mock_check_token): + """Test password reset functionality with valid data.""" + # Create a user + user = get_user_model().objects.create_user( + username='testuserpwres', + email='testuserpwres@example.com', + password='oldpassword123' + ) + + # Generate UID and token + uidb64 = urlsafe_base64_encode(force_bytes(user.pk)) + token = default_token_generator.make_token(user) + + # Prepare the POST request + response = self.client.post( + reverse('reset_password', args=[uidb64, token]), # Adjust URL name if needed + data=json.dumps({ + 'new_password': 'newpassword123', + 'confirm_password': 'newpassword123', + }), + content_type="application/json" + ) + + # Debug response if the test fails + if response.status_code != 200: + print(f"Response Status Code: {response.status_code}") + print(f"Response Content: {response.json()}") + + # Assert the response is OK + self.assertEqual(response.status_code, 200) + + # Refresh user data and verify password change + user.refresh_from_db() + self.assertTrue(user.check_password('newpassword123')) # Confirm password is updated + + def test_list_most_contributed_five_person(self): + """Test listing the top 5 users by contributions.""" + + # Create users and simulate contributions + user2 = get_user_model().objects.create_user(username='user2', email='user2@example.com', password='testpassword123') + user3 = get_user_model().objects.create_user(username='user3', email='user3@example.com', password='testpassword123') + user4 = get_user_model().objects.create_user(username='user4', email='user4@example.com', password='testpassword123') + user5 = get_user_model().objects.create_user(username='user5', email='user5@example.com', password='testpassword123') + + # Simulate contributions + question1 = self.user.questions.create(title='Question 1', details='Details 1') # 2 points + question2 = self.user.questions.create(title='Question 2', details='Details 2') # 2 points + self.user.authored_comments.create(details='Answer1', answer_of_the_question=True, question=question1) # 5 points + user2.authored_comments.create(details='Answer2', answer_of_the_question=True, question=question2) # 5 points + question3 = user3.questions.create(title='Question 3', details='Details 3') # 2 points + user3.authored_comments.create(details='Answer3', answer_of_the_question=True, question=question3) # 5 points + user4.questions.create(title='Question 4', details='Details 4') # 2 points + user4.questions.create(title='Question 5', details='Details 5') # 2 points + user5.questions.create(title='Question 6', details='Details 6') # 2 points + + # Now, call the endpoint to get top contributors + url = reverse('get_top_five_contributors') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertIn('message', response.json()) - - # def test_reset_password_view(self): - # """Test resetting password with a valid token.""" - # token = 'fake-token' - # uidb64 = 'fake-uid' - # url = reverse('reset_password', args=[uidb64, token]) - # data = { - # 'new_password': 'newpassword123', - # 'confirm_password': 'newpassword123' - # } - # response = self.client.post(url, data) - # self.assertEqual(response.status_code, status.HTTP_200_OK) - - # def test_list_most_contributed_five_person(self): - # """Test listing the top 5 users by contributions.""" - # url = reverse('get_top_five_contributors') - # response = self.client.get(url) - # self.assertEqual(response.status_code, status.HTTP_200_OK) - # self.assertIn('users', response.json()) - # self.assertEqual(len(response.json()['users']), 5) + users = response.json()['users'] + + # Assert that the order is correct + self.assertEqual(users[0]['username'], 'testuser') # Highest contribution (9 points) + self.assertEqual(users[1]['username'], 'user3') # 7 points + self.assertEqual(users[2]['username'], 'user2') # 5 points + self.assertEqual(users[3]['username'], 'user4') # 4 points + self.assertEqual(users[4]['username'], 'user5') # 2 points class QuestionViewTests(TestCase): def setUp(self): @@ -625,5 +766,156 @@ def fetch_all_at_once(self): response_data = response.json() self.assertIn('questions', response_data) +class CommentViewsTest(TestCase): + def setUp(self): + self.client = APIClient() + + # Create a test user + self.user = User.objects.create_user( + username='testuser', + email='testuser@example.com', + password='testpassword123', + ) + self.user.userType = UserType.USER.value + self.user.save() + + # Create an admin user + self.admin = User.objects.create_superuser( + username='admin', + email='admin@example.com', + password='adminpassword123', + ) + self.admin.userType = UserType.ADMIN.value + self.admin.save() + + # Generate tokens + self.user_token = str(AccessToken.for_user(self.user)) + self.admin_token = str(AccessToken.for_user(self.admin)) + + # Create a test question (Ensure that question is created before the comment) + self.question = Question.objects.create( + title='Test Question', + language='Python', + language_id=71, + tags=['tag1', 'tag2'], + details='This is a test question.', + code_snippet='print("Test")', + author=self.user + ) + + def test_create_comment(self): + """Test creating a comment for a question.""" + data = { + "details": "This is a test comment.", + "code_snippet": "print('Hello World')", + "language": "Python (3.12.5)" # Lang2ID valid language + } + response = self.client.post( + reverse('create_comment', args=[self.question._id]), + data=json.dumps(data), + content_type='application/json', + **{'HTTP_User-ID': str(self.user.user_id)} + ) + # Check the status code + self.assertEqual(response.status_code, 201) + + # Parse the response JSON + response_data = response.json() + + # Check if the response contains 'success' and 'comment_id' + self.assertIn('success', response_data) + self.assertIn('comment_id', response_data) + + def test_edit_comment(self): + """Test editing a comment.""" + # Create a comment to edit + comment = Comment.objects.create( + details="Initial Comment", + code_snippet="print('Hello')", + language="Python", + language_id=71, + author=self.user, + question=self.question, + ) + + # URL for the edit comment endpoint + url = reverse('edit_comment', args=[comment._id]) + + # Prepare data to update the comment + data = { + "details": "Updated Comment", + "code_snippet": "print('Updated Hello')", + "language_id": 2, # Assuming this is a valid language ID + } + + # Set the correct user ID in headers + headers = {'HTTP_User-ID': str(self.user.user_id)} + + # Authenticate using the user token + self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.user_token}') + + # Perform the POST request + response = self.client.post(url, data, format='json', **headers) + + # Check that the status code is 200 (success) + self.assertEqual(response.status_code, 200) + + # Retrieve the updated comment from the database + updated_comment = Comment.objects.get(_id=comment._id) + + # Check that the comment was actually updated + self.assertEqual(updated_comment.details, "Updated Comment") + self.assertEqual(updated_comment.code_snippet, "print('Updated Hello')") + self.assertEqual(updated_comment.language_id, 2) + + def test_delete_comment(self): + """Test deleting a comment.""" + # Create a comment to delete + comment = Comment.objects.create( + details="Comment to delete", + code_snippet="", + language="Python (3.12.5)", # Valid language + language_id=71, + author=self.user, + question=self.question, + ) + url = reverse('delete_comment', args=[comment._id]) + headers = {'HTTP_User-ID': str(self.user.user_id)} + self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.user_token}') + + # Perform the DELETE request instead of POST + response = self.client.delete(url, **headers) + + # Check that the response status code is 200 (success) + self.assertEqual(response.status_code, 200) + + # Ensure the comment no longer exists in the database + self.assertFalse(Comment.objects.filter(_id=comment._id).exists()) + + def test_mark_comment_as_answer(self): + """Test marking a comment as the answer.""" + # Create a comment to mark as an answer + comment = Comment.objects.create( + details="Answer to mark", + code_snippet="", + language="Python (3.12.5)", # Valid language + language_id=71, + author=self.user, + question=self.question, + ) + + url = reverse('mark_comment_as_answer', args=[comment._id]) + headers = {'HTTP_User-ID': str(self.user.user_id)} # Correct user ID header + self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.user_token}') + + # Perform the request to mark the comment as an answer + response = self.client.post(url, format='json', **headers) + + # Check that the response status code is 200 (success) + self.assertEqual(response.status_code, 200) + + # Refresh the comment object to check if it's marked as an answer + comment.refresh_from_db() + self.assertTrue(comment.answer_of_the_question) # Ensure the comment is marked as the answer diff --git a/django_project_491/django_app/urls.py b/django_project_491/django_app/urls.py index 21201311..19e50dda 100644 --- a/django_project_491/django_app/urls.py +++ b/django_project_491/django_app/urls.py @@ -23,6 +23,7 @@ path('reset_password/', user_views.reset_password_request_mail, name='reset_password'), path('reset_password///', user_views.reset_password, name='reset_password'), path('interested_languages/', user_views.add_interested_languages_for_a_user, name='interested_languages'), + path('preferred_languages/', user_views.get_user_preferred_languages, name='preferred_languages'), path('get_top_five_contributors/', user_views.list_most_contributed_five_person, name='get_top_five_contributors'), path('fetch_feed_at_once//', question_views.fetch_all_at_once, name='get_user_profile'), diff --git a/django_project_491/django_app/views/comment_views.py b/django_project_491/django_app/views/comment_views.py index 4728211a..437bdfb1 100644 --- a/django_project_491/django_app/views/comment_views.py +++ b/django_project_491/django_app/views/comment_views.py @@ -105,9 +105,9 @@ def edit_comment(request: HttpRequest, comment_id: int) -> HttpResponse: try: comment = Comment.objects.get(_id=comment_id) - comment_owner_user_id = comment.author.id + comment_owner_user_id = comment.author.user_id - if editor_user.id != comment_owner_user_id and editor_user.userType != UserType.ADMIN: + if editor_user.user_id != comment_owner_user_id and editor_user.userType != UserType.ADMIN: return JsonResponse({'error': 'Only admins and owner of the comments can edit comments'}, status=403) @@ -125,6 +125,8 @@ def edit_comment(request: HttpRequest, comment_id: int) -> HttpResponse: except (KeyError, json.JSONDecodeError) as e: return JsonResponse({'error': f'Malformed data: {str(e)}'}, status=400) + return JsonResponse({'success': 'Comment updated successfully'}, status=200) + @csrf_exempt def delete_comment(request: HttpRequest, comment_id: int) -> HttpResponse: @@ -156,19 +158,20 @@ def delete_comment(request: HttpRequest, comment_id: int) -> HttpResponse: try: comment = Comment.objects.get(_id=comment_id) - comment_owner_user_id = comment.author.id + comment_owner_user_id = comment.author.user_id - if deletor_user.id != comment_owner_user_id and deletor_user.userType != UserType.ADMIN: + if deletor_user.user_id != comment_owner_user_id and deletor_user.userType != UserType.ADMIN: return JsonResponse({'error': 'Only admins and owner of the comments can delete comments'}, status=403) comment.delete() - user.authored_comments.remove(comment) - except Comment.DoesNotExist: return JsonResponse({'error': 'Comment not found'}, status=404) except Exception as e: return JsonResponse({'error': f'An error occurred: {str(e)}'}, status=500) + + return JsonResponse({'success': f'Comment {comment_id} is deleted'}, status=200) + @csrf_exempt def mark_comment_as_answer(request: HttpRequest, comment_id : int) -> HttpResponse: @@ -195,7 +198,7 @@ def mark_comment_as_answer(request: HttpRequest, comment_id : int) -> HttpRespon try: comment = Comment.objects.get(_id=comment_id) - question : Question = Comment.question + question = comment.question user_id = request.headers.get('User-ID', None) if user_id is None: return JsonResponse({'error': 'User ID parameter is required in the header'}, status=400) From 2882afa5152b8b0773a25da8f2670064c72986dc Mon Sep 17 00:00:00 2001 From: mehmeteminipekdal Date: Mon, 25 Nov 2024 20:25:00 +0300 Subject: [PATCH 10/14] tests are added again --- django_project_491/django_app/test.py | 809 ++++++++++++++++++++++++++ 1 file changed, 809 insertions(+) diff --git a/django_project_491/django_app/test.py b/django_project_491/django_app/test.py index c5e11ad9..f80feed6 100644 --- a/django_project_491/django_app/test.py +++ b/django_project_491/django_app/test.py @@ -217,3 +217,812 @@ def test_user_search_and_view_results(self, mock_sparql_query): def tearDown(self): # Clean up by deleting the test user self.user.delete() + +class CommentModelTest(TestCase): + def setUp(self): + # Create a test user + self.user = User.objects.create_user( + username='testuser', + email='test@example.com', + password='testpassword' + ) + + # Create a test question + self.question = Question.objects.create( + title='Sample Question', + language='Python', + details='How to test models in Django?', + code_snippet='print("Test")', + author=self.user + ) + + # Create a test comment + self.comment = Comment.objects.create( + details='This is a test comment', + code_snippet='print("Test comment")', + language_id=71, + question=self.question, + author=self.user + ) + + def test_comment_creation(self): + # Test if comment is created successfully + self.assertEqual(self.comment.details, 'This is a test comment') + self.assertEqual(self.comment.language_id, 71) + self.assertEqual(self.comment.question.title, 'Sample Question') + self.assertEqual(self.comment.author.username, 'testuser') + + def test_run_snippet(self): + # Test if the `run_snippet` method returns correct output + result = self.comment.run_snippet() + self.assertIn('Test comment', result) # Ensure the output matches the snippet's print statement + +class QuestionModelTest(TestCase): + def setUp(self): + # Create a test user + self.user = User.objects.create_user( + username='testuser', + email='test@example.com', + password='testpassword' + ) + + # Create a test question + self.question = Question.objects.create( + title='Sample Question', + language='Python', + language_id=71, # Language ID for Python + details='How to test models in Django?', + code_snippet='print("Test")', + tags=['Django', 'Testing'], + topic='Django Testing', + author=self.user + ) + + def test_question_snippet_execution(self): + # Test if the `run_snippet` method works + result = self.question.run_snippet() + self.assertIn('Test', result) # Ensure the output matches the snippet's print statement + + def test_mark_as_answered(self): + # Test if the `mark_as_answered` method works + # Create a comment for the question + comment = Comment.objects.create( + details='This is a test comment', + code_snippet='print("Test comment")', + language='Python', + question=self.question, + author=self.user + ) + + # Mark the comment as the answer + self.question.mark_as_answered(comment._id) + self.question.refresh_from_db() # Refresh the instance + self.assertTrue(self.question.answered) + + def test_question_str_method(self): + """Test the string representation of the Question model.""" + question_str = str(self.question) + self.assertIn(self.question.title, question_str) # The title should be in the string representation + self.assertIn(self.question.language, question_str) # The language should also be in the string representation + +class VoteModelTest(TestCase): + def setUp(self): + # Create a test user + self.user = User.objects.create_user( + username='testuser', + email='test@example.com', + password='testpassword' + ) + + # Create a test question + self.question = Question.objects.create( + title='Sample Question', + language='Python', + details='How to test models in Django?', + code_snippet='print("Test")', + author=self.user + ) + + # Create a test comment + self.comment = Comment.objects.create( + details='This is a test comment', + code_snippet='print("Test comment")', + language='Python', + question=self.question, + author=self.user + ) + + # Create a test vote for the question + self.vote = Question_Vote.objects.create( + vote_type=VoteType.UPVOTE.value, + user=self.user, + question=self.question + ) + + # Create a test vote for the comment + self.comment_vote = Comment_Vote.objects.create( + vote_type=VoteType.UPVOTE.value, + user=self.user, + comment=self.comment + ) + + def test_question_vote_creation(self): + # Test if the question vote is created correctly + self.assertEqual(self.vote.vote_type, 'upvote') + self.assertEqual(self.vote.user.username, 'testuser') + self.assertEqual(self.vote.question.title, 'Sample Question') + + def test_comment_vote_creation(self): + # Test if the comment vote is created correctly + self.assertEqual(self.comment_vote.vote_type, 'upvote') + self.assertEqual(self.comment_vote.user.username, 'testuser') + self.assertEqual(self.comment_vote.comment.details, 'This is a test comment') + + def test_vote_str_method(self): + # Test the string representation of the vote for the question + vote_str = str(self.vote) + self.assertIn(self.user.username, vote_str) + self.assertIn(self.vote.vote_type, vote_str) + self.assertIn(self.question.title, vote_str) + + # Test the string representation of the vote for the comment + comment_vote_str = str(self.comment_vote) + self.assertIn(self.user.username, comment_vote_str) + self.assertIn(self.comment_vote.vote_type, comment_vote_str) + self.assertIn(self.comment.details, comment_vote_str) + +class UserModelTest(TestCase): + def setUp(self): + """Set up a test user.""" + self.username = 'testuser' + self.password = 'testpassword123' + self.email = 'testuser@example.com' + self.user = get_user_model().objects.create_user( + username=self.username, + email=self.email, + password=self.password + ) + + # Create a question to associate with comments + self.question = Question.objects.create( + title="Sample Question", + details="Details of the sample question", + author=self.user + ) + + # The reason this unittest is commented is, the self.questions.count() call in calculate_total_points + # returns 1 all the time. Test will be uncommented after the count fix. + # def test_calculate_total_points(self): + # """Test that the total points calculation works correctly.""" + # user = self.user + + # # Test with no questions or comments + # self.assertEqual(user.calculate_total_points(), 0) + + # # Add a question (2 points) + # self.user.questions.add(self.question) # Correctly associate the question with the user + # self.assertEqual(user.calculate_total_points(), 2) + + # # Add a comment (1 point since it's not an answer), associated with the question + # user.authored_comments.create(details="This is a comment.", answer_of_the_question=False, question=self.question) + # self.assertEqual(user.calculate_total_points(), 3) + + # # Add a comment answering a question (5 points), associated with the question + # user.authored_comments.create(details="This is an answer.", answer_of_the_question=True, question=self.question) + # self.assertEqual(user.calculate_total_points(), 8) + + def test_check_and_promote_to_super_user(self): + """Test that the user is promoted to SUPER_USER when their points exceed the threshold.""" + user = self.user + + # Simulate enough points for promotion + user.calculate_total_points = lambda: 150 # 150 points for promotion + user.check_and_promote() + + # Assert that the user has been promoted to SUPER_USER + self.assertEqual(user.userType, UserType.SUPER_USER) + + def test_check_and_demote_to_user(self): + """Test that the user is demoted to USER if their points fall below the threshold.""" + user = self.user + + # Simulate enough points for promotion + user.calculate_total_points = lambda: 150 # 150 points for promotion + user.check_and_promote() + + # Simulate a drop in points below the promotion threshold + user.calculate_total_points = lambda: 50 # Less than the threshold for promotion + user.check_and_promote() + + # Assert that the user has been demoted to USER + self.assertEqual(user.userType, UserType.USER) + +class UserViewsTests(TestCase): + def setUp(self): + self.username = 'testuser' + self.password = 'testpassword123' + self.email = 'testuser@example.com' + self.user = get_user_model().objects.create_user( + username=self.username, + email=self.email, + password=self.password + ) + + self.user.known_languages = ['Python', 'JavaScript'] + self.user.interested_topics = ['AI', 'Web Development'] + self.user.save() + + self.admin = get_user_model().objects.create_superuser( + username='admin', + email='admin@example.com', + password='adminpassword123' + ) + self.client = APIClient() + # Generate tokens for the user + self.refresh_token = RefreshToken.for_user(self.user) + self.access_token = str(AccessToken.for_user(self.user)) + + def test_get_user_profile_by_username(self): + """Test retrieving user profile by username.""" + url = reverse('get_user_profile_by_username', args=[self.username]) + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIn('user', response.json()) + self.assertEqual(response.json()['user']['username'], self.username) + + def test_get_user_profile_by_id(self): + """Test retrieving user profile by user ID.""" + url = reverse('user_profile_by_id', args=[self.user.pk]) + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.json()['user']['username'], self.username) + + def test_edit_user_profile(self): + """Test editing user profile.""" + url = reverse('edit_user_profile', args=[self.user.pk]) + self.client.login(username=self.username, password=self.password) + headers = {'HTTP_USER_ID': str(self.user.pk)} + data = { + 'username': 'newusername', + 'email': 'newemail@example.com', + 'bio': 'Updated bio' + } + response = self.client.post(url, json.dumps(data), content_type="application/json", **headers) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.user.refresh_from_db() + self.assertEqual(self.user.username, 'newusername') + self.assertEqual(self.user.email, 'newemail@example.com') + + def test_delete_user_profile(self): + """Test deleting user profile.""" + url = reverse('delete_user_profile', args=[self.user.pk]) + self.client.login(username=self.username, password=self.password) + headers = {'HTTP_USER_ID': str(self.user.pk)} + response = self.client.delete(url, **headers) + self.assertEqual(response.status_code, status.HTTP_200_OK) + with self.assertRaises(get_user_model().DoesNotExist): + self.user.refresh_from_db() # Ensure user is deleted + + def test_signup(self): + """Test user signup.""" + url = reverse('signup') + data = { + 'username': 'newuser', + 'email': 'newuser@example.com', + 'password1': 'newpassword123', + 'password2': 'newpassword123' + } + response = self.client.post(url, json.dumps(data), content_type="application/json") + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertIn('token', response.json()) # Check that JWT token is returned + + def test_login_user(self): + """Test user login.""" + url = reverse('login') + data = { + 'username': self.username, + 'password': self.password + } + response = self.client.post(url, json.dumps(data), content_type="application/json") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIn('token', response.json()) # Check for the JWT token in response + + def test_add_interested_languages_for_a_user(self): + """Test updating user's interested languages.""" + url = reverse('interested_languages') + data = { + 'user_id': self.user.pk, + 'interested_topics': ['Python', 'Django'], + 'known_languages': ['English', 'Spanish'] + } + response = self.client.post(url, json.dumps(data), content_type="application/json") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.user.refresh_from_db() + self.assertEqual(self.user.interested_topics, ['Python', 'Django']) + self.assertEqual(self.user.known_languages, ['English', 'Spanish']) + + def test_get_user_preferred_languages(self): + """Test retrieving user's preferred languages and interested topics.""" + url = reverse('preferred_languages') # Update with the actual URL name/path + response = self.client.post( + url, + data=json.dumps({'user_id': self.user.pk}), + content_type='application/json' + ) + self.assertEqual(response.status_code, 200) + response_data = response.json() + self.assertEqual(response_data['known_languages'], ['Python', 'JavaScript']) + self.assertEqual(response_data['interested_topics'], ['AI', 'Web Development']) + + def test_logout_user(self): + # Authenticate the client with the access token + self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}') + + # Send the POST request with the refresh token + response = self.client.post( + reverse('logout'), + data=json.dumps({'token': str(self.refresh_token)}), + content_type='application/json', + ) + + # Assert the status code and response content + self.assertEqual(response.status_code, 200, f"Response data: {response.content}") + response_data = response.json() + self.assertEqual(response_data['status'], 'success') + self.assertEqual(response_data['message'], 'User logged out successfully') + + def test_check_token(self): + """Test checking the validity of a token.""" + url = reverse('check_token') # Update with the actual URL name/path + self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}') + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json()['status'], 'Token is valid') + + # Test with an invalid token + self.client.credentials(HTTP_AUTHORIZATION='Bearer invalidtoken') + response = self.client.get(url) + self.assertEqual(response.status_code, 401) + + def test_upload_profile_pic(self): + """Test uploading a profile picture.""" + url = reverse('upload_profile_pic') # Update with the actual URL name/path + image = BytesIO() + image.write(b'test image content') + image.seek(0) + uploaded_file = SimpleUploadedFile("test.jpg", image.read(), content_type="image/jpeg") + + # Test valid upload + response = self.client.post( + url, + {'profile_pic': uploaded_file}, + HTTP_User_ID=str(self.user.pk) # Include the user ID in the header + ) + self.assertEqual(response.status_code, 200) + response_data = response.json() + self.assertEqual(response_data['success'], 'Profile picture uploaded successfully') + self.assertIn('url', response_data) + + # Test missing user ID header + response = self.client.post( + url, + {'profile_pic': uploaded_file} + ) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json()['error'], 'User ID parameter is required in the header') + + # Test invalid request method + response = self.client.get(url) + self.assertEqual(response.status_code, 405) + self.assertEqual(response.json()['error'], 'Invalid request method') + + def test_reset_password_request(self): + """Test password reset request email functionality.""" + # Create a user + user = get_user_model().objects.create_user( + username='testuserpwres', + email='testuserpwres@example.com', + password='testpassword123' + ) + + # Mock a POST request with the user's email + response = self.client.post( + reverse('reset_password'), # Replace with the actual URL name + data=json.dumps({'email': 'testuserpwres@example.com'}), + content_type="application/json" + ) + + # Assert that the response is OK + self.assertEqual(response.status_code, 200) + + # Assert that an email was sent + self.assertEqual(len(mail.outbox), 1) # Verify that exactly one email was sent + + # Check the reset link in the HTML content + email_html = mail.outbox[0].alternatives[0][0] # HTML version is stored here + self.assertIn('reset_password', email_html) # Verify the reset link is in the email HTML content + + + @patch('django_app.views.user_views.default_token_generator.check_token', return_value=True) # Mock valid token + def test_reset_password_view(self, mock_check_token): + """Test password reset functionality with valid data.""" + # Create a user + user = get_user_model().objects.create_user( + username='testuserpwres', + email='testuserpwres@example.com', + password='oldpassword123' + ) + + # Generate UID and token + uidb64 = urlsafe_base64_encode(force_bytes(user.pk)) + token = default_token_generator.make_token(user) + + # Prepare the POST request + response = self.client.post( + reverse('reset_password', args=[uidb64, token]), # Adjust URL name if needed + data=json.dumps({ + 'new_password': 'newpassword123', + 'confirm_password': 'newpassword123', + }), + content_type="application/json" + ) + + # Debug response if the test fails + if response.status_code != 200: + print(f"Response Status Code: {response.status_code}") + print(f"Response Content: {response.json()}") + + # Assert the response is OK + self.assertEqual(response.status_code, 200) + + # Refresh user data and verify password change + user.refresh_from_db() + self.assertTrue(user.check_password('newpassword123')) # Confirm password is updated + + def test_list_most_contributed_five_person(self): + """Test listing the top 5 users by contributions.""" + + # Create users and simulate contributions + user2 = get_user_model().objects.create_user(username='user2', email='user2@example.com', password='testpassword123') + user3 = get_user_model().objects.create_user(username='user3', email='user3@example.com', password='testpassword123') + user4 = get_user_model().objects.create_user(username='user4', email='user4@example.com', password='testpassword123') + user5 = get_user_model().objects.create_user(username='user5', email='user5@example.com', password='testpassword123') + + # Simulate contributions + question1 = self.user.questions.create(title='Question 1', details='Details 1') # 2 points + question2 = self.user.questions.create(title='Question 2', details='Details 2') # 2 points + self.user.authored_comments.create(details='Answer1', answer_of_the_question=True, question=question1) # 5 points + user2.authored_comments.create(details='Answer2', answer_of_the_question=True, question=question2) # 5 points + question3 = user3.questions.create(title='Question 3', details='Details 3') # 2 points + user3.authored_comments.create(details='Answer3', answer_of_the_question=True, question=question3) # 5 points + user4.questions.create(title='Question 4', details='Details 4') # 2 points + user4.questions.create(title='Question 5', details='Details 5') # 2 points + user5.questions.create(title='Question 6', details='Details 6') # 2 points + + # Now, call the endpoint to get top contributors + url = reverse('get_top_five_contributors') + response = self.client.get(url) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + users = response.json()['users'] + + # Assert that the order is correct + self.assertEqual(users[0]['username'], 'testuser') # Highest contribution (9 points) + self.assertEqual(users[1]['username'], 'user3') # 7 points + self.assertEqual(users[2]['username'], 'user2') # 5 points + self.assertEqual(users[3]['username'], 'user4') # 4 points + self.assertEqual(users[4]['username'], 'user5') # 2 points + +class QuestionViewTests(TestCase): + def setUp(self): + self.client = Client() + self.user = get_user_model().objects.create_user( + username='testuser', + password='password', + email="test@gmail.com", + ) + self.question = Question.objects.create( + title='Test Question', + language='Python', + language_id=71, + tags=['tag1', 'tag2'], + details='This is a test question.', + code_snippet='print("Test")', + author=self.user + ) + self.comment = Comment.objects.create( + details='This is a test comment.', + author=self.user, + question=self.question + ) + def test_get_question(self): + response = self.client.get(reverse('get_question', args=[self.question._id])) + self.assertEqual(response.status_code, 200) + response_data = response.json() + self.assertIn('question', response_data) + self.assertEqual(response_data['question']['title'], self.question.title) + self.assertEqual(response_data['question']['language'], self.question.language) + + def test_get_question_comments(self): + response = self.client.get(reverse('get_question_comments', args=[self.question._id])) + self.assertEqual(response.status_code, 200) + response_data = response.json() + self.assertIn('comments', response_data) + self.assertEqual(len(response_data['comments']), 1) + self.assertEqual(response_data['comments'][0]['details'], self.comment.details) + + def test_create_question(self): + data = { + 'title': 'New Question', + 'language': 'Python (3.12.5)', + 'language_id': 71, + 'details': 'This is a new question.', + 'code_snippet': 'print("Hello, world!");', + 'tags': ['tag3', 'tag4'], + } + response = self.client.post( + reverse('create_question'), + data=json.dumps(data), + content_type='application/json', + **{'HTTP_User-ID': self.user.user_id} + ) + self.assertEqual(response.status_code, 201) + response_data = response.json() + self.assertIn('success', response_data) + self.assertTrue(response_data['success']) + + def test_edit_question(self): + data = { + 'title': 'Updated Question', + 'language': 'Python (3.12.5)', + 'language_id': 71, + 'details': 'This is an updated question.' + } + response = self.client.put( + reverse('edit_question', args=[self.question._id]), + data=json.dumps(data), + content_type='application/json', + **{'HTTP_User-ID': self.user.user_id} + ) + self.assertEqual(response.status_code, 200) + self.question.refresh_from_db() + self.assertEqual(self.question.title, data['title']) + self.assertEqual(self.question.details, data['details']) + + def test_delete_question(self): + response = self.client.delete( + reverse('delete_question', args=[self.question._id]), + **{'HTTP_User-ID': self.user.user_id} + ) + self.assertEqual(response.status_code, 200) + self.assertFalse(Question.objects.filter(_id=self.question._id).exists()) + + def test_mark_as_answered(self): + response = self.client.post( + reverse('mark_as_answered', args=[self.question._id]), + **{'HTTP_User-ID': self.user.user_id} + ) + self.assertEqual(response.status_code, 200) + self.question.refresh_from_db() + self.assertTrue(self.question.answered) + + def test_report_question(self): + response = self.client.post( + reverse('report_question', args=[self.question._id]), + **{'HTTP_User-ID': self.user.user_id} + ) + self.assertEqual(response.status_code, 200) + self.question.refresh_from_db() + self.assertTrue(self.user in self.question.reported_by.all()) + + def test_list_questions_by_language(self): + response = self.client.get(reverse('list_questions_by_language', args=['Python', 1])) + self.assertEqual(response.status_code, 200) + response_data = response.json() + self.assertIn('questions', response_data) + self.assertEqual(response_data['questions'][0]['title'], self.question.title) + + # def test_list_questions_by_tags(self): + # response = self.client.get(reverse('list_questions_by_tags', args=['tag1', 1])) + # self.assertEqual(response.status_code, 200) + # response_data = response.json() + # print("tagsssss", response_data) + # self.assertIn('questions', response_data) + # self.assertEqual(response_data['questions'][0]['tags'], self.question.tags) + + def test_list_questions_by_hotness(self): + response = self.client.get(reverse('list_questions_by_hotness', args=[1])) + self.assertEqual(response.status_code, 200) + + def test_random_questions(self): + response = self.client.get(reverse('random_questions')) + self.assertEqual(response.status_code, 200) + + def test_bookmark_question(self): + response = self.client.post( + reverse('bookmark_question', args=[self.question._id]), + **{'HTTP_User-ID': self.user.user_id} + ) + self.assertEqual(response.status_code, 200) + self.question.refresh_from_db() + self.assertTrue(self.user in self.question.bookmarked_by.all()) + + def test_remove_bookmark(self): + response = self.client.post( + reverse('remove_bookmark', args=[self.question._id]), + **{'HTTP_User-ID': self.user.user_id} + ) + self.assertEqual(response.status_code, 200) + self.question.refresh_from_db() + self.assertFalse(self.user in self.question.bookmarked_by.all()) + + def test_fetch_random_reported_question(self): + response = self.client.post( + reverse('report_question', args=[self.question._id]), + **{'HTTP_User-ID': self.user.user_id} + ) + self.assertEqual(response.status_code, 200) + response = self.client.get(reverse('get_random_reported_question')) + self.assertEqual(response.status_code, 200) + self.assertIn('question', response.json()) + + def fetch_all_at_once(self): + response = self.client.get(reverse('fetch_feed_at_once', args=[self.user._id])) + self.assertEqual(response.status_code, 200) + response_data = response.json() + self.assertIn('questions', response_data) + +class CommentViewsTest(TestCase): + def setUp(self): + self.client = APIClient() + + # Create a test user + self.user = User.objects.create_user( + username='testuser', + email='testuser@example.com', + password='testpassword123', + ) + self.user.userType = UserType.USER.value + self.user.save() + + # Create an admin user + self.admin = User.objects.create_superuser( + username='admin', + email='admin@example.com', + password='adminpassword123', + ) + self.admin.userType = UserType.ADMIN.value + self.admin.save() + + # Generate tokens + self.user_token = str(AccessToken.for_user(self.user)) + self.admin_token = str(AccessToken.for_user(self.admin)) + + # Create a test question (Ensure that question is created before the comment) + self.question = Question.objects.create( + title='Test Question', + language='Python', + language_id=71, + tags=['tag1', 'tag2'], + details='This is a test question.', + code_snippet='print("Test")', + author=self.user + ) + + def test_create_comment(self): + """Test creating a comment for a question.""" + data = { + "details": "This is a test comment.", + "code_snippet": "print('Hello World')", + "language": "Python (3.12.5)" # Lang2ID valid language + } + + response = self.client.post( + reverse('create_comment', args=[self.question._id]), + data=json.dumps(data), + content_type='application/json', + **{'HTTP_User-ID': str(self.user.user_id)} + ) + + # Check the status code + self.assertEqual(response.status_code, 201) + + # Parse the response JSON + response_data = response.json() + + # Check if the response contains 'success' and 'comment_id' + self.assertIn('success', response_data) + self.assertIn('comment_id', response_data) + + def test_edit_comment(self): + """Test editing a comment.""" + # Create a comment to edit + comment = Comment.objects.create( + details="Initial Comment", + code_snippet="print('Hello')", + language="Python", + language_id=71, + author=self.user, + question=self.question, + ) + + # URL for the edit comment endpoint + url = reverse('edit_comment', args=[comment._id]) + + # Prepare data to update the comment + data = { + "details": "Updated Comment", + "code_snippet": "print('Updated Hello')", + "language_id": 2, # Assuming this is a valid language ID + } + + # Set the correct user ID in headers + headers = {'HTTP_User-ID': str(self.user.user_id)} + + # Authenticate using the user token + self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.user_token}') + + # Perform the POST request + response = self.client.post(url, data, format='json', **headers) + + # Check that the status code is 200 (success) + self.assertEqual(response.status_code, 200) + + # Retrieve the updated comment from the database + updated_comment = Comment.objects.get(_id=comment._id) + + # Check that the comment was actually updated + self.assertEqual(updated_comment.details, "Updated Comment") + self.assertEqual(updated_comment.code_snippet, "print('Updated Hello')") + self.assertEqual(updated_comment.language_id, 2) + + def test_delete_comment(self): + """Test deleting a comment.""" + # Create a comment to delete + comment = Comment.objects.create( + details="Comment to delete", + code_snippet="", + language="Python (3.12.5)", # Valid language + language_id=71, + author=self.user, + question=self.question, + ) + url = reverse('delete_comment', args=[comment._id]) + headers = {'HTTP_User-ID': str(self.user.user_id)} + self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.user_token}') + + # Perform the DELETE request instead of POST + response = self.client.delete(url, **headers) + + # Check that the response status code is 200 (success) + self.assertEqual(response.status_code, 200) + + # Ensure the comment no longer exists in the database + self.assertFalse(Comment.objects.filter(_id=comment._id).exists()) + + def test_mark_comment_as_answer(self): + """Test marking a comment as the answer.""" + # Create a comment to mark as an answer + comment = Comment.objects.create( + details="Answer to mark", + code_snippet="", + language="Python (3.12.5)", # Valid language + language_id=71, + author=self.user, + question=self.question, + ) + + url = reverse('mark_comment_as_answer', args=[comment._id]) + headers = {'HTTP_User-ID': str(self.user.user_id)} # Correct user ID header + self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.user_token}') + + # Perform the request to mark the comment as an answer + response = self.client.post(url, format='json', **headers) + + # Check that the response status code is 200 (success) + self.assertEqual(response.status_code, 200) + + # Refresh the comment object to check if it's marked as an answer + comment.refresh_from_db() + self.assertTrue(comment.answer_of_the_question) # Ensure the comment is marked as the answer From 37dfacc3d395ac2e5aeacadff467da154e4c267d Mon Sep 17 00:00:00 2001 From: mehmeteminipekdal Date: Mon, 25 Nov 2024 20:28:37 +0300 Subject: [PATCH 11/14] main merge errors fix --- django_project_491/django_app/views/comment_views.py | 11 ----------- django_project_491/django_app/views/question_views.py | 2 -- 2 files changed, 13 deletions(-) diff --git a/django_project_491/django_app/views/comment_views.py b/django_project_491/django_app/views/comment_views.py index 5e85c5ab..1b510c21 100644 --- a/django_project_491/django_app/views/comment_views.py +++ b/django_project_491/django_app/views/comment_views.py @@ -129,12 +129,9 @@ def edit_comment(request: HttpRequest, comment_id: int) -> HttpResponse: try: comment = Comment.objects.get(_id=comment_id) comment_owner_user_id = comment.author.user_id - comment_owner_user_id = comment.author.user_id - if editor_user.user_id != comment_owner_user_id and editor_user.userType != UserType.ADMIN: if editor_user.user_id != comment_owner_user_id and editor_user.userType != UserType.ADMIN: return JsonResponse({'error': 'Only admins and owner of the comments can edit comments'}, status=403) - data = json.loads(request.body) @@ -152,9 +149,6 @@ def edit_comment(request: HttpRequest, comment_id: int) -> HttpResponse: except (KeyError, json.JSONDecodeError) as e: return JsonResponse({'error': f'Malformed data: {str(e)}'}, status=400) - return JsonResponse({'success': 'Comment updated successfully'}, status=200) - - @csrf_exempt @invalidate_user_cache() def delete_comment(request: HttpRequest, comment_id: int) -> HttpResponse: @@ -187,9 +181,7 @@ def delete_comment(request: HttpRequest, comment_id: int) -> HttpResponse: try: comment = Comment.objects.get(_id=comment_id) comment_owner_user_id = comment.author.user_id - comment_owner_user_id = comment.author.user_id - if deletor_user.user_id != comment_owner_user_id and deletor_user.userType != UserType.ADMIN: if deletor_user.user_id != comment_owner_user_id and deletor_user.userType != UserType.ADMIN: return JsonResponse({'error': 'Only admins and owner of the comments can delete comments'}, status=403) @@ -202,9 +194,6 @@ def delete_comment(request: HttpRequest, comment_id: int) -> HttpResponse: return JsonResponse({'error': 'Comment not found'}, status=404) except Exception as e: return JsonResponse({'error': f'An error occurred: {str(e)}'}, status=500) - - return JsonResponse({'success': f'Comment {comment_id} is deleted'}, status=200) - @csrf_exempt @invalidate_user_cache() diff --git a/django_project_491/django_app/views/question_views.py b/django_project_491/django_app/views/question_views.py index 0769abaf..007ae7d2 100644 --- a/django_project_491/django_app/views/question_views.py +++ b/django_project_491/django_app/views/question_views.py @@ -243,9 +243,7 @@ def edit_question(request: HttpRequest, question_id: int) -> HttpResponse: try: question = Question.objects.get(_id=question_id) question_owner_user_id = question.author.user_id - question_owner_user_id = question.author.user_id - if editor_user.user_id != question_owner_user_id and editor_user.userType != UserType.ADMIN: if editor_user.user_id != question_owner_user_id and editor_user.userType != UserType.ADMIN: return JsonResponse({'error': 'Only admins and owner of the questions can edit questions'}, status=403) From b1b68253e6a3b060158fff86715fc130bacf2cee Mon Sep 17 00:00:00 2001 From: mehmeteminipekdal Date: Mon, 25 Nov 2024 20:36:41 +0300 Subject: [PATCH 12/14] merge fix 2 - import --- django_project_491/django_app/test.py | 33 ++------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/django_project_491/django_app/test.py b/django_project_491/django_app/test.py index f80feed6..5bbfb328 100644 --- a/django_project_491/django_app/test.py +++ b/django_project_491/django_app/test.py @@ -20,39 +20,10 @@ from .Utils.utils import run_code from .views.utilization_views import wiki_result, wiki_search -from .models import User +from .models import User, Comment, Question, Comment_Vote, Question_Vote, UserType, VoteType from django.urls import reverse from unittest.mock import patch, MagicMock -# class TestRunCode(TestCase): -# # def setUp(self): -# # # Create a sample user for testing -# # self.user = User.objects.create_user( -# # username='testuser', -# # email="test", -# # password='testpassword' -# # ) -# # -# # def test_run_code(self): -# # # Test the run_code function with a simple Python code -# # self.client.login(username='testuser', email='test', password='testpassword') -# # -# # response = self.client.post('/run_code/', { -# # 'source_code': 'print("Hello, World!")', -# # 'language_name': 'Python (3.8.1)'}) -# # -# # self.assertEqual(response.status_code, 200) -# # self.assertTrue('stdout' in response.json()) -# # self.assertTrue(response.json()['stdout'].startswith('Hello, World!')) -# # -# # def test_authentication(self): -# # response = self.client.get('/run_code/', { -# # 'source_code': 'print("Hello, World!")', -# # 'language_name': 'Invalid Language Name'}) -# # self.assertEqual(response.status_code, 302) # Redirect to login page -# - - class TestSearchResult(TestCase): def setUp(self): # Create a test user @@ -217,7 +188,7 @@ def test_user_search_and_view_results(self, mock_sparql_query): def tearDown(self): # Clean up by deleting the test user self.user.delete() - + class CommentModelTest(TestCase): def setUp(self): # Create a test user From 86442ebffd0c633605c3600a21edc211ad7338a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serhan=20=C3=87akmak?= Date: Mon, 25 Nov 2024 21:18:16 +0300 Subject: [PATCH 13/14] add additional utilsviewtests --- .../0007_merge_0006_annotation_0006_topic.py | 14 ++++ django_project_491/django_app/test.py | 67 +++++++++++++++++-- 2 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 django_project_491/django_app/migrations/0007_merge_0006_annotation_0006_topic.py diff --git a/django_project_491/django_app/migrations/0007_merge_0006_annotation_0006_topic.py b/django_project_491/django_app/migrations/0007_merge_0006_annotation_0006_topic.py new file mode 100644 index 00000000..a51e68b9 --- /dev/null +++ b/django_project_491/django_app/migrations/0007_merge_0006_annotation_0006_topic.py @@ -0,0 +1,14 @@ +# Generated by Django 5.1.2 on 2024-11-25 17:46 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_app', '0006_annotation'), + ('django_app', '0006_topic'), + ] + + operations = [ + ] diff --git a/django_project_491/django_app/test.py b/django_project_491/django_app/test.py index 5bbfb328..70b8954b 100644 --- a/django_project_491/django_app/test.py +++ b/django_project_491/django_app/test.py @@ -201,7 +201,7 @@ def setUp(self): # Create a test question self.question = Question.objects.create( title='Sample Question', - language='Python', + language='Python (3.12.5)', details='How to test models in Django?', code_snippet='print("Test")', author=self.user @@ -240,7 +240,7 @@ def setUp(self): # Create a test question self.question = Question.objects.create( title='Sample Question', - language='Python', + language='Python (3.12.5)', language_id=71, # Language ID for Python details='How to test models in Django?', code_snippet='print("Test")', @@ -260,7 +260,7 @@ def test_mark_as_answered(self): comment = Comment.objects.create( details='This is a test comment', code_snippet='print("Test comment")', - language='Python', + language='Python (3.12.5)', question=self.question, author=self.user ) @@ -288,7 +288,7 @@ def setUp(self): # Create a test question self.question = Question.objects.create( title='Sample Question', - language='Python', + language='Python (3.12.5)', details='How to test models in Django?', code_snippet='print("Test")', author=self.user @@ -298,7 +298,7 @@ def setUp(self): self.comment = Comment.objects.create( details='This is a test comment', code_snippet='print("Test comment")', - language='Python', + language='Python (3.12.5)', question=self.question, author=self.user ) @@ -997,3 +997,60 @@ def test_mark_comment_as_answer(self): # Refresh the comment object to check if it's marked as an answer comment.refresh_from_db() self.assertTrue(comment.answer_of_the_question) # Ensure the comment is marked as the answer + + + +class UtilsViews(TestCase): + def setUp(self): + self.user = User.objects.create_user( + username='testuser', + password='password', + email="test@gmail.com" + ) + + self.question = Question.objects.create( + title='Test Question', + language='Python (3.12.5)', + language_id=71, + tags=['tag1', 'tag2'], + details='This is a test question.', + code_snippet='print("Test")', + author=self.user + ) + + self.comment = Comment.objects.create( + details='This is a test comment.', + author=self.user, + question=self.question, + language='Python (3.12.5)', + language_id=71, + code_snippet='print("Testfrom comment)' + ) + + + + def test_run_code_of_comment(self): + response = self.client.get(reverse('run_code', args=["comment", self.comment._id])) + self.assertEqual(response.status_code, 200) + response_data = response.json() + self.assertTrue( 'SyntaxError: EOL while scanning string literal' in response_data['output'][-1],) + + def test_run_code_of_question(self): + response = self.client.get(reverse('run_code', args=["question", self.question._id])) + self.assertEqual(response.status_code, 200) + response_data = response.json() + print(response_data) + self.assertEqual(response_data['output'][0], 'Test') + + + def test_upvote_comment(self): + response = self.client.post(reverse('upvote_object', args=["question",self.question._id]), **{'HTTP_User-ID': self.user.user_id}) + self.assertEqual(response.status_code, 200) + self.question.refresh_from_db() + self.assertEqual(self.question.upvotes, 1) + + def test_downvote_comment(self): + response = self.client.post(reverse('downvote_object', args=["comment", self.comment._id]), **{'HTTP_User-ID': self.user.user_id}) + self.assertEqual(response.status_code, 200) + self.comment.refresh_from_db() + self.assertEqual(self.comment.upvotes, -1) From 3edce0ae161f8f1dd54dce3085e3a3138efa36df Mon Sep 17 00:00:00 2001 From: mehmeteminipekdal Date: Mon, 25 Nov 2024 21:21:50 +0300 Subject: [PATCH 14/14] marked as todo: mark_as_answered --- django_project_491/django_app/views/question_views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_project_491/django_app/views/question_views.py b/django_project_491/django_app/views/question_views.py index 007ae7d2..f2a8d9e7 100644 --- a/django_project_491/django_app/views/question_views.py +++ b/django_project_491/django_app/views/question_views.py @@ -350,7 +350,7 @@ def mark_as_answered(request, question_id : int) -> HttpResponse: if author.user_id != request_user_id: return JsonResponse({'error': 'Only the owner of the question can mark it as answered'}, status=403) - question.mark_as_answered() + # question.mark_as_answered() #TODO ADD COMMENT ID HERE return JsonResponse({'success': 'Question marked as answered successfully'}, status=200)