Просмотр исходного кода

improved test suite for start/reply thread cases

Rafał Pitoń 10 лет назад
Родитель
Сommit
73d58e65d2

+ 4 - 4
misago/threads/posting/floodprotection.py

@@ -10,10 +10,10 @@ MIN_POSTING_PAUSE = 3
 class FloodProtectionMiddleware(PostingMiddleware):
     def interrupt_posting(self, form):
         message = _("You can't post message so quickly after previous one.")
-        if self.user.last_post:
-            previous_post = timezone.now() - self.user.last_post
+        if self.user.last_posted_on:
+            previous_post = timezone.now() - self.user.last_posted_on
             if previous_post.total_seconds() < MIN_POSTING_PAUSE:
                 raise PostingInterrupt(message)
 
-        self.user.last_post = timezone.now()
-        self.user.update_fields.append('last_post')
+        self.user.last_posted_on = timezone.now()
+        self.user.update_fields.append('last_posted_on')

+ 4 - 4
misago/threads/posting/threadclose.py

@@ -6,7 +6,7 @@ from misago.threads.posting import PostingMiddleware, START
 class ThreadCloseFormMiddleware(PostingMiddleware):
     def use_this_middleware(self):
         if self.forum.acl['can_close_threads']:
-            self.thread_is_closed = self.thread.is_closed
+            self.is_closed = self.thread.is_closed
             return True
         else:
             return False
@@ -15,18 +15,18 @@ class ThreadCloseFormMiddleware(PostingMiddleware):
         if self.request.method == 'POST':
             return ThreadCloseForm(self.request.POST, prefix=self.prefix)
         else:
-            initial = {'is_closed': self.thread_is_closed}
+            initial = {'is_closed': self.is_closed}
             return ThreadCloseForm(prefix=self.prefix, initial=initial)
 
     def pre_save(self, form):
         if form.is_valid() and self.mode == START:
-            if self.thread_is_closed != form.cleaned_data.get('is_closed'):
+            if form.cleaned_data.get('is_closed'):
                 self.thread.is_closed = form.cleaned_data.get('is_closed')
                 self.thread.update_fields.append('is_closed')
 
     def post_save(self, form):
         if form.is_valid() and self.mode != START:
-            if self.thread_is_closed != form.cleaned_data.get('is_closed'):
+            if self.is_closed != form.cleaned_data.get('is_closed'):
                 if self.thread.is_closed:
                     moderation.open_thread(self.user, self.thread)
                 else:

+ 11 - 9
misago/threads/posting/threadlabel.py

@@ -7,28 +7,30 @@ from misago.threads.posting import PostingMiddleware, START, EDIT
 
 class ThreadLabelFormMiddleware(PostingMiddleware):
     def use_this_middleware(self):
-        if self.forum.acl['can_change_threads_labels'] and self.forum.labels:
-            self.thread_label_id = self.thread.label_id
+        if self.forum.labels and self.forum.acl['can_change_threads_labels']:
+            self.label_id = self.thread.label_id
+
+            if self.mode == START:
+                return True
+
             if self.mode == EDIT and can_edit_thread(self.user, self.thread):
                 return True
-            else:
-                return self.mode == START
-        else:
-            return False
+
+        return False
 
     def make_form(self):
         if self.request.method == 'POST':
             return ThreadLabelForm(self.request.POST, prefix=self.prefix,
                                     labels=self.forum.labels)
         else:
-            initial = {'label_id': self.thread_label_id}
+            initial = {'label_id': self.label_id}
             return ThreadLabelForm(prefix=self.prefix,
                                     labels=self.forum.labels,
                                     initial=initial)
 
     def pre_save(self, form):
         if form.is_valid() and self.mode == START:
-            if self.thread_label_id != form.cleaned_data.get('label'):
+            if self.label_id != form.cleaned_data.get('label'):
                 if form.cleaned_data.get('label'):
                     self.thread.label_id = form.cleaned_data.get('label')
                     self.thread.update_fields.append('label')
@@ -38,7 +40,7 @@ class ThreadLabelFormMiddleware(PostingMiddleware):
 
     def post_save(self, form):
         if form.is_valid() and self.mode != START:
-            if self.thread_label_id != form.cleaned_data.get('label'):
+            if self.label_id != form.cleaned_data.get('label'):
                 if form.cleaned_data.get('label'):
                     labels_dict = Label.objects.get_cached_labels_dict()
                     new_label = labels_dict.get(form.cleaned_data.get('label'))

+ 1 - 1
misago/threads/posting/threadpin.py

@@ -20,7 +20,7 @@ class ThreadPinFormMiddleware(PostingMiddleware):
 
     def pre_save(self, form):
         if form.is_valid() and self.mode == START:
