test_threadslists.py 58 KB

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