예제 #1
0
    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)
예제 #2
0
    def test_user_post(self):
        """user post doesn't show in feed because its not first post in thread"""
        testutils.reply_thread(self.thread, poster=self.user)

        response = self.client.get(self.api_link)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json()['count'], 0)
예제 #3
0
    def test_merge_threads_merge_conflict_best_answer(self):
        """api errors on merge conflict, returning list of available best answers"""
        self.override_acl({'can_merge_threads': 1})
        self.override_other_acl({'can_merge_threads': 1})

        best_answer = testutils.reply_thread(self.thread)
        self.thread.set_best_answer(self.user, best_answer)
        self.thread.save()
        
        other_thread = testutils.post_thread(self.category_b)
        other_best_answer = testutils.reply_thread(other_thread)
        other_thread.set_best_answer(self.user, other_best_answer)
        other_thread.save()

        response = self.client.post(
            self.api_link, {
                'other_thread': other_thread.get_absolute_url(),
            }
        )
        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json(), {
            'best_answers': [
                ['0', "Unmark all best answers"],
                [str(self.thread.id), self.thread.title],
                [str(other_thread.id), other_thread.title],
            ]
        })

        # best answers were untouched
        self.assertEqual(self.thread.post_set.count(), 2)
        self.assertEqual(other_thread.post_set.count(), 2)
        self.assertEqual(Thread.objects.get(pk=self.thread.pk).best_answer_id, best_answer.id)
        self.assertEqual(
            Thread.objects.get(pk=other_thread.pk).best_answer_id, other_best_answer.id)
예제 #4
0
    def test_user_event(self):
        """events don't show in feeds at all"""
        testutils.reply_thread(self.thread, poster=self.user, is_event=True)

        response = self.client.get(self.api_link)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json()['count'], 0)
예제 #5
0
    def test_merge_best_answer_in_protected(self):
        """api merges best answer into protected post"""
        best_answer = testutils.reply_thread(self.thread, poster="Bob")

        self.thread.set_best_answer(self.user, best_answer)
        self.thread.save()

        response = self.client.post(
            self.api_link,
            json.dumps({
                'posts': [
                    best_answer.pk,
                    testutils.reply_thread(self.thread,
                                           poster="Bob",
                                           is_protected=True).pk,
                ]
            }),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, 200)

        self.refresh_thread()
        self.assertEqual(self.thread.best_answer, best_answer)
        self.assertTrue(self.thread.best_answer.is_protected)
        self.assertTrue(self.thread.best_answer_is_protected)
예제 #6
0
    def test_category_prune_by_last_reply(self):
        """command prunes category content based on last reply date"""
        category = Category.objects.all_categories()[:1][0]

        category.prune_replied_after = 20
        category.save()

        # post old threads with recent replies
        started_on = timezone.now() - timedelta(days=30)
        for t in range(10):
            thread = testutils.post_thread(category, started_on=started_on)
            testutils.reply_thread(thread)

        # post recent threads that will be preserved
        threads = [testutils.post_thread(category) for t in range(10)]

        category.synchronize()
        self.assertEqual(category.threads, 20)
        self.assertEqual(category.posts, 30)

        # run command
        command = prunecategories.Command()

        out = StringIO()
        command.execute(stdout=out)

        category.synchronize()
        self.assertEqual(category.threads, 10)
        self.assertEqual(category.posts, 10)

        for thread in threads:
            category.thread_set.get(id=thread.id)

        command_output = out.getvalue().strip()
        self.assertEqual(command_output, 'Categories were pruned')
예제 #7
0
    def test_threads_merge_conflict_unmark_all_best_answers(self):
        """api unmarks all best answers when unmark all choice is selected"""
        self.override_acl({'can_merge_threads': 1})
        self.override_other_acl({'can_merge_threads': 1})

        best_answer = testutils.reply_thread(self.thread)
        self.thread.set_best_answer(self.user, best_answer)
        self.thread.save()

        other_thread = testutils.post_thread(self.category_b)
        other_best_answer = testutils.reply_thread(other_thread)
        other_thread.set_best_answer(self.user, other_best_answer)
        other_thread.save()

        response = self.client.post(
            self.api_link, {
                'other_thread': other_thread.get_absolute_url(),
                'best_answer': 0,
            })
        self.assertContains(response,
                            other_thread.get_absolute_url(),
                            status_code=200)

        # other thread has four posts and an event now
        self.assertEqual(other_thread.post_set.count(), 5)

        # first thread is gone
        with self.assertRaises(Thread.DoesNotExist):
            Thread.objects.get(pk=self.thread.pk)

        # final thread has no marked best answer
        self.assertIsNone(
            Thread.objects.get(pk=other_thread.pk).best_answer_id)
    def test_middleware_counts_unread_thread(self):
        """middleware counts thread with unread reply, post read flags user for recount"""
        self.user.sync_unread_private_threads = True
        self.user.save()

        self.client.post(self.thread.last_post.get_read_api_url())

        # post read zeroed list of unread private threads
        self.reload_user()
        self.assertFalse(self.user.sync_unread_private_threads)
        self.assertEqual(self.user.unread_private_threads, 0)

        # reply to thread
        testutils.reply_thread(self.thread)

        self.user.sync_unread_private_threads = True
        self.user.save()

        # middleware did recount and accounted for new unread post
        response = self.client.get('/')
        self.assertEqual(response.status_code, 200)

        self.reload_user()
        self.assertFalse(self.user.sync_unread_private_threads)
        self.assertEqual(self.user.unread_private_threads, 1)
