test_gotoviews.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. from django.utils import timezone
  2. from .. import test
  3. from ...categories.models import Category
  4. from ...conf.test import override_dynamic_settings
  5. from ...readtracker.poststracker import save_read
  6. from ...users.test import AuthenticatedUserTestCase
  7. from ..test import patch_category_acl
  8. GOTO_URL = "%s#post-%s"
  9. GOTO_PAGE_URL = "%s%s/#post-%s"
  10. POSTS_PER_PAGE = 7
  11. POSTS_PER_PAGE_ORPHANS = 3
  12. class GotoViewTestCase(AuthenticatedUserTestCase):
  13. def setUp(self):
  14. super().setUp()
  15. self.category = Category.objects.get(slug="first-category")
  16. self.thread = test.post_thread(category=self.category)
  17. class GotoPostTests(GotoViewTestCase):
  18. def test_goto_first_post(self):
  19. """first post redirect url is valid"""
  20. response = self.client.get(self.thread.first_post.get_absolute_url())
  21. self.assertEqual(response.status_code, 302)
  22. self.assertEqual(
  23. response["location"],
  24. GOTO_URL % (self.thread.get_absolute_url(), self.thread.first_post_id),
  25. )
  26. response = self.client.get(response["location"])
  27. self.assertContains(response, self.thread.first_post.get_absolute_url())
  28. @override_dynamic_settings(
  29. posts_per_page=POSTS_PER_PAGE, posts_per_page_orphans=POSTS_PER_PAGE_ORPHANS
  30. )
  31. def test_goto_last_post_on_page(self):
  32. """last post on page redirect url is valid"""
  33. for _ in range(POSTS_PER_PAGE + POSTS_PER_PAGE_ORPHANS - 1):
  34. post = test.reply_thread(self.thread)
  35. response = self.client.get(post.get_absolute_url())
  36. self.assertEqual(response.status_code, 302)
  37. self.assertEqual(
  38. response["location"], GOTO_URL % (self.thread.get_absolute_url(), post.pk)
  39. )
  40. response = self.client.get(response["location"])
  41. self.assertContains(response, post.get_absolute_url())
  42. @override_dynamic_settings(
  43. posts_per_page=POSTS_PER_PAGE, posts_per_page_orphans=POSTS_PER_PAGE_ORPHANS
  44. )
  45. def test_goto_first_post_on_next_page(self):
  46. """first post on next page redirect url is valid"""
  47. for _ in range(POSTS_PER_PAGE + POSTS_PER_PAGE_ORPHANS):
  48. post = test.reply_thread(self.thread)
  49. response = self.client.get(post.get_absolute_url())
  50. self.assertEqual(response.status_code, 302)
  51. self.assertEqual(
  52. response["location"],
  53. GOTO_PAGE_URL % (self.thread.get_absolute_url(), 2, post.pk),
  54. )
  55. response = self.client.get(response["location"])
  56. self.assertContains(response, post.get_absolute_url())
  57. @override_dynamic_settings(
  58. posts_per_page=POSTS_PER_PAGE, posts_per_page_orphans=POSTS_PER_PAGE_ORPHANS
  59. )
  60. def test_goto_first_post_on_page_three_out_of_five(self):
  61. """first post on next page redirect url is valid"""
  62. posts = []
  63. for _ in range(POSTS_PER_PAGE * 4 - 1):
  64. post = test.reply_thread(self.thread)
  65. posts.append(post)
  66. post = posts[POSTS_PER_PAGE * 2 - 3]
  67. response = self.client.get(post.get_absolute_url())
  68. self.assertEqual(response.status_code, 302)
  69. self.assertEqual(
  70. response["location"],
  71. GOTO_PAGE_URL % (self.thread.get_absolute_url(), 3, post.pk),
  72. )
  73. response = self.client.get(response["location"])
  74. self.assertContains(response, post.get_absolute_url())
  75. @override_dynamic_settings(
  76. posts_per_page=POSTS_PER_PAGE, posts_per_page_orphans=POSTS_PER_PAGE_ORPHANS
  77. )
  78. def test_goto_first_event_on_page_three_out_of_five(self):
  79. """event redirect url is valid"""
  80. posts = []
  81. for _ in range(POSTS_PER_PAGE * 4 - 1):
  82. post = test.reply_thread(self.thread)
  83. posts.append(post)
  84. post = posts[POSTS_PER_PAGE * 2 - 2]
  85. self.thread.has_events = True
  86. self.thread.save()
  87. post.is_event = True
  88. post.save()
  89. response = self.client.get(post.get_absolute_url())
  90. self.assertEqual(response.status_code, 302)
  91. self.assertEqual(
  92. response["location"],
  93. GOTO_PAGE_URL % (self.thread.get_absolute_url(), 3, post.pk),
  94. )
  95. response = self.client.get(response["location"])
  96. self.assertContains(response, post.get_absolute_url())
  97. class GotoLastTests(GotoViewTestCase):
  98. def test_goto_last_post(self):
  99. """first post redirect url is valid"""
  100. response = self.client.get(self.thread.get_last_post_url())
  101. self.assertEqual(response.status_code, 302)
  102. self.assertEqual(
  103. response["location"],
  104. GOTO_URL % (self.thread.get_absolute_url(), self.thread.first_post_id),
  105. )
  106. response = self.client.get(response["location"])
  107. self.assertContains(response, self.thread.last_post.get_absolute_url())
  108. @override_dynamic_settings(
  109. posts_per_page=POSTS_PER_PAGE, posts_per_page_orphans=POSTS_PER_PAGE_ORPHANS
  110. )
  111. def test_goto_last_post_on_page(self):
  112. """last post on page redirect url is valid"""
  113. for _ in range(POSTS_PER_PAGE + POSTS_PER_PAGE_ORPHANS - 1):
  114. post = test.reply_thread(self.thread)
  115. response = self.client.get(self.thread.get_last_post_url())
  116. self.assertEqual(response.status_code, 302)
  117. self.assertEqual(
  118. response["location"], GOTO_URL % (self.thread.get_absolute_url(), post.pk)
  119. )
  120. response = self.client.get(response["location"])
  121. self.assertContains(response, post.get_absolute_url())
  122. class GotoNewTests(GotoViewTestCase):
  123. def test_goto_first_post(self):
  124. """first unread post redirect url is valid"""
  125. response = self.client.get(self.thread.get_new_post_url())
  126. self.assertEqual(response.status_code, 302)
  127. self.assertEqual(
  128. response["location"],
  129. GOTO_URL % (self.thread.get_absolute_url(), self.thread.first_post_id),
  130. )
  131. @override_dynamic_settings(
  132. posts_per_page=POSTS_PER_PAGE, posts_per_page_orphans=POSTS_PER_PAGE_ORPHANS
  133. )
  134. def test_goto_first_new_post(self):
  135. """first unread post redirect url in already read thread is valid"""
  136. save_read(self.user, self.thread.first_post)
  137. post = test.reply_thread(self.thread, posted_on=timezone.now())
  138. for _ in range(POSTS_PER_PAGE + POSTS_PER_PAGE_ORPHANS - 1):
  139. test.reply_thread(self.thread, posted_on=timezone.now())
  140. response = self.client.get(self.thread.get_new_post_url())
  141. self.assertEqual(response.status_code, 302)
  142. self.assertEqual(
  143. response["location"], GOTO_URL % (self.thread.get_absolute_url(), post.pk)
  144. )
  145. @override_dynamic_settings(
  146. posts_per_page=POSTS_PER_PAGE, posts_per_page_orphans=POSTS_PER_PAGE_ORPHANS
  147. )
  148. def test_goto_first_new_post_on_next_page(self):
  149. """first unread post redirect url in already read multipage thread is valid"""
  150. save_read(self.user, self.thread.first_post)
  151. for _ in range(POSTS_PER_PAGE + POSTS_PER_PAGE_ORPHANS):
  152. last_post = test.reply_thread(self.thread, posted_on=timezone.now())
  153. save_read(self.user, last_post)
  154. post = test.reply_thread(self.thread, posted_on=timezone.now())
  155. for _ in range(POSTS_PER_PAGE + POSTS_PER_PAGE_ORPHANS - 1):
  156. test.reply_thread(self.thread, posted_on=timezone.now())
  157. response = self.client.get(self.thread.get_new_post_url())
  158. self.assertEqual(response.status_code, 302)
  159. self.assertEqual(
  160. response["location"],
  161. GOTO_PAGE_URL % (self.thread.get_absolute_url(), 2, post.pk),
  162. )
  163. @override_dynamic_settings(
  164. posts_per_page=POSTS_PER_PAGE, posts_per_page_orphans=POSTS_PER_PAGE_ORPHANS
  165. )
  166. def test_goto_first_new_post_in_read_thread(self):
  167. """goto new in read thread points to last post"""
  168. save_read(self.user, self.thread.first_post)
  169. for _ in range(POSTS_PER_PAGE + POSTS_PER_PAGE_ORPHANS):
  170. post = test.reply_thread(self.thread, posted_on=timezone.now())
  171. save_read(self.user, post)
  172. response = self.client.get(self.thread.get_new_post_url())
  173. self.assertEqual(response.status_code, 302)
  174. self.assertEqual(
  175. response["location"],
  176. GOTO_PAGE_URL % (self.thread.get_absolute_url(), 2, post.pk),
  177. )
  178. @override_dynamic_settings(
  179. posts_per_page=POSTS_PER_PAGE, posts_per_page_orphans=POSTS_PER_PAGE_ORPHANS
  180. )
  181. def test_guest_goto_first_new_post_in_thread(self):
  182. """guest goto new in read thread points to last post"""
  183. for _ in range(POSTS_PER_PAGE + POSTS_PER_PAGE_ORPHANS):
  184. post = test.reply_thread(self.thread, posted_on=timezone.now())
  185. self.logout_user()
  186. response = self.client.get(self.thread.get_new_post_url())
  187. self.assertEqual(response.status_code, 302)
  188. self.assertEqual(
  189. response["location"],
  190. GOTO_PAGE_URL % (self.thread.get_absolute_url(), 2, post.pk),
  191. )
  192. class GotoBestAnswerTests(GotoViewTestCase):
  193. def test_view_handles_no_best_answer(self):
  194. """if thread has no best answer, redirect to first post"""
  195. response = self.client.get(self.thread.get_best_answer_url())
  196. self.assertEqual(response.status_code, 302)
  197. self.assertEqual(
  198. response["location"],
  199. GOTO_URL % (self.thread.get_absolute_url(), self.thread.first_post_id),
  200. )
  201. @override_dynamic_settings(
  202. posts_per_page=POSTS_PER_PAGE, posts_per_page_orphans=POSTS_PER_PAGE_ORPHANS
  203. )
  204. def test_view_handles_best_answer(self):
  205. """if thread has best answer, redirect to it"""
  206. for _ in range(POSTS_PER_PAGE + POSTS_PER_PAGE_ORPHANS):
  207. test.reply_thread(self.thread, posted_on=timezone.now())
  208. best_answer = test.reply_thread(self.thread, posted_on=timezone.now())
  209. self.thread.set_best_answer(self.user, best_answer)
  210. self.thread.save()
  211. for _ in range(POSTS_PER_PAGE + POSTS_PER_PAGE_ORPHANS - 1):
  212. test.reply_thread(self.thread, posted_on=timezone.now())
  213. response = self.client.get(self.thread.get_best_answer_url())
  214. self.assertEqual(response.status_code, 302)
  215. self.assertEqual(
  216. response["location"],
  217. GOTO_PAGE_URL % (self.thread.get_absolute_url(), 2, best_answer.pk),
  218. )
  219. class GotoUnapprovedTests(GotoViewTestCase):
  220. def test_view_validates_permission(self):
  221. """view validates permission to see unapproved posts"""
  222. response = self.client.get(self.thread.get_unapproved_post_url())
  223. self.assertContains(
  224. response, "You need permission to approve content", status_code=403
  225. )
  226. with patch_category_acl({"can_approve_content": True}):
  227. response = self.client.get(self.thread.get_unapproved_post_url())
  228. self.assertEqual(response.status_code, 302)
  229. @patch_category_acl({"can_approve_content": True})
  230. def test_view_handles_no_unapproved_posts(self):
  231. """if thread has no unapproved posts, redirect to last post"""
  232. response = self.client.get(self.thread.get_unapproved_post_url())
  233. self.assertEqual(response.status_code, 302)
  234. self.assertEqual(
  235. response["location"],
  236. GOTO_URL % (self.thread.get_absolute_url(), self.thread.first_post_id),
  237. )
  238. @override_dynamic_settings(
  239. posts_per_page=POSTS_PER_PAGE, posts_per_page_orphans=POSTS_PER_PAGE_ORPHANS
  240. )
  241. @patch_category_acl({"can_approve_content": True})
  242. def test_view_handles_unapproved_posts(self):
  243. """if thread has unapproved posts, redirect to first of them"""
  244. for _ in range(POSTS_PER_PAGE + POSTS_PER_PAGE_ORPHANS):
  245. test.reply_thread(self.thread, posted_on=timezone.now())
  246. post = test.reply_thread(
  247. self.thread, is_unapproved=True, posted_on=timezone.now()
  248. )
  249. for _ in range(POSTS_PER_PAGE + POSTS_PER_PAGE_ORPHANS - 1):
  250. test.reply_thread(self.thread, posted_on=timezone.now())
  251. response = self.client.get(self.thread.get_unapproved_post_url())
  252. self.assertEqual(response.status_code, 302)
  253. self.assertEqual(
  254. response["location"],
  255. GOTO_PAGE_URL % (self.thread.get_absolute_url(), 2, post.pk),
  256. )
  257. class ThreadGotoPostTests(GotoViewTestCase):
  258. """brureforcing regression tests for regression test for #869"""
  259. def test_thread_growing_post_goto(self):
  260. """growing thread goto views don't fail"""
  261. for _ in range(60):
  262. post = test.reply_thread(self.thread, posted_on=timezone.now())
  263. # go to post link is valid
  264. post_url = self.client.get(post.get_absolute_url())["location"]
  265. response = self.client.get(post_url)
  266. self.assertContains(response, post.get_absolute_url())
  267. # go to last post link is valid
  268. last_url = self.client.get(self.thread.get_last_post_url())["location"]
  269. self.assertEqual(post_url, last_url)
  270. def test_thread_growing_event_goto(self):
  271. """growing thread goto views don't fail for events"""
  272. for i in range(60):
  273. test.reply_thread(self.thread, posted_on=timezone.now())
  274. post = test.reply_thread(self.thread, posted_on=timezone.now())
  275. post.is_event = True
  276. post.save()
  277. # go to post link is valid
  278. post_url = self.client.get(post.get_absolute_url())["location"]
  279. if i == 0:
  280. # manually set events flag after first event was created
  281. self.thread.has_events = True
  282. self.thread.save()
  283. response = self.client.get(post_url)
  284. self.assertContains(response, post.get_absolute_url())
  285. # go to last post link is valid
  286. last_url = self.client.get(self.thread.get_last_post_url())["location"]
  287. self.assertEqual(post_url, last_url)
  288. def test_thread_post_goto(self):
  289. """thread goto views don't fail"""
  290. for _ in range(60):
  291. test.reply_thread(self.thread, posted_on=timezone.now())
  292. for post in self.thread.post_set.order_by("id").iterator():
  293. # go to post link is valid
  294. post_url = self.client.get(post.get_absolute_url())["location"]
  295. response = self.client.get(post_url)
  296. self.assertContains(response, post.get_absolute_url())
  297. # go to last post link is valid
  298. last_url = self.client.get(self.thread.get_last_post_url())["location"]
  299. self.assertEqual(post_url, last_url)
  300. def test_thread_event_goto(self):
  301. """thread goto views don't fail for events"""
  302. for _ in range(60):
  303. test.reply_thread(self.thread, posted_on=timezone.now())
  304. post = test.reply_thread(self.thread, posted_on=timezone.now())
  305. post.is_event = True
  306. post.save()
  307. for post in (
  308. self.thread.post_set.filter(is_event=True).order_by("id").iterator()
  309. ):
  310. # go to post link is valid
  311. post_url = self.client.get(post.get_absolute_url())["location"]
  312. response = self.client.get(post_url)
  313. self.assertContains(response, post.get_absolute_url())
  314. # go to last post link is valid
  315. last_url = self.client.get(self.thread.get_last_post_url())["location"]
  316. self.assertEqual(post_url, last_url)