-            if self.is_pinned != form.cleaned_data.get('is_pinned'):
+            if form.cleaned_data.get('is_pinned'):
                 self.thread.is_pinned = form.cleaned_data.get('is_pinned')
                 self.thread.update_fields.append('is_pinned')
 

+ 7 - 8
misago/threads/tests/test_floodprotection_middleware.py

@@ -11,31 +11,30 @@ from misago.threads.posting.floodprotection import (MIN_POSTING_PAUSE,
 
 class FloodProtectionMiddlewareTests(AuthenticatedUserTestCase):
     def test_flood_protection_middleware_on_no_posts(self):
-        """middleware sets last_post on user"""
+        """middleware sets last_posted_on on user"""
         self.user.update_fields = []
-        self.assertIsNone(self.user.last_post)
+        self.assertIsNone(self.user.last_posted_on)
 
         middleware = FloodProtectionMiddleware(user=self.user)
         middleware.interrupt_posting(None)
 
-        self.assertIsNotNone(self.user.last_post)
+        self.assertIsNotNone(self.user.last_posted_on)
 
     def test_flood_protection_middleware_old_posts(self):
         """middleware is not complaining about old post"""
         self.user.update_fields = []
 
-        original_last_post = timezone.now() - timedelta(days=1)
-        self.user.last_post = original_last_post
+        original_last_posted_on = timezone.now() - timedelta(days=1)
+        self.user.last_posted_on = original_last_posted_on
 
         middleware = FloodProtectionMiddleware(user=self.user)
         middleware.interrupt_posting(None)
 
-        self.assertTrue(self.user.last_post > original_last_post)
+        self.assertTrue(self.user.last_posted_on > original_last_posted_on)
 
     def test_flood_protection_middleware_on_flood(self):
         """middleware is complaining about flood"""
-        original_last_post = timezone.now() - timedelta(days=1)
-        self.user.last_post = timezone.now()
+        self.user.last_posted_on = timezone.now()
 
         with self.assertRaises(PostingInterrupt):
             middleware = FloodProtectionMiddleware(user=self.user)

+ 87 - 18
misago/threads/tests/test_replythread_view.py

@@ -7,7 +7,7 @@ from misago.acl.testutils import override_acl
 from misago.forums.models import Forum
 from misago.users.testutils import AuthenticatedUserTestCase
 
-from misago.threads.models import Thread
+from misago.threads.models import Label, Thread
 from misago.threads.testutils import post_thread
 
 
@@ -24,15 +24,22 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
             'thread_id': self.thread.id,
         })
 
-    def allow_reply_thread(self, level=2):
+        Label.objects.clear_cache()
+
+    def tearDown(self):
+        Label.objects.clear_cache()
+
+    def allow_reply_thread(self, extra_acl=None):
         forums_acl = self.user.acl
         forums_acl['visible_forums'].append(self.forum.pk)
         forums_acl['forums'][self.forum.pk] = {
             'can_see': 1,
             'can_browse': 1,
             'can_see_all_threads': 1,
-            'can_reply_threads': level,
+            'can_reply_threads': 2,
         }
+        if extra_acl:
+            forums_acl['forums'][self.forum.pk].update(extra_acl)
         override_acl(self.user, forums_acl)
 
     def test_cant_see(self):
@@ -47,7 +54,7 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
         }
         override_acl(self.user, forums_acl)
 
-        response = self.client.get(self.link)
+        response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 404)
 
     def test_cant_browse(self):
@@ -62,7 +69,7 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
         }
         override_acl(self.user, forums_acl)
 
-        response = self.client.get(self.link)
+        response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 403)
 
     def test_cant_reply_thread_in_locked_forum(self):
@@ -80,7 +87,7 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
         }
         override_acl(self.user, forums_acl)
 
-        response = self.client.get(self.link)
+        response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 403)
 
     def test_cant_reply_closed_thread(self):
@@ -88,24 +95,20 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
         self.thread.is_closed = True
         self.thread.save()
 
-        forums_acl = self.user.acl
-        forums_acl['visible_forums'].append(self.forum.pk)
-        forums_acl['forums'][self.forum.pk] = {
-            'can_see': 1,
-            'can_browse': 1,
-            'can_see_all_threads': 1,
-            'can_reply_threads': 1,
-        }
-        override_acl(self.user, forums_acl)
-
-        response = self.client.get(self.link)
+        self.allow_reply_thread()
+        response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 403)
 
+        # now let us reply to closed threads
+        self.allow_reply_thread({'can_close_threads': 1})
+        response = self.client.get(self.link, **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+
     def test_cant_reply_thread_as_guest(self):
         """guests can't reply threads"""
         self.client.post(reverse(settings.LOGOUT_URL))
 
-        response = self.client.get(self.link)
+        response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 403)
 
     def test_can_reply_thread(self):
