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

fix #869: changed the math in goto view and added bruteforcing tests for it

Rafał Pitoń 8 лет назад
Родитель
Сommit
a70edde8bc
2 измененных файлов с 97 добавлено и 20 удалено
  1. 79 0
      misago/threads/tests/test_gotoviews.py
  2. 18 20
      misago/threads/views/goto.py

+ 79 - 0
misago/threads/tests/test_gotoviews.py

@@ -248,3 +248,82 @@ class GotoUnapprovedTests(GotoViewTestCase):
         self.assertEqual(
             response['location'], GOTO_PAGE_URL % (self.thread.get_absolute_url(), 2, post.pk)
         )
+
+
+class ThreadGotoPostTests(GotoViewTestCase):
+    """brureforcing regression tests for regression test for #869"""
+    def test_thread_growing_post_goto(self):
+        """growing thread goto views don't fail"""
+        for _ in range(60):
+            post = testutils.reply_thread(self.thread, posted_on=timezone.now())
+
+            # go to post link is valid
+            post_url = self.client.get(post.get_absolute_url())['location']
+
+            response = self.client.get(post_url)
+            self.assertContains(response, post.get_absolute_url())
+
+            # go to last post link is valid
+            last_url = self.client.get(self.thread.get_last_post_url())['location']
+            self.assertEqual(post_url, last_url)
+
+    def test_thread_growing_event_goto(self):
+        """growing thread goto views don't fail for events"""
+        for i in range(60):
+            testutils.reply_thread(self.thread, posted_on=timezone.now())
+
+            post = testutils.reply_thread(self.thread, posted_on=timezone.now())
+            post.is_event = True
+            post.save()
+
+            # go to post link is valid
+            post_url = self.client.get(post.get_absolute_url())['location']
+
+            if i == 0:
+                # manually set events flag after first event was created
+                self.thread.has_events = True
+                self.thread.save()
+
+            response = self.client.get(post_url)
+            self.assertContains(response, post.get_absolute_url())
+
+            # go to last post link is valid
+            last_url = self.client.get(self.thread.get_last_post_url())['location']
+            self.assertEqual(post_url, last_url)
+
+    def test_thread_post_goto(self):
+        """thread goto views don't fail"""
+        for _ in range(60):
+            testutils.reply_thread(self.thread, posted_on=timezone.now())
+
+        for post in self.thread.post_set.order_by('id').iterator():
+            # go to post link is valid
+            post_url = self.client.get(post.get_absolute_url())['location']
+
+            response = self.client.get(post_url)
+            self.assertContains(response, post.get_absolute_url())
+
+        # go to last post link is valid
+        last_url = self.client.get(self.thread.get_last_post_url())['location']
+        self.assertEqual(post_url, last_url)
+
+    def test_thread_event_goto(self):
+        """thread goto views don't fail for events"""
+        for _ in range(60):
+            testutils.reply_thread(self.thread, posted_on=timezone.now())
+
+            post = testutils.reply_thread(self.thread, posted_on=timezone.now())
+            post.is_event = True
+            post.save()
+
+        for post in self.thread.post_set.filter(is_event=True).order_by('id').iterator():
+            # go to post link is valid
+            post_url = self.client.get(post.get_absolute_url())['location']
+
+            response = self.client.get(post_url)
+            self.assertContains(response, post.get_absolute_url())
+
+        # go to last post link is valid
+        last_url = self.client.get(self.thread.get_last_post_url())['location']
+        self.assertEqual(post_url, last_url)
+

+ 18 - 20
misago/threads/views/goto.py

@@ -35,34 +35,32 @@ class GotoView(View):
         raise NotImplementedError("goto views should define their own get_target_post method")
 
     def compute_post_page(self, target_post, posts_queryset):
-        # filter out events
-        posts_queryset = posts_queryset.filter(is_event=False)
+        # filter out events, order queryset
+        posts_queryset = posts_queryset.filter(is_event=False).order_by('id')
 
-        thread_len = posts_queryset.count()
-        if thread_len <= settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL:
-            return 1  # no chance for post to be on other page than only page
-
-        # compute total count of thread pages
-        hits = max(1, thread_len - settings.MISAGO_POSTS_TAIL)
-        hits += int(ceil(hits / float(settings.MISAGO_POSTS_PER_PAGE)))
-        thread_pages = int(ceil(hits / float(settings.MISAGO_POSTS_PER_PAGE)))
+        thread_length = posts_queryset.count()
 
         # is target an event?
         if target_post.is_event:
             target_event = target_post
-            previous_posts = posts_queryset.filter(pk__lt=target_event.pk)
-            target_post = previous_posts.order_by('id').last()
+            previous_posts = posts_queryset.filter(id__lt=target_event.id)
+        else:
+            previous_posts = posts_queryset.filter(id__lte=target_post.id)
+
+        post_position = previous_posts.count()
+
+        per_page = settings.MISAGO_POSTS_PER_PAGE - 1
+        orphans = settings.MISAGO_POSTS_TAIL
+        if orphans:
+            orphans += 1
 
-        # resolve post's page
-        post_offset = posts_queryset.filter(pk__lte=target_post.pk).count()
-        hits = max(1, post_offset)
-        hits += int(ceil(hits / float(settings.MISAGO_POSTS_PER_PAGE)))
-        post_page = int(ceil(hits / float(settings.MISAGO_POSTS_PER_PAGE)))
+        hits = max(1, thread_length - orphans)
+        thread_pages = int(ceil(hits / float(per_page)))
 
-        if post_page > thread_pages:
-            post_page = thread_pages
+        if post_position >= thread_pages * per_page:
+            return thread_pages
 
-        return post_page
+        return int(ceil(float(post_position) / (per_page)))
 
     def get_redirect(self, thread, target_post, target_page):
         thread_url = thread.thread_type.get_thread_absolute_url(thread, target_page)