예제 #9
0
    def test_threads_merge_conflict_best_answer_invalid_resolution(self):
        """api errors on invalid merge conflict resolution"""
        self.override_acl({'can_merge_threads': 1})
        self.override_other_acl({'can_merge_threads': 1})

        best_answer = testutils.reply_thread(self.thread)
        self.thread.set_best_answer(self.user, best_answer)
        self.thread.save()

        other_thread = testutils.post_thread(self.category_b)
        other_best_answer = testutils.reply_thread(other_thread)
        other_thread.set_best_answer(self.user, other_best_answer)
        other_thread.save()

        response = self.client.post(
            self.api_link, {
                'other_thread': other_thread.get_absolute_url(),
                'best_answer': other_thread.id + 10,
            })
        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json(), {'detail': "Invalid choice."})

        # best answers were untouched
        self.assertEqual(self.thread.post_set.count(), 2)
        self.assertEqual(other_thread.post_set.count(), 2)
        self.assertEqual(
            Thread.objects.get(pk=self.thread.pk).best_answer_id,
            best_answer.id)
        self.assertEqual(
            Thread.objects.get(pk=other_thread.pk).best_answer_id,
            other_best_answer.id)
예제 #10
0
    def test_new(self):
        """new returns link to first unread post"""
        self.user.new_threads = MockThreadsCounter()
        self.user.unread_threads = MockThreadsCounter()

        post_link = goto.new(self.user, self.thread, self.thread.post_set)
        last_link = goto.last(self.thread, self.thread.post_set)
        self.assertEqual(post_link, last_link)

        # add 18 posts to add extra page to thread, then read them
        [reply_thread(self.thread) for p in xrange(18)]
        threadstracker.read_thread(
            self.user, self.thread, self.thread.last_post)

        # add extra unread posts
        first_unread = reply_thread(self.thread)
        [reply_thread(self.thread) for p in xrange(30)]

        new_link = goto.new(self.user, self.thread, self.thread.post_set)
        post_link = goto.get_post_link(
            50, self.thread.post_set, self.thread, first_unread)
        self.assertEqual(new_link, post_link)

        # read thread
        threadstracker.read_thread(
            self.user, self.thread, self.thread.last_post)

        # assert new() points to last reply
        post_link = goto.new(self.user, self.thread, self.thread.post_set)
        last_link = goto.last(self.thread, self.thread.post_set)
        self.assertEqual(post_link, last_link)
예제 #11
0
    def test_new(self):
        """new returns link to first unread post"""
        self.user.new_threads = MockThreadsCounter()
        self.user.unread_threads = MockThreadsCounter()

        post_link = goto.new(self.user, self.thread, self.thread.post_set)
        last_link = goto.last(self.thread, self.thread.post_set)
        self.assertEqual(post_link, last_link)

        # add extra page to thread, then read them
        [reply_thread(self.thread) for p in xrange(MAX_PAGE_LEN)]
        threadstracker.read_thread(self.user, self.thread,
                                   self.thread.last_post)

        # add extra unread posts
        first_unread = reply_thread(self.thread)
        [reply_thread(self.thread) for p in xrange(20)]

        new_link = goto.new(self.user, self.thread, self.thread.post_set)
        post_link = goto.get_post_link(MAX_PAGE_LEN + 21, self.thread.post_set,
                                       self.thread, first_unread)
        self.assertEqual(new_link, post_link)

        # read thread
        threadstracker.read_thread(self.user, self.thread,
                                   self.thread.last_post)

        # assert new() points to last reply
        post_link = goto.new(self.user, self.thread, self.thread.post_set)
        last_link = goto.last(self.thread, self.thread.post_set)
        self.assertEqual(post_link, last_link)
    def setUp(self):
        super(ThreadPostSplitApiTestCase, self).setUp()

        self.category = Category.objects.get(slug='first-category')
        self.thread = testutils.post_thread(category=self.category)
        self.posts = [
            testutils.reply_thread(self.thread).pk, testutils.reply_thread(self.thread).pk
        ]

        self.api_link = reverse(
            'misago:api:thread-post-split', kwargs={
                'thread_pk': self.thread.pk,
            }
        )

        Category(
            name='Category B',
            slug='category-b',
        ).insert_at(
            self.category,
            position='last-child',
            save=True,
        )
        self.category_b = Category.objects.get(slug='category-b')

        self.override_acl()
        self.override_other_acl()
    def test_move_posts(self):
        """api moves posts to other thread"""
        self.override_other_acl({'can_reply_threads': 1})

        other_thread = testutils.post_thread(self.category_b)

        posts = (
            testutils.reply_thread(self.thread).pk, testutils.reply_thread(self.thread).pk,
            testutils.reply_thread(self.thread).pk, testutils.reply_thread(self.thread).pk,
        )

        self.refresh_thread()
        self.assertEqual(self.thread.replies, 4)

        response = self.client.post(
            self.api_link,
            json.dumps({
                'thread_url': other_thread.get_absolute_url(),
                'posts': posts,
            }),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, 200)

        # replies were moved
        self.refresh_thread()
        self.assertEqual(self.thread.replies, 0)

        other_thread = Thread.objects.get(pk=other_thread.pk)
        self.assertEqual(other_thread.post_set.filter(pk__in=posts).count(), 4)
        self.assertEqual(other_thread.replies, 4)
예제 #14
0
    def test_merge_posts(self):
        """moderation allows for merging multiple posts into one"""
        posts = []
        for p in xrange(4):
            posts.append(reply_thread(self.thread, poster=self.user))
        for p in xrange(4):
            posts.append(reply_thread(self.thread))

        self.thread.synchronize()
        self.assertEqual(self.thread.replies, 8)

        test_acl = {
            'can_merge_posts': 1
        }

        self.override_acl(test_acl)
        response = self.client.get(self.thread.get_absolute_url())
        self.assertEqual(response.status_code, 200)
        self.assertIn("Merge posts into one", response.content)

        self.override_acl(test_acl)
        response = self.client.post(self.thread.get_absolute_url(), data={
            'action': 'merge', 'item': [p.pk for p in posts[:1]]
        })
        self.assertEqual(response.status_code, 200)
        self.assertIn("select at least two posts", response.content)

        thread = Thread.objects.get(pk=self.thread.pk)
        self.assertEqual(thread.replies, 8)

        self.override_acl(test_acl)
        response = self.client.post(self.thread.get_absolute_url(), data={
            'action': 'merge', 'item': [p.pk for p in posts[3:5]]
        })
        self.assertEqual(response.status_code, 200)
        self.assertIn("merge posts made by different authors",
                      response.content)

        thread = Thread.objects.get(pk=self.thread.pk)
        self.assertEqual(thread.replies, 8)

        self.override_acl(test_acl)
        response = self.client.post(self.thread.get_absolute_url(), data={
            'action': 'merge', 'item': [p.pk for p in posts[5:7]]
        })
        self.assertEqual(response.status_code, 200)
        self.assertIn("merge posts made by different authors",
                      response.content)

        thread = Thread.objects.get(pk=self.thread.pk)
        self.assertEqual(thread.replies, 8)

        self.override_acl(test_acl)
        response = self.client.post(self.thread.get_absolute_url(), data={
            'action': 'merge', 'item': [p.pk for p in posts[:4]]
        })
        self.assertEqual(response.status_code, 302)

        thread = Thread.objects.get(pk=self.thread.pk)
        self.assertEqual(thread.replies, 5)