@@ -159,3 +162,69 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
         self.assertEqual(updated_forum.last_poster_name,
                          updated_user.username)
         self.assertEqual(updated_forum.last_poster_slug, updated_user.slug)
+
+    def test_can_close_replied_thread(self):
+        """can close/open thread while replying to it"""
+        prefix = 'misago.threads.posting.threadclose.ThreadCloseFormMiddleware'
+        field_name = '%s-is_closed' % prefix
+
+        self.allow_reply_thread({'can_close_threads': 1})
+        response = self.client.get(self.link, **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+        self.assertIn(field_name, response.content)
+
+        self.allow_reply_thread({'can_close_threads': 1})
+        response = self.client.post(self.link, data={
+            'post': 'Lorem ipsum dolor met!',
+            field_name: 1,
+            'submit': True,
+        },
+        **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+        self.assertTrue(Thread.objects.get(id=self.thread.id).is_closed)
+
+        self.user.last_posted_on = None
+        self.user.save()
+
+        self.allow_reply_thread({'can_close_threads': 1})
+        response = self.client.post(self.link, data={
+            'post': 'Lorem ipsum dolor met!',
+            field_name: 0,
+            'submit': True,
+        },
+        **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+        self.assertFalse(Thread.objects.get(id=self.thread.id).is_closed)
+
+    def test_can_pin_replied_thread(self):
+        """can pin/unpin thread while replying to it"""
+        prefix = 'misago.threads.posting.threadpin.ThreadPinFormMiddleware'
+        field_name = '%s-is_pinned' % prefix
+
+        self.allow_reply_thread({'can_pin_threads': 1})
+        response = self.client.get(self.link, **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+        self.assertIn(field_name, response.content)
+
+        self.allow_reply_thread({'can_pin_threads': 1})
+        response = self.client.post(self.link, data={
+            'post': 'Lorem ipsum dolor met!',
+            field_name: 1,
+            'submit': True,
+        },
+        **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+        self.assertTrue(Thread.objects.get(id=self.thread.id).is_pinned)
+
+        self.user.last_posted_on = None
+        self.user.save()
+
+        self.allow_reply_thread({'can_pin_threads': 1})
+        response = self.client.post(self.link, data={
+            'post': 'Lorem ipsum dolor met!',
+            field_name: 0,
+            'submit': True,
+        },
+        **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+        self.assertFalse(Thread.objects.get(id=self.thread.id).is_pinned)

+ 86 - 6
misago/threads/tests/test_startthread_view.py

@@ -7,7 +7,7 @@ from misago.acl.testutils import override_acl
 from misago.forums.models import Forum
 from misago.users.testutils import AuthenticatedUserTestCase
 
-from misago.threads.models import Thread
+from misago.threads.models import Label, Thread
 
 
 class StartThreadTests(AuthenticatedUserTestCase):
@@ -21,7 +21,12 @@ class StartThreadTests(AuthenticatedUserTestCase):
             'forum_id': self.forum.id
         })
 
-    def allow_start_thread(self):
+        Label.objects.clear_cache()
+
+    def tearDown(self):
+        Label.objects.clear_cache()
+
+    def allow_start_thread(self, extra_acl=None):
         forums_acl = self.user.acl
         forums_acl['visible_forums'].append(self.forum.pk)
         forums_acl['forums'][self.forum.pk] = {
@@ -29,6 +34,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
             'can_browse': 1,
             'can_start_threads': 1,
         }
+
+        if extra_acl:
+            forums_acl['forums'][self.forum.pk].update(extra_acl)
         override_acl(self.user, forums_acl)
 
     def test_cant_see(self):
@@ -42,7 +50,7 @@ class StartThreadTests(AuthenticatedUserTestCase):
         }
         override_acl(self.user, forums_acl)
 
-        response = self.client.get(self.link)
+        response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 404)
 
     def test_cant_browse(self):
@@ -56,7 +64,7 @@ class StartThreadTests(AuthenticatedUserTestCase):
         }
         override_acl(self.user, forums_acl)
 
-        response = self.client.get(self.link)
+        response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 403)
 
     def test_cant_start_thread_in_locked_forum(self):
@@ -73,14 +81,14 @@ class StartThreadTests(AuthenticatedUserTestCase):
         }
         override_acl(self.user, forums_acl)
 
-        response = self.client.get(self.link)
+        response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 403)
 
     def test_cant_start_thread_as_guest(self):
         """guests can't start threads"""
         self.client.post(reverse(settings.LOGOUT_URL))
 
-        response = self.client.get(self.link)
+        response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 403)
 
     def test_can_start_thread(self):
