test_threadslists.py 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309
  1. from datetime import timedelta
  2. from django.urls import reverse
  3. from django.utils import timezone
  4. from django.utils.encoding import smart_str
  5. from .. import test
  6. from ...acl.test import patch_user_acl
  7. from ...categories.models import Category
  8. from ...conf import settings
  9. from ...conf.test import override_dynamic_settings
  10. from ...readtracker import poststracker
  11. from ...users.test import AuthenticatedUserTestCase
  12. LISTS_URLS = ("", "my/", "new/", "unread/", "subscribed/")
  13. def patch_categories_acl(category_acl=None, base_acl=None):
  14. def patch_acl(_, user_acl):
  15. first_category = Category.objects.get(slug="first-category")
  16. first_category_acl = user_acl["categories"][first_category.id].copy()
  17. user_acl.update(
  18. {
  19. "categories": {},
  20. "visible_categories": [],
  21. "browseable_categories": [],
  22. "can_approve_content": [],
  23. }
  24. )
  25. # copy first category's acl to other categories to make base for overrides
  26. for category in Category.objects.all_categories():
  27. user_acl["categories"][category.id] = first_category_acl
  28. if base_acl:
  29. user_acl.update(base_acl)
  30. for category in Category.objects.all_categories():
  31. user_acl["visible_categories"].append(category.id)
  32. user_acl["browseable_categories"].append(category.id)
  33. user_acl["categories"][category.id].update(
  34. {
  35. "can_see": 1,
  36. "can_browse": 1,
  37. "can_see_all_threads": 1,
  38. "can_see_own_threads": 0,
  39. "can_hide_threads": 0,
  40. "can_approve_content": 0,
  41. }
  42. )
  43. if category_acl:
  44. user_acl["categories"][category.id].update(category_acl)
  45. if category_acl.get("can_approve_content"):
  46. user_acl["can_approve_content"].append(category.id)
  47. return patch_user_acl(patch_acl)
  48. class ThreadsListTestCase(AuthenticatedUserTestCase):
  49. def setUp(self):
  50. """
  51. Create categories tree for test cases:
  52. First category (created by migration)
  53. Category A
  54. + Category B
  55. + Subcategory C
  56. + Subcategory D
  57. Category E
  58. + Subcategory F
  59. """
  60. super().setUp()
  61. self.api_link = reverse("misago:api:thread-list")
  62. self.root = Category.objects.root_category()
  63. self.first_category = Category.objects.get(slug="first-category")
  64. Category(
  65. name="Category A", slug="category-a", css_class="showing-category-a"
  66. ).insert_at(self.root, position="last-child", save=True)
  67. Category(
  68. name="Category E", slug="category-e", css_class="showing-category-e"
  69. ).insert_at(self.root, position="last-child", save=True)
  70. self.root = Category.objects.root_category()
  71. self.category_a = Category.objects.get(slug="category-a")
  72. Category(
  73. name="Category B", slug="category-b", css_class="showing-category-b"
  74. ).insert_at(self.category_a, position="last-child", save=True)
  75. self.category_b = Category.objects.get(slug="category-b")
  76. Category(
  77. name="Category C", slug="category-c", css_class="showing-category-c"
  78. ).insert_at(self.category_b, position="last-child", save=True)
  79. Category(
  80. name="Category D", slug="category-d", css_class="showing-category-d"
  81. ).insert_at(self.category_b, position="last-child", save=True)
  82. self.category_c = Category.objects.get(slug="category-c")
  83. self.category_d = Category.objects.get(slug="category-d")
  84. self.category_e = Category.objects.get(slug="category-e")
  85. Category(
  86. name="Category F", slug="category-f", css_class="showing-category-f"
  87. ).insert_at(self.category_e, position="last-child", save=True)
  88. self.category_f = Category.objects.get(slug="category-f")
  89. Category.objects.partial_rebuild(self.root.tree_id)
  90. self.root = Category.objects.root_category()
  91. self.category_a = Category.objects.get(slug="category-a")
  92. self.category_b = Category.objects.get(slug="category-b")
  93. self.category_c = Category.objects.get(slug="category-c")
  94. self.category_d = Category.objects.get(slug="category-d")
  95. self.category_e = Category.objects.get(slug="category-e")
  96. self.category_f = Category.objects.get(slug="category-f")
  97. def assertContainsThread(self, response, thread):
  98. self.assertContains(response, ' href="%s"' % thread.get_absolute_url())
  99. def assertNotContainsThread(self, response, thread):
  100. self.assertNotContains(response, ' href="%s"' % thread.get_absolute_url())
  101. class ApiTests(ThreadsListTestCase):
  102. def test_root_category(self):
  103. """its possible to access threads endpoint with category=ROOT_ID"""
  104. response = self.client.get("%s?category=%s" % (self.api_link, self.root.pk))
  105. self.assertEqual(response.status_code, 200)
  106. def test_invalid_list_type(self):
  107. """api returns 404 for invalid list type"""
  108. response = self.client.get(
  109. "%s?category=%s&list=nope" % (self.api_link, self.root.pk)
  110. )
  111. self.assertEqual(response.status_code, 404)
  112. class AllThreadsListTests(ThreadsListTestCase):
  113. @patch_categories_acl()
  114. def test_list_renders_empty(self):
  115. """empty threads list renders"""
  116. for url in LISTS_URLS:
  117. response = self.client.get("/" + url)
  118. self.assertEqual(response.status_code, 200)
  119. self.assertContains(response, "empty-message")
  120. if url:
  121. self.assertContains(response, "No threads matching specified criteria")
  122. else:
  123. self.assertContains(response, "There are no threads on this forum")
  124. response = self.client.get(self.category_b.get_absolute_url() + url)
  125. self.assertEqual(response.status_code, 200)
  126. self.assertContains(response, self.category_b.name)
  127. self.assertContains(response, "empty-message")
  128. if url:
  129. self.assertContains(response, "No threads matching specified criteria")
  130. else:
  131. self.assertContains(response, "There are no threads in this category")
  132. response = self.client.get(
  133. "%s?list=%s" % (self.api_link, url.strip("/") or "all")
  134. )
  135. self.assertEqual(response.status_code, 200)
  136. response_json = response.json()
  137. self.assertEqual(len(response_json["results"]), 0)
  138. # empty lists render for anonymous user?
  139. self.logout_user()
  140. self.user = self.get_anonymous_user()
  141. response = self.client.get("/")
  142. self.assertEqual(response.status_code, 200)
  143. self.assertContains(response, "empty-message")
  144. self.assertContains(response, "There are no threads on this forum")
  145. response = self.client.get(self.category_b.get_absolute_url())
  146. self.assertEqual(response.status_code, 200)
  147. self.assertContains(response, self.category_b.name)
  148. self.assertContains(response, "empty-message")
  149. self.assertContains(response, "There are no threads in this category")
  150. response = self.client.get("%s?list=all" % self.api_link)
  151. self.assertEqual(response.status_code, 200)
  152. response_json = response.json()
  153. self.assertEqual(len(response_json["results"]), 0)
  154. @patch_categories_acl()
  155. def test_list_authenticated_only_views(self):
  156. """authenticated only views return 403 for guests"""
  157. for url in LISTS_URLS:
  158. response = self.client.get("/" + url)
  159. self.assertEqual(response.status_code, 200)
  160. response = self.client.get(self.category_b.get_absolute_url() + url)
  161. self.assertEqual(response.status_code, 200)
  162. self.assertContains(response, self.category_b.name)
  163. response = self.client.get(
  164. "%s?category=%s&list=%s"
  165. % (self.api_link, self.category_b.pk, url.strip("/") or "all")
  166. )
  167. self.assertEqual(response.status_code, 200)
  168. self.logout_user()
  169. self.user = self.get_anonymous_user()
  170. for url in LISTS_URLS[1:]:
  171. response = self.client.get("/" + url)
  172. self.assertEqual(response.status_code, 403)
  173. response = self.client.get(self.category_b.get_absolute_url() + url)
  174. self.assertEqual(response.status_code, 403)
  175. response = self.client.get(
  176. "%s?category=%s&list=%s"
  177. % (self.api_link, self.category_b.pk, url.strip("/") or "all")
  178. )
  179. self.assertEqual(response.status_code, 403)
  180. @patch_categories_acl()
  181. def test_list_renders_categories_picker(self):
  182. """categories picker renders valid categories"""
  183. Category(name="Hidden Category", slug="hidden-category").insert_at(
  184. self.root, position="last-child", save=True
  185. )
  186. test_category = Category.objects.get(slug="hidden-category")
  187. test.post_thread(category=self.category_b)
  188. response = self.client.get("/")
  189. self.assertEqual(response.status_code, 200)
  190. self.assertContains(response, "subcategory-%s" % self.category_a.css_class)
  191. # readable categories, but non-accessible directly
  192. self.assertNotContains(response, "subcategory-%s" % self.category_b.css_class)
  193. self.assertNotContains(response, "subcategory-%s" % self.category_c.css_class)
  194. self.assertNotContains(response, "subcategory-%s" % self.category_d.css_class)
  195. self.assertNotContains(response, "subcategory-%s" % self.category_f.css_class)
  196. # hidden category
  197. self.assertNotContains(response, "subcategory-%s" % test_category.css_class)
  198. response = self.client.get(self.api_link)
  199. self.assertEqual(response.status_code, 200)
  200. response_json = response.json()
  201. self.assertIn(self.category_a.pk, response_json["subcategories"])
  202. self.assertNotIn(self.category_b.pk, response_json["subcategories"])
  203. # test category view
  204. response = self.client.get(self.category_a.get_absolute_url())
  205. self.assertEqual(response.status_code, 200)
  206. self.assertContains(response, "subcategory-%s" % self.category_b.css_class)
  207. # readable categories, but non-accessible directly
  208. self.assertNotContains(response, "subcategory-%s" % self.category_c.css_class)
  209. self.assertNotContains(response, "subcategory-%s" % self.category_d.css_class)
  210. self.assertNotContains(response, "subcategory-%s" % self.category_f.css_class)
  211. response = self.client.get(
  212. "%s?category=%s" % (self.api_link, self.category_a.pk)
  213. )
  214. self.assertEqual(response.status_code, 200)
  215. response_json = response.json()
  216. self.assertEqual(response_json["subcategories"][0], self.category_b.pk)
  217. def test_display_pinned_threads(self):
  218. """
  219. threads list displays globally pinned threads first
  220. and locally ones inbetween other
  221. """
  222. globally = test.post_thread(category=self.first_category, is_global=True)
  223. locally = test.post_thread(category=self.first_category, is_pinned=True)
  224. standard = test.post_thread(category=self.first_category)
  225. response = self.client.get("/")
  226. self.assertEqual(response.status_code, 200)
  227. content = smart_str(response.content)
  228. positions = {
  229. "g": content.find(globally.get_absolute_url()),
  230. "l": content.find(locally.get_absolute_url()),
  231. "s": content.find(standard.get_absolute_url()),
  232. }
  233. # global announcement before others
  234. self.assertTrue(positions["g"] < positions["l"])
  235. self.assertTrue(positions["g"] < positions["s"])
  236. # standard in the middle
  237. self.assertTrue(positions["s"] < positions["l"])
  238. self.assertTrue(positions["s"] > positions["g"])
  239. # pinned last
  240. self.assertTrue(positions["l"] > positions["g"])
  241. self.assertTrue(positions["l"] > positions["s"])
  242. # API behaviour is identic
  243. response = self.client.get("/api/threads/")
  244. self.assertEqual(response.status_code, 200)
  245. content = smart_str(response.content)
  246. positions = {
  247. "g": content.find(globally.get_absolute_url()),
  248. "l": content.find(locally.get_absolute_url()),
  249. "s": content.find(standard.get_absolute_url()),
  250. }
  251. # global announcement before others
  252. self.assertTrue(positions["g"] < positions["l"])
  253. self.assertTrue(positions["g"] < positions["s"])
  254. # standard in the middle
  255. self.assertTrue(positions["s"] < positions["l"])
  256. self.assertTrue(positions["s"] > positions["g"])
  257. # pinned last
  258. self.assertTrue(positions["l"] > positions["g"])
  259. self.assertTrue(positions["l"] > positions["s"])
  260. @override_dynamic_settings(threads_per_page=5)
  261. def test_noscript_pagination(self):
  262. """threads list is paginated for users with js disabled"""
  263. threads_per_page = 5
  264. # post and discard thread to move last_post_id count by one
  265. test.post_thread(category=self.first_category).delete()
  266. # create test threads
  267. threads = []
  268. for _ in range(threads_per_page * 2):
  269. threads.append(test.post_thread(category=self.first_category))
  270. # threads starting with given one are on the list
  271. response = self.client.get("/?start=%s" % threads[-2].last_post_id)
  272. self.assertEqual(response.status_code, 200)
  273. # first thread is skipped by cursor pagination
  274. self.assertNotContainsThread(response, threads[-1])
  275. # starting thread is present
  276. self.assertContainsThread(response, threads[-2])
  277. # slice contains expected threads
  278. for visible_thread in threads[threads_per_page - 1 : -1]:
  279. self.assertContainsThread(response, visible_thread)
  280. # threads after slice are hidden
  281. for invisible_thread in threads[: threads_per_page - 1]:
  282. self.assertNotContainsThread(response, invisible_thread)
  283. # nonexisting start gives 404
  284. response = self.client.get("/?start=%s" % (threads[0].last_post_id - 1))
  285. self.assertEqual(response.status_code, 404)
  286. class CategoryThreadsListTests(ThreadsListTestCase):
  287. def test_access_hidden_category(self):
  288. """hidden category returns 404"""
  289. Category(name="Hidden Category", slug="hidden-category").insert_at(
  290. self.root, position="last-child", save=True
  291. )
  292. test_category = Category.objects.get(slug="hidden-category")
  293. for url in LISTS_URLS:
  294. response = self.client.get(test_category.get_absolute_url() + url)
  295. self.assertEqual(response.status_code, 404)
  296. response = self.client.get(
  297. "%s?category=%s" % (self.api_link, test_category.id)
  298. )
  299. self.assertEqual(response.status_code, 404)
  300. def test_access_protected_category(self):
  301. """protected category returns 403"""
  302. Category(name="Hidden Category", slug="hidden-category").insert_at(
  303. self.root, position="last-child", save=True
  304. )
  305. test_category = Category.objects.get(slug="hidden-category")
  306. for url in LISTS_URLS:
  307. with patch_user_acl(
  308. {
  309. "visible_categories": [test_category.id],
  310. "browseable_categories": [],
  311. "categories": {test_category.id: {"can_see": 1, "can_browse": 0}},
  312. }
  313. ):
  314. response = self.client.get(test_category.get_absolute_url() + url)
  315. self.assertEqual(response.status_code, 403)
  316. response = self.client.get(
  317. "%s?category=%s&list=%s"
  318. % (self.api_link, test_category.id, url.strip("/"))
  319. )
  320. self.assertEqual(response.status_code, 403)
  321. def test_display_pinned_threads(self):
  322. """
  323. category threads list displays globally pinned threads first
  324. then locally ones and unpinned last
  325. """
  326. globally = test.post_thread(category=self.first_category, is_global=True)
  327. locally = test.post_thread(category=self.first_category, is_pinned=True)
  328. standard = test.post_thread(category=self.first_category)
  329. response = self.client.get(self.first_category.get_absolute_url())
  330. self.assertEqual(response.status_code, 200)
  331. content = smart_str(response.content)
  332. positions = {
  333. "g": content.find(globally.get_absolute_url()),
  334. "l": content.find(locally.get_absolute_url()),
  335. "s": content.find(standard.get_absolute_url()),
  336. }
  337. # global announcement before others
  338. self.assertTrue(positions["g"] < positions["l"])
  339. self.assertTrue(positions["g"] < positions["s"])
  340. # pinned in the middle
  341. self.assertTrue(positions["l"] < positions["s"])
  342. self.assertTrue(positions["l"] > positions["g"])
  343. # standard last
  344. self.assertTrue(positions["s"] > positions["g"])
  345. self.assertTrue(positions["s"] > positions["g"])
  346. # API behaviour is identic
  347. response = self.client.get("/api/threads/?category=%s" % self.first_category.id)
  348. self.assertEqual(response.status_code, 200)
  349. content = smart_str(response.content)
  350. positions = {
  351. "g": content.find(globally.get_absolute_url()),
  352. "l": content.find(locally.get_absolute_url()),
  353. "s": content.find(standard.get_absolute_url()),
  354. }
  355. # global announcement before others
  356. self.assertTrue(positions["g"] < positions["l"])
  357. self.assertTrue(positions["g"] < positions["s"])
  358. # pinned in the middle
  359. self.assertTrue(positions["l"] < positions["s"])
  360. self.assertTrue(positions["l"] > positions["g"])
  361. # standard last
  362. self.assertTrue(positions["s"] > positions["g"])
  363. self.assertTrue(positions["s"] > positions["g"])
  364. class ThreadsVisibilityTests(ThreadsListTestCase):
  365. @patch_categories_acl()
  366. def test_list_renders_test_thread(self):
  367. """list renders test thread with valid top category"""
  368. test_thread = test.post_thread(category=self.category_c)
  369. response = self.client.get("/")
  370. self.assertEqual(response.status_code, 200)
  371. self.assertContainsThread(response, test_thread)
  372. self.assertContains(response, "subcategory-%s" % self.category_a.css_class)
  373. self.assertContains(response, "subcategory-%s" % self.category_e.css_class)
  374. # api displays same data
  375. response = self.client.get(self.api_link)
  376. self.assertEqual(response.status_code, 200)
  377. response_json = response.json()
  378. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  379. self.assertEqual(len(response_json["subcategories"]), 3)
  380. self.assertIn(self.category_a.pk, response_json["subcategories"])
  381. # test category view
  382. response = self.client.get(self.category_b.get_absolute_url())
  383. self.assertEqual(response.status_code, 200)
  384. # thread displays
  385. self.assertContainsThread(response, test_thread)
  386. # api displays same data
  387. response = self.client.get(
  388. "%s?category=%s" % (self.api_link, self.category_b.pk)
  389. )
  390. self.assertEqual(response.status_code, 200)
  391. response_json = response.json()
  392. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  393. self.assertEqual(len(response_json["subcategories"]), 2)
  394. self.assertEqual(response_json["subcategories"][0], self.category_c.pk)
  395. def test_list_hides_hidden_thread(self):
  396. """list renders empty due to no permission to see thread"""
  397. Category(name="Hidden Category", slug="hidden-category").insert_at(
  398. self.root, position="last-child", save=True
  399. )
  400. test_category = Category.objects.get(slug="hidden-category")
  401. test_thread = test.post_thread(category=test_category)
  402. response = self.client.get("/")
  403. self.assertEqual(response.status_code, 200)
  404. self.assertContains(response, "empty-message")
  405. self.assertNotContainsThread(response, test_thread)
  406. def test_api_hides_hidden_thread(self):
  407. """api returns empty due to no permission to see thread"""
  408. Category(name="Hidden Category", slug="hidden-category").insert_at(
  409. self.root, position="last-child", save=True
  410. )
  411. test_category = Category.objects.get(slug="hidden-category")
  412. test.post_thread(category=test_category)
  413. response = self.client.get(self.api_link)
  414. self.assertEqual(response.status_code, 200)
  415. response_json = response.json()
  416. self.assertEqual(len(response_json["results"]), 0)
  417. @patch_categories_acl()
  418. def test_list_user_see_own_unapproved_thread(self):
  419. """list renders unapproved thread that belongs to viewer"""
  420. test_thread = test.post_thread(
  421. category=self.category_a, poster=self.user, is_unapproved=True
  422. )
  423. response = self.client.get("/")
  424. self.assertEqual(response.status_code, 200)
  425. self.assertContainsThread(response, test_thread)
  426. # test api
  427. response = self.client.get(self.api_link)
  428. self.assertEqual(response.status_code, 200)
  429. response_json = response.json()
  430. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  431. @patch_categories_acl()
  432. def test_list_user_cant_see_unapproved_thread(self):
  433. """list hides unapproved thread that belongs to other user"""
  434. test_thread = test.post_thread(category=self.category_a, is_unapproved=True)
  435. response = self.client.get("/")
  436. self.assertEqual(response.status_code, 200)
  437. self.assertNotContainsThread(response, test_thread)
  438. # test api
  439. response = self.client.get(self.api_link)
  440. self.assertEqual(response.status_code, 200)
  441. response_json = response.json()
  442. self.assertEqual(len(response_json["results"]), 0)
  443. @patch_categories_acl()
  444. def test_list_user_cant_see_hidden_thread(self):
  445. """list hides hidden thread that belongs to other user"""
  446. test_thread = test.post_thread(category=self.category_a, is_hidden=True)
  447. response = self.client.get("/")
  448. self.assertEqual(response.status_code, 200)
  449. self.assertNotContainsThread(response, test_thread)
  450. # test api
  451. response = self.client.get(self.api_link)
  452. self.assertEqual(response.status_code, 200)
  453. response_json = response.json()
  454. self.assertEqual(len(response_json["results"]), 0)
  455. @patch_categories_acl()
  456. def test_list_user_cant_see_own_hidden_thread(self):
  457. """list hides hidden thread that belongs to viewer"""
  458. test_thread = test.post_thread(
  459. category=self.category_a, poster=self.user, is_hidden=True
  460. )
  461. response = self.client.get("/")
  462. self.assertEqual(response.status_code, 200)
  463. self.assertNotContainsThread(response, test_thread)
  464. # test api
  465. response = self.client.get(self.api_link)
  466. self.assertEqual(response.status_code, 200)
  467. response_json = response.json()
  468. self.assertEqual(len(response_json["results"]), 0)
  469. @patch_categories_acl({"can_hide_threads": 1})
  470. def test_list_user_can_see_own_hidden_thread(self):
  471. """list shows hidden thread that belongs to viewer due to permission"""
  472. test_thread = test.post_thread(
  473. category=self.category_a, poster=self.user, is_hidden=True
  474. )
  475. response = self.client.get("/")
  476. self.assertEqual(response.status_code, 200)
  477. self.assertContainsThread(response, test_thread)
  478. # test api
  479. response = self.client.get(self.api_link)
  480. self.assertEqual(response.status_code, 200)
  481. response_json = response.json()
  482. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  483. @patch_categories_acl({"can_hide_threads": 1})
  484. def test_list_user_can_see_hidden_thread(self):
  485. """list shows hidden thread that belongs to other user due to permission"""
  486. test_thread = test.post_thread(category=self.category_a, is_hidden=True)
  487. response = self.client.get("/")
  488. self.assertEqual(response.status_code, 200)
  489. self.assertContainsThread(response, test_thread)
  490. # test api
  491. response = self.client.get(self.api_link)
  492. self.assertEqual(response.status_code, 200)
  493. response_json = response.json()
  494. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  495. @patch_categories_acl({"can_approve_content": 1})
  496. def test_list_user_can_see_unapproved_thread(self):
  497. """list shows hidden thread that belongs to other user due to permission"""
  498. test_thread = test.post_thread(category=self.category_a, is_unapproved=True)
  499. response = self.client.get("/")
  500. self.assertEqual(response.status_code, 200)
  501. self.assertContainsThread(response, test_thread)
  502. # test api
  503. response = self.client.get(self.api_link)
  504. self.assertEqual(response.status_code, 200)
  505. response_json = response.json()
  506. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  507. class MyThreadsListTests(ThreadsListTestCase):
  508. @patch_categories_acl()
  509. def test_list_renders_empty(self):
  510. """list renders empty"""
  511. response = self.client.get("/my/")
  512. self.assertEqual(response.status_code, 200)
  513. self.assertContains(response, "empty-message")
  514. response = self.client.get(self.category_a.get_absolute_url() + "my/")
  515. self.assertEqual(response.status_code, 200)
  516. self.assertContains(response, "empty-message")
  517. # test api
  518. response = self.client.get("%s?list=my" % self.api_link)
  519. self.assertEqual(response.status_code, 200)
  520. response_json = response.json()
  521. self.assertEqual(len(response_json["results"]), 0)
  522. response = self.client.get(
  523. "%s?list=my&category=%s" % (self.api_link, self.category_a.pk)
  524. )
  525. response_json = response.json()
  526. self.assertEqual(len(response_json["results"]), 0)
  527. @patch_categories_acl()
  528. def test_list_renders_test_thread(self):
  529. """list renders only threads posted by user"""
  530. test_thread = test.post_thread(category=self.category_a, poster=self.user)
  531. other_thread = test.post_thread(category=self.category_a)
  532. response = self.client.get("/my/")
  533. self.assertEqual(response.status_code, 200)
  534. self.assertContainsThread(response, test_thread)
  535. self.assertNotContainsThread(response, other_thread)
  536. response = self.client.get(self.category_a.get_absolute_url() + "my/")
  537. self.assertEqual(response.status_code, 200)
  538. self.assertContainsThread(response, test_thread)
  539. self.assertNotContainsThread(response, other_thread)
  540. # test api
  541. response = self.client.get("%s?list=my" % self.api_link)
  542. self.assertEqual(response.status_code, 200)
  543. response_json = response.json()
  544. self.assertEqual(len(response_json["results"]), 1)
  545. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  546. response = self.client.get(
  547. "%s?list=my&category=%s" % (self.api_link, self.category_a.pk)
  548. )
  549. self.assertEqual(response.status_code, 200)
  550. response_json = response.json()
  551. self.assertEqual(len(response_json["results"]), 1)
  552. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  553. class NewThreadsListTests(ThreadsListTestCase):
  554. @patch_categories_acl()
  555. def test_list_renders_empty(self):
  556. """list renders empty"""
  557. response = self.client.get("/new/")
  558. self.assertEqual(response.status_code, 200)
  559. self.assertContains(response, "empty-message")
  560. response = self.client.get(self.category_a.get_absolute_url() + "new/")
  561. self.assertEqual(response.status_code, 200)
  562. self.assertContains(response, "empty-message")
  563. # test api
  564. response = self.client.get("%s?list=new" % self.api_link)
  565. self.assertEqual(response.status_code, 200)
  566. response_json = response.json()
  567. self.assertEqual(len(response_json["results"]), 0)
  568. response = self.client.get(
  569. "%s?list=new&category=%s" % (self.api_link, self.category_a.pk)
  570. )
  571. response_json = response.json()
  572. self.assertEqual(len(response_json["results"]), 0)
  573. @patch_categories_acl()
  574. def test_list_renders_new_thread(self):
  575. """list renders new thread"""
  576. test_thread = test.post_thread(category=self.category_a)
  577. response = self.client.get("/new/")
  578. self.assertEqual(response.status_code, 200)
  579. self.assertContainsThread(response, test_thread)
  580. response = self.client.get(self.category_a.get_absolute_url() + "new/")
  581. self.assertEqual(response.status_code, 200)
  582. self.assertContainsThread(response, test_thread)
  583. # test api
  584. response = self.client.get("%s?list=new" % self.api_link)
  585. self.assertEqual(response.status_code, 200)
  586. response_json = response.json()
  587. self.assertEqual(len(response_json["results"]), 1)
  588. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  589. response = self.client.get(
  590. "%s?list=new&category=%s" % (self.api_link, self.category_a.pk)
  591. )
  592. self.assertEqual(response.status_code, 200)
  593. response_json = response.json()
  594. self.assertEqual(len(response_json["results"]), 1)
  595. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  596. @patch_categories_acl()
  597. def test_list_renders_thread_bumped_after_user_cutoff(self):
  598. """list renders new thread bumped after user cutoff"""
  599. self.user.joined_on = timezone.now() - timedelta(days=10)
  600. self.user.save()
  601. test_thread = test.post_thread(
  602. category=self.category_a, started_on=self.user.joined_on - timedelta(days=2)
  603. )
  604. test.reply_thread(
  605. test_thread, posted_on=self.user.joined_on + timedelta(days=4)
  606. )
  607. response = self.client.get("/new/")
  608. self.assertEqual(response.status_code, 200)
  609. self.assertContainsThread(response, test_thread)
  610. response = self.client.get(self.category_a.get_absolute_url() + "new/")
  611. self.assertEqual(response.status_code, 200)
  612. self.assertContainsThread(response, test_thread)
  613. # test api
  614. response = self.client.get("%s?list=new" % self.api_link)
  615. self.assertEqual(response.status_code, 200)
  616. response_json = response.json()
  617. self.assertEqual(len(response_json["results"]), 1)
  618. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  619. response = self.client.get(
  620. "%s?list=new&category=%s" % (self.api_link, self.category_a.pk)
  621. )
  622. self.assertEqual(response.status_code, 200)
  623. response_json = response.json()
  624. self.assertEqual(len(response_json["results"]), 1)
  625. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  626. @override_dynamic_settings(readtracker_cutoff=3)
  627. @patch_categories_acl()
  628. def test_list_hides_global_cutoff_thread(self):
  629. """list hides thread started before global cutoff"""
  630. self.user.joined_on = timezone.now() - timedelta(days=10)
  631. self.user.save()
  632. test_thread = test.post_thread(
  633. category=self.category_a, started_on=timezone.now() - timedelta(days=5)
  634. )
  635. response = self.client.get("/new/")
  636. self.assertEqual(response.status_code, 200)
  637. self.assertNotContainsThread(response, test_thread)
  638. response = self.client.get(self.category_a.get_absolute_url() + "new/")
  639. self.assertEqual(response.status_code, 200)
  640. self.assertNotContainsThread(response, test_thread)
  641. # test api
  642. response = self.client.get("%s?list=new" % self.api_link)
  643. self.assertEqual(response.status_code, 200)
  644. response_json = response.json()
  645. self.assertEqual(len(response_json["results"]), 0)
  646. response = self.client.get(
  647. "%s?list=new&category=%s" % (self.api_link, self.category_a.pk)
  648. )
  649. self.assertEqual(response.status_code, 200)
  650. response_json = response.json()
  651. self.assertEqual(len(response_json["results"]), 0)
  652. @patch_categories_acl()
  653. def test_list_hides_user_cutoff_thread(self):
  654. """list hides thread started before users cutoff"""
  655. self.user.joined_on = timezone.now() - timedelta(days=5)
  656. self.user.save()
  657. test_thread = test.post_thread(
  658. category=self.category_a,
  659. started_on=self.user.joined_on - timedelta(minutes=1),
  660. )
  661. response = self.client.get("/new/")
  662. self.assertEqual(response.status_code, 200)
  663. self.assertNotContainsThread(response, test_thread)
  664. response = self.client.get(self.category_a.get_absolute_url() + "new/")
  665. self.assertEqual(response.status_code, 200)
  666. self.assertNotContainsThread(response, test_thread)
  667. # test api
  668. response = self.client.get("%s?list=new" % self.api_link)
  669. self.assertEqual(response.status_code, 200)
  670. response_json = response.json()
  671. self.assertEqual(len(response_json["results"]), 0)
  672. response = self.client.get(
  673. "%s?list=new&category=%s" % (self.api_link, self.category_a.pk)
  674. )
  675. self.assertEqual(response.status_code, 200)
  676. response_json = response.json()
  677. self.assertEqual(len(response_json["results"]), 0)
  678. @patch_categories_acl()
  679. def test_list_hides_user_read_thread(self):
  680. """list hides thread already read by user"""
  681. self.user.joined_on = timezone.now() - timedelta(days=5)
  682. self.user.save()
  683. test_thread = test.post_thread(category=self.category_a)
  684. poststracker.save_read(self.user, test_thread.first_post)
  685. response = self.client.get("/new/")
  686. self.assertEqual(response.status_code, 200)
  687. self.assertNotContainsThread(response, test_thread)
  688. response = self.client.get(self.category_a.get_absolute_url() + "new/")
  689. self.assertEqual(response.status_code, 200)
  690. self.assertNotContainsThread(response, test_thread)
  691. # test api
  692. response = self.client.get("%s?list=new" % self.api_link)
  693. self.assertEqual(response.status_code, 200)
  694. response_json = response.json()
  695. self.assertEqual(len(response_json["results"]), 0)
  696. response = self.client.get(
  697. "%s?list=new&category=%s" % (self.api_link, self.category_a.pk)
  698. )
  699. self.assertEqual(response.status_code, 200)
  700. response_json = response.json()
  701. self.assertEqual(len(response_json["results"]), 0)
  702. class UnreadThreadsListTests(ThreadsListTestCase):
  703. @patch_categories_acl()
  704. def test_list_renders_empty(self):
  705. """list renders empty"""
  706. response = self.client.get("/unread/")
  707. self.assertEqual(response.status_code, 200)
  708. self.assertContains(response, "empty-message")
  709. response = self.client.get(self.category_a.get_absolute_url() + "unread/")
  710. self.assertEqual(response.status_code, 200)
  711. self.assertContains(response, "empty-message")
  712. # test api
  713. response = self.client.get("%s?list=unread" % self.api_link)
  714. self.assertEqual(response.status_code, 200)
  715. response_json = response.json()
  716. self.assertEqual(len(response_json["results"]), 0)
  717. response = self.client.get(
  718. "%s?list=unread&category=%s" % (self.api_link, self.category_a.pk)
  719. )
  720. self.assertEqual(response.status_code, 200)
  721. response_json = response.json()
  722. self.assertEqual(len(response_json["results"]), 0)
  723. @patch_categories_acl()
  724. def test_list_renders_unread_thread(self):
  725. """list renders thread with unread posts"""
  726. self.user.joined_on = timezone.now() - timedelta(days=5)
  727. self.user.save()
  728. test_thread = test.post_thread(category=self.category_a)
  729. poststracker.save_read(self.user, test_thread.first_post)
  730. test.reply_thread(test_thread)
  731. response = self.client.get("/unread/")
  732. self.assertEqual(response.status_code, 200)
  733. self.assertContainsThread(response, test_thread)
  734. response = self.client.get(self.category_a.get_absolute_url() + "unread/")
  735. self.assertEqual(response.status_code, 200)
  736. self.assertContainsThread(response, test_thread)
  737. # test api
  738. response = self.client.get("%s?list=unread" % self.api_link)
  739. self.assertEqual(response.status_code, 200)
  740. response_json = response.json()
  741. self.assertEqual(len(response_json["results"]), 1)
  742. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  743. response = self.client.get(
  744. "%s?list=unread&category=%s" % (self.api_link, self.category_a.pk)
  745. )
  746. self.assertEqual(response.status_code, 200)
  747. response_json = response.json()
  748. self.assertEqual(len(response_json["results"]), 1)
  749. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  750. @patch_categories_acl()
  751. def test_list_hides_never_read_thread(self):
  752. """list hides never read thread"""
  753. self.user.joined_on = timezone.now() - timedelta(days=5)
  754. self.user.save()
  755. test_thread = test.post_thread(category=self.category_a)
  756. response = self.client.get("/unread/")
  757. self.assertEqual(response.status_code, 200)
  758. self.assertNotContainsThread(response, test_thread)
  759. response = self.client.get(self.category_a.get_absolute_url() + "unread/")
  760. self.assertEqual(response.status_code, 200)
  761. self.assertNotContainsThread(response, test_thread)
  762. # test api
  763. response = self.client.get("%s?list=unread" % self.api_link)
  764. self.assertEqual(response.status_code, 200)
  765. response_json = response.json()
  766. self.assertEqual(len(response_json["results"]), 0)
  767. response = self.client.get(
  768. "%s?list=unread&category=%s" % (self.api_link, self.category_a.pk)
  769. )
  770. self.assertEqual(response.status_code, 200)
  771. response_json = response.json()
  772. self.assertEqual(len(response_json["results"]), 0)
  773. @patch_categories_acl()
  774. def test_list_hides_read_thread(self):
  775. """list hides read thread"""
  776. self.user.joined_on = timezone.now() - timedelta(days=5)
  777. self.user.save()
  778. test_thread = test.post_thread(category=self.category_a)
  779. poststracker.save_read(self.user, test_thread.first_post)
  780. response = self.client.get("/unread/")
  781. self.assertEqual(response.status_code, 200)
  782. self.assertNotContainsThread(response, test_thread)
  783. response = self.client.get(self.category_a.get_absolute_url() + "unread/")
  784. self.assertEqual(response.status_code, 200)
  785. self.assertNotContainsThread(response, test_thread)
  786. # test api
  787. response = self.client.get("%s?list=unread" % self.api_link)
  788. self.assertEqual(response.status_code, 200)
  789. response_json = response.json()
  790. self.assertEqual(len(response_json["results"]), 0)
  791. response = self.client.get(
  792. "%s?list=unread&category=%s" % (self.api_link, self.category_a.pk)
  793. )
  794. self.assertEqual(response.status_code, 200)
  795. response_json = response.json()
  796. self.assertEqual(len(response_json["results"]), 0)
  797. @override_dynamic_settings(readtracker_cutoff=3)
  798. @patch_categories_acl()
  799. def test_list_hides_global_cutoff_thread(self):
  800. """list hides thread replied before global cutoff"""
  801. self.user.joined_on = timezone.now() - timedelta(days=10)
  802. self.user.save()
  803. test_thread = test.post_thread(
  804. category=self.category_a, started_on=timezone.now() - timedelta(days=5)
  805. )
  806. poststracker.save_read(self.user, test_thread.first_post)
  807. test.reply_thread(
  808. test_thread, posted_on=test_thread.started_on + timedelta(days=1)
  809. )
  810. response = self.client.get("/unread/")
  811. self.assertEqual(response.status_code, 200)
  812. self.assertNotContainsThread(response, test_thread)
  813. response = self.client.get(self.category_a.get_absolute_url() + "unread/")
  814. self.assertEqual(response.status_code, 200)
  815. self.assertNotContainsThread(response, test_thread)
  816. # test api
  817. response = self.client.get("%s?list=unread" % self.api_link)
  818. self.assertEqual(response.status_code, 200)
  819. response_json = response.json()
  820. self.assertEqual(len(response_json["results"]), 0)
  821. response = self.client.get(
  822. "%s?list=unread&category=%s" % (self.api_link, self.category_a.pk)
  823. )
  824. self.assertEqual(response.status_code, 200)
  825. response_json = response.json()
  826. self.assertEqual(len(response_json["results"]), 0)
  827. @patch_categories_acl()
  828. def test_list_hides_user_cutoff_thread(self):
  829. """list hides thread replied before user cutoff"""
  830. self.user.joined_on = timezone.now() - timedelta(days=10)
  831. self.user.save()
  832. test_thread = test.post_thread(
  833. category=self.category_a, started_on=self.user.joined_on - timedelta(days=2)
  834. )
  835. poststracker.save_read(self.user, test_thread.first_post)
  836. test.reply_thread(
  837. test_thread, posted_on=test_thread.started_on + timedelta(days=1)
  838. )
  839. response = self.client.get("/unread/")
  840. self.assertEqual(response.status_code, 200)
  841. self.assertNotContainsThread(response, test_thread)
  842. response = self.client.get(self.category_a.get_absolute_url() + "unread/")
  843. self.assertEqual(response.status_code, 200)
  844. self.assertNotContainsThread(response, test_thread)
  845. # test api
  846. response = self.client.get("%s?list=unread" % self.api_link)
  847. self.assertEqual(response.status_code, 200)
  848. response_json = response.json()
  849. self.assertEqual(len(response_json["results"]), 0)
  850. response = self.client.get(
  851. "%s?list=unread&category=%s" % (self.api_link, self.category_a.pk)
  852. )
  853. self.assertEqual(response.status_code, 200)
  854. response_json = response.json()
  855. self.assertEqual(len(response_json["results"]), 0)
  856. class SubscribedThreadsListTests(ThreadsListTestCase):
  857. @patch_categories_acl()
  858. def test_list_shows_subscribed_thread(self):
  859. """list shows subscribed thread"""
  860. test_thread = test.post_thread(category=self.category_a)
  861. self.user.subscription_set.create(
  862. thread=test_thread,
  863. category=self.category_a,
  864. last_read_on=test_thread.last_post_on,
  865. )
  866. response = self.client.get("/subscribed/")
  867. self.assertEqual(response.status_code, 200)
  868. self.assertContainsThread(response, test_thread)
  869. response = self.client.get(self.category_a.get_absolute_url() + "subscribed/")
  870. self.assertEqual(response.status_code, 200)
  871. self.assertContainsThread(response, test_thread)
  872. # test api
  873. response = self.client.get("%s?list=subscribed" % self.api_link)
  874. self.assertEqual(response.status_code, 200)
  875. response_json = response.json()
  876. self.assertEqual(len(response_json["results"]), 1)
  877. self.assertContains(response, test_thread.get_absolute_url())
  878. response = self.client.get(
  879. "%s?list=subscribed&category=%s" % (self.api_link, self.category_a.pk)
  880. )
  881. self.assertEqual(response.status_code, 200)
  882. response_json = response.json()
  883. self.assertEqual(len(response_json["results"]), 1)
  884. self.assertContains(response, test_thread.get_absolute_url())
  885. @patch_categories_acl()
  886. def test_list_hides_unsubscribed_thread(self):
  887. """list shows subscribed thread"""
  888. test_thread = test.post_thread(category=self.category_a)
  889. response = self.client.get("/subscribed/")
  890. self.assertEqual(response.status_code, 200)
  891. self.assertNotContainsThread(response, test_thread)
  892. response = self.client.get(self.category_a.get_absolute_url() + "subscribed/")
  893. self.assertEqual(response.status_code, 200)
  894. self.assertNotContainsThread(response, test_thread)
  895. # test api
  896. response = self.client.get("%s?list=subscribed" % self.api_link)
  897. self.assertEqual(response.status_code, 200)
  898. response_json = response.json()
  899. self.assertEqual(len(response_json["results"]), 0)
  900. self.assertNotContainsThread(response, test_thread)
  901. response = self.client.get(
  902. "%s?list=subscribed&category=%s" % (self.api_link, self.category_a.pk)
  903. )
  904. self.assertEqual(response.status_code, 200)
  905. response_json = response.json()
  906. self.assertEqual(len(response_json["results"]), 0)
  907. self.assertNotContainsThread(response, test_thread)
  908. class UnapprovedListTests(ThreadsListTestCase):
  909. def test_list_errors_without_permission(self):
  910. """list errors if user has no permission to access it"""
  911. TEST_URLS = (
  912. "/unapproved/",
  913. self.category_a.get_absolute_url() + "unapproved/",
  914. "%s?list=unapproved" % self.api_link,
  915. )
  916. with patch_categories_acl():
  917. for test_url in TEST_URLS:
  918. response = self.client.get(test_url)
  919. self.assertEqual(response.status_code, 403)
  920. # approval perm has no influence on visibility
  921. with patch_categories_acl({"can_approve_content": True}):
  922. for test_url in TEST_URLS:
  923. response = self.client.get(test_url)
  924. self.assertEqual(response.status_code, 403)
  925. # approval perm has no influence on visibility
  926. with patch_categories_acl(base_acl={"can_see_unapproved_content_lists": True}):
  927. for test_url in TEST_URLS:
  928. response = self.client.get(test_url)
  929. self.assertEqual(response.status_code, 200)
  930. @patch_categories_acl(
  931. {"can_approve_content": True}, {"can_see_unapproved_content_lists": True}
  932. )
  933. def test_list_shows_all_threads_for_approving_user(self):
  934. """list shows all threads with unapproved posts when user has perm"""
  935. visible_thread = test.post_thread(category=self.category_b, is_unapproved=True)
  936. hidden_thread = test.post_thread(category=self.category_b, is_unapproved=False)
  937. response = self.client.get("/unapproved/")
  938. self.assertEqual(response.status_code, 200)
  939. self.assertContainsThread(response, visible_thread)
  940. self.assertNotContainsThread(response, hidden_thread)
  941. response = self.client.get(self.category_a.get_absolute_url() + "unapproved/")
  942. self.assertEqual(response.status_code, 200)
  943. self.assertContainsThread(response, visible_thread)
  944. self.assertNotContainsThread(response, hidden_thread)
  945. # test api
  946. response = self.client.get("%s?list=unapproved" % self.api_link)
  947. self.assertEqual(response.status_code, 200)
  948. self.assertContains(response, visible_thread.get_absolute_url())
  949. self.assertNotContains(response, hidden_thread.get_absolute_url())
  950. @patch_categories_acl(base_acl={"can_see_unapproved_content_lists": True})
  951. def test_list_shows_owned_threads_for_unapproving_user(self):
  952. """list shows owned threads with unapproved posts for user without perm"""
  953. visible_thread = test.post_thread(
  954. poster=self.user, category=self.category_b, is_unapproved=True
  955. )
  956. hidden_thread = test.post_thread(category=self.category_b, is_unapproved=True)
  957. response = self.client.get("/unapproved/")
  958. self.assertEqual(response.status_code, 200)
  959. self.assertContainsThread(response, visible_thread)
  960. self.assertNotContainsThread(response, hidden_thread)
  961. response = self.client.get(self.category_a.get_absolute_url() + "unapproved/")
  962. self.assertEqual(response.status_code, 200)
  963. self.assertContainsThread(response, visible_thread)
  964. self.assertNotContainsThread(response, hidden_thread)
  965. # test api
  966. response = self.client.get("%s?list=unapproved" % self.api_link)
  967. self.assertEqual(response.status_code, 200)
  968. self.assertContains(response, visible_thread.get_absolute_url())
  969. self.assertNotContains(response, hidden_thread.get_absolute_url())
  970. def patch_category_see_all_threads_acl():
  971. def patch_acl(_, user_acl):
  972. category = Category.objects.get(slug="first-category")
  973. category_acl = user_acl["categories"][category.id].copy()
  974. category_acl.update({"can_see_all_threads": 0})
  975. user_acl["categories"][category.id] = category_acl
  976. return patch_user_acl(patch_acl)
  977. class OwnerOnlyThreadsVisibilityTests(AuthenticatedUserTestCase):
  978. def setUp(self):
  979. super().setUp()
  980. self.category = Category.objects.get(slug="first-category")
  981. def test_owned_threads_visibility(self):
  982. """only user-posted threads are visible in category"""
  983. visible_thread = test.post_thread(
  984. poster=self.user, category=self.category, is_unapproved=True
  985. )
  986. hidden_thread = test.post_thread(category=self.category, is_unapproved=True)
  987. with patch_category_see_all_threads_acl():
  988. response = self.client.get(self.category.get_absolute_url())
  989. self.assertEqual(response.status_code, 200)
  990. self.assertContains(response, visible_thread.get_absolute_url())
  991. self.assertNotContains(response, hidden_thread.get_absolute_url())
  992. def test_owned_threads_visibility_anonymous(self):
  993. """anons can't see any threads in limited visibility category"""
  994. self.logout_user()
  995. user_thread = test.post_thread(
  996. poster=self.user, category=self.category, is_unapproved=True
  997. )
  998. guest_thread = test.post_thread(category=self.category, is_unapproved=True)
  999. with patch_category_see_all_threads_acl():
  1000. response = self.client.get(self.category.get_absolute_url())
  1001. self.assertEqual(response.status_code, 200)
  1002. self.assertNotContains(response, user_thread.get_absolute_url())
  1003. self.assertNotContains(response, guest_thread.get_absolute_url())