예제 #15
0
    def test_threads_merge_conflict_keep_other_best_answer(self):
        """api unmarks first best answer on merge"""
        self.override_acl({'can_merge_threads': 1})
        self.override_other_acl({'can_merge_threads': 1})

        best_answer = testutils.reply_thread(self.thread)
        self.thread.set_best_answer(self.user, best_answer)
        self.thread.save()

        other_thread = testutils.post_thread(self.category_b)
        other_best_answer = testutils.reply_thread(other_thread)
        other_thread.set_best_answer(self.user, other_best_answer)
        other_thread.save()

        response = self.client.post(
            self.api_link, {
                'other_thread': other_thread.get_absolute_url(),
                'best_answer': other_thread.pk,
            })
        self.assertContains(response,
                            other_thread.get_absolute_url(),
                            status_code=200)

        # other thread has four posts and an event now
        self.assertEqual(other_thread.post_set.count(), 5)

        # first thread is gone
        with self.assertRaises(Thread.DoesNotExist):
            Thread.objects.get(pk=self.thread.pk)

        # other thread's best answer was changed to merged in thread's answer
        self.assertEqual(
            Thread.objects.get(pk=other_thread.pk).best_answer_id,
            other_best_answer.id)
예제 #16
0
    def test_approve_posts(self):
        """moderation allows for approving multiple posts"""
        posts = []
        for p in xrange(4):
            posts.append(reply_thread(self.thread, is_moderated=True))
        for p in xrange(4):
            posts.append(reply_thread(self.thread))

        self.assertTrue(self.reload_thread().has_moderated_posts)
        self.assertEqual(self.thread.replies, 4)

        test_acl = {'can_review_moderated_content': 1}

        self.override_acl(test_acl)
        response = self.client.get(self.thread.get_absolute_url())
        self.assertEqual(response.status_code, 200)
        self.assertIn("Approve posts", response.content)

        self.override_acl(test_acl)
        response = self.client.post(self.thread.get_absolute_url(),
                                    data={
                                        'action': 'approve',
                                        'item': [p.pk for p in posts]
                                    })
        self.assertEqual(response.status_code, 302)

        self.assertFalse(self.reload_thread().has_moderated_posts)
        self.assertEqual(self.reload_thread().replies, 8)

        for post in posts:
            self.assertFalse(self.thread.post_set.get(id=post.id).is_moderated)
    def test_middleware_counts_unread_thread(self):
        """middleware counts thread with unread reply, post read flags user for recount"""
        self.user.sync_unread_private_threads = True
        self.user.save()

        self.client.post(self.thread.last_post.get_read_api_url())

        # post read zeroed list of unread private threads
        self.reload_user()
        self.assertFalse(self.user.sync_unread_private_threads)
        self.assertEqual(self.user.unread_private_threads, 0)

        # reply to thread
        testutils.reply_thread(self.thread)

        self.user.sync_unread_private_threads = True
        self.user.save()

        # middleware did recount and accounted for new unread post
        response = self.client.get('/')
        self.assertEqual(response.status_code, 200)

        self.reload_user()
        self.assertFalse(self.user.sync_unread_private_threads)
        self.assertEqual(self.user.unread_private_threads, 1)
예제 #18
0
    def test_threads_merge_conflict_keep_other_best_answer(self):
        """api unmarks first best answer on merge"""
        self.override_acl({'can_merge_threads': 1})

        best_answer = testutils.reply_thread(self.thread)
        self.thread.set_best_answer(self.user, best_answer)
        self.thread.save()

        other_thread = testutils.post_thread(self.category)
        other_best_answer = testutils.reply_thread(other_thread)
        other_thread.set_best_answer(self.user, other_best_answer)
        other_thread.save()

        response = self.client.post(
            self.api_link,
            json.dumps({
                'threads': [self.thread.id, other_thread.id],
                'title': 'Merged thread!',
                'category': self.category.id,
                'best_answer': other_thread.pk,
            }),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, 200)

        # selected best answer is set on new thread
        new_thread = Thread.objects.get(pk=response.json()['id'])
        self.assertEqual(new_thread.best_answer_id, other_best_answer.id)
예제 #19
0
    def test_threads_merge_conflict_best_answer_invalid_resolution(self):
        """api errors on invalid merge conflict resolution"""
        self.override_acl({'can_merge_threads': 1})

        best_answer = testutils.reply_thread(self.thread)
        self.thread.set_best_answer(self.user, best_answer)
        self.thread.save()

        other_thread = testutils.post_thread(self.category)
        other_best_answer = testutils.reply_thread(other_thread)
        other_thread.set_best_answer(self.user, other_best_answer)
        other_thread.save()

        response = self.client.post(
            self.api_link,
            json.dumps({
                'threads': [self.thread.id, other_thread.id],
                'title': 'Merged thread!',
                'category': self.category.id,
                'best_answer': other_thread.id + 10,
            }),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json(), {'best_answer': ["Invalid choice."]})

        # best answers were untouched
        self.assertEqual(self.thread.post_set.count(), 2)
        self.assertEqual(other_thread.post_set.count(), 2)
        self.assertEqual(
            Thread.objects.get(pk=self.thread.pk).best_answer_id,
            best_answer.id)
        self.assertEqual(
            Thread.objects.get(pk=other_thread.pk).best_answer_id,
            other_best_answer.id)
