Skip to content

Commit

Permalink
ref: Refactor tests for better debugging
Browse files Browse the repository at this point in the history
- almost all tests now aim to tests just one thing.
- make namesapce for DRF URLs consistent with that used for django.
- change use of raw URLs in the codebase to use url names.
  • Loading branch information
abhiabhi94 authored and Radi85 committed May 2, 2021
1 parent 7677106 commit bdfdea1
Show file tree
Hide file tree
Showing 21 changed files with 1,051 additions and 660 deletions.
14 changes: 8 additions & 6 deletions comment/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@

from comment.api import views

app_name = 'comment-api'

urlpatterns = [
path('comments/', views.CommentList.as_view(), name='comments-list'),
path('comments/create/', views.CommentCreate.as_view(), name='comments-create'),
path('comments/<int:pk>/', views.CommentDetail.as_view(), name='comment-detail'),
path('comments/<int:pk>/react/<str:reaction>/', views.CommentDetailForReaction.as_view(), name='comments-react'),
path('comments/<int:pk>/flag/', views.CommentDetailForFlag.as_view(), name='comments-flag'),
path('comments/', views.CommentList.as_view(), name='list'),
path('comments/create/', views.CommentCreate.as_view(), name='create'),
path('comments/<int:pk>/', views.CommentDetail.as_view(), name='detail'),
path('comments/<int:pk>/react/<str:reaction>/', views.CommentDetailForReaction.as_view(), name='react'),
path('comments/<int:pk>/flag/', views.CommentDetailForFlag.as_view(), name='flag'),
path(
'comments/<int:pk>/flag/state/change/',
views.CommentDetailForFlagStateChange.as_view(),
name='comments-flag-state-change'
name='flag-state-change'
),
re_path(r'^comments/confirm/(?P<key>[^/]+)/$', views.ConfirmComment.as_view(), name='confirm-comment'),
path('comments/toggle-subscription/', views.ToggleFollowAPI.as_view(), name='toggle-subscription'),
Expand Down
2 changes: 1 addition & 1 deletion comment/models/flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def toggle_state(self, state, moderator):
self.save()

def toggle_flagged_state(self):
allowed_flags = getattr(settings, 'COMMENT_FLAGS_ALLOWED', 0)
allowed_flags = settings.COMMENT_FLAGS_ALLOWED
if not allowed_flags:
return
self.refresh_from_db()
Expand Down
2 changes: 1 addition & 1 deletion comment/service/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def send_confirmation_request(self, api=False):
html_template = 'comment/anonymous/confirmation_request.html'
subject = EmailInfo.CONFIRMATION_SUBJECT
if api:
confirmation_url = f'/api/comments/confirm/{key}/'
confirmation_url = reverse('comment-api:confirm-comment', args=[key])
else:
confirmation_url = reverse('comment:confirm-comment', args=[key])

Expand Down
18 changes: 18 additions & 0 deletions comment/tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,24 @@ def transform(x):
return super().assertQuerysetEqual(qs, values, transform=transform, ordered=True, msg=msg)


class BaseAPITest(BaseCommentTest):
@classmethod
def setUpTestData(cls):
super().setUpTestData()
cls.comment = cls.create_comment(cls.content_object_1)
cls.comment_1 = cls.create_comment(cls.content_object_1)
cls.comment_2 = cls.create_comment(cls.content_object_1)
cls.comment_3 = cls.create_comment(cls.content_object_1)
cls.comment_4 = cls.create_comment(cls.content_object_1, parent=cls.comment_1)
cls.reaction_1 = cls.create_reaction_instance(cls.user_1, cls.comment_1, 'like')

cls.comment_5 = cls.create_comment(cls.content_object_2)
cls.comment_6 = cls.create_comment(cls.content_object_2)
cls.comment_7 = cls.create_comment(cls.content_object_2, parent=cls.comment_5)
cls.comment_8 = cls.create_comment(cls.content_object_2, parent=cls.comment_5)
cls.reaction_2 = cls.create_reaction_instance(cls.user_1, cls.comment_5, 'dislike')


class BaseCommentManagerTest(BaseCommentTest):
content_object_2 = None

Expand Down
164 changes: 164 additions & 0 deletions comment/tests/test_api/test_permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
from unittest.mock import patch

from django.test import RequestFactory

from comment.tests.test_api.test_views import BaseAPITest
from comment.api.permissions import (
IsOwnerOrReadOnly, FlagEnabledPermission, CanChangeFlaggedCommentState, SubscriptionEnabled,
CanGetSubscribers)
from comment.api.views import CommentList
from comment.models import FlagInstanceManager
from comment.conf import settings


class BaseAPIPermissionsTest(BaseAPITest):
@classmethod
def setUpTestData(cls):
super().setUpTestData()
cls.factory = RequestFactory()

def setUp(self):
super().setUp()
self.view = CommentList()


class OwnerPermissionTest(BaseAPIPermissionsTest):
def setUp(self):
super().setUp()
self.permission = IsOwnerOrReadOnly()

def test_get_request(self):
request = self.factory.get('/')

self.assertTrue(self.permission.has_object_permission(request, self.view, self.comment_1))

def test_put_method_from_different_user(self):
request = self.factory.put('/')
request.user = self.user_2
self.assertEqual(self.comment_1.user, self.user_1)

self.assertFalse(self.permission.has_object_permission(request, self.view, self.comment_1))

def test_put_method_from_admin(self):
request = self.factory.put('/')
request.user = self.admin
self.assertEqual(self.comment_1.user, self.user_1)

self.assertFalse(self.permission.has_object_permission(request, self.view, self.comment_1))

def test_put_method_from_same_user(self):
request = self.factory.put('/')
request.user = self.user_1
self.assertEqual(self.comment_1.user, self.user_1)

self.assertTrue(self.permission.has_object_permission(request, self.view, self.comment_1))


class FlagEnabledPermissionTest(BaseAPIPermissionsTest):
@classmethod
def setUpTestData(cls):
super().setUpTestData()
cls.request = cls.factory.get('/')

def setUp(self):
super().setUp()
self.permission = FlagEnabledPermission()

@patch.object(settings, 'COMMENT_FLAGS_ALLOWED', 0)
def test_flagging_disabled(self):
self.assertIs(False, self.permission.has_permission(self.request, self.view))

@patch.object(settings, 'COMMENT_FLAGS_ALLOWED', 1)
def test_flagging_enabled(self):
self.assertIs(True, self.permission.has_permission(self.request, self.view))


class CanChangeFlaggedCommentStateTest(BaseAPIPermissionsTest):
@classmethod
@patch.object(settings, 'COMMENT_FLAGS_ALLOWED', 1)
def setUpTestData(cls):
super().setUpTestData()
cls.flag_data = {
'reason': FlagInstanceManager.reason_values[0],
'info': '',
}
cls.create_flag_instance(cls.user_1, cls.comment_1, **cls.flag_data)
cls.create_flag_instance(cls.user_2, cls.comment_1, **cls.flag_data)
cls.comment_1.flag.refresh_from_db()
cls.flagged_comment = cls.comment_1
cls.unflagged_comment = cls.comment_2

def setUp(self):
super().setUp()
self.permission = CanChangeFlaggedCommentState()
self.request = self.factory.get('/')
self.request.user = self.user_1

def test_normal_user(self):
self.assertFalse(self.permission.has_permission(self.request, self.view))

def test_moderator(self):
self.request.user = self.moderator

self.assertTrue(self.permission.has_permission(self.request, self.view))

def test_moderator_for_unflagged_comment(self):
self.request.user = self.moderator

self.assertFalse(
self.permission.has_object_permission(self.request, self.view, self.unflagged_comment)
)

def test_moderator_for_flagged_comment(self):
self.request.user = self.moderator

self.assertIs(
True,
self.permission.has_object_permission(self.request, self.view, self.flagged_comment)
)

def test_normal_user_for_flagged_comment(self):
self.assertIs(
False,
self.permission.has_object_permission(self.request, self.view, self.flagged_comment)
)


class CanGetSubscribersTest(BaseAPIPermissionsTest):
def setUp(self):
super().setUp()
self.permission = CanGetSubscribers()
self.request = self.factory.get('/')

@patch.object(settings, 'COMMENT_ALLOW_SUBSCRIPTION', True)
def test_normal_users_cannot_retrieve_subscribers(self):
self.request.user = self.user_1

self.assertFalse(self.permission.has_permission(self.request, self.view))

@patch.object(settings, 'COMMENT_ALLOW_SUBSCRIPTION', True)
def test_only_moderators_can_retrieve_subscribers(self):
self.request.user = self.moderator

self.assertTrue(self.permission.has_permission(self.request, self.view))

@patch.object(settings, 'COMMENT_ALLOW_SUBSCRIPTION', False)
def test_cannot_retrieve_subscribers_when_system_disabled(self):
self.request.user = self.moderator

self.assertFalse(self.permission.has_permission(self.request, self.view))


class SubscriptionEnabledTest(BaseAPIPermissionsTest):
def setUp(self):
super().setUp()
self.request = self.factory.post('/')
self.permission = SubscriptionEnabled()

@patch.object(settings, 'COMMENT_ALLOW_SUBSCRIPTION', False)
def test_when_subscription_disabled(self):
self.assertFalse(self.permission.has_permission(self.request, self.view))

@patch.object(settings, 'COMMENT_ALLOW_SUBSCRIPTION', True)
def test_when_permission(self):
self.assertTrue(self.permission.has_permission(self.request, self.view))
88 changes: 43 additions & 45 deletions comment/tests/test_api/test_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
from comment.models import Comment, Follower
from comment.api.serializers import get_profile_model, get_user_fields, UserSerializerDAB, CommentCreateSerializer, \
CommentSerializer
from comment.tests.test_api.test_views import APIBaseTest
from comment.tests.test_api.test_views import BaseAPITest


class APICommentSerializers(APIBaseTest):
class APICommentSerializersTest(BaseAPITest):
def setUp(self):
super().setUp()
self.parent_count = Comment.objects.filter_parents_by_object(self.post_1).count()
Expand All @@ -25,48 +25,9 @@ def comment_count_test(self):
self.assertEqual(Comment.objects.filter_parents_by_object(self.post_1).count(), self.parent_count)
self.assertEqual(Comment.objects.all().count(), self.all_count)

def test_get_profile_model(self):
# missing settings attrs
with patch.object(settings, 'PROFILE_APP_NAME', None):
profile = get_profile_model()
self.assertIsNone(profile)

# providing wrong attribute value, an exception is raised
with patch.object(settings, 'PROFILE_APP_NAME', 'wrong'):
self.assertRaises(LookupError, get_profile_model)

# attribute value is None
with patch.object(settings, 'PROFILE_APP_NAME', None):
profile = get_profile_model()
self.assertIsNone(profile)

# success
with patch.object(settings, 'PROFILE_APP_NAME', 'user_profile'):
profile = get_profile_model()
self.assertIsNotNone(profile)

def test_user_serializer(self):
# PROFILE_MODEL_NAME not provided
with patch.object(settings, 'PROFILE_MODEL_NAME', None):
profile = UserSerializerDAB.get_profile(self.user_1)
self.assertIsNone(profile)

# PROFILE_MODEL_NAME is wrong
with patch.object(settings, 'PROFILE_MODEL_NAME', 'wrong'):
profile = UserSerializerDAB.get_profile(self.user_1)
self.assertIsNone(profile)

# success
with patch.object(settings, 'PROFILE_MODEL_NAME', 'userprofile'):
profile = UserSerializerDAB.get_profile(self.user_1)
self.assertIsNotNone(profile)

@patch.object(settings, 'COMMENT_ALLOW_SUBSCRIPTION', False)
@patch.object(settings, 'COMMENT_ALLOW_ANONYMOUS', False)
def test_create_parent_comment_serializer(self):
self.assertEqual(self.parent_count, 3)
self.assertEqual(self.all_count, 8)

factory = RequestFactory()
request = factory.get('/')
request.user = self.user_1
Expand Down Expand Up @@ -96,9 +57,6 @@ def test_create_parent_comment_serializer(self):
@patch.object(settings, 'COMMENT_ALLOW_SUBSCRIPTION', False)
@patch.object(settings, 'COMMENT_ALLOW_ANONYMOUS', False)
def test_create_child_comment_serializer(self):
self.assertEqual(self.parent_count, 3)
self.assertEqual(self.all_count, 8)

factory = RequestFactory()
request = factory.get('/')
request.user = self.user_1
Expand Down Expand Up @@ -188,9 +146,10 @@ def test_passing_context_to_serializer(self):
self.assertTrue(serializer.fields['content'].read_only)


class TestProfileSerializer(APIBaseTest):
class TestProfileSerializer(BaseAPITest):
def test_default_fields(self):
fields = get_user_fields()

self.assertSetEqual(set(fields), set(settings.COMMENT_USER_API_FIELDS + ['profile']))

@patch('comment.api.serializers.isinstance')
Expand All @@ -199,4 +158,43 @@ def test_has_image_field(self, mocked_hasattr, mocked_isinstance):
mocked_isinstance.return_value = True
mocked_hasattr.return_value = True
fields = get_user_fields()

self.assertIs('logentry' in fields, True)


class GetProfileTest(BaseAPITest):
@patch.object(settings, 'PROFILE_APP_NAME', None)
def test_setting_attribute_not_set(self):
profile = get_profile_model()

self.assertIsNone(profile)

@patch.object(settings, 'PROFILE_APP_NAME', 'wrong')
def test_setting_attribute_set_wrong(self):
self.assertRaises(LookupError, get_profile_model)

@patch.object(settings, 'PROFILE_APP_NAME', 'user_profile')
def tests_success(self):
profile = get_profile_model()

self.assertIsNotNone(profile)


class TestUserSerializer(BaseAPITest):
@patch.object(settings, 'PROFILE_MODEL_NAME', None)
def test_profile_model_name_not_provided(self):
profile = UserSerializerDAB.get_profile(self.user_1)

self.assertIsNone(profile)

@patch.object(settings, 'PROFILE_MODEL_NAME', 'wrong')
def test_profile_model_wrong(self):
profile = UserSerializerDAB.get_profile(self.user_1)

self.assertIsNone(profile)

@patch.object(settings, 'PROFILE_MODEL_NAME', 'userprofile')
def test_success(self):
profile = UserSerializerDAB.get_profile(self.user_1)

self.assertIsNotNone(profile)
Loading

0 comments on commit bdfdea1

Please sign in to comment.