@@ -138,3 +146,75 @@ class StartThreadTests(AuthenticatedUserTestCase):
         self.assertEqual(updated_forum.last_poster_name,
                          updated_user.username)
         self.assertEqual(updated_forum.last_poster_slug, updated_user.slug)
+
+    def test_start_closed_thread(self):
+        """can post closed thread"""
+        prefix = 'misago.threads.posting.threadclose.ThreadCloseFormMiddleware'
+        field_name = '%s-is_closed' % prefix
+
+        self.allow_start_thread({'can_close_threads': 1})
+        response = self.client.get(self.link, **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+        self.assertIn(field_name, response.content)
+
+        self.allow_start_thread({'can_close_threads': 1})
+        response = self.client.post(self.link, data={
+            'title': 'Hello, I am test thread!',
+            'post': 'Lorem ipsum dolor met!',
+            field_name: 1,
+            'submit': True,
+        },
+        **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+
+        last_thread = self.user.thread_set.all()[:1][0]
+        self.assertTrue(last_thread.is_closed)
+
+    def test_start_pinned_thread(self):
+        """can post pinned thread"""
+        prefix = 'misago.threads.posting.threadpin.ThreadPinFormMiddleware'
+        field_name = '%s-is_pinned' % prefix
+
+        self.allow_start_thread({'can_pin_threads': 1})
+        response = self.client.get(self.link, **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+        self.assertIn(field_name, response.content)
+
+        self.allow_start_thread({'can_pin_threads': 1})
+        response = self.client.post(self.link, data={
+            'title': 'Hello, I am test thread!',
+            'post': 'Lorem ipsum dolor met!',
+            field_name: 1,
+            'submit': True,
+        },
+        **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+
+        last_thread = self.user.thread_set.all()[:1][0]
+        self.assertTrue(last_thread.is_pinned)
+
+    def test_start_labeled_thread(self):
+        """can post labeled thread"""
+        prefix = 'misago.threads.posting.threadlabel.ThreadLabelFormMiddleware'
+        field_name = '%s-label' % prefix
+
+        label = Label.objects.create(name="Label", slug="label")
+        label.forums.add(self.forum)
+
+        self.allow_start_thread({'can_change_threads_labels': 1})
+        response = self.client.get(self.link, **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+        self.assertIn(field_name, response.content)
+
+        self.allow_start_thread({'can_change_threads_labels': 1})
+        response = self.client.post(self.link, data={
+            'title': 'Hello, I am test thread!',
+            'post': 'Lorem ipsum dolor met!',
+            field_name: label.pk,
+            'submit': True,
+        },
+        **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+
+        last_thread = self.user.thread_set.all()[:1][0]
+        self.assertEqual(last_thread.label_id, label.id)

+ 1 - 1
misago/threads/views/posting.py

@@ -1,7 +1,7 @@
 from django.contrib import messages
 from django.db.transaction import atomic
 from django.http import JsonResponse
-from django.shortcuts import redirect
+from django.shortcuts import redirect, render
 from django.utils.translation import ugettext as _
 from django.views.generic import View
 

+ 2 - 2
misago/users/migrations/0001_initial.py

@@ -62,8 +62,8 @@ class Migration(migrations.Migration):
                 ('subscribe_to_replied_threads', models.PositiveIntegerField(default=0)),
                 ('threads', models.PositiveIntegerField(default=0)),
                 ('posts', models.PositiveIntegerField(default=0, db_index=True)),
-                ('last_post', models.DateTimeField(null=True, blank=True)),
-                ('last_search', models.DateTimeField(null=True, blank=True)),
+                ('last_posted_on', models.DateTimeField(null=True, blank=True)),
+                ('last_searched_on', models.DateTimeField(null=True, blank=True)),
                 ('reads_cutoff', models.DateTimeField(default=django.utils.timezone.now)),
                 ('new_threads_cutoff', models.DateTimeField(default=django.utils.timezone.now)),
                 ('unread_threads_cutoff', models.DateTimeField(default=django.utils.timezone.now))

+ 2 - 2
misago/users/models/user.py

@@ -217,8 +217,8 @@ class User(AbstractBaseUser, PermissionsMixin):
     threads = models.PositiveIntegerField(default=0)
     posts = models.PositiveIntegerField(default=0, db_index=True)
 
-    last_post = models.DateTimeField(null=True, blank=True)
-    last_search = models.DateTimeField(null=True, blank=True)
+    last_posted_on = models.DateTimeField(null=True, blank=True)
+    last_searched_on = models.DateTimeField(null=True, blank=True)
 
     reads_cutoff = models.DateTimeField(default=dj_timezone.now)
     new_threads_cutoff = models.DateTimeField(default=dj_timezone.now)