예제 #20
0
    def test_merge_posts(self):
        """api merges two posts"""
        post_a = testutils.reply_thread(self.thread,
                                        poster=self.user,
                                        message="Battęry")
        post_b = testutils.reply_thread(self.thread,
                                        poster=self.user,
                                        message="Hórse")

        thread_replies = self.thread.replies

        response = self.client.post(
            self.api_link,
            json.dumps({'posts': [post_a.pk, post_b.pk]}),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, 200)

        self.refresh_thread()
        self.assertEqual(self.thread.replies, thread_replies - 1)

        with self.assertRaises(Post.DoesNotExist):
            Post.objects.get(pk=post_b.pk)

        merged_post = Post.objects.get(pk=post_a.pk)
        self.assertEqual(merged_post.parsed,
                         '{}\n{}'.format(post_a.parsed, post_b.parsed))
예제 #21
0
    def test_forum_prune_by_last_reply(self):
        """command prunes forum content based on last reply date"""
        forum = Forum.objects.all_forums().filter(role="forum")[:1][0]

        forum.prune_replied_after = 20
        forum.save()

        # post old threads with recent replies
        started_on = timezone.now() - timedelta(days=30)
        for t in xrange(10):
            thread = testutils.post_thread(forum, started_on=started_on)
            testutils.reply_thread(thread)

        # post recent threads that will be preserved
        threads = [testutils.post_thread(forum) for t in xrange(10)]

        forum.synchronize()
        self.assertEqual(forum.threads, 20)
        self.assertEqual(forum.posts, 30)

        # run command
        command = pruneforums.Command()

        out = StringIO()
        command.execute(stdout=out)

        forum.synchronize()
        self.assertEqual(forum.threads, 10)
        self.assertEqual(forum.posts, 10)

        for thread in threads:
            forum.thread_set.get(id=thread.id)

        command_output = out.getvalue().strip()
        self.assertEqual(command_output, 'Forums were pruned')
예제 #22
0
    def setUp(self):
        super(ThreadPostSplitApiTestCase, self).setUp()

        self.category = Category.objects.get(slug='first-category')
        self.thread = testutils.post_thread(category=self.category)
        self.posts = [
            testutils.reply_thread(self.thread).pk,
            testutils.reply_thread(self.thread).pk
        ]

        self.api_link = reverse('misago:api:thread-post-split',
                                kwargs={
                                    'thread_pk': self.thread.pk,
                                })

        Category(
            name='Category B',
            slug='category-b',
        ).insert_at(
            self.category,
            position='last-child',
            save=True,
        )
        self.category_b = Category.objects.get(slug='category-b')

        self.override_acl()
        self.override_other_acl()
예제 #23
0
    def test_closed_category(self):
        """api validates permission to merge in closed category"""
        self.category.is_closed = True
        self.category.save()

        posts = [
            testutils.reply_thread(self.thread, poster=self.user).pk,
            testutils.reply_thread(self.thread, poster=self.user).pk,
        ]

        response = self.client.post(
            self.api_link,
            json.dumps({'posts': posts}),
            content_type="application/json",
        )
        self.assertContains(
            response,
            "This category is closed. You can't merge posts in it.",
            status_code=400,
        )

        # allow closing threads
        self.override_acl({'can_close_threads': 1})

        response = self.client.post(
            self.api_link,
            json.dumps({'posts': posts}),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, 200)
예제 #24
0
    def test_user_post(self):
        """user post doesn't show in feed because its not first post in thread"""
        testutils.reply_thread(self.thread, poster=self.user)

        response = self.client.get(self.api_link)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json()['count'], 0)
예제 #25
0
    def test_api_validates_posts_visibility(self):
        """api validates posts visiblity"""
        self.override_acl({'can_hide_posts': 0})

        hidden_post = testutils.reply_thread(
            self.thread,
            is_hidden=True,
            message="I'am hidden test message!",
        )

        response = self.client.get(self.tested_links[1])
        self.assertNotContains(response,
                               hidden_post.parsed)  # post's body is hidden

        # add permission to see hidden posts
        self.override_acl({'can_hide_posts': 1})

        response = self.client.get(self.tested_links[1])
        self.assertContains(response, hidden_post.parsed
                            )  # hidden post's body is visible with permission

        # unapproved posts shouldn't show at all
        unapproved_post = testutils.reply_thread(
            self.thread,
            is_unapproved=True,
        )

        self.override_acl({'can_approve_content': 0})
        response = self.client.get(self.tested_links[1])
        self.assertNotContains(response, unapproved_post.get_absolute_url())

        # add permission to see unapproved posts
        self.override_acl({'can_approve_content': 1})
        response = self.client.get(self.tested_links[1])
        self.assertContains(response, unapproved_post.parsed)
예제 #26
0
    def test_move_posts(self):
        """api moves posts to other thread"""
        self.override_other_acl({'can_reply_threads': 1})

        other_thread = testutils.post_thread(self.category_b)

        posts = (
            testutils.reply_thread(self.thread).pk,
            testutils.reply_thread(self.thread).pk,
            testutils.reply_thread(self.thread).pk,
            testutils.reply_thread(self.thread).pk,
        )

        self.refresh_thread()
        self.assertEqual(self.thread.replies, 4)

        response = self.client.post(self.api_link,
                                    json.dumps({
                                        'thread_url':
                                        other_thread.get_absolute_url(),
                                        'posts':
                                        posts
                                    }),
                                    content_type="application/json")
        self.assertEqual(response.status_code, 200)

        # replies were moved
        self.refresh_thread()
        self.assertEqual(self.thread.replies, 0)

        other_thread = Thread.objects.get(pk=other_thread.pk)
        self.assertEqual(other_thread.post_set.filter(pk__in=posts).count(), 4)
        self.assertEqual(other_thread.replies, 4)
