test_threadslists.py 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268
  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 misago.acl.testutils import override_acl
  6. from misago.categories.models import Category
  7. from misago.core import threadstore
  8. from misago.core.cache import cache
  9. from misago.readtracker import categoriestracker, threadstracker
  10. from misago.users.testutils import AuthenticatedUserTestCase
  11. from misago.threads import testutils
  12. LISTS_URLS = (
  13. '',
  14. 'my/',
  15. 'new/',
  16. 'unread/',
  17. 'subscribed/'
  18. )
  19. class ThreadsListTestCase(AuthenticatedUserTestCase):
  20. def setUp(self):
  21. super(ThreadsListTestCase, self).setUp()
  22. self.api_link = '/api/threads/'
  23. cache.clear()
  24. threadstore.clear()
  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.access_all_categories()
  75. def access_all_categories(self, extra=None):
  76. cache.clear()
  77. threadstore.clear()
  78. categories_acl = {'categories': {}, 'visible_categories': []}
  79. for category in Category.objects.all_categories():
  80. categories_acl['visible_categories'].append(category.pk)
  81. categories_acl['categories'][category.pk] = {
  82. 'can_see': 1,
  83. 'can_browse': 1,
  84. 'can_see_all_threads': 1,
  85. 'can_see_own_threads': 0,
  86. 'can_hide_threads': 0,
  87. 'can_review_moderated_content': 0,
  88. }
  89. if extra:
  90. categories_acl['categories'][category.pk].update(extra)
  91. override_acl(self.user, categories_acl)
  92. return categories_acl
  93. class ListsTests(ThreadsListTestCase):
  94. def test_list_renders_empty(self):
  95. """empty threads list renders"""
  96. for url in LISTS_URLS:
  97. self.access_all_categories()
  98. response = self.client.get('/' + url)
  99. self.assertEqual(response.status_code, 200)
  100. self.assertIn("empty-message", response.content)
  101. self.access_all_categories()
  102. response = self.client.get(self.category_b.get_absolute_url() + url)
  103. self.assertEqual(response.status_code, 200)
  104. self.assertIn(self.category_b.name, response.content)
  105. self.assertIn("empty-message", response.content)
  106. self.access_all_categories()
  107. response = self.client.get(
  108. '%s?list=%s' % (self.api_link, url.strip('/')))
  109. self.assertEqual(response.status_code, 200)
  110. response_json = json_loads(response.content)
  111. self.assertEqual(len(response_json['results']), 0)
  112. def test_list_authenticated_only_views(self):
  113. """authenticated only views return 403 for guests"""
  114. for url in LISTS_URLS:
  115. self.access_all_categories()
  116. response = self.client.get('/' + url)
  117. self.assertEqual(response.status_code, 200)
  118. self.access_all_categories()
  119. response = self.client.get(self.category_b.get_absolute_url() + url)
  120. self.assertEqual(response.status_code, 200)
  121. self.assertIn(self.category_b.name, response.content)
  122. self.access_all_categories()
  123. self.access_all_categories()
  124. response = self.client.get('%s?category=%s&list=%s' % (
  125. self.api_link,
  126. self.category_b.pk,
  127. url.strip('/'),
  128. ))
  129. self.assertEqual(response.status_code, 200)
  130. self.logout_user()
  131. self.user = self.get_anonymous_user()
  132. for url in LISTS_URLS[1:]:
  133. self.access_all_categories()
  134. response = self.client.get('/' + url)
  135. self.assertEqual(response.status_code, 403)
  136. self.access_all_categories()
  137. response = self.client.get(self.category_b.get_absolute_url() + url)
  138. self.assertEqual(response.status_code, 403)
  139. self.access_all_categories()
  140. response = self.client.get('%s?category=%s&list=%s' % (
  141. self.api_link,
  142. self.category_b.pk,
  143. url.strip('/'),
  144. ))
  145. self.assertEqual(response.status_code, 403)
  146. def test_empty_list_hides_categories_picker(self):
  147. """categories picker is hidden on empty list"""
  148. Category(
  149. name='Hidden Category',
  150. slug='hidden-category',
  151. ).insert_at(self.root, position='last-child', save=True)
  152. test_category = Category.objects.get(slug='hidden-category')
  153. self.access_all_categories()
  154. response = self.client.get('/')
  155. self.assertEqual(response.status_code, 200)
  156. self.assertNotIn(
  157. 'subcategory-%s' % self.category_a.css_class, response.content)
  158. self.assertNotIn(
  159. 'subcategory-%s' % self.category_e.css_class, response.content)
  160. self.access_all_categories()
  161. response = self.client.get(self.category_a.get_absolute_url())
  162. self.assertEqual(response.status_code, 200)
  163. self.assertNotIn(
  164. 'subcategory-%s' % self.category_b.css_class, response.content)
  165. self.access_all_categories()
  166. response = self.client.get(self.api_link)
  167. self.assertEqual(response.status_code, 200)
  168. response_json = json_loads(response.content)
  169. self.assertEqual(len(response_json['subcategories']), 0)
  170. def test_list_renders_categories_picker(self):
  171. """categories picker renders valid categories"""
  172. Category(
  173. name='Hidden Category',
  174. slug='hidden-category',
  175. ).insert_at(self.root, position='last-child', save=True)
  176. test_category = Category.objects.get(slug='hidden-category')
  177. testutils.post_thread(
  178. category=self.category_b,
  179. )
  180. response = self.client.get('/')
  181. self.assertEqual(response.status_code, 200)
  182. self.assertIn(
  183. 'subcategory-%s' % self.category_a.css_class, response.content)
  184. # readable categories, but non-accessible directly
  185. self.assertNotIn(
  186. 'subcategory-%s' % self.category_b.css_class, response.content)
  187. self.assertNotIn(
  188. 'subcategory-%s' % self.category_c.css_class, response.content)
  189. self.assertNotIn(
  190. 'subcategory-%s' % self.category_d.css_class, response.content)
  191. self.assertNotIn(
  192. 'subcategory-%s' % self.category_f.css_class, response.content)
  193. # hidden category
  194. self.assertNotIn(
  195. 'subcategory-%s' % test_category.css_class, response.content)
  196. self.access_all_categories()
  197. response = self.client.get(self.api_link)
  198. self.assertEqual(response.status_code, 200)
  199. response_json = json_loads(response.content)
  200. self.assertEqual(
  201. response_json['subcategories'][0]['id'], self.category_a.pk)
  202. # test category view
  203. self.access_all_categories()
  204. response = self.client.get(self.category_a.get_absolute_url())
  205. self.assertEqual(response.status_code, 200)
  206. self.assertIn(
  207. 'subcategory-%s' % self.category_b.css_class, response.content)
  208. # readable categories, but non-accessible directly
  209. self.assertNotIn(
  210. 'subcategory-%s' % self.category_c.css_class, response.content)
  211. self.assertNotIn(
  212. 'subcategory-%s' % self.category_d.css_class, response.content)
  213. self.assertNotIn(
  214. 'subcategory-%s' % self.category_f.css_class, response.content)
  215. self.access_all_categories()
  216. response = self.client.get(
  217. '%s?category=%s' % (self.api_link, self.category_a.pk))
  218. self.assertEqual(response.status_code, 200)
  219. response_json = json_loads(response.content)
  220. self.assertEqual(
  221. response_json['subcategories'][0]['id'], self.category_b.pk)
  222. class CategoryThreadsListTests(ThreadsListTestCase):
  223. def test_access_hidden_category(self):
  224. """hidden category returns 404"""
  225. Category(
  226. name='Hidden Category',
  227. slug='hidden-category',
  228. ).insert_at(self.root, position='last-child', save=True)
  229. test_category = Category.objects.get(slug='hidden-category')
  230. for url in LISTS_URLS:
  231. response = self.client.get(test_category.get_absolute_url() + url)
  232. self.assertEqual(response.status_code, 404)
  233. response = self.client.get(
  234. '%s?category=%s' % (self.api_link, test_category.pk))
  235. self.assertEqual(response.status_code, 404)
  236. def test_access_protected_category(self):
  237. """protected category returns 403"""
  238. Category(
  239. name='Hidden Category',
  240. slug='hidden-category',
  241. ).insert_at(self.root, position='last-child', save=True)
  242. test_category = Category.objects.get(slug='hidden-category')
  243. for url in LISTS_URLS:
  244. override_acl(self.user, {
  245. 'visible_categories': [test_category.pk],
  246. 'categories': {
  247. test_category.pk: {
  248. 'can_see': 1,
  249. 'can_browse': 0,
  250. }
  251. }
  252. });
  253. response = self.client.get(test_category.get_absolute_url() + url)
  254. self.assertEqual(response.status_code, 403)
  255. override_acl(self.user, {
  256. 'visible_categories': [test_category.pk],
  257. 'categories': {
  258. test_category.pk: {
  259. 'can_see': 1,
  260. 'can_browse': 0,
  261. }
  262. }
  263. });
  264. response = self.client.get('%s?category=%s&list=%s' % (
  265. self.api_link,
  266. test_category.pk,
  267. url.strip('/'),
  268. ))
  269. self.assertEqual(response.status_code, 403)
  270. class ThreadsVisibilityTests(ThreadsListTestCase):
  271. def test_list_renders_test_thread(self):
  272. """list renders test thread with valid top category"""
  273. test_thread = testutils.post_thread(
  274. category=self.category_c,
  275. )
  276. response = self.client.get('/')
  277. self.assertEqual(response.status_code, 200)
  278. self.assertIn(test_thread.get_absolute_url(), response.content)
  279. self.assertIn(
  280. 'subcategory-%s' % self.category_a.css_class, response.content)
  281. self.assertIn(
  282. 'thread-category-%s' % self.category_a.css_class, response.content)
  283. self.assertIn(
  284. 'thread-category-%s' % self.category_c.css_class, response.content)
  285. # other top category is hidden from user
  286. self.assertNotIn(
  287. 'subcategory-%s' % self.category_e.css_class, response.content)
  288. # api displays same data
  289. self.access_all_categories()
  290. response = self.client.get(self.api_link)
  291. self.assertEqual(response.status_code, 200)
  292. response_json = json_loads(response.content)
  293. self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
  294. self.assertEqual(len(response_json['subcategories']), 1)
  295. self.assertEqual(
  296. response_json['subcategories'][0]['id'], self.category_a.pk)
  297. # test category view
  298. self.access_all_categories()
  299. response = self.client.get(self.category_b.get_absolute_url())
  300. self.assertEqual(response.status_code, 200)
  301. # thread displays
  302. self.assertIn(test_thread.get_absolute_url(), response.content)
  303. self.assertNotIn(
  304. 'thread-category-%s' % self.category_b.css_class, response.content)
  305. self.assertIn(
  306. 'thread-category-%s' % self.category_c.css_class, response.content)
  307. # category picker was changed
  308. self.assertIn(
  309. 'subcategory-%s' % self.category_c.css_class, response.content)
  310. self.assertNotIn(
  311. 'subcategory-%s' % self.category_d.css_class, response.content)
  312. # api displays same data
  313. self.access_all_categories()
  314. response = self.client.get(
  315. '%s?category=%s' % (self.api_link, self.category_b.pk))
  316. self.assertEqual(response.status_code, 200)
  317. response_json = json_loads(response.content)
  318. self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
  319. self.assertEqual(len(response_json['subcategories']), 1)
  320. self.assertEqual(
  321. response_json['subcategories'][0]['id'], self.category_c.pk)
  322. def test_list_hides_hidden_thread(self):
  323. """list renders empty due to no permission to see thread"""
  324. Category(
  325. name='Hidden Category',
  326. slug='hidden-category',
  327. ).insert_at(self.root, position='last-child', save=True)
  328. test_category = Category.objects.get(slug='hidden-category')
  329. test_thread = testutils.post_thread(
  330. category=test_category,
  331. )
  332. response = self.client.get('/')
  333. self.assertEqual(response.status_code, 200)
  334. self.assertIn("empty-message", response.content)
  335. def test_api_hides_hidden_thread(self):
  336. """api returns empty due to no permission to see thread"""
  337. Category(
  338. name='Hidden Category',
  339. slug='hidden-category',
  340. ).insert_at(self.root, position='last-child', save=True)
  341. test_category = Category.objects.get(slug='hidden-category')
  342. test_thread = testutils.post_thread(
  343. category=test_category,
  344. )
  345. response = self.client.get(self.api_link)
  346. self.assertEqual(response.status_code, 200)
  347. response_json = json_loads(response.content)
  348. self.assertEqual(len(response_json['results']), 0)
  349. def test_list_user_see_own_moderated_thread(self):
  350. """list renders moderated thread that belongs to viewer"""
  351. test_thread = testutils.post_thread(
  352. category=self.category_a,
  353. poster=self.user,
  354. is_moderated=True,
  355. )
  356. response = self.client.get('/')
  357. self.assertEqual(response.status_code, 200)
  358. self.assertIn(test_thread.get_absolute_url(), response.content)
  359. # test api
  360. self.access_all_categories()
  361. response = self.client.get(self.api_link)
  362. self.assertEqual(response.status_code, 200)
  363. response_json = json_loads(response.content)
  364. self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
  365. def test_list_user_cant_see_moderated_thread(self):
  366. """list hides moderated thread that belongs to other user"""
  367. test_thread = testutils.post_thread(
  368. category=self.category_a,
  369. is_moderated=True,
  370. )
  371. response = self.client.get('/')
  372. self.assertEqual(response.status_code, 200)
  373. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  374. # test api
  375. self.access_all_categories()
  376. response = self.client.get(self.api_link)
  377. self.assertEqual(response.status_code, 200)
  378. response_json = json_loads(response.content)
  379. self.assertEqual(len(response_json['results']), 0)
  380. def test_list_user_cant_see_hidden_thread(self):
  381. """list hides hidden thread that belongs to other user"""
  382. test_thread = testutils.post_thread(
  383. category=self.category_a,
  384. is_hidden=True,
  385. )
  386. response = self.client.get('/')
  387. self.assertEqual(response.status_code, 200)
  388. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  389. # test api
  390. self.access_all_categories()
  391. response = self.client.get(self.api_link)
  392. self.assertEqual(response.status_code, 200)
  393. response_json = json_loads(response.content)
  394. self.assertEqual(len(response_json['results']), 0)
  395. def test_list_user_cant_see_own_hidden_thread(self):
  396. """list hides hidden thread that belongs to viewer"""
  397. test_thread = testutils.post_thread(
  398. category=self.category_a,
  399. poster=self.user,
  400. is_hidden=True,
  401. )
  402. response = self.client.get('/')
  403. self.assertEqual(response.status_code, 200)
  404. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  405. # test api
  406. self.access_all_categories()
  407. response = self.client.get(self.api_link)
  408. self.assertEqual(response.status_code, 200)
  409. response_json = json_loads(response.content)
  410. self.assertEqual(len(response_json['results']), 0)
  411. def test_list_user_can_see_own_hidden_thread(self):
  412. """list shows hidden thread that belongs to viewer due to permission"""
  413. test_thread = testutils.post_thread(
  414. category=self.category_a,
  415. poster=self.user,
  416. is_hidden=True,
  417. )
  418. self.access_all_categories({
  419. 'can_hide_threads': 1
  420. })
  421. response = self.client.get('/')
  422. self.assertEqual(response.status_code, 200)
  423. self.assertIn(test_thread.get_absolute_url(), response.content)
  424. # test api
  425. self.access_all_categories({
  426. 'can_hide_threads': 1
  427. })
  428. response = self.client.get(self.api_link)
  429. self.assertEqual(response.status_code, 200)
  430. response_json = json_loads(response.content)
  431. self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
  432. def test_list_user_can_see_hidden_thread(self):
  433. """
  434. list shows hidden thread that belongs to other user due to permission
  435. """
  436. test_thread = testutils.post_thread(
  437. category=self.category_a,
  438. is_hidden=True,
  439. )
  440. self.access_all_categories({
  441. 'can_hide_threads': 1
  442. })
  443. response = self.client.get('/')
  444. self.assertEqual(response.status_code, 200)
  445. self.assertIn(test_thread.get_absolute_url(), response.content)
  446. # test api
  447. self.access_all_categories({
  448. 'can_hide_threads': 1
  449. })
  450. response = self.client.get(self.api_link)
  451. self.assertEqual(response.status_code, 200)
  452. response_json = json_loads(response.content)
  453. self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
  454. def test_list_user_can_see_moderated_thread(self):
  455. """
  456. list shows hidden thread that belongs to other user due to permission
  457. """
  458. test_thread = testutils.post_thread(
  459. category=self.category_a,
  460. is_moderated=True,
  461. )
  462. self.access_all_categories({
  463. 'can_review_moderated_content': 1
  464. })
  465. response = self.client.get('/')
  466. self.assertEqual(response.status_code, 200)
  467. self.assertIn(test_thread.get_absolute_url(), response.content)
  468. # test api
  469. self.access_all_categories({
  470. 'can_review_moderated_content': 1
  471. })
  472. response = self.client.get(self.api_link)
  473. self.assertEqual(response.status_code, 200)
  474. response_json = json_loads(response.content)
  475. self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
  476. class MyThreadsListTests(ThreadsListTestCase):
  477. def test_list_renders_empty(self):
  478. """list renders empty"""
  479. self.access_all_categories()
  480. response = self.client.get('/my/')
  481. self.assertEqual(response.status_code, 200)
  482. self.assertIn("empty-message", response.content)
  483. self.access_all_categories()
  484. response = self.client.get(self.category_a.get_absolute_url() + 'my/')
  485. self.assertEqual(response.status_code, 200)
  486. self.assertIn("empty-message", response.content)
  487. # test api
  488. self.access_all_categories()
  489. response = self.client.get('%s?list=my' % self.api_link)
  490. self.assertEqual(response.status_code, 200)
  491. response_json = json_loads(response.content)
  492. self.assertEqual(len(response_json['results']), 0)
  493. self.access_all_categories()
  494. response = self.client.get(
  495. '%s?list=my&category=%s' % (self.api_link, self.category_a.pk))
  496. response_json = json_loads(response.content)
  497. self.assertEqual(len(response_json['results']), 0)
  498. def test_list_renders_test_thread(self):
  499. """list renders only threads posted by user"""
  500. test_thread = testutils.post_thread(
  501. category=self.category_a,
  502. poster=self.user,
  503. )
  504. other_thread = testutils.post_thread(
  505. category=self.category_a,
  506. )
  507. self.access_all_categories()
  508. response = self.client.get('/my/')
  509. self.assertEqual(response.status_code, 200)
  510. self.assertIn(test_thread.get_absolute_url(), response.content)
  511. self.assertNotIn(other_thread.get_absolute_url(), response.content)
  512. self.access_all_categories()
  513. response = self.client.get(self.category_a.get_absolute_url() + 'my/')
  514. self.assertEqual(response.status_code, 200)
  515. self.assertIn(test_thread.get_absolute_url(), response.content)
  516. self.assertNotIn(other_thread.get_absolute_url(), response.content)
  517. # test api
  518. self.access_all_categories()
  519. response = self.client.get('%s?list=my' % self.api_link)
  520. self.assertEqual(response.status_code, 200)
  521. response_json = json_loads(response.content)
  522. self.assertEqual(len(response_json['results']), 1)
  523. self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
  524. self.access_all_categories()
  525. response = self.client.get(
  526. '%s?list=my&category=%s' % (self.api_link, self.category_a.pk))
  527. self.assertEqual(response.status_code, 200)
  528. response_json = json_loads(response.content)
  529. self.assertEqual(len(response_json['results']), 1)
  530. self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
  531. class NewThreadsListTests(ThreadsListTestCase):
  532. def test_list_renders_empty(self):
  533. """list renders empty"""
  534. self.access_all_categories()
  535. response = self.client.get('/new/')
  536. self.assertEqual(response.status_code, 200)
  537. self.assertIn("empty-message", response.content)
  538. self.access_all_categories()
  539. response = self.client.get(self.category_a.get_absolute_url() + 'new/')
  540. self.assertEqual(response.status_code, 200)
  541. self.assertIn("empty-message", response.content)
  542. # test api
  543. self.access_all_categories()
  544. response = self.client.get('%s?list=new' % self.api_link)
  545. self.assertEqual(response.status_code, 200)
  546. response_json = json_loads(response.content)
  547. self.assertEqual(len(response_json['results']), 0)
  548. self.access_all_categories()
  549. response = self.client.get(
  550. '%s?list=new&category=%s' % (self.api_link, self.category_a.pk))
  551. response_json = json_loads(response.content)
  552. self.assertEqual(len(response_json['results']), 0)
  553. def test_list_renders_new_thread(self):
  554. """list renders new thread"""
  555. test_thread = testutils.post_thread(
  556. category=self.category_a,
  557. )
  558. self.access_all_categories()
  559. response = self.client.get('/new/')
  560. self.assertEqual(response.status_code, 200)
  561. self.assertIn(test_thread.get_absolute_url(), response.content)
  562. self.access_all_categories()
  563. response = self.client.get(self.category_a.get_absolute_url() + 'new/')
  564. self.assertEqual(response.status_code, 200)
  565. self.assertIn(test_thread.get_absolute_url(), response.content)
  566. # test api
  567. self.access_all_categories()
  568. response = self.client.get('%s?list=new' % self.api_link)
  569. self.assertEqual(response.status_code, 200)
  570. response_json = json_loads(response.content)
  571. self.assertEqual(len(response_json['results']), 1)
  572. self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
  573. self.access_all_categories()
  574. response = self.client.get(
  575. '%s?list=new&category=%s' % (self.api_link, self.category_a.pk))
  576. self.assertEqual(response.status_code, 200)
  577. response_json = json_loads(response.content)
  578. self.assertEqual(len(response_json['results']), 1)
  579. self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
  580. def test_list_renders_thread_bumped_after_user_cutoff(self):
  581. """list renders new thread bumped after user cutoff"""
  582. self.user.reads_cutoff = timezone.now() - timedelta(days=10)
  583. self.user.joined_on = self.user.reads_cutoff
  584. self.user.save()
  585. test_thread = testutils.post_thread(
  586. category=self.category_a,
  587. started_on=self.user.reads_cutoff - timedelta(days=2)
  588. )
  589. testutils.reply_thread(test_thread,
  590. posted_on=self.user.reads_cutoff + timedelta(days=4)
  591. )
  592. self.access_all_categories()
  593. response = self.client.get('/new/')
  594. self.assertEqual(response.status_code, 200)
  595. self.assertIn(test_thread.get_absolute_url(), response.content)
  596. self.access_all_categories()
  597. response = self.client.get(
  598. self.category_a.get_absolute_url() + 'new/')
  599. self.assertEqual(response.status_code, 200)
  600. self.assertIn(test_thread.get_absolute_url(), response.content)
  601. # test api
  602. self.access_all_categories()
  603. response = self.client.get('%s?list=new' % self.api_link)
  604. self.assertEqual(response.status_code, 200)
  605. response_json = json_loads(response.content)
  606. self.assertEqual(len(response_json['results']), 1)
  607. self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
  608. self.access_all_categories()
  609. response = self.client.get(
  610. '%s?list=new&category=%s' % (self.api_link, self.category_a.pk))
  611. self.assertEqual(response.status_code, 200)
  612. response_json = json_loads(response.content)
  613. self.assertEqual(len(response_json['results']), 1)
  614. self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
  615. def test_list_hides_global_cutoff_thread(self):
  616. """list hides thread started before global cutoff"""
  617. self.user.reads_cutoff = timezone.now() - timedelta(days=10)
  618. self.user.joined_on = self.user.reads_cutoff
  619. self.user.save()
  620. test_thread = testutils.post_thread(
  621. category=self.category_a,
  622. started_on=timezone.now() - timedelta(
  623. days=settings.MISAGO_FRESH_CONTENT_PERIOD + 1
  624. )
  625. )
  626. self.access_all_categories()
  627. response = self.client.get('/new/')
  628. self.assertEqual(response.status_code, 200)
  629. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  630. self.access_all_categories()
  631. response = self.client.get(self.category_a.get_absolute_url() + 'new/')
  632. self.assertEqual(response.status_code, 200)
  633. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  634. # test api
  635. self.access_all_categories()
  636. response = self.client.get('%s?list=new' % self.api_link)
  637. self.assertEqual(response.status_code, 200)
  638. response_json = json_loads(response.content)
  639. self.assertEqual(len(response_json['results']), 0)
  640. self.access_all_categories()
  641. response = self.client.get(
  642. '%s?list=new&category=%s' % (self.api_link, self.category_a.pk))
  643. self.assertEqual(response.status_code, 200)
  644. response_json = json_loads(response.content)
  645. self.assertEqual(len(response_json['results']), 0)
  646. def test_list_hides_user_cutoff_thread(self):
  647. """list hides thread started before users cutoff"""
  648. self.user.reads_cutoff = timezone.now() - timedelta(days=5)
  649. self.user.joined_on = self.user.reads_cutoff
  650. self.user.save()
  651. test_thread = testutils.post_thread(
  652. category=self.category_a,
  653. started_on=self.user.reads_cutoff - timedelta(minutes=1)
  654. )
  655. self.access_all_categories()
  656. response = self.client.get('/new/')
  657. self.assertEqual(response.status_code, 200)
  658. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  659. self.access_all_categories()
  660. response = self.client.get(self.category_a.get_absolute_url() + 'new/')
  661. self.assertEqual(response.status_code, 200)
  662. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  663. # test api
  664. self.access_all_categories()
  665. response = self.client.get('%s?list=new' % self.api_link)
  666. self.assertEqual(response.status_code, 200)
  667. response_json = json_loads(response.content)
  668. self.assertEqual(len(response_json['results']), 0)
  669. self.access_all_categories()
  670. response = self.client.get(
  671. '%s?list=new&category=%s' % (self.api_link, self.category_a.pk))
  672. self.assertEqual(response.status_code, 200)
  673. response_json = json_loads(response.content)
  674. self.assertEqual(len(response_json['results']), 0)
  675. def test_list_hides_user_read_thread(self):
  676. """list hides thread already read by user"""
  677. self.user.reads_cutoff = timezone.now() - timedelta(days=5)
  678. self.user.joined_on = self.user.reads_cutoff
  679. self.user.save()
  680. test_thread = testutils.post_thread(
  681. category=self.category_a
  682. )
  683. threadstracker.make_thread_read_aware(self.user, test_thread)
  684. threadstracker.read_thread(
  685. self.user, test_thread, test_thread.last_post)
  686. self.access_all_categories()
  687. response = self.client.get('/new/')
  688. self.assertEqual(response.status_code, 200)
  689. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  690. self.access_all_categories()
  691. response = self.client.get(self.category_a.get_absolute_url() + 'new/')
  692. self.assertEqual(response.status_code, 200)
  693. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  694. # test api
  695. self.access_all_categories()
  696. response = self.client.get('%s?list=new' % self.api_link)
  697. self.assertEqual(response.status_code, 200)
  698. response_json = json_loads(response.content)
  699. self.assertEqual(len(response_json['results']), 0)
  700. self.access_all_categories()
  701. response = self.client.get(
  702. '%s?list=new&category=%s' % (self.api_link, self.category_a.pk))
  703. self.assertEqual(response.status_code, 200)
  704. response_json = json_loads(response.content)
  705. self.assertEqual(len(response_json['results']), 0)
  706. def test_list_hides_category_read_thread(self):
  707. """list hides thread already read by user"""
  708. self.user.reads_cutoff = timezone.now() - timedelta(days=5)
  709. self.user.joined_on = self.user.reads_cutoff
  710. self.user.save()
  711. test_thread = testutils.post_thread(
  712. category=self.category_a
  713. )
  714. self.user.categoryread_set.create(
  715. category=self.category_a,
  716. last_read_on=timezone.now(),
  717. )
  718. self.access_all_categories()
  719. response = self.client.get('/new/')
  720. self.assertEqual(response.status_code, 200)
  721. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  722. self.access_all_categories()
  723. response = self.client.get(self.category_a.get_absolute_url() + 'new/')
  724. self.assertEqual(response.status_code, 200)
  725. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  726. # test api
  727. self.access_all_categories()
  728. response = self.client.get('%s?list=new' % self.api_link)
  729. self.assertEqual(response.status_code, 200)
  730. response_json = json_loads(response.content)
  731. self.assertEqual(len(response_json['results']), 0)
  732. self.access_all_categories()
  733. response = self.client.get(
  734. '%s?list=new&category=%s' % (self.api_link, self.category_a.pk))
  735. self.assertEqual(response.status_code, 200)
  736. response_json = json_loads(response.content)
  737. self.assertEqual(len(response_json['results']), 0)
  738. class UnreadThreadsListTests(ThreadsListTestCase):
  739. def test_list_renders_empty(self):
  740. """list renders empty"""
  741. self.access_all_categories()
  742. response = self.client.get('/unread/')
  743. self.assertEqual(response.status_code, 200)
  744. self.assertIn("empty-message", response.content)
  745. self.access_all_categories()
  746. response = self.client.get(
  747. self.category_a.get_absolute_url() + 'unread/')
  748. self.assertEqual(response.status_code, 200)
  749. self.assertIn("empty-message", response.content)
  750. # test api
  751. self.access_all_categories()
  752. response = self.client.get('%s?list=unread' % self.api_link)
  753. self.assertEqual(response.status_code, 200)
  754. response_json = json_loads(response.content)
  755. self.assertEqual(len(response_json['results']), 0)
  756. self.access_all_categories()
  757. response = self.client.get(
  758. '%s?list=unread&category=%s' % (self.api_link, self.category_a.pk))
  759. self.assertEqual(response.status_code, 200)
  760. response_json = json_loads(response.content)
  761. self.assertEqual(len(response_json['results']), 0)
  762. def test_list_renders_unread_thread(self):
  763. """list renders thread with unread posts"""
  764. self.user.reads_cutoff = timezone.now() - timedelta(days=5)
  765. self.user.joined_on = self.user.reads_cutoff
  766. self.user.save()
  767. test_thread = testutils.post_thread(
  768. category=self.category_a
  769. )
  770. threadstracker.make_thread_read_aware(self.user, test_thread)
  771. threadstracker.read_thread(
  772. self.user, test_thread, test_thread.last_post)
  773. testutils.reply_thread(test_thread)
  774. self.access_all_categories()
  775. response = self.client.get('/unread/')
  776. self.assertEqual(response.status_code, 200)
  777. self.assertIn(test_thread.get_absolute_url(), response.content)
  778. self.access_all_categories()
  779. response = self.client.get(
  780. self.category_a.get_absolute_url() + 'unread/')
  781. self.assertEqual(response.status_code, 200)
  782. self.assertIn(test_thread.get_absolute_url(), response.content)
  783. # test api
  784. self.access_all_categories()
  785. response = self.client.get('%s?list=unread' % self.api_link)
  786. self.assertEqual(response.status_code, 200)
  787. response_json = json_loads(response.content)
  788. self.assertEqual(len(response_json['results']), 1)
  789. self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
  790. self.access_all_categories()
  791. response = self.client.get(
  792. '%s?list=unread&category=%s' % (self.api_link, self.category_a.pk))
  793. self.assertEqual(response.status_code, 200)
  794. response_json = json_loads(response.content)
  795. self.assertEqual(len(response_json['results']), 1)
  796. self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
  797. def test_list_hides_never_read_thread(self):
  798. """list hides never read thread"""
  799. self.user.reads_cutoff = timezone.now() - timedelta(days=5)
  800. self.user.joined_on = self.user.reads_cutoff
  801. self.user.save()
  802. test_thread = testutils.post_thread(
  803. category=self.category_a
  804. )
  805. self.access_all_categories()
  806. response = self.client.get('/unread/')
  807. self.assertEqual(response.status_code, 200)
  808. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  809. self.access_all_categories()
  810. response = self.client.get(
  811. self.category_a.get_absolute_url() + 'unread/')
  812. self.assertEqual(response.status_code, 200)
  813. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  814. # test api
  815. self.access_all_categories()
  816. response = self.client.get('%s?list=unread' % self.api_link)
  817. self.assertEqual(response.status_code, 200)
  818. response_json = json_loads(response.content)
  819. self.assertEqual(len(response_json['results']), 0)
  820. self.access_all_categories()
  821. response = self.client.get(
  822. '%s?list=unread&category=%s' % (self.api_link, self.category_a.pk))
  823. self.assertEqual(response.status_code, 200)
  824. response_json = json_loads(response.content)
  825. self.assertEqual(len(response_json['results']), 0)
  826. def test_list_hides_read_thread(self):
  827. """list hides read thread"""
  828. self.user.reads_cutoff = timezone.now() - timedelta(days=5)
  829. self.user.joined_on = self.user.reads_cutoff
  830. self.user.save()
  831. test_thread = testutils.post_thread(
  832. category=self.category_a
  833. )
  834. threadstracker.make_thread_read_aware(self.user, test_thread)
  835. threadstracker.read_thread(
  836. self.user, test_thread, test_thread.last_post)
  837. self.access_all_categories()
  838. response = self.client.get('/unread/')
  839. self.assertEqual(response.status_code, 200)
  840. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  841. self.access_all_categories()
  842. response = self.client.get(
  843. self.category_a.get_absolute_url() + 'unread/')
  844. self.assertEqual(response.status_code, 200)
  845. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  846. # test api
  847. self.access_all_categories()
  848. response = self.client.get('%s?list=unread' % self.api_link)
  849. self.assertEqual(response.status_code, 200)
  850. response_json = json_loads(response.content)
  851. self.assertEqual(len(response_json['results']), 0)
  852. self.access_all_categories()
  853. response = self.client.get(
  854. '%s?list=unread&category=%s' % (self.api_link, self.category_a.pk))
  855. self.assertEqual(response.status_code, 200)
  856. response_json = json_loads(response.content)
  857. self.assertEqual(len(response_json['results']), 0)
  858. def test_list_hides_global_cutoff_thread(self):
  859. """list hides thread replied before global cutoff"""
  860. self.user.reads_cutoff = timezone.now() - timedelta(days=10)
  861. self.user.joined_on = self.user.reads_cutoff
  862. self.user.save()
  863. test_thread = testutils.post_thread(
  864. category=self.category_a,
  865. started_on=timezone.now() - timedelta(
  866. days=settings.MISAGO_FRESH_CONTENT_PERIOD + 5
  867. )
  868. )
  869. threadstracker.make_thread_read_aware(self.user, test_thread)
  870. threadstracker.read_thread(
  871. self.user, test_thread, test_thread.last_post)
  872. testutils.reply_thread(test_thread,
  873. posted_on=test_thread.started_on + timedelta(days=1)
  874. )
  875. self.access_all_categories()
  876. response = self.client.get('/unread/')
  877. self.assertEqual(response.status_code, 200)
  878. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  879. self.access_all_categories()
  880. response = self.client.get(
  881. self.category_a.get_absolute_url() + 'unread/')
  882. self.assertEqual(response.status_code, 200)
  883. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  884. # test api
  885. self.access_all_categories()
  886. response = self.client.get('%s?list=unread' % self.api_link)
  887. self.assertEqual(response.status_code, 200)
  888. response_json = json_loads(response.content)
  889. self.assertEqual(len(response_json['results']), 0)
  890. self.access_all_categories()
  891. response = self.client.get(
  892. '%s?list=unread&category=%s' % (self.api_link, self.category_a.pk))
  893. self.assertEqual(response.status_code, 200)
  894. response_json = json_loads(response.content)
  895. self.assertEqual(len(response_json['results']), 0)
  896. def test_list_hides_user_cutoff_thread(self):
  897. """list hides thread replied before user cutoff"""
  898. self.user.reads_cutoff = timezone.now() - timedelta(days=10)
  899. self.user.joined_on = self.user.reads_cutoff
  900. self.user.save()
  901. test_thread = testutils.post_thread(
  902. category=self.category_a,
  903. started_on=self.user.reads_cutoff - timedelta(days=2)
  904. )
  905. threadstracker.make_thread_read_aware(self.user, test_thread)
  906. threadstracker.read_thread(
  907. self.user, test_thread, test_thread.last_post)
  908. testutils.reply_thread(test_thread,
  909. posted_on=test_thread.started_on + timedelta(days=1)
  910. )
  911. self.access_all_categories()
  912. response = self.client.get('/unread/')
  913. self.assertEqual(response.status_code, 200)
  914. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  915. self.access_all_categories()
  916. response = self.client.get(
  917. self.category_a.get_absolute_url() + 'unread/')
  918. self.assertEqual(response.status_code, 200)
  919. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  920. # test api
  921. self.access_all_categories()
  922. response = self.client.get('%s?list=unread' % self.api_link)
  923. self.assertEqual(response.status_code, 200)
  924. response_json = json_loads(response.content)
  925. self.assertEqual(len(response_json['results']), 0)
  926. self.access_all_categories()
  927. response = self.client.get(
  928. '%s?list=unread&category=%s' % (self.api_link, self.category_a.pk))
  929. self.assertEqual(response.status_code, 200)
  930. response_json = json_loads(response.content)
  931. self.assertEqual(len(response_json['results']), 0)
  932. def test_list_hides_category_cutoff_thread(self):
  933. """list hides thread replied before category cutoff"""
  934. self.user.reads_cutoff = timezone.now() - timedelta(days=10)
  935. self.user.joined_on = self.user.reads_cutoff
  936. self.user.save()
  937. test_thread = testutils.post_thread(
  938. category=self.category_a,
  939. started_on=self.user.reads_cutoff - timedelta(days=2)
  940. )
  941. threadstracker.make_thread_read_aware(self.user, test_thread)
  942. threadstracker.read_thread(
  943. self.user, test_thread, test_thread.last_post)
  944. testutils.reply_thread(test_thread)
  945. self.user.categoryread_set.create(
  946. category=self.category_a,
  947. last_read_on=timezone.now(),
  948. )
  949. self.access_all_categories()
  950. response = self.client.get('/unread/')
  951. self.assertEqual(response.status_code, 200)
  952. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  953. self.access_all_categories()
  954. response = self.client.get(
  955. self.category_a.get_absolute_url() + 'unread/')
  956. self.assertEqual(response.status_code, 200)
  957. self.assertNotIn(test_thread.get_absolute_url(), response.content)
  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(response.content)
  963. self.assertEqual(len(response_json['results']), 0)
  964. self.access_all_categories()
  965. response = self.client.get(
  966. '%s?list=unread&category=%s' % (self.api_link, self.category_a.pk))
  967. self.assertEqual(response.status_code, 200)
  968. response_json = json_loads(response.content)
  969. self.assertEqual(len(response_json['results']), 0)