123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 |
- from misago.acl.testutils import override_acl
- from misago.categories.models import Category
- from misago.conf import settings
- from misago.users.testutils import AuthenticatedUserTestCase
- from misago.threads import testutils
- from misago.threads.events import record_event
- from misago.threads.models import Post, Thread
- from misago.threads.moderation import threads as threads_moderation
- from misago.threads.moderation.posts import hide_post
- class MockRequest(object):
- def __init__(self, user):
- self.user = user
- self.user_ip = '127.0.0.1'
- class ThreadViewTestCase(AuthenticatedUserTestCase):
- def setUp(self):
- super(ThreadViewTestCase, self).setUp()
- self.category = Category.objects.get(slug='first-category')
- self.thread = testutils.post_thread(category=self.category)
- def override_acl(self, acl=None):
- category_acl = self.user.acl['categories'][self.category.pk]
- category_acl.update({
- 'can_see': 1,
- 'can_browse': 1,
- 'can_see_all_threads': 1,
- 'can_see_own_threads': 0,
- 'can_hide_threads': 0,
- 'can_approve_content': 0,
- 'can_edit_posts': 0,
- 'can_hide_posts': 0,
- 'can_hide_own_posts': 0,
- 'can_close_threads': 0,
- 'post_edit_time': 0,
- 'can_hide_events': 0,
- })
- if acl:
- category_acl.update(acl)
- override_acl(self.user, {
- 'categories': {
- self.category.pk: category_acl
- }
- })
- class ThreadVisibilityTests(ThreadViewTestCase):
- def test_thread_displays(self):
- """thread view has no showstoppers"""
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, self.thread.title)
- def test_view_shows_owner_thread(self):
- """view handles "owned threads only" """
- self.override_acl({
- 'can_see_all_threads': 0
- })
- response = self.client.get(self.thread.get_absolute_url())
- self.assertEqual(response.status_code, 404)
- self.thread.starter = self.user
- self.thread.save()
- self.override_acl({
- 'can_see_all_threads': 0
- })
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, self.thread.title)
- def test_view_validates_category_permissions(self):
- """view validates category visiblity"""
- self.override_acl({
- 'can_see': 0
- })
- response = self.client.get(self.thread.get_absolute_url())
- self.assertEqual(response.status_code, 404)
- self.override_acl({
- 'can_browse': 0
- })
- response = self.client.get(self.thread.get_absolute_url())
- self.assertEqual(response.status_code, 404)
- def test_view_shows_unapproved_thread(self):
- """view handles unapproved thread"""
- self.override_acl({
- 'can_approve_content': 0
- })
- self.thread.is_unapproved = True
- self.thread.save()
- response = self.client.get(self.thread.get_absolute_url())
- self.assertEqual(response.status_code, 404)
- # grant permission to see unapproved content
- self.override_acl({
- 'can_approve_content': 1
- })
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, self.thread.title)
- # make test user thread's owner and remove permission to see unapproved
- # user should be able to see thread as its author anyway
- self.thread.starter = self.user
- self.thread.save()
- self.override_acl({
- 'can_approve_content': 0
- })
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, self.thread.title)
- def test_view_shows_hidden_thread(self):
- """view handles hidden thread"""
- self.override_acl({
- 'can_hide_threads': 0
- })
- self.thread.is_hidden = True
- self.thread.save()
- response = self.client.get(self.thread.get_absolute_url())
- self.assertEqual(response.status_code, 404)
- # threads owners are not extempt from hidden threads check
- self.thread.starter = self.user
- self.thread.save()
- response = self.client.get(self.thread.get_absolute_url())
- self.assertEqual(response.status_code, 404)
- # grant permission to see hidden content
- self.override_acl({
- 'can_hide_threads': 1
- })
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, self.thread.title)
- class ThreadPostsVisibilityTests(ThreadViewTestCase):
- def test_post_renders(self):
- """post renders"""
- post = testutils.reply_thread(self.thread, poster=self.user)
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, post.get_absolute_url())
- def test_invalid_post_renders(self):
- """invalid post renders"""
- post = testutils.reply_thread(self.thread, poster=self.user)
- post.parsed = 'fiddled post content'
- post.save()
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, post.get_absolute_url())
- self.assertContains(response, "This post's contents cannot be displayed.")
- self.assertNotContains(response, post.parsed)
- def test_hidden_post_visibility(self):
- """hidden post renders correctly"""
- post = testutils.reply_thread(self.thread, message="Hello, I'm hidden post!")
- hide_post(self.user, post)
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, post.get_absolute_url())
- self.assertContains(response, "This post is hidden. You cannot not see its contents.")
- self.assertNotContains(response, post.parsed)
- # posts authors are not extempt from seeing hidden posts content
- post.posted_by = self.user
- post.save()
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, post.get_absolute_url())
- self.assertContains(response, "This post is hidden. You cannot not see its contents.")
- self.assertNotContains(response, post.parsed)
- # permission to hide own posts isn't enought to see post content
- self.override_acl({
- 'can_hide_own_posts': 1
- })
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, post.get_absolute_url())
- self.assertContains(response, "This post is hidden. You cannot not see its contents.")
- self.assertNotContains(response, post.parsed)
- # post's content is displayed after permission to see posts is granted
- self.override_acl({
- 'can_hide_posts': 1
- })
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, post.get_absolute_url())
- self.assertContains(response, "This post is hidden. Only users with permission may see its contents.")
- self.assertNotContains(response, "This post is hidden. You cannot not see its contents.")
- self.assertContains(response, post.parsed)
- 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)
- class ThreadEventVisibilityTests(ThreadViewTestCase):
- def test_thread_events_render(self):
- """different thread events render"""
- TEST_ACTIONS = (
- (threads_moderation.pin_thread_globally, "Thread has been pinned globally."),
- (threads_moderation.pin_thread_locally, "Thread has been pinned locally."),
- (threads_moderation.unpin_thread, "Thread has been unpinned."),
- (threads_moderation.approve_thread, "Thread has been approved."),
- (threads_moderation.close_thread, "Thread has been closed."),
- (threads_moderation.open_thread, "Thread has been opened."),
- (threads_moderation.hide_thread, "Thread has been made hidden."),
- (threads_moderation.unhide_thread, "Thread has been revealed."),
- )
- self.thread.is_unapproved = True
- self.thread.save()
- for action, message in TEST_ACTIONS:
- self.override_acl({
- 'can_approve_content': 1,
- 'can_hide_threads': 1,
- })
- self.thread.post_set.filter(is_event=True).delete()
- action(MockRequest(self.user), self.thread)
- event = self.thread.post_set.filter(is_event=True)[0]
- # event renders
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, event.get_absolute_url())
- self.assertContains(response, message)
- # hidden events don't render without permission
- hide_post(self.user, event)
- self.override_acl({
- 'can_approve_content': 1,
- 'can_hide_threads': 1,
- })
- response = self.client.get(self.thread.get_absolute_url())
- self.assertNotContains(response, event.get_absolute_url())
- self.assertNotContains(response, message)
- # hidden event renders with permission
- hide_post(self.user, event)
- self.override_acl({
- 'can_approve_content': 1,
- 'can_hide_threads': 1,
- 'can_hide_events': 1,
- })
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, event.get_absolute_url())
- self.assertContains(response, message)
- self.assertContains(response, "Hidden by")
- # Event is only loaded if thread has events flag
- self.thread.has_events = False
- self.thread.save()
- self.override_acl({
- 'can_approve_content': 1,
- 'can_hide_threads': 1,
- 'can_hide_events': 1,
- })
- response = self.client.get(self.thread.get_absolute_url())
- self.assertNotContains(response, event.get_absolute_url())
- def test_events_limit(self):
- """forum will trim oldest events if theres more than allowed by config"""
- events_limit = settings.MISAGO_EVENTS_PER_PAGE
- events = []
- for i in range(events_limit + 5):
- event = record_event(MockRequest(self.user), self.thread, 'closed')
- events.append(event)
- # test that only events within limits were rendered
- response = self.client.get(self.thread.get_absolute_url())
- for event in events[5:]:
- self.assertContains(response, event.get_absolute_url())
- for event in events[:5]:
- self.assertNotContains(response, event.get_absolute_url())
- def test_events_dont_take_space(self):
- """events dont take space away from posts"""
- posts_limit = settings.MISAGO_POSTS_PER_PAGE
- events_limit = settings.MISAGO_EVENTS_PER_PAGE
- events = []
- for i in range(events_limit + 5):
- event = record_event(MockRequest(self.user), self.thread, 'closed')
- events.append(event)
- posts = []
- for i in range(posts_limit - 1):
- post = testutils.reply_thread(self.thread)
- posts.append(post)
- # test that all events and posts within limits were rendered
- response = self.client.get(self.thread.get_absolute_url())
- for event in events[5:]:
- self.assertContains(response, event.get_absolute_url())
- for post in posts:
- self.assertContains(response, post.get_absolute_url())
- # add second page to thread with more events
- for i in range(posts_limit):
- post = testutils.reply_thread(self.thread)
- for i in range(events_limit):
- event = record_event(MockRequest(self.user), self.thread, 'closed')
- events.append(event)
- # see first page
- response = self.client.get(self.thread.get_absolute_url())
- for event in events[5:events_limit]:
- self.assertContains(response, event.get_absolute_url())
- for post in posts[:posts_limit - 1]:
- self.assertContains(response, post.get_absolute_url())
- # see second page
- response = self.client.get('%s2/' % self.thread.get_absolute_url())
- for event in events[5 + events_limit:]:
- self.assertContains(response, event.get_absolute_url())
- for post in posts[posts_limit - 1:]:
- self.assertContains(response, post.get_absolute_url())
- def test_changed_thread_title_event_renders(self):
- """changed thread title event renders"""
- threads_moderation.change_thread_title(MockRequest(self.user), self.thread, "Lorem renamed ipsum!")
- event = self.thread.post_set.filter(is_event=True)[0]
- self.assertEqual(event.event_type, 'changed_title')
- # event renders
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, event.get_absolute_url())
- self.assertContains(response, "title has been changed from")
- self.assertContains(response, self.thread.title)
- def test_thread_move_event_renders(self):
- """moved thread event renders"""
- self.thread.category = self.thread.category.parent
- self.thread.save()
- threads_moderation.move_thread(MockRequest(self.user), self.thread, self.category)
- event = self.thread.post_set.filter(is_event=True)[0]
- self.assertEqual(event.event_type, 'moved')
- # event renders
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, event.get_absolute_url())
- self.assertContains(response, "Thread has been moved from")
- def test_thread_merged_event_renders(self):
- """merged thread event renders"""
- other_thread = testutils.post_thread(category=self.category)
- threads_moderation.merge_thread(MockRequest(self.user), self.thread, other_thread)
- event = self.thread.post_set.filter(is_event=True)[0]
- self.assertEqual(event.event_type, 'merged')
- # event renders
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, event.get_absolute_url())
- self.assertContains(response, "thread has been merged into this thread")
- class ThreadAttachmentsViewTests(ThreadViewTestCase):
- def mock_attachment_cache(self, data):
- json = {
- 'url': {},
- 'size': 16914,
- 'filename': 'Archiwum.zip',
- 'filetype': 'ZIP',
- 'is_image': False,
- 'uploaded_on': '2016-10-22T21:17:40.408710Z',
- 'uploader_name': 'BobBoberson'
- }
- json.update(data)
- return json
- def test_attachments_display(self):
- """thread posts show list of attachments below them"""
- post = self.thread.first_post
- post.attachments_cache = [
- self.mock_attachment_cache({
- 'url': {
- 'index': '/attachment/loremipsum-123/',
- 'thumb': None,
- 'uploader': '/user/bobboberson-123/'
- },
- 'filename': 'Archiwum-1.zip',
- }),
- self.mock_attachment_cache({
- 'url': {
- 'index': '/attachment/loremipsum-223/',
- 'thumb': '/attachment/thumb/loremipsum-223/',
- 'uploader': '/user/bobboberson-223/'
- },
- 'is_image': True,
- 'filename': 'Archiwum-2.zip'
- }),
- self.mock_attachment_cache({
- 'url': {
- 'index': '/attachment/loremipsum-323/',
- 'thumb': None,
- 'uploader': '/user/bobboberson-323/'
- },
- 'filename': 'Archiwum-3.zip'
- })
- ]
- post.save()
- # attachments render
- response = self.client.get(self.thread.get_absolute_url())
- for attachment in post.attachments_cache:
- self.assertContains(response, attachment['filename'])
- self.assertContains(response, attachment['uploader_name'])
- self.assertContains(response, attachment['url']['index'])
- self.assertContains(response, attachment['url']['uploader'])
- if attachment['url']['thumb']:
- self.assertContains(response, attachment['url']['thumb'])
- class ThreadPollViewTests(ThreadViewTestCase):
- def test_poll_voted_display(self):
- """view has no showstoppers when displaying voted poll"""
- poll = testutils.post_poll(self.thread, self.user)
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, poll.question)
- self.assertContains(response, '4 votes')
- self.assertNotContains(response, 'Save your vote')
- def test_poll_unvoted_display(self):
- """view has no showstoppers when displaying poll vote form"""
- poll = testutils.post_poll(self.thread, self.user)
- poll.pollvote_set.all().delete()
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, poll.question)
- self.assertContains(response, 'Save your vote')
- def test_poll_anonymous_view(self):
- """view has no showstoppers when displaying poll to anon user"""
- poll = testutils.post_poll(self.thread, self.user)
- self.logout_user()
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, poll.question)
- self.assertContains(response, '4 votes')
- self.assertNotContains(response, 'Save your vote')
- class ThreadLikedPostsViewTests(ThreadViewTestCase):
- def test_liked_posts_display(self):
- """view has no showstoppers on displaying posts with likes"""
- testutils.like_post(self.thread.first_post, self.user)
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, '"is_liked": true')
- def test_liked_posts_no_permission(self):
- """
- view has no showstoppers on displaying posts with likes without perm
- """
- testutils.like_post(self.thread.first_post, self.user)
- self.override_acl({
- 'can_see_posts_likes': 0
- })
- response = self.client.get(self.thread.get_absolute_url())
- self.assertNotContains(response, '"is_liked": true')
- self.assertNotContains(response, '"is_liked": false')
- self.assertContains(response, '"is_liked": null')
- class ThreadAnonViewTests(ThreadViewTestCase):
- def test_anonymous_user_view_no_showstoppers_display(self):
- """kitchensink thread view has no showstoppers for anons"""
- poll = testutils.post_poll(self.thread, self.user)
- event = record_event(MockRequest(self.user), self.thread, 'closed')
- hidden_event = record_event(MockRequest(self.user), self.thread, 'opened')
- hide_post(self.user, hidden_event)
- unapproved_post = testutils.reply_thread(self.thread, is_unapproved=True)
- post = testutils.reply_thread(self.thread)
- self.logout_user()
- response = self.client.get(self.thread.get_absolute_url())
- self.assertContains(response, poll.question)
- self.assertContains(response, event.get_absolute_url())
- self.assertContains(response, post.get_absolute_url())
- self.assertNotContains(response, hidden_event.get_absolute_url())
- self.assertNotContains(response, unapproved_post.get_absolute_url())
|