예제 #27
0
    def test_category_prune_by_last_reply(self):
        """command prunes category content based on last reply date"""
        category = Category.objects.all_categories()[:1][0]

        category.prune_replied_after = 20
        category.save()

        # post old threads with recent replies
        started_on = timezone.now() - timedelta(days=30)
        for _ in range(10):
            thread = testutils.post_thread(category, started_on=started_on)
            testutils.reply_thread(thread)

        # post recent threads that will be preserved
        threads = [testutils.post_thread(category) for _ in range(10)]

        category.synchronize()
        self.assertEqual(category.threads, 20)
        self.assertEqual(category.posts, 30)

        # run command
        command = prunecategories.Command()

        out = StringIO()
        call_command(command, stdout=out)

        category.synchronize()
        self.assertEqual(category.threads, 10)
        self.assertEqual(category.posts, 10)

        for thread in threads:
            category.thread_set.get(id=thread.id)

        command_output = out.getvalue().strip()
        self.assertEqual(command_output, 'Categories were pruned')
예제 #28
0
    def test_delete_posts(self):
        """moderation allows for deleting posts"""
        posts = [reply_thread(self.thread) for t in xrange(10)]

        self.thread.synchronize()
        self.assertEqual(self.thread.replies, 10)

        test_acl = {'can_hide_posts': 2}

        self.override_acl(test_acl)
        response = self.client.get(self.thread.get_absolute_url())
        self.assertEqual(response.status_code, 200)
        self.assertIn("Delete posts", response.content)

        self.override_acl(test_acl)
        response = self.client.post(self.thread.get_absolute_url(),
                                    data={
                                        'action': 'delete',
                                        'item': [p.pk for p in posts]
                                    })
        self.assertEqual(response.status_code, 302)
        self.assertTrue(response['location'].endswith(
            self.thread.get_absolute_url()))

        thread = Thread.objects.get(pk=self.thread.pk)
        self.assertEqual(thread.replies, 0)

        posts = [reply_thread(self.thread) for t in xrange(30)]

        second_page_link = reverse('misago:thread',
                                   kwargs={
                                       'thread_id': self.thread.id,
                                       'thread_slug': self.thread.slug,
                                       'page': 2
                                   })

        self.override_acl(test_acl)
        response = self.client.post(second_page_link,
                                    data={
                                        'action': 'delete',
                                        'item': [p.pk for p in posts[10:20]]
                                    })
        self.assertEqual(response.status_code, 302)
        self.assertTrue(response['location'].endswith(second_page_link))

        thread = Thread.objects.get(pk=self.thread.pk)
        self.assertEqual(thread.replies, 20)

        self.override_acl(test_acl)
        response = self.client.post(second_page_link,
                                    data={
                                        'action': 'delete',
                                        'item': [p.pk for p in posts[:-10]]
                                    })
        self.assertEqual(response.status_code, 302)
        self.assertTrue(response['location'].endswith(
            self.thread.get_absolute_url()))

        thread = Thread.objects.get(pk=self.thread.pk)
        self.assertEqual(thread.replies, 10)
예제 #29
0
    def test_approve_posts(self):
        """moderation allows for approving multiple posts"""
        posts = []
        for p in xrange(4):
            posts.append(reply_thread(self.thread, is_moderated=True))
        for p in xrange(4):
            posts.append(reply_thread(self.thread))

        self.assertTrue(self.reload_thread().has_moderated_posts)
        self.assertEqual(self.thread.replies, 4)

        test_acl = {
            'can_review_moderated_content': 1
        }

        self.override_acl(test_acl)
        response = self.client.get(self.thread.get_absolute_url())
        self.assertEqual(response.status_code, 200)
        self.assertIn("Approve posts", response.content)

        self.override_acl(test_acl)
        response = self.client.post(self.thread.get_absolute_url(), data={
            'action': 'approve', 'item': [p.pk for p in posts]
        })
        self.assertEqual(response.status_code, 302)

        self.assertFalse(self.reload_thread().has_moderated_posts)
        self.assertEqual(self.reload_thread().replies, 8)

        for post in posts:
            self.assertFalse(self.thread.post_set.get(id=post.id).is_moderated)
    def test_filled_threads_list(self):
        """filled threads list is rendered"""
        forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
        threads = [testutils.post_thread(forum) for t in xrange(10)]

        # only unread tracker threads are shown on unread list
        response = self.client.get(reverse('misago:unread_threads'))
        self.assertEqual(response.status_code, 200)
        self.assertIn("There are no threads with unread", response.content)

        # we'll read and reply to first five threads
        for thread in threads[5:]:
            response = self.client.get(thread.get_absolute_url())
            testutils.reply_thread(thread, posted_on=timezone.now())

        # assert that replied threads show on list
        response = self.client.get(reverse('misago:unread_threads'))
        self.assertEqual(response.status_code, 200)
        for thread in threads[5:]:
            self.assertIn(thread.get_absolute_url(), response.content)
        for thread in threads[:5]:
            self.assertNotIn(thread.get_absolute_url(), response.content)

        # clear list
        response = self.client.post(reverse('misago:clear_unread_threads'))
        self.assertEqual(response.status_code, 302)

        response = self.client.get(response['location'])
        self.assertEqual(response.status_code, 200)
        self.assertIn("There are no threads with unread", response.content)
예제 #31
0
    def test_threads_merge_conflict_unmark_all_best_answers(self):
        """api unmarks all best answers when unmark all choice is selected"""
        self.override_acl({'can_merge_threads': 1})

        best_answer = testutils.reply_thread(self.thread)
        self.thread.set_best_answer(self.user, best_answer)
        self.thread.save()

        other_thread = testutils.post_thread(self.category)
        other_best_answer = testutils.reply_thread(other_thread)
        other_thread.set_best_answer(self.user, other_best_answer)
        other_thread.save()

        response = self.client.post(
            self.api_link,
            json.dumps({
                'threads': [self.thread.id, other_thread.id],
                'title': 'Merged thread!',
                'category': self.category.id,
                'best_answer': 0,
            }),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, 200)

        # best answer is not set on new thread
        new_thread = Thread.objects.get(pk=response.json()['id'])
        self.assertFalse(new_thread.has_best_answer)
        self.assertIsNone(new_thread.best_answer_id)
예제 #32
0
    def test_user_event(self):
        """events don't show in feeds at all"""
        testutils.reply_thread(self.thread, poster=self.user, is_event=True)

        response = self.client.get(self.api_link)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json()['count'], 0)
