test_threadslists.py 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323
  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. self.assertNotContains(
  375. response, "thread-detail-category-%s" % self.category_a.css_class
  376. )
  377. self.assertContains(
  378. response, "thread-detail-category-%s" % self.category_c.css_class
  379. )
  380. # api displays same data
  381. response = self.client.get(self.api_link)
  382. self.assertEqual(response.status_code, 200)
  383. response_json = response.json()
  384. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  385. self.assertEqual(len(response_json["subcategories"]), 3)
  386. self.assertIn(self.category_a.pk, response_json["subcategories"])
  387. # test category view
  388. response = self.client.get(self.category_b.get_absolute_url())
  389. self.assertEqual(response.status_code, 200)
  390. # thread displays
  391. self.assertContainsThread(response, test_thread)
  392. self.assertNotContains(
  393. response, "thread-detail-category-%s" % self.category_b.css_class
  394. )
  395. self.assertContains(
  396. response, "thread-detail-category-%s" % self.category_c.css_class
  397. )
  398. # api displays same data
  399. response = self.client.get(
  400. "%s?category=%s" % (self.api_link, self.category_b.pk)
  401. )
  402. self.assertEqual(response.status_code, 200)
  403. response_json = response.json()
  404. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  405. self.assertEqual(len(response_json["subcategories"]), 2)
  406. self.assertEqual(response_json["subcategories"][0], self.category_c.pk)
  407. def test_list_hides_hidden_thread(self):
  408. """list renders empty due to no permission to see thread"""
  409. Category(name="Hidden Category", slug="hidden-category").insert_at(
  410. self.root, position="last-child", save=True
  411. )
  412. test_category = Category.objects.get(slug="hidden-category")
  413. test_thread = test.post_thread(category=test_category)
  414. response = self.client.get("/")
  415. self.assertEqual(response.status_code, 200)
  416. self.assertContains(response, "empty-message")
  417. self.assertNotContainsThread(response, test_thread)
  418. def test_api_hides_hidden_thread(self):
  419. """api returns empty due to no permission to see thread"""
  420. Category(name="Hidden Category", slug="hidden-category").insert_at(
  421. self.root, position="last-child", save=True
  422. )
  423. test_category = Category.objects.get(slug="hidden-category")
  424. test.post_thread(category=test_category)
  425. response = self.client.get(self.api_link)
  426. self.assertEqual(response.status_code, 200)
  427. response_json = response.json()
  428. self.assertEqual(len(response_json["results"]), 0)
  429. @patch_categories_acl()
  430. def test_list_user_see_own_unapproved_thread(self):
  431. """list renders unapproved thread that belongs to viewer"""
  432. test_thread = test.post_thread(
  433. category=self.category_a, poster=self.user, is_unapproved=True
  434. )
  435. response = self.client.get("/")
  436. self.assertEqual(response.status_code, 200)
  437. self.assertContainsThread(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(response_json["results"][0]["id"], test_thread.pk)
  443. @patch_categories_acl()
  444. def test_list_user_cant_see_unapproved_thread(self):
  445. """list hides unapproved thread that belongs to other user"""
  446. test_thread = test.post_thread(category=self.category_a, is_unapproved=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_hidden_thread(self):
  457. """list hides hidden thread that belongs to other user"""
  458. test_thread = test.post_thread(category=self.category_a, is_hidden=True)
  459. response = self.client.get("/")
  460. self.assertEqual(response.status_code, 200)
  461. self.assertNotContainsThread(response, test_thread)
  462. # test api
  463. response = self.client.get(self.api_link)
  464. self.assertEqual(response.status_code, 200)
  465. response_json = response.json()
  466. self.assertEqual(len(response_json["results"]), 0)
  467. @patch_categories_acl()
  468. def test_list_user_cant_see_own_hidden_thread(self):
  469. """list hides hidden thread that belongs to viewer"""
  470. test_thread = test.post_thread(
  471. category=self.category_a, poster=self.user, is_hidden=True
  472. )
  473. response = self.client.get("/")
  474. self.assertEqual(response.status_code, 200)
  475. self.assertNotContainsThread(response, test_thread)
  476. # test api
  477. response = self.client.get(self.api_link)
  478. self.assertEqual(response.status_code, 200)
  479. response_json = response.json()
  480. self.assertEqual(len(response_json["results"]), 0)
  481. @patch_categories_acl({"can_hide_threads": 1})
  482. def test_list_user_can_see_own_hidden_thread(self):
  483. """list shows hidden thread that belongs to viewer due to permission"""
  484. test_thread = test.post_thread(
  485. category=self.category_a, poster=self.user, is_hidden=True
  486. )
  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_hide_threads": 1})
  496. def test_list_user_can_see_hidden_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_hidden=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. @patch_categories_acl({"can_approve_content": 1})
  508. def test_list_user_can_see_unapproved_thread(self):
  509. """list shows hidden thread that belongs to other user due to permission"""
  510. test_thread = test.post_thread(category=self.category_a, is_unapproved=True)
  511. response = self.client.get("/")
  512. self.assertEqual(response.status_code, 200)
  513. self.assertContainsThread(response, test_thread)
  514. # test api
  515. response = self.client.get(self.api_link)
  516. self.assertEqual(response.status_code, 200)
  517. response_json = response.json()
  518. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  519. class MyThreadsListTests(ThreadsListTestCase):
  520. @patch_categories_acl()
  521. def test_list_renders_empty(self):
  522. """list renders empty"""
  523. response = self.client.get("/my/")
  524. self.assertEqual(response.status_code, 200)
  525. self.assertContains(response, "empty-message")
  526. response = self.client.get(self.category_a.get_absolute_url() + "my/")
  527. self.assertEqual(response.status_code, 200)
  528. self.assertContains(response, "empty-message")
  529. # test api
  530. response = self.client.get("%s?list=my" % self.api_link)
  531. self.assertEqual(response.status_code, 200)
  532. response_json = response.json()
  533. self.assertEqual(len(response_json["results"]), 0)
  534. response = self.client.get(
  535. "%s?list=my&category=%s" % (self.api_link, self.category_a.pk)
  536. )
  537. response_json = response.json()
  538. self.assertEqual(len(response_json["results"]), 0)
  539. @patch_categories_acl()
  540. def test_list_renders_test_thread(self):
  541. """list renders only threads posted by user"""
  542. test_thread = test.post_thread(category=self.category_a, poster=self.user)
  543. other_thread = test.post_thread(category=self.category_a)
  544. response = self.client.get("/my/")
  545. self.assertEqual(response.status_code, 200)
  546. self.assertContainsThread(response, test_thread)
  547. self.assertNotContainsThread(response, other_thread)
  548. response = self.client.get(self.category_a.get_absolute_url() + "my/")
  549. self.assertEqual(response.status_code, 200)
  550. self.assertContainsThread(response, test_thread)
  551. self.assertNotContainsThread(response, other_thread)
  552. # test api
  553. response = self.client.get("%s?list=my" % self.api_link)
  554. self.assertEqual(response.status_code, 200)
  555. response_json = response.json()
  556. self.assertEqual(len(response_json["results"]), 1)
  557. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  558. response = self.client.get(
  559. "%s?list=my&category=%s" % (self.api_link, self.category_a.pk)
  560. )
  561. self.assertEqual(response.status_code, 200)
  562. response_json = response.json()
  563. self.assertEqual(len(response_json["results"]), 1)
  564. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  565. class NewThreadsListTests(ThreadsListTestCase):
  566. @patch_categories_acl()
  567. def test_list_renders_empty(self):
  568. """list renders empty"""
  569. response = self.client.get("/new/")
  570. self.assertEqual(response.status_code, 200)
  571. self.assertContains(response, "empty-message")
  572. response = self.client.get(self.category_a.get_absolute_url() + "new/")
  573. self.assertEqual(response.status_code, 200)
  574. self.assertContains(response, "empty-message")
  575. # test api
  576. response = self.client.get("%s?list=new" % self.api_link)
  577. self.assertEqual(response.status_code, 200)
  578. response_json = response.json()
  579. self.assertEqual(len(response_json["results"]), 0)
  580. response = self.client.get(
  581. "%s?list=new&category=%s" % (self.api_link, self.category_a.pk)
  582. )
  583. response_json = response.json()
  584. self.assertEqual(len(response_json["results"]), 0)
  585. @patch_categories_acl()
  586. def test_list_renders_new_thread(self):
  587. """list renders new thread"""
  588. test_thread = test.post_thread(category=self.category_a)
  589. response = self.client.get("/new/")
  590. self.assertEqual(response.status_code, 200)
  591. self.assertContainsThread(response, test_thread)
  592. response = self.client.get(self.category_a.get_absolute_url() + "new/")
  593. self.assertEqual(response.status_code, 200)
  594. self.assertContainsThread(response, test_thread)
  595. # test api
  596. response = self.client.get("%s?list=new" % self.api_link)
  597. self.assertEqual(response.status_code, 200)
  598. response_json = response.json()
  599. self.assertEqual(len(response_json["results"]), 1)
  600. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  601. response = self.client.get(
  602. "%s?list=new&category=%s" % (self.api_link, self.category_a.pk)
  603. )
  604. self.assertEqual(response.status_code, 200)
  605. response_json = response.json()
  606. self.assertEqual(len(response_json["results"]), 1)
  607. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  608. @patch_categories_acl()
  609. def test_list_renders_thread_bumped_after_user_cutoff(self):
  610. """list renders new thread bumped after user cutoff"""
  611. self.user.joined_on = timezone.now() - timedelta(days=10)
  612. self.user.save()
  613. test_thread = test.post_thread(
  614. category=self.category_a, started_on=self.user.joined_on - timedelta(days=2)
  615. )
  616. test.reply_thread(
  617. test_thread, posted_on=self.user.joined_on + timedelta(days=4)
  618. )
  619. response = self.client.get("/new/")
  620. self.assertEqual(response.status_code, 200)
  621. self.assertContainsThread(response, test_thread)
  622. response = self.client.get(self.category_a.get_absolute_url() + "new/")
  623. self.assertEqual(response.status_code, 200)
  624. self.assertContainsThread(response, test_thread)
  625. # test api
  626. response = self.client.get("%s?list=new" % self.api_link)
  627. self.assertEqual(response.status_code, 200)
  628. response_json = response.json()
  629. self.assertEqual(len(response_json["results"]), 1)
  630. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  631. response = self.client.get(
  632. "%s?list=new&category=%s" % (self.api_link, self.category_a.pk)
  633. )
  634. self.assertEqual(response.status_code, 200)
  635. response_json = response.json()
  636. self.assertEqual(len(response_json["results"]), 1)
  637. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  638. @override_dynamic_settings(readtracker_cutoff=3)
  639. @patch_categories_acl()
  640. def test_list_hides_global_cutoff_thread(self):
  641. """list hides thread started before global cutoff"""
  642. self.user.joined_on = timezone.now() - timedelta(days=10)
  643. self.user.save()
  644. test_thread = test.post_thread(
  645. category=self.category_a, started_on=timezone.now() - timedelta(days=5)
  646. )
  647. response = self.client.get("/new/")
  648. self.assertEqual(response.status_code, 200)
  649. self.assertNotContainsThread(response, test_thread)
  650. response = self.client.get(self.category_a.get_absolute_url() + "new/")
  651. self.assertEqual(response.status_code, 200)
  652. self.assertNotContainsThread(response, test_thread)
  653. # test api
  654. response = self.client.get("%s?list=new" % self.api_link)
  655. self.assertEqual(response.status_code, 200)
  656. response_json = response.json()
  657. self.assertEqual(len(response_json["results"]), 0)
  658. response = self.client.get(
  659. "%s?list=new&category=%s" % (self.api_link, self.category_a.pk)
  660. )
  661. self.assertEqual(response.status_code, 200)
  662. response_json = response.json()
  663. self.assertEqual(len(response_json["results"]), 0)
  664. @patch_categories_acl()
  665. def test_list_hides_user_cutoff_thread(self):
  666. """list hides thread started before users cutoff"""
  667. self.user.joined_on = timezone.now() - timedelta(days=5)
  668. self.user.save()
  669. test_thread = test.post_thread(
  670. category=self.category_a,
  671. started_on=self.user.joined_on - timedelta(minutes=1),
  672. )
  673. response = self.client.get("/new/")
  674. self.assertEqual(response.status_code, 200)
  675. self.assertNotContainsThread(response, test_thread)
  676. response = self.client.get(self.category_a.get_absolute_url() + "new/")
  677. self.assertEqual(response.status_code, 200)
  678. self.assertNotContainsThread(response, test_thread)
  679. # test api
  680. response = self.client.get("%s?list=new" % self.api_link)
  681. self.assertEqual(response.status_code, 200)
  682. response_json = response.json()
  683. self.assertEqual(len(response_json["results"]), 0)
  684. response = self.client.get(
  685. "%s?list=new&category=%s" % (self.api_link, self.category_a.pk)
  686. )
  687. self.assertEqual(response.status_code, 200)
  688. response_json = response.json()
  689. self.assertEqual(len(response_json["results"]), 0)
  690. @patch_categories_acl()
  691. def test_list_hides_user_read_thread(self):
  692. """list hides thread already read by user"""
  693. self.user.joined_on = timezone.now() - timedelta(days=5)
  694. self.user.save()
  695. test_thread = test.post_thread(category=self.category_a)
  696. poststracker.save_read(self.user, test_thread.first_post)
  697. response = self.client.get("/new/")
  698. self.assertEqual(response.status_code, 200)
  699. self.assertNotContainsThread(response, test_thread)
  700. response = self.client.get(self.category_a.get_absolute_url() + "new/")
  701. self.assertEqual(response.status_code, 200)
  702. self.assertNotContainsThread(response, test_thread)
  703. # test api
  704. response = self.client.get("%s?list=new" % self.api_link)
  705. self.assertEqual(response.status_code, 200)
  706. response_json = response.json()
  707. self.assertEqual(len(response_json["results"]), 0)
  708. response = self.client.get(
  709. "%s?list=new&category=%s" % (self.api_link, self.category_a.pk)
  710. )
  711. self.assertEqual(response.status_code, 200)
  712. response_json = response.json()
  713. self.assertEqual(len(response_json["results"]), 0)
  714. class UnreadThreadsListTests(ThreadsListTestCase):
  715. @patch_categories_acl()
  716. def test_list_renders_empty(self):
  717. """list renders empty"""
  718. response = self.client.get("/unread/")
  719. self.assertEqual(response.status_code, 200)
  720. self.assertContains(response, "empty-message")
  721. response = self.client.get(self.category_a.get_absolute_url() + "unread/")
  722. self.assertEqual(response.status_code, 200)
  723. self.assertContains(response, "empty-message")
  724. # test api
  725. response = self.client.get("%s?list=unread" % self.api_link)
  726. self.assertEqual(response.status_code, 200)
  727. response_json = response.json()
  728. self.assertEqual(len(response_json["results"]), 0)
  729. response = self.client.get(
  730. "%s?list=unread&category=%s" % (self.api_link, self.category_a.pk)
  731. )
  732. self.assertEqual(response.status_code, 200)
  733. response_json = response.json()
  734. self.assertEqual(len(response_json["results"]), 0)
  735. @patch_categories_acl()
  736. def test_list_renders_unread_thread(self):
  737. """list renders thread with unread posts"""
  738. self.user.joined_on = timezone.now() - timedelta(days=5)
  739. self.user.save()
  740. test_thread = test.post_thread(category=self.category_a)
  741. poststracker.save_read(self.user, test_thread.first_post)
  742. test.reply_thread(test_thread)
  743. response = self.client.get("/unread/")
  744. self.assertEqual(response.status_code, 200)
  745. self.assertContainsThread(response, test_thread)
  746. response = self.client.get(self.category_a.get_absolute_url() + "unread/")
  747. self.assertEqual(response.status_code, 200)
  748. self.assertContainsThread(response, test_thread)
  749. # test api
  750. response = self.client.get("%s?list=unread" % self.api_link)
  751. self.assertEqual(response.status_code, 200)
  752. response_json = response.json()
  753. self.assertEqual(len(response_json["results"]), 1)
  754. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  755. response = self.client.get(
  756. "%s?list=unread&category=%s" % (self.api_link, self.category_a.pk)
  757. )
  758. self.assertEqual(response.status_code, 200)
  759. response_json = response.json()
  760. self.assertEqual(len(response_json["results"]), 1)
  761. self.assertEqual(response_json["results"][0]["id"], test_thread.pk)
  762. @patch_categories_acl()
  763. def test_list_hides_never_read_thread(self):
  764. """list hides never read thread"""
  765. self.user.joined_on = timezone.now() - timedelta(days=5)
  766. self.user.save()
  767. test_thread = test.post_thread(category=self.category_a)
  768. response = self.client.get("/unread/")
  769. self.assertEqual(response.status_code, 200)
  770. self.assertNotContainsThread(response, test_thread)
  771. response = self.client.get(self.category_a.get_absolute_url() + "unread/")
  772. self.assertEqual(response.status_code, 200)
  773. self.assertNotContainsThread(response, test_thread)
  774. # test api
  775. response = self.client.get("%s?list=unread" % self.api_link)
  776. self.assertEqual(response.status_code, 200)
  777. response_json = response.json()
  778. self.assertEqual(len(response_json["results"]), 0)
  779. response = self.client.get(
  780. "%s?list=unread&category=%s" % (self.api_link, self.category_a.pk)
  781. )
  782. self.assertEqual(response.status_code, 200)
  783. response_json = response.json()
  784. self.assertEqual(len(response_json["results"]), 0)
  785. @patch_categories_acl()
  786. def test_list_hides_read_thread(self):
  787. """list hides read thread"""
  788. self.user.joined_on = timezone.now() - timedelta(days=5)
  789. self.user.save()
  790. test_thread = test.post_thread(category=self.category_a)
  791. poststracker.save_read(self.user, test_thread.first_post)
  792. response = self.client.get("/unread/")
  793. self.assertEqual(response.status_code, 200)
  794. self.assertNotContainsThread(response, test_thread)
  795. response = self.client.get(self.category_a.get_absolute_url() + "unread/")
  796. self.assertEqual(response.status_code, 200)
  797. self.assertNotContainsThread(response, test_thread)
  798. # test api
  799. response = self.client.get("%s?list=unread" % self.api_link)
  800. self.assertEqual(response.status_code, 200)
  801. response_json = response.json()
  802. self.assertEqual(len(response_json["results"]), 0)
  803. response = self.client.get(
  804. "%s?list=unread&category=%s" % (self.api_link, self.category_a.pk)
  805. )
  806. self.assertEqual(response.status_code, 200)
  807. response_json = response.json()
  808. self.assertEqual(len(response_json["results"]), 0)
  809. @override_dynamic_settings(readtracker_cutoff=3)
  810. @patch_categories_acl()
  811. def test_list_hides_global_cutoff_thread(self):
  812. """list hides thread replied before global cutoff"""
  813. self.user.joined_on = timezone.now() - timedelta(days=10)
  814. self.user.save()
  815. test_thread = test.post_thread(
  816. category=self.category_a, started_on=timezone.now() - timedelta(days=5)
  817. )
  818. poststracker.save_read(self.user, test_thread.first_post)
  819. test.reply_thread(
  820. test_thread, posted_on=test_thread.started_on + timedelta(days=1)
  821. )
  822. response = self.client.get("/unread/")
  823. self.assertEqual(response.status_code, 200)
  824. self.assertNotContainsThread(response, test_thread)
  825. response = self.client.get(self.category_a.get_absolute_url() + "unread/")
  826. self.assertEqual(response.status_code, 200)
  827. self.assertNotContainsThread(response, test_thread)
  828. # test api
  829. response = self.client.get("%s?list=unread" % self.api_link)
  830. self.assertEqual(response.status_code, 200)
  831. response_json = response.json()
  832. self.assertEqual(len(response_json["results"]), 0)
  833. response = self.client.get(
  834. "%s?list=unread&category=%s" % (self.api_link, self.category_a.pk)
  835. )
  836. self.assertEqual(response.status_code, 200)
  837. response_json = response.json()
  838. self.assertEqual(len(response_json["results"]), 0)
  839. @patch_categories_acl()
  840. def test_list_hides_user_cutoff_thread(self):
  841. """list hides thread replied before user cutoff"""
  842. self.user.joined_on = timezone.now() - timedelta(days=10)
  843. self.user.save()
  844. test_thread = test.post_thread(
  845. category=self.category_a, started_on=self.user.joined_on - timedelta(days=2)
  846. )
  847. poststracker.save_read(self.user, test_thread.first_post)
  848. test.reply_thread(
  849. test_thread, posted_on=test_thread.started_on + timedelta(days=1)
  850. )
  851. response = self.client.get("/unread/")
  852. self.assertEqual(response.status_code, 200)
  853. self.assertNotContainsThread(response, test_thread)
  854. response = self.client.get(self.category_a.get_absolute_url() + "unread/")
  855. self.assertEqual(response.status_code, 200)
  856. self.assertNotContainsThread(response, test_thread)
  857. # test api
  858. response = self.client.get("%s?list=unread" % self.api_link)
  859. self.assertEqual(response.status_code, 200)
  860. response_json = response.json()
  861. self.assertEqual(len(response_json["results"]), 0)
  862. response = self.client.get(
  863. "%s?list=unread&category=%s" % (self.api_link, self.category_a.pk)
  864. )
  865. self.assertEqual(response.status_code, 200)
  866. response_json = response.json()
  867. self.assertEqual(len(response_json["results"]), 0)
  868. class SubscribedThreadsListTests(ThreadsListTestCase):
  869. @patch_categories_acl()
  870. def test_list_shows_subscribed_thread(self):
  871. """list shows subscribed thread"""
  872. test_thread = test.post_thread(category=self.category_a)
  873. self.user.subscription_set.create(
  874. thread=test_thread,
  875. category=self.category_a,
  876. last_read_on=test_thread.last_post_on,
  877. )
  878. response = self.client.get("/subscribed/")
  879. self.assertEqual(response.status_code, 200)
  880. self.assertContainsThread(response, test_thread)
  881. response = self.client.get(self.category_a.get_absolute_url() + "subscribed/")
  882. self.assertEqual(response.status_code, 200)
  883. self.assertContainsThread(response, test_thread)
  884. # test api
  885. response = self.client.get("%s?list=subscribed" % self.api_link)
  886. self.assertEqual(response.status_code, 200)
  887. response_json = response.json()
  888. self.assertEqual(len(response_json["results"]), 1)
  889. self.assertContains(response, test_thread.get_absolute_url())
  890. response = self.client.get(
  891. "%s?list=subscribed&category=%s" % (self.api_link, self.category_a.pk)
  892. )
  893. self.assertEqual(response.status_code, 200)
  894. response_json = response.json()
  895. self.assertEqual(len(response_json["results"]), 1)
  896. self.assertContains(response, test_thread.get_absolute_url())
  897. @patch_categories_acl()
  898. def test_list_hides_unsubscribed_thread(self):
  899. """list shows subscribed thread"""
  900. test_thread = test.post_thread(category=self.category_a)
  901. response = self.client.get("/subscribed/")
  902. self.assertEqual(response.status_code, 200)
  903. self.assertNotContainsThread(response, test_thread)
  904. response = self.client.get(self.category_a.get_absolute_url() + "subscribed/")
  905. self.assertEqual(response.status_code, 200)
  906. self.assertNotContainsThread(response, test_thread)
  907. # test api
  908. response = self.client.get("%s?list=subscribed" % self.api_link)
  909. self.assertEqual(response.status_code, 200)
  910. response_json = response.json()
  911. self.assertEqual(len(response_json["results"]), 0)
  912. self.assertNotContainsThread(response, test_thread)
  913. response = self.client.get(
  914. "%s?list=subscribed&category=%s" % (self.api_link, self.category_a.pk)
  915. )
  916. self.assertEqual(response.status_code, 200)
  917. response_json = response.json()
  918. self.assertEqual(len(response_json["results"]), 0)
  919. self.assertNotContainsThread(response, test_thread)
  920. class UnapprovedListTests(ThreadsListTestCase):
  921. def test_list_errors_without_permission(self):
  922. """list errors if user has no permission to access it"""
  923. TEST_URLS = (
  924. "/unapproved/",
  925. self.category_a.get_absolute_url() + "unapproved/",
  926. "%s?list=unapproved" % self.api_link,
  927. )
  928. with patch_categories_acl():
  929. for test_url in TEST_URLS:
  930. response = self.client.get(test_url)
  931. self.assertEqual(response.status_code, 403)
  932. # approval perm has no influence on visibility
  933. with patch_categories_acl({"can_approve_content": True}):
  934. for test_url in TEST_URLS:
  935. response = self.client.get(test_url)
  936. self.assertEqual(response.status_code, 403)
  937. # approval perm has no influence on visibility
  938. with patch_categories_acl(base_acl={"can_see_unapproved_content_lists": True}):
  939. for test_url in TEST_URLS:
  940. response = self.client.get(test_url)
  941. self.assertEqual(response.status_code, 200)
  942. @patch_categories_acl(
  943. {"can_approve_content": True}, {"can_see_unapproved_content_lists": True}
  944. )
  945. def test_list_shows_all_threads_for_approving_user(self):
  946. """list shows all threads with unapproved posts when user has perm"""
  947. visible_thread = test.post_thread(category=self.category_b, is_unapproved=True)
  948. hidden_thread = test.post_thread(category=self.category_b, is_unapproved=False)
  949. response = self.client.get("/unapproved/")
  950. self.assertEqual(response.status_code, 200)
  951. self.assertContainsThread(response, visible_thread)
  952. self.assertNotContainsThread(response, hidden_thread)
  953. response = self.client.get(self.category_a.get_absolute_url() + "unapproved/")
  954. self.assertEqual(response.status_code, 200)
  955. self.assertContainsThread(response, visible_thread)
  956. self.assertNotContainsThread(response, hidden_thread)
  957. # test api
  958. response = self.client.get("%s?list=unapproved" % self.api_link)
  959. self.assertEqual(response.status_code, 200)
  960. self.assertContains(response, visible_thread.get_absolute_url())
  961. self.assertNotContains(response, hidden_thread.get_absolute_url())
  962. @patch_categories_acl(base_acl={"can_see_unapproved_content_lists": True})
  963. def test_list_shows_owned_threads_for_unapproving_user(self):
  964. """list shows owned threads with unapproved posts for user without perm"""
  965. visible_thread = test.post_thread(
  966. poster=self.user, category=self.category_b, is_unapproved=True
  967. )
  968. hidden_thread = test.post_thread(category=self.category_b, is_unapproved=True)
  969. response = self.client.get("/unapproved/")
  970. self.assertEqual(response.status_code, 200)
  971. self.assertContainsThread(response, visible_thread)
  972. self.assertNotContainsThread(response, hidden_thread)
  973. response = self.client.get(self.category_a.get_absolute_url() + "unapproved/")
  974. self.assertEqual(response.status_code, 200)
  975. self.assertContainsThread(response, visible_thread)
  976. self.assertNotContainsThread(response, hidden_thread)
  977. # test api
  978. response = self.client.get("%s?list=unapproved" % self.api_link)
  979. self.assertEqual(response.status_code, 200)
  980. self.assertContains(response, visible_thread.get_absolute_url())
  981. self.assertNotContains(response, hidden_thread.get_absolute_url())
  982. def patch_category_see_all_threads_acl():
  983. def patch_acl(_, user_acl):
  984. category = Category.objects.get(slug="first-category")
  985. category_acl = user_acl["categories"][category.id].copy()
  986. category_acl.update({"can_see_all_threads": 0})
  987. user_acl["categories"][category.id] = category_acl
  988. return patch_user_acl(patch_acl)
  989. class OwnerOnlyThreadsVisibilityTests(AuthenticatedUserTestCase):
  990. def setUp(self):
  991. super().setUp()
  992. self.category = Category.objects.get(slug="first-category")
  993. def test_owned_threads_visibility(self):
  994. """only user-posted threads are visible in category"""
  995. visible_thread = test.post_thread(
  996. poster=self.user, category=self.category, is_unapproved=True
  997. )
  998. hidden_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.assertContains(response, visible_thread.get_absolute_url())
  1003. self.assertNotContains(response, hidden_thread.get_absolute_url())
  1004. def test_owned_threads_visibility_anonymous(self):
  1005. """anons can't see any threads in limited visibility category"""
  1006. self.logout_user()
  1007. user_thread = test.post_thread(
  1008. poster=self.user, category=self.category, is_unapproved=True
  1009. )
  1010. guest_thread = test.post_thread(category=self.category, is_unapproved=True)
  1011. with patch_category_see_all_threads_acl():
  1012. response = self.client.get(self.category.get_absolute_url())
  1013. self.assertEqual(response.status_code, 200)
  1014. self.assertNotContains(response, user_thread.get_absolute_url())
  1015. self.assertNotContains(response, guest_thread.get_absolute_url())