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