예제 #33
0
    def test_user_posts(self):
        """user posts show in feed"""
        post = testutils.reply_thread(self.thread, poster=self.user)
        other_post = testutils.reply_thread(self.thread, poster=self.user)

        response = self.client.get(self.api_link)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json()['count'], 2)
        self.assertEqual(response.json()['results'][0]['id'], other_post.pk)
        self.assertEqual(response.json()['results'][1]['id'], post.pk)
예제 #34
0
    def test_user_posts(self):
        """user posts show in feed"""
        post = testutils.reply_thread(self.thread, poster=self.user)
        other_post = testutils.reply_thread(self.thread, poster=self.user)

        response = self.client.get(self.api_link)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json()['count'], 2)
        self.assertEqual(response.json()['results'][0]['id'], other_post.pk)
        self.assertEqual(response.json()['results'][1]['id'], post.pk)
예제 #35
0
    def test_delete_posts(self):
        """moderation allows for deleting posts"""
        posts = [reply_thread(self.thread) for t in xrange(10)]

        self.thread.synchronize()
        self.assertEqual(self.thread.replies, 10)

        test_acl = {
            'can_hide_posts': 2
        }

        self.override_acl(test_acl)
        response = self.client.get(self.thread.get_absolute_url())
        self.assertEqual(response.status_code, 200)
        self.assertIn("Delete posts", response.content)

        self.override_acl(test_acl)
        response = self.client.post(self.thread.get_absolute_url(), data={
            'action': 'delete', 'item': [p.pk for p in posts]
        })
        self.assertEqual(response.status_code, 302)
        self.assertTrue(
            response['location'].endswith(self.thread.get_absolute_url()))

        thread = Thread.objects.get(pk=self.thread.pk)
        self.assertEqual(thread.replies, 0)

        posts = [reply_thread(self.thread) for t in xrange(30)]

        second_page_link = reverse('misago:thread', kwargs={
            'thread_id': self.thread.id,
            'thread_slug': self.thread.slug,
            'page': 2
        })

        self.override_acl(test_acl)
        response = self.client.post(second_page_link, data={
            'action': 'delete', 'item': [p.pk for p in posts[10:20]]
        })
        self.assertEqual(response.status_code, 302)
        self.assertTrue(response['location'].endswith(second_page_link))

        thread = Thread.objects.get(pk=self.thread.pk)
        self.assertEqual(thread.replies, 20)

        self.override_acl(test_acl)
        response = self.client.post(second_page_link, data={
            'action': 'delete', 'item': [p.pk for p in posts[:-10]]
        })
        self.assertEqual(response.status_code, 302)
        self.assertTrue(
            response['location'].endswith(self.thread.get_absolute_url()))

        thread = Thread.objects.get(pk=self.thread.pk)
        self.assertEqual(thread.replies, 10)
예제 #36
0
    def test_moderated_list(self):
        """moderated posts list works"""
        self.override_acl({'can_review_moderated_content': True})
        response = self.client.get(self.thread.get_moderated_url(),
                                   **self.ajax_header)
        self.assertEqual(response.status_code, 200)
        self.assertIn("0 unapproved posts", response.content)
        self.assertIn("There are no posts to display on this list.",
                      response.content)

        # post 10 not moderated posts
        [reply_thread(self.thread) for i in xrange(10)]

        # assert that posts don't show
        self.override_acl({'can_review_moderated_content': True})
        response = self.client.get(self.thread.get_moderated_url(),
                                   **self.ajax_header)
        self.assertEqual(response.status_code, 200)
        self.assertIn("0 unapproved posts", response.content)
        self.assertIn("There are no posts to display on this list.",
                      response.content)

        # post 10 reported posts
        posts = []
        for i in xrange(10):
            posts.append(reply_thread(self.thread, is_moderated=True))

        # assert that posts show
        self.override_acl({'can_review_moderated_content': True})
        response = self.client.get(self.thread.get_moderated_url(),
                                   **self.ajax_header)
        self.assertEqual(response.status_code, 200)
        self.assertIn("10 unapproved posts", response.content)
        self.assertNotIn("There are no posts to display on this list.",
                         response.content)

        for post in posts:
            self.assertIn(post.get_absolute_url(), response.content)

        # overflow list via posting extra 20 reported posts
        posts = []
        for i in xrange(20):
            posts.append(reply_thread(self.thread, is_moderated=True))

        # assert that posts don't show
        self.override_acl({'can_review_moderated_content': True})
        response = self.client.get(self.thread.get_moderated_url(),
                                   **self.ajax_header)
        self.assertEqual(response.status_code, 200)
        self.assertIn("30 unapproved posts", response.content)
        self.assertIn("This list is limited to last 15 posts.",
                      response.content)

        for post in posts[15:]:
            self.assertIn(post.get_absolute_url(), response.content)
예제 #37
0
    def test_get_post_page(self):
        """get_post_page returns valid page number for given queryset"""
        self.assertEqual(goto.get_post_page(1, self.thread.post_set), 1)

        # add 12 posts, bumping no of posts on page to to 13
        [reply_thread(self.thread) for p in xrange(12)]
        self.assertEqual(goto.get_post_page(13, self.thread.post_set), 1)

        # add 2 posts
        [reply_thread(self.thread) for p in xrange(2)]
        self.assertEqual(goto.get_post_page(15, self.thread.post_set), 2)
예제 #38
0
    def test_user_first_read_post_unread_event(self):
        """tracked thread with read first post and unread event"""
        thread = testutils.post_thread(self.category,
                                       started_on=timezone.now())
        poststracker.save_read(self.user, thread.first_post)

        testutils.reply_thread(thread, posted_on=timezone.now(), is_event=True)

        categoriestracker.make_read_aware(self.user, self.category)
        self.assertFalse(self.category.is_read)
        self.assertTrue(self.category.is_new)
예제 #39
0
    def test_user_thread(self):
        """user thread shows in feed"""
        thread = testutils.post_thread(category=self.category, poster=self.user)

        # this post will not show in feed
        testutils.reply_thread(thread, poster=self.user)

        response = self.client.get(self.api_link)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json()['count'], 1)
        self.assertEqual(response.json()['results'][0]['id'], thread.first_post_id)
