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_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)
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)
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)
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)
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')
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)
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)
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)
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)
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)
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)
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_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)
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)
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))
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')
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_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)
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)
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)
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')
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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))
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.", })
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)
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)
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)