Skip to content

Commit

Permalink
Add unittests for models, views and templatetags
Browse files Browse the repository at this point in the history
Changes in view handling reaction
- bad react requests are handled now
  • Loading branch information
abhiabhi94 committed May 14, 2020
1 parent 2c4243c commit ee1a7e4
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 6 deletions.
2 changes: 1 addition & 1 deletion comment/models/reactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def clean_reaction_type(cls, reaction_type):
"""
reaction = getattr(cls.ReactionType, reaction_type.upper(), None)
if not reaction:
return ValidationError(
raise ValidationError(
_('%(reaction)s is an invalid reaction'),
code='invalid',
params={'reaction': reaction}
Expand Down
39 changes: 37 additions & 2 deletions comment/tests/base.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase

from comment.models import Comment, Reaction, ReactionInstance
from test.example.post.models import Post
from comment.models import Comment


class BaseCommentTest(TestCase):
Expand Down Expand Up @@ -33,6 +34,7 @@ def setUp(self):
self.content_object_1 = content_type.get_object_for_this_type(id=self.post_1.id)
self.content_object_2 = content_type.get_object_for_this_type(id=self.post_2.id)
self.increment = 0
self.reactions = 0

def create_comment(self, ct_object, parent=None):
self.increment += 1
Expand All @@ -42,3 +44,36 @@ def create_comment(self, ct_object, parent=None):
user=self.user_1,
parent=parent,
)

def create_reaction(self, user, comment, reaction):
"""Increment total reactions, create a new ReactionInstance
Args:
user (get_user_model()): user to be associated to the reaction instance
comment (Comment): comment to be associated with the reaction instance
reaction (str): reaction to be recorded
Returns:
ReactionInstance: The ReactionInstance created
"""
reaction_type = getattr(ReactionInstance.ReactionType, reaction.upper(), None)
if reaction_type:
reaction_obj = Reaction.objects.get(comment=comment)
return ReactionInstance.objects.create(
user=user,
reaction_type=reaction_type.value,
reaction=reaction_obj
)
self.reactions += 1
raise ValueError('{} is not a valid reaction type'.format(reaction))

def set_reaction(self, user, comment, reaction):
"""
Set a reaction using the model function `set_reaction` of ReactionInstance
Args:
user (`get_user_model()`): user to be associated with the reaction.
comment (Comment): comment which needs to set the reaction.
reaction (str): the reaction to be set.
"""
ReactionInstance.set_reaction(user, comment, reaction)
76 changes: 75 additions & 1 deletion comment/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from time import sleep
from comment.models import Comment

from django.core.exceptions import ValidationError

from comment.models import Comment, Reaction
from comment.tests.base import BaseCommentTest


Expand All @@ -23,6 +26,13 @@ def test_can_create_comment(self):
parent_comment.save()
self.assertTrue(parent_comment.is_edited)

def test_reaction_signal(self):
"""Test reaction model instance is created when a comment is created"""
parent_comment = self.create_comment(self.content_object_1)
self.assertIsNotNone(Reaction.objects.get(comment=parent_comment))
# 1 reaction instance is created for every comment
self.assertEqual(Reaction.objects.count(), self.increment)


class CommentModelManagerTest(BaseCommentTest):
def setUp(self):
Expand Down Expand Up @@ -108,3 +118,67 @@ def test_create_comment_with_not_exist_model(self):
user=self.user_1
)
self.assertIsNone(comment)


class ReactionInstanceModelTest(CommentModelManagerTest):
def test_user_can_create_reaction(self):
"""Test whether reaction instance can be created"""
instance = self.create_reaction(self.user_2, self.child_comment_1, 'like')
self.assertIsNotNone(instance)

def test_comment_property_likes_increment_and_decrement(self):
"""Test decrement and increment on likes property with subsequent request."""
comment = self.child_comment_2
self.create_reaction(self.user_2, comment, 'like')
comment.refresh_from_db()
user = self.user_1
self.create_reaction(user, comment, 'like')
comment.refresh_from_db()
self.assertEqual(comment.likes, 2)

self.set_reaction(user, comment, 'like')
comment.refresh_from_db()
self.assertEqual(comment.likes, 1)

def test_comment_property_dislikes_increment_and_decrement(self):
"""Test decrement and increment on dislikes property with subsequent request."""
comment = self.child_comment_3
self.create_reaction(self.user_1, comment, 'dislike')
comment.refresh_from_db()
user = self.user_2
self.create_reaction(user, comment, 'dislike')
comment.refresh_from_db()
self.assertEqual(comment.dislikes, 2)

# can't use create_reaction: one user can't create multiple reaction instances for a comment.
self.set_reaction(user, comment, 'dislike')
comment.refresh_from_db()
self.assertEqual(comment.dislikes, 1)

def test_set_reaction(self):
"""Test set reactions increments the likes and dislikes property appropriately for subsequent calls"""
comment = self.child_comment_4
user = self.user_1
self.set_reaction(user, comment, 'dislike')
comment.refresh_from_db()
self.assertEqual(comment.dislikes, 1)
self.assertEqual(comment.likes, 0)

self.set_reaction(user, comment, 'dislike')
comment.refresh_from_db()
self.assertEqual(comment.dislikes, 0)
self.assertEqual(comment.likes, 0)

self.set_reaction(user, comment, 'like')
comment.refresh_from_db()
self.assertEqual(comment.dislikes, 0)
self.assertEqual(comment.likes, 1)

self.set_reaction(user, comment, 'dislike')
comment.refresh_from_db()
self.assertEqual(comment.dislikes, 1)
self.assertEqual(comment.likes, 0)

def test_set_reaction_on_incorrect_reaction(self):
"""Test ValidationError is raised for incorrect when incorrect reactions are passed"""
self.assertRaises(ValidationError, self.set_reaction, self.user_1, self.child_comment_5, 'likes')
42 changes: 41 additions & 1 deletion comment/tests/test_template_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

from django.core.exceptions import ImproperlyConfigured
from django.test import RequestFactory
from django.template import TemplateSyntaxError

from comment.forms import CommentForm
from comment.templatetags.comment_tags import (
get_model_name, get_app_name, get_comment_count, get_img_path, get_profile_url, render_comments,
include_static_jquery, include_bootstrap, include_static, render_field
include_static_jquery, include_bootstrap, include_static, render_field, add_one_arg, has_reacted
)
from comment.tests.base import BaseCommentTest
from django.conf import settings
Expand All @@ -23,6 +24,9 @@ def setUp(self):
self.child_comment_1 = self.create_comment(self.content_object_1, parent=self.parent_comment_1)
self.child_comment_2 = self.create_comment(self.content_object_1, parent=self.parent_comment_2)
self.child_comment_3 = self.create_comment(self.content_object_1, parent=self.parent_comment_2)
self.reaction_1 = self.create_reaction(self.user_1, self.parent_comment_1, 'like')
self.reaction_2 = self.create_reaction(self.user_1, self.parent_comment_2, 'dislike')
self.reaction_3 = self.create_reaction(self.user_1, self.parent_comment_3, 'like')

def test_get_model_name(self):
model_name = get_model_name(self.post_1)
Expand Down Expand Up @@ -113,3 +117,39 @@ def test_render_field(self):
self.assertIsNone(field.field.widget.attrs.get('placeholder'))
field = render_field(field, placeholder='placeholder')
self.assertEqual(field.field.widget.attrs.get('placeholder'), 'placeholder')

def test_add_one_arg(self):
"""Test whether this function returns a tuple of the elements passed"""
comment = self.parent_comment_1
user = self.user_1
self.assertTupleEqual((comment, user), add_one_arg(comment, user))

def test_has_reacted_on_incorrect_reaction(self):
"""Test whether this function raises an error when incorrect reaction is passed"""
comment = self.parent_comment_1
user = self.user_1
self.client.force_login(user)
comment_and_user = add_one_arg(comment, user)
self.assertRaises(TemplateSyntaxError, has_reacted, comment_and_user, 'likes')

def test_has_reacted_on_correct_reaction(self):
"""Test whether this function returns an appropriate boolean when correct reaction is passed"""
comment = self.parent_comment_1
user = self.user_1
self.client.force_login(user)
comment_and_user = add_one_arg(comment, user)
self.assertEqual(True, has_reacted(comment_and_user, 'like'))
self.assertEqual(False, has_reacted(comment_and_user, 'dislike'))

# check for other users
user = self.user_2
self.client.force_login(user)
comment_and_user = add_one_arg(comment, user)

self.assertEqual(False, has_reacted(comment_and_user, 'like'))
self.assertEqual(False, has_reacted(comment_and_user, 'dislike'))

# check for other comments
comment_and_user = add_one_arg(self.parent_comment_2, user)
self.assertEqual(False, has_reacted(comment_and_user, 'like'))
self.assertEqual(False, has_reacted(comment_and_user, 'dislike'))
77 changes: 77 additions & 0 deletions comment/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.urls import reverse
from rest_framework import status

from comment.models import Comment
from comment.tests.base import BaseCommentTest
Expand Down Expand Up @@ -150,3 +151,79 @@ def test_cannot_delete_comment_by_different_user(self):
response = self.client.post(reverse('comment:delete', kwargs={'pk': comment.id}), data=data)
self.assertEqual(response.status_code, 403)
self.assertEqual(response.reason_phrase, 'Forbidden')


class SetReactionTest(BaseCommentTest):

def setUp(self):
super().setUp()
self.comment = self.create_comment(self.content_object_1)

def get_url(self, obj_id, action):
"""
A utility function to construct url.
Args:
obj_id (int): comment id
action (str): reaction(like/dislike)
Returns:
str
"""
return reverse('comment:react', kwargs={
'comment_id': obj_id,
'reaction': action
})

def test_set_reaction_for_authenticated_users(self):
"""Test whether users can create/change reactions using view"""
url = self.get_url(self.comment.id, 'like')
user = self.user_2
self.client.force_login(user)
response = self.client.post(url, **{
'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'
})
data = {
'status': 0,
'likes': 1,
'dislikes': 0,
'msg': 'Your reaction has been updated successfully'
}
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertDictEqual(response.json(), data)

def test_set_reaction_for_unauthenticated_users(self):
"""Test whether unauthenticated users can create/change reactions using view"""
url = self.get_url(self.comment.id, 'dislike')
self.client.logout()
response = self.client.post(url, **{
'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'
})
self.assertEqual(response.status_code, status.HTTP_302_FOUND)
self.assertEqual(response.url, '/login?next={}'.format(url))

def test_get_request(self):
"""Test whether GET requests are allowed or not"""
url = self.get_url(self.comment.id, 'like')
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED)

def test_non_ajax_requests(self):
"""Test response if non AJAX requests are sent"""
url = self.get_url(self.comment.id, 'like')
response = self.client.post(url)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

def test_incorrect_comment_id(self):
"""Test response when an incorrect comment id is passed"""
url = self.get_url(102_876, 'like')
response = self.client.post(url)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

def test_incorrect_reaction(self):
"""Test response when incorrect reaction is passed"""
url = self.get_url(self.comment.id, 'likes')
response = self.client.post(url, **{
'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'
})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
5 changes: 4 additions & 1 deletion comment/views/reactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ def react(request, comment_id, reaction):
if not request.is_ajax():
return HttpResponseBadRequest('Only AJAX request are allowed')

if not getattr(ReactionInstance.ReactionType, reaction.upper(), None):
return HttpResponseBadRequest(_('This is not a valid reaction'))

comment = get_object_or_404(Comment, id=comment_id)
response = {
'status': 1,
Expand All @@ -31,6 +34,6 @@ def react(request, comment_id, reaction):
'status': 0,
'likes': comment.likes,
'dislikes': comment.dislikes,
'msg': _('Your action has been updated successfully')
'msg': _('Your reaction has been updated successfully')
})
return JsonResponse(response)

0 comments on commit ee1a7e4

Please sign in to comment.