예제 #40
0
    def test_get_post_page(self):
        """get_post_page returns valid page number for given queryset"""
        self.assertEqual(goto.get_post_page(1, self.thread.post_set), 1)

        # add 12 posts, bumping no of posts on page to to 13
        [reply_thread(self.thread) for p in xrange(12)]
        self.assertEqual(goto.get_post_page(13, self.thread.post_set), 1)

        # add 2 posts
        [reply_thread(self.thread) for p in xrange(2)]
        self.assertEqual(goto.get_post_page(15, self.thread.post_set), 2)
예제 #41
0
    def test_user_post_anonymous(self):
        """user post shows in feed requested by unauthenticated user"""
        post = testutils.reply_thread(self.thread, poster=self.user)
        other_post = testutils.reply_thread(self.thread, poster=self.user)

        self.logout_user()

        response = self.client.get(self.api_link)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json()['count'], 2)
        self.assertEqual(response.json()['results'][0]['id'], other_post.pk)
        self.assertEqual(response.json()['results'][1]['id'], post.pk)
예제 #42
0
    def test_user_post_anonymous(self):
        """user post shows in feed requested by unauthenticated user"""
        post = testutils.reply_thread(self.thread, poster=self.user)
        other_post = testutils.reply_thread(self.thread, poster=self.user)

        self.logout_user()

        response = self.client.get(self.api_link)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json()['count'], 2)
        self.assertEqual(response.json()['results'][0]['id'], other_post.pk)
        self.assertEqual(response.json()['results'][1]['id'], post.pk)
예제 #43
0
    def test_user_thread(self):
        """user thread shows in feed"""
        thread = testutils.post_thread(category=self.category,
                                       poster=self.user)

        # this post will not show in feed
        testutils.reply_thread(thread, poster=self.user)

        response = self.client.get(self.api_link)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json()['count'], 1)
        self.assertEqual(response.json()['results'][0]['id'],
                         thread.first_post_id)
예제 #44
0
    def test_get_post_page(self):
        """get_post_page returns valid page number for given queryset"""
        self.assertEqual(goto.get_post_page(1, self.thread.post_set), 1)

        # fill out page
        [reply_thread(self.thread) for p in xrange(MAX_PAGE_LEN - 1)]
        self.assertEqual(
            goto.get_post_page(MAX_PAGE_LEN, self.thread.post_set), 1)

        # add 2 posts, adding second page
        [reply_thread(self.thread) for p in xrange(2)]
        self.assertEqual(
            goto.get_post_page(MAX_PAGE_LEN + 2, self.thread.post_set), 2)
예제 #45
0
    def setUp(self):
        super(PostBulkDeleteApiTests, self).setUp()

        self.posts = [
            testutils.reply_thread(self.thread, poster=self.user),
            testutils.reply_thread(self.thread),
            testutils.reply_thread(self.thread, poster=self.user),
        ]

        self.api_link = reverse('misago:api:thread-post-list',
                                kwargs={
                                    'thread_pk': self.thread.pk,
                                })
 def test_merge_guest_posts(self):
     """api recjects attempt to merge posts made by same guest"""
     response = self.client.post(
         self.api_link,
         json.dumps({
             'posts': [
                 testutils.reply_thread(self.thread, poster="Bob").pk,
                 testutils.reply_thread(self.thread, poster="Bob").pk,
             ]
         }),
         content_type="application/json",
     )
     self.assertEqual(response.status_code, 200)
예제 #47
0
    def test_goto_first_new_post(self):
        """first unread post redirect url in already read thread is valid"""
        save_read(self.user, self.thread.first_post)

        post = testutils.reply_thread(self.thread, posted_on=timezone.now())
        for _ in range(settings.MISAGO_POSTS_PER_PAGE +
                       settings.MISAGO_POSTS_TAIL - 1):
            testutils.reply_thread(self.thread, posted_on=timezone.now())

        response = self.client.get(self.thread.get_new_post_url())
        self.assertEqual(response.status_code, 302)
        self.assertEqual(response['location'],
                         GOTO_URL % (self.thread.get_absolute_url(), post.pk))
예제 #48
0
    def test_category_archive_by_start_date(self):
        """command archives category content based on start date"""
        category = Category.objects.all_categories()[:1][0]
        archive = Category.objects.create(
            lft=7,
            rght=8,
            tree_id=2,
            level=0,
            name='Archive',
            slug='archive',
        )

        category.prune_started_after = 20
        category.archive_pruned_in = archive
        category.save()

        # post old threads with recent replies
        started_on = timezone.now() - timedelta(days=30)
        posted_on = timezone.now()
        for t in range(10):
            thread = testutils.post_thread(category, started_on=started_on)
            testutils.reply_thread(thread, posted_on=posted_on)

        # post recent threads that will be preserved
        threads = [testutils.post_thread(category) for t in range(10)]

        category.synchronize()
        self.assertEqual(category.threads, 20)
        self.assertEqual(category.posts, 30)

        # run command
        command = prunecategories.Command()

        out = StringIO()
        command.execute(stdout=out)

        category.synchronize()
        self.assertEqual(category.threads, 10)
        self.assertEqual(category.posts, 10)

        archive.synchronize()
        self.assertEqual(archive.threads, 10)
        self.assertEqual(archive.posts, 20)

        for thread in threads:
            category.thread_set.get(id=thread.id)

        command_output = out.getvalue().strip()
        self.assertEqual(command_output, 'Categories were pruned')
    def setUp(self):
        super(PostBulkDeleteApiTests, self).setUp()

        self.posts = [
            testutils.reply_thread(self.thread, poster=self.user),
            testutils.reply_thread(self.thread),
            testutils.reply_thread(self.thread, poster=self.user),
        ]

        self.api_link = reverse(
            'misago:api:thread-post-list',
            kwargs={
                'thread_pk': self.thread.pk,
            }
        )
    def test_reply_to_other_thread_post(self):
        """api validates is replied post belongs to same thread"""
        other_thread = testutils.post_thread(category=self.category)
        reply_to = testutils.reply_thread(other_thread)

        response = self.client.get('{}?reply={}'.format(self.api_link, reply_to.pk))
        self.assertEqual(response.status_code, 404)
    def test_filtered_query(self):
        """search filters are used by search system"""
        thread = testutils.post_thread(self.category)
        post = testutils.reply_thread(
            thread,
            message="You just do MMM in 4th minute and its pwnt",
        )

        self.index_post(post)

        response = self.client.get('%s?q=MMM' % self.api_link)
        self.assertEqual(response.status_code, 200)

        reponse_json = response.json()
        self.assertIn('threads', [p['id'] for p in reponse_json])

        for provider in reponse_json:
            if provider['id'] == 'threads':
                results = provider['results']['results']
                self.assertEqual(len(results), 1)
                self.assertEqual(results[0]['id'], post.id)

        response = self.client.get('%s?q=Marines Medics' % self.api_link)
        self.assertEqual(response.status_code, 200)

        for provider in reponse_json:
            if provider['id'] == 'threads':
                results = provider['results']['results']
                self.assertEqual(len(results), 1)
                self.assertEqual(results[0]['id'], post.id)
    def test_posts_not_found(self):
        """api fails to find posts"""
        posts = [
            testutils.reply_thread(self.thread, is_hidden=True),
            testutils.reply_thread(self.thread, is_unapproved=True),
        ]

        response = self.patch(self.api_link, {
            'ids': [p.id for p in posts],
            'ops': [{}],
        })

        self.assertEqual(response.status_code, 403)
        self.assertEqual(response.json(), {
            'detail': "One or more posts to update could not be found.",
        })
예제 #53
0
 def reply_thread(self, is_hidden=False, is_moderated=False):
     self.post = testutils.reply_thread(
         thread=self.thread,
         is_hidden=is_hidden,
         is_moderated=is_moderated,
         posted_on=timezone.now())
     return self.post
    def test_unapproved_post_visibility(self):
        """unapproved post renders for its author and users with perm to approve content"""
        post = testutils.reply_thread(self.thread, is_unapproved=True)

        # post is hdden because we aren't its author nor user with permission to approve
        response = self.client.get(self.thread.get_absolute_url())
        self.assertNotContains(response, post.get_absolute_url())

        # post displays because we have permission to approve unapproved content
        self.override_acl({'can_approve_content': 1})

        response = self.client.get(self.thread.get_absolute_url())
        self.assertContains(response, post.get_absolute_url())
        self.assertContains(response, "This post is unapproved.")
        self.assertContains(response, post.parsed)

        # post displays because we are its author
        post.poster = self.user
        post.save()

        self.override_acl({'can_approve_content': 0})

        response = self.client.get(self.thread.get_absolute_url())
        self.assertContains(response, post.get_absolute_url())
        self.assertContains(response, "This post is unapproved.")
        self.assertContains(response, post.parsed)
예제 #55
0
    def create_thread(self):
        datetime = timezone.now()

        thread = testutils.post_thread(self.forum)
        post = testutils.reply_thread(thread)

        return thread
    def test_delete_posts_view(self):
        """delete user posts view deletes posts"""
        test_user = UserModel.objects.create_user('Bob', '*****@*****.**', 'pass123')
        test_link = reverse(
            'misago:admin:users:accounts:delete-posts', kwargs={
                'pk': test_user.pk,
            }
        )

        category = Category.objects.all_categories()[:1][0]
        thread = post_thread(category)
        [reply_thread(thread, poster=test_user) for _ in range(10)]

        response = self.client.post(test_link, **self.AJAX_HEADER)
        self.assertEqual(response.status_code, 200)

        response_dict = response.json()
        self.assertEqual(response_dict['deleted_count'], 10)
        self.assertFalse(response_dict['is_completed'])

        response = self.client.post(test_link, **self.AJAX_HEADER)
        self.assertEqual(response.status_code, 200)

        response_dict = response.json()
        self.assertEqual(response_dict['deleted_count'], 0)
        self.assertTrue(response_dict['is_completed'])
    def test_steal_attachments(self):
        """middleware validates if attachments are already assigned to other posts"""
        other_post = testutils.reply_thread(self.thread)

        attachments = [
            self.mock_attachment(post=other_post),
            self.mock_attachment(),
        ]

        middleware = AttachmentsMiddleware(
            request=RequestMock({
                'attachments': [attachments[0].pk, attachments[1].pk]
            }),
            mode=PostingEndpoint.EDIT,
            user=self.user,
            post=self.post,
        )

        serializer = middleware.get_serializer()
        self.assertTrue(serializer.is_valid())
        middleware.save(serializer)

        # only unassociated attachment was associated with post
        self.assertEqual(self.post.update_fields, ['attachments_cache'])
        self.assertEqual(self.post.attachment_set.count(), 1)

        self.assertEqual(Attachment.objects.get(pk=attachments[0].pk).post, other_post)
        self.assertEqual(Attachment.objects.get(pk=attachments[1].pk).post, self.post)
    def test_filled_threads_list(self):
        """filled threads list is rendered"""
        forum = Forum.objects.all_forums().filter(role="forum")[:1][0]

        threads = []
        for t in xrange(10):
            threads.append(testutils.post_thread(forum, is_moderated=True))

        for t in xrange(10):
            threads.append(testutils.post_thread(forum))
            testutils.reply_thread(threads[-1], is_moderated=True)

        self.override_acl();
        response = self.client.get(reverse('misago:moderated_content'))
        self.assertEqual(response.status_code, 200)
        for thread in threads:
            self.assertIn(thread.get_absolute_url(), response.content)
예제 #59
0
    def test_unhide_post_no_permission(self):
        """view fails due to lack of permissions"""
        post = reply_thread(self.thread, is_hidden=True)
        post_link = reverse('misago:unhide_post', kwargs={'post_id': post.id})

        self.override_acl({'can_hide_posts': 0, 'can_hide_own_posts': 0})
        response = self.client.post(post_link)
        self.assertEqual(response.status_code, 403)