test_thread_postpatch_api.py 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. import json
  4. from datetime import timedelta
  5. from django.urls import reverse
  6. from django.utils import timezone
  7. from misago.acl.testutils import override_acl
  8. from misago.categories.models import Category
  9. from misago.threads import testutils
  10. from misago.threads.models import Post
  11. from misago.users.testutils import AuthenticatedUserTestCase
  12. class ThreadPostPatchApiTestCase(AuthenticatedUserTestCase):
  13. def setUp(self):
  14. super(ThreadPostPatchApiTestCase, self).setUp()
  15. self.category = Category.objects.get(slug='first-category')
  16. self.thread = testutils.post_thread(category=self.category)
  17. self.post = testutils.reply_thread(self.thread, poster=self.user)
  18. self.api_link = reverse(
  19. 'misago:api:thread-post-detail',
  20. kwargs={
  21. 'thread_pk': self.thread.pk,
  22. 'pk': self.post.pk,
  23. }
  24. )
  25. def patch(self, api_link, ops):
  26. return self.client.patch(api_link, json.dumps(ops), content_type="application/json")
  27. def refresh_post(self):
  28. self.post = self.thread.post_set.get(pk=self.post.pk)
  29. def override_acl(self, extra_acl=None):
  30. new_acl = self.user.acl_cache
  31. new_acl['categories'][self.category.pk].update({
  32. 'can_see': 1,
  33. 'can_browse': 1,
  34. 'can_start_threads': 0,
  35. 'can_reply_threads': 0,
  36. 'can_edit_posts': 1,
  37. })
  38. if extra_acl:
  39. new_acl['categories'][self.category.pk].update(extra_acl)
  40. override_acl(self.user, new_acl)
  41. class PostAddAclApiTests(ThreadPostPatchApiTestCase):
  42. def test_add_acl_true(self):
  43. """api adds current event's acl to response"""
  44. response = self.patch(self.api_link, [
  45. {
  46. 'op': 'add',
  47. 'path': 'acl',
  48. 'value': True,
  49. },
  50. ])
  51. self.assertEqual(response.status_code, 200)
  52. response_json = response.json()
  53. self.assertTrue(response_json['acl'])
  54. def test_add_acl_false(self):
  55. """if value is false, api won't add acl to the response, but will set empty key"""
  56. response = self.patch(self.api_link, [
  57. {
  58. 'op': 'add',
  59. 'path': 'acl',
  60. 'value': False,
  61. },
  62. ])
  63. self.assertEqual(response.status_code, 200)
  64. response_json = response.json()
  65. self.assertIsNone(response_json['acl'])
  66. response = self.patch(self.api_link, [
  67. {
  68. 'op': 'add',
  69. 'path': 'acl',
  70. 'value': True,
  71. },
  72. ])
  73. self.assertEqual(response.status_code, 200)
  74. class PostProtectApiTests(ThreadPostPatchApiTestCase):
  75. def test_protect_post(self):
  76. """api makes it possible to protect post"""
  77. self.override_acl({'can_protect_posts': 1})
  78. response = self.patch(
  79. self.api_link, [
  80. {
  81. 'op': 'replace',
  82. 'path': 'is-protected',
  83. 'value': True,
  84. },
  85. ]
  86. )
  87. self.assertEqual(response.status_code, 200)
  88. reponse_json = response.json()
  89. self.assertTrue(reponse_json['is_protected'])
  90. self.refresh_post()
  91. self.assertTrue(self.post.is_protected)
  92. def test_unprotect_post(self):
  93. """api makes it possible to unprotect protected post"""
  94. self.post.is_protected = True
  95. self.post.save()
  96. self.override_acl({'can_protect_posts': 1})
  97. response = self.patch(
  98. self.api_link, [
  99. {
  100. 'op': 'replace',
  101. 'path': 'is-protected',
  102. 'value': False,
  103. },
  104. ]
  105. )
  106. self.assertEqual(response.status_code, 200)
  107. reponse_json = response.json()
  108. self.assertFalse(reponse_json['is_protected'])
  109. self.refresh_post()
  110. self.assertFalse(self.post.is_protected)
  111. def test_protect_post_no_permission(self):
  112. """api validates permission to protect post"""
  113. self.override_acl({'can_protect_posts': 0})
  114. response = self.patch(
  115. self.api_link, [
  116. {
  117. 'op': 'replace',
  118. 'path': 'is-protected',
  119. 'value': True,
  120. },
  121. ]
  122. )
  123. self.assertEqual(response.status_code, 400)
  124. response_json = response.json()
  125. self.assertEqual(response_json['detail'][0], "You can't protect posts in this category.")
  126. self.refresh_post()
  127. self.assertFalse(self.post.is_protected)
  128. def test_unprotect_post_no_permission(self):
  129. """api validates permission to unprotect post"""
  130. self.post.is_protected = True
  131. self.post.save()
  132. self.override_acl({'can_protect_posts': 0})
  133. response = self.patch(
  134. self.api_link, [
  135. {
  136. 'op': 'replace',
  137. 'path': 'is-protected',
  138. 'value': False,
  139. },
  140. ]
  141. )
  142. self.assertEqual(response.status_code, 400)
  143. response_json = response.json()
  144. self.assertEqual(response_json['detail'][0], "You can't protect posts in this category.")
  145. self.refresh_post()
  146. self.assertTrue(self.post.is_protected)
  147. def test_unprotect_post_not_editable(self):
  148. """api validates if we can edit post we want to protect"""
  149. self.override_acl({'can_edit_posts': 0, 'can_protect_posts': 1})
  150. response = self.patch(
  151. self.api_link, [
  152. {
  153. 'op': 'replace',
  154. 'path': 'is-protected',
  155. 'value': True,
  156. },
  157. ]
  158. )
  159. self.assertEqual(response.status_code, 400)
  160. response_json = response.json()
  161. self.assertEqual(response_json['detail'][0], "You can't protect posts you can't edit.")
  162. self.refresh_post()
  163. self.assertFalse(self.post.is_protected)
  164. class PostApproveApiTests(ThreadPostPatchApiTestCase):
  165. def test_approve_post(self):
  166. """api makes it possible to approve post"""
  167. self.post.is_unapproved = True
  168. self.post.save()
  169. self.override_acl({'can_approve_content': 1})
  170. response = self.patch(
  171. self.api_link, [
  172. {
  173. 'op': 'replace',
  174. 'path': 'is-unapproved',
  175. 'value': False,
  176. },
  177. ]
  178. )
  179. self.assertEqual(response.status_code, 200)
  180. reponse_json = response.json()
  181. self.assertFalse(reponse_json['is_unapproved'])
  182. self.refresh_post()
  183. self.assertFalse(self.post.is_unapproved)
  184. def test_unapprove_post(self):
  185. """unapproving posts is not supported by api"""
  186. self.override_acl({'can_approve_content': 1})
  187. response = self.patch(
  188. self.api_link, [
  189. {
  190. 'op': 'replace',
  191. 'path': 'is-unapproved',
  192. 'value': True,
  193. },
  194. ]
  195. )
  196. self.assertEqual(response.status_code, 200)
  197. self.refresh_post()
  198. self.assertFalse(self.post.is_unapproved)
  199. def test_approve_post_no_permission(self):
  200. """api validates approval permission"""
  201. self.post.is_unapproved = True
  202. self.post.save()
  203. self.override_acl({'can_approve_content': 0})
  204. response = self.patch(
  205. self.api_link, [
  206. {
  207. 'op': 'replace',
  208. 'path': 'is-unapproved',
  209. 'value': False,
  210. },
  211. ]
  212. )
  213. self.assertEqual(response.status_code, 400)
  214. response_json = response.json()
  215. self.assertEqual(response_json['detail'][0], "You can't approve posts in this category.")
  216. self.refresh_post()
  217. self.assertTrue(self.post.is_unapproved)
  218. def test_approve_first_post(self):
  219. """api approve first post fails"""
  220. self.post.is_unapproved = True
  221. self.post.save()
  222. self.thread.set_first_post(self.post)
  223. self.thread.save()
  224. self.override_acl({'can_approve_content': 1})
  225. response = self.patch(
  226. self.api_link, [
  227. {
  228. 'op': 'replace',
  229. 'path': 'is-unapproved',
  230. 'value': False,
  231. },
  232. ]
  233. )
  234. self.assertEqual(response.status_code, 400)
  235. response_json = response.json()
  236. self.assertEqual(response_json['detail'][0], "You can't approve thread's first post.")
  237. self.refresh_post()
  238. self.assertTrue(self.post.is_unapproved)
  239. def test_approve_hidden_post(self):
  240. """api approve hidden post fails"""
  241. self.post.is_unapproved = True
  242. self.post.is_hidden = True
  243. self.post.save()
  244. self.override_acl({'can_approve_content': 1})
  245. response = self.patch(
  246. self.api_link, [
  247. {
  248. 'op': 'replace',
  249. 'path': 'is-unapproved',
  250. 'value': False,
  251. },
  252. ]
  253. )
  254. self.assertEqual(response.status_code, 400)
  255. response_json = response.json()
  256. self.assertEqual(
  257. response_json['detail'][0], "You can't approve posts the content you can't see."
  258. )
  259. self.refresh_post()
  260. self.assertTrue(self.post.is_unapproved)
  261. class PostHideApiTests(ThreadPostPatchApiTestCase):
  262. def test_hide_post(self):
  263. """api makes it possible to hide post"""
  264. self.override_acl({'can_hide_posts': 1})
  265. response = self.patch(
  266. self.api_link, [
  267. {
  268. 'op': 'replace',
  269. 'path': 'is-hidden',
  270. 'value': True,
  271. },
  272. ]
  273. )
  274. self.assertEqual(response.status_code, 200)
  275. reponse_json = response.json()
  276. self.assertTrue(reponse_json['is_hidden'])
  277. self.refresh_post()
  278. self.assertTrue(self.post.is_hidden)
  279. def test_show_post(self):
  280. """api makes it possible to unhide post"""
  281. self.post.is_hidden = True
  282. self.post.save()
  283. self.refresh_post()
  284. self.assertTrue(self.post.is_hidden)
  285. self.override_acl({'can_hide_posts': 1})
  286. response = self.patch(
  287. self.api_link, [
  288. {
  289. 'op': 'replace',
  290. 'path': 'is-hidden',
  291. 'value': False,
  292. },
  293. ]
  294. )
  295. self.assertEqual(response.status_code, 200)
  296. reponse_json = response.json()
  297. self.assertFalse(reponse_json['is_hidden'])
  298. self.refresh_post()
  299. self.assertFalse(self.post.is_hidden)
  300. def test_hide_own_post(self):
  301. """api makes it possible to hide owned post"""
  302. self.override_acl({'can_hide_own_posts': 1})
  303. response = self.patch(
  304. self.api_link, [
  305. {
  306. 'op': 'replace',
  307. 'path': 'is-hidden',
  308. 'value': True,
  309. },
  310. ]
  311. )
  312. self.assertEqual(response.status_code, 200)
  313. reponse_json = response.json()
  314. self.assertTrue(reponse_json['is_hidden'])
  315. self.refresh_post()
  316. self.assertTrue(self.post.is_hidden)
  317. def test_show_own_post(self):
  318. """api makes it possible to unhide owned post"""
  319. self.post.is_hidden = True
  320. self.post.save()
  321. self.refresh_post()
  322. self.assertTrue(self.post.is_hidden)
  323. self.override_acl({'can_hide_own_posts': 1})
  324. response = self.patch(
  325. self.api_link, [
  326. {
  327. 'op': 'replace',
  328. 'path': 'is-hidden',
  329. 'value': False,
  330. },
  331. ]
  332. )
  333. self.assertEqual(response.status_code, 200)
  334. reponse_json = response.json()
  335. self.assertFalse(reponse_json['is_hidden'])
  336. self.refresh_post()
  337. self.assertFalse(self.post.is_hidden)
  338. def test_hide_post_no_permission(self):
  339. """api hide post with no permission fails"""
  340. self.override_acl({'can_hide_posts': 0})
  341. response = self.patch(
  342. self.api_link, [
  343. {
  344. 'op': 'replace',
  345. 'path': 'is-hidden',
  346. 'value': True,
  347. },
  348. ]
  349. )
  350. self.assertEqual(response.status_code, 400)
  351. response_json = response.json()
  352. self.assertEqual(response_json['detail'][0], "You can't hide posts in this category.")
  353. self.refresh_post()
  354. self.assertFalse(self.post.is_hidden)
  355. def test_show_post_no_permission(self):
  356. """api unhide post with no permission fails"""
  357. self.post.is_hidden = True
  358. self.post.save()
  359. self.refresh_post()
  360. self.assertTrue(self.post.is_hidden)
  361. self.override_acl({'can_hide_posts': 0})
  362. response = self.patch(
  363. self.api_link, [
  364. {
  365. 'op': 'replace',
  366. 'path': 'is-hidden',
  367. 'value': False,
  368. },
  369. ]
  370. )
  371. self.assertEqual(response.status_code, 400)
  372. response_json = response.json()
  373. self.assertEqual(response_json['detail'][0], "You can't reveal posts in this category.")
  374. self.refresh_post()
  375. self.assertTrue(self.post.is_hidden)
  376. def test_hide_own_protected_post(self):
  377. """api validates if we are trying to hide protected post"""
  378. self.post.is_protected = True
  379. self.post.save()
  380. self.override_acl({'can_protect_posts': 0, 'can_hide_own_posts': 1})
  381. response = self.patch(
  382. self.api_link, [
  383. {
  384. 'op': 'replace',
  385. 'path': 'is-hidden',
  386. 'value': True,
  387. },
  388. ]
  389. )
  390. self.assertEqual(response.status_code, 400)
  391. response_json = response.json()
  392. self.assertEqual(response_json['detail'][0], "This post is protected. You can't hide it.")
  393. self.refresh_post()
  394. self.assertFalse(self.post.is_hidden)
  395. def test_show_own_protected_post(self):
  396. """api validates if we are trying to reveal protected post"""
  397. self.post.is_hidden = True
  398. self.post.save()
  399. self.override_acl({'can_protect_posts': 0, 'can_hide_own_posts': 1})
  400. self.post.is_protected = True
  401. self.post.save()
  402. response = self.patch(
  403. self.api_link, [
  404. {
  405. 'op': 'replace',
  406. 'path': 'is-hidden',
  407. 'value': False,
  408. },
  409. ]
  410. )
  411. self.assertEqual(response.status_code, 400)
  412. response_json = response.json()
  413. self.assertEqual(
  414. response_json['detail'][0], "This post is protected. You can't reveal it."
  415. )
  416. self.refresh_post()
  417. self.assertTrue(self.post.is_hidden)
  418. def test_hide_other_user_post(self):
  419. """api validates post ownership when hiding"""
  420. self.post.poster = None
  421. self.post.save()
  422. self.override_acl({'can_hide_own_posts': 1})
  423. response = self.patch(
  424. self.api_link, [
  425. {
  426. 'op': 'replace',
  427. 'path': 'is-hidden',
  428. 'value': True,
  429. },
  430. ]
  431. )
  432. self.assertEqual(response.status_code, 400)
  433. response_json = response.json()
  434. self.assertEqual(
  435. response_json['detail'][0], "You can't hide other users posts in this category."
  436. )
  437. self.refresh_post()
  438. self.assertFalse(self.post.is_hidden)
  439. def test_show_other_user_post(self):
  440. """api validates post ownership when revealing"""
  441. self.post.is_hidden = True
  442. self.post.poster = None
  443. self.post.save()
  444. self.override_acl({'can_hide_own_posts': 1})
  445. response = self.patch(
  446. self.api_link, [
  447. {
  448. 'op': 'replace',
  449. 'path': 'is-hidden',
  450. 'value': False,
  451. },
  452. ]
  453. )
  454. self.assertEqual(response.status_code, 400)
  455. response_json = response.json()
  456. self.assertEqual(
  457. response_json['detail'][0], "You can't reveal other users posts in this category."
  458. )
  459. self.refresh_post()
  460. self.assertTrue(self.post.is_hidden)
  461. def test_hide_own_post_after_edit_time(self):
  462. """api validates if we are trying to hide post after edit time"""
  463. self.post.posted_on = timezone.now() - timedelta(minutes=10)
  464. self.post.save()
  465. self.override_acl({'post_edit_time': 1, 'can_hide_own_posts': 1})
  466. response = self.patch(
  467. self.api_link, [
  468. {
  469. 'op': 'replace',
  470. 'path': 'is-hidden',
  471. 'value': True,
  472. },
  473. ]
  474. )
  475. self.assertEqual(response.status_code, 400)
  476. response_json = response.json()
  477. self.assertEqual(
  478. response_json['detail'][0], "You can't hide posts that are older than 1 minute."
  479. )
  480. self.refresh_post()
  481. self.assertFalse(self.post.is_hidden)
  482. def test_show_own_post_after_edit_time(self):
  483. """api validates if we are trying to reveal post after edit time"""
  484. self.post.is_hidden = True
  485. self.post.posted_on = timezone.now() - timedelta(minutes=10)
  486. self.post.save()
  487. self.override_acl({'post_edit_time': 1, 'can_hide_own_posts': 1})
  488. response = self.patch(
  489. self.api_link, [
  490. {
  491. 'op': 'replace',
  492. 'path': 'is-hidden',
  493. 'value': False,
  494. },
  495. ]
  496. )
  497. self.assertEqual(response.status_code, 400)
  498. response_json = response.json()
  499. self.assertEqual(
  500. response_json['detail'][0], "You can't reveal posts that are older than 1 minute."
  501. )
  502. self.refresh_post()
  503. self.assertTrue(self.post.is_hidden)
  504. def test_hide_post_in_closed_thread(self):
  505. """api validates if we are trying to hide post in closed thread"""
  506. self.thread.is_closed = True
  507. self.thread.save()
  508. self.override_acl({'can_hide_own_posts': 1})
  509. response = self.patch(
  510. self.api_link, [
  511. {
  512. 'op': 'replace',
  513. 'path': 'is-hidden',
  514. 'value': True,
  515. },
  516. ]
  517. )
  518. self.assertEqual(response.status_code, 400)
  519. response_json = response.json()
  520. self.assertEqual(
  521. response_json['detail'][0], "This thread is closed. You can't hide posts in it."
  522. )
  523. self.refresh_post()
  524. self.assertFalse(self.post.is_hidden)
  525. def test_show_post_in_closed_thread(self):
  526. """api validates if we are trying to reveal post in closed thread"""
  527. self.thread.is_closed = True
  528. self.thread.save()
  529. self.post.is_hidden = True
  530. self.post.save()
  531. self.override_acl({'can_hide_own_posts': 1})
  532. response = self.patch(
  533. self.api_link, [
  534. {
  535. 'op': 'replace',
  536. 'path': 'is-hidden',
  537. 'value': False,
  538. },
  539. ]
  540. )
  541. self.assertEqual(response.status_code, 400)
  542. response_json = response.json()
  543. self.assertEqual(
  544. response_json['detail'][0], "This thread is closed. You can't reveal posts in it."
  545. )
  546. self.refresh_post()
  547. self.assertTrue(self.post.is_hidden)
  548. def test_hide_post_in_closed_category(self):
  549. """api validates if we are trying to hide post in closed category"""
  550. self.category.is_closed = True
  551. self.category.save()
  552. self.override_acl({'can_hide_own_posts': 1})
  553. response = self.patch(
  554. self.api_link, [
  555. {
  556. 'op': 'replace',
  557. 'path': 'is-hidden',
  558. 'value': True,
  559. },
  560. ]
  561. )
  562. self.assertEqual(response.status_code, 400)
  563. response_json = response.json()
  564. self.assertEqual(
  565. response_json['detail'][0], "This category is closed. You can't hide posts in it."
  566. )
  567. self.refresh_post()
  568. self.assertFalse(self.post.is_hidden)
  569. def test_show_post_in_closed_category(self):
  570. """api validates if we are trying to reveal post in closed category"""
  571. self.category.is_closed = True
  572. self.category.save()
  573. self.post.is_hidden = True
  574. self.post.save()
  575. self.override_acl({'can_hide_own_posts': 1})
  576. response = self.patch(
  577. self.api_link, [
  578. {
  579. 'op': 'replace',
  580. 'path': 'is-hidden',
  581. 'value': False,
  582. },
  583. ]
  584. )
  585. self.assertEqual(response.status_code, 400)
  586. response_json = response.json()
  587. self.assertEqual(
  588. response_json['detail'][0], "This category is closed. You can't reveal posts in it."
  589. )
  590. self.refresh_post()
  591. self.assertTrue(self.post.is_hidden)
  592. def test_hide_first_post(self):
  593. """api hide first post fails"""
  594. self.thread.set_first_post(self.post)
  595. self.thread.save()
  596. self.override_acl({'can_hide_posts': 1})
  597. response = self.patch(
  598. self.api_link, [
  599. {
  600. 'op': 'replace',
  601. 'path': 'is-hidden',
  602. 'value': True,
  603. },
  604. ]
  605. )
  606. self.assertEqual(response.status_code, 400)
  607. response_json = response.json()
  608. self.assertEqual(response_json['detail'][0], "You can't hide thread's first post.")
  609. def test_show_first_post(self):
  610. """api unhide first post fails"""
  611. self.thread.set_first_post(self.post)
  612. self.thread.save()
  613. self.override_acl({'can_hide_posts': 1})
  614. response = self.patch(
  615. self.api_link, [
  616. {
  617. 'op': 'replace',
  618. 'path': 'is-hidden',
  619. 'value': False,
  620. },
  621. ]
  622. )
  623. self.assertEqual(response.status_code, 400)
  624. response_json = response.json()
  625. self.assertEqual(response_json['detail'][0], "You can't reveal thread's first post.")
  626. class PostLikeApiTests(ThreadPostPatchApiTestCase):
  627. def test_like_no_see_permission(self):
  628. """api validates user's permission to see posts likes"""
  629. self.override_acl({'can_see_posts_likes': 0})
  630. response = self.patch(
  631. self.api_link, [
  632. {
  633. 'op': 'replace',
  634. 'path': 'is-liked',
  635. 'value': True,
  636. },
  637. ]
  638. )
  639. self.assertContains(response, "You can't like posts in this category.", status_code=400)
  640. def test_like_no_like_permission(self):
  641. """api validates user's permission to see posts likes"""
  642. self.override_acl({'can_like_posts': False})
  643. response = self.patch(
  644. self.api_link, [
  645. {
  646. 'op': 'replace',
  647. 'path': 'is-liked',
  648. 'value': True,
  649. },
  650. ]
  651. )
  652. self.assertContains(response, "You can't like posts in this category.", status_code=400)
  653. def test_like_post(self):
  654. """api adds user like to post"""
  655. response = self.patch(
  656. self.api_link, [
  657. {
  658. 'op': 'replace',
  659. 'path': 'is-liked',
  660. 'value': True,
  661. },
  662. ]
  663. )
  664. self.assertEqual(response.status_code, 200)
  665. response_json = response.json()
  666. self.assertEqual(response_json['likes'], 1)
  667. self.assertEqual(response_json['is_liked'], True)
  668. self.assertEqual(
  669. response_json['last_likes'], [
  670. {
  671. 'id': self.user.id,
  672. 'username': self.user.username,
  673. },
  674. ]
  675. )
  676. post = Post.objects.get(pk=self.post.pk)
  677. self.assertEqual(post.likes, response_json['likes'])
  678. self.assertEqual(post.last_likes, response_json['last_likes'])
  679. def test_like_liked_post(self):
  680. """api adds user like to post"""
  681. testutils.like_post(self.post, username='Myo')
  682. testutils.like_post(self.post, username='Mugi')
  683. testutils.like_post(self.post, username='Bob')
  684. testutils.like_post(self.post, username='Miku')
  685. response = self.patch(
  686. self.api_link, [
  687. {
  688. 'op': 'replace',
  689. 'path': 'is-liked',
  690. 'value': True,
  691. },
  692. ]
  693. )
  694. self.assertEqual(response.status_code, 200)
  695. response_json = response.json()
  696. self.assertEqual(response_json['likes'], 5)
  697. self.assertEqual(response_json['is_liked'], True)
  698. self.assertEqual(
  699. response_json['last_likes'], [
  700. {
  701. 'id': self.user.id,
  702. 'username': self.user.username
  703. },
  704. {
  705. 'id': None,
  706. 'username': 'Miku',
  707. },
  708. {
  709. 'id': None,
  710. 'username': 'Bob',
  711. },
  712. {
  713. 'id': None,
  714. 'username': 'Mugi',
  715. },
  716. ]
  717. )
  718. post = Post.objects.get(pk=self.post.pk)
  719. self.assertEqual(post.likes, response_json['likes'])
  720. self.assertEqual(post.last_likes, response_json['last_likes'])
  721. def test_unlike_post(self):
  722. """api removes user like from post"""
  723. testutils.like_post(self.post, self.user)
  724. response = self.patch(
  725. self.api_link, [
  726. {
  727. 'op': 'replace',
  728. 'path': 'is-liked',
  729. 'value': False,
  730. },
  731. ]
  732. )
  733. self.assertEqual(response.status_code, 200)
  734. response_json = response.json()
  735. self.assertEqual(response_json['likes'], 0)
  736. self.assertEqual(response_json['is_liked'], False)
  737. self.assertEqual(response_json['last_likes'], [])
  738. post = Post.objects.get(pk=self.post.pk)
  739. self.assertEqual(post.likes, response_json['likes'])
  740. self.assertEqual(post.last_likes, response_json['last_likes'])
  741. def test_like_post_no_change(self):
  742. """api does no state change if we are linking liked post"""
  743. testutils.like_post(self.post, self.user)
  744. response = self.patch(
  745. self.api_link, [
  746. {
  747. 'op': 'replace',
  748. 'path': 'is-liked',
  749. 'value': True,
  750. },
  751. ]
  752. )
  753. self.assertEqual(response.status_code, 200)
  754. response_json = response.json()
  755. self.assertEqual(response_json['likes'], 1)
  756. self.assertEqual(response_json['is_liked'], True)
  757. self.assertEqual(
  758. response_json['last_likes'], [
  759. {
  760. 'id': self.user.id,
  761. 'username': self.user.username,
  762. },
  763. ]
  764. )
  765. post = Post.objects.get(pk=self.post.pk)
  766. self.assertEqual(post.likes, response_json['likes'])
  767. self.assertEqual(post.last_likes, response_json['last_likes'])
  768. def test_unlike_post_no_change(self):
  769. """api does no state change if we are unlinking unliked post"""
  770. response = self.patch(
  771. self.api_link, [
  772. {
  773. 'op': 'replace',
  774. 'path': 'is-liked',
  775. 'value': False,
  776. },
  777. ]
  778. )
  779. self.assertEqual(response.status_code, 200)
  780. response_json = response.json()
  781. self.assertEqual(response_json['likes'], 0)
  782. self.assertEqual(response_json['is_liked'], False)
  783. self.assertEqual(response_json['last_likes'], [])
  784. class ThreadEventPatchApiTestCase(ThreadPostPatchApiTestCase):
  785. def setUp(self):
  786. super(ThreadEventPatchApiTestCase, self).setUp()
  787. self.event = testutils.reply_thread(self.thread, poster=self.user, is_event=True)
  788. self.api_link = reverse(
  789. 'misago:api:thread-post-detail',
  790. kwargs={
  791. 'thread_pk': self.thread.pk,
  792. 'pk': self.event.pk,
  793. }
  794. )
  795. def refresh_event(self):
  796. self.event = self.thread.post_set.get(pk=self.event.pk)
  797. class EventAnonPatchApiTests(ThreadEventPatchApiTestCase):
  798. def test_anonymous_user(self):
  799. """anonymous users can't change event state"""
  800. self.logout_user()
  801. response = self.patch(self.api_link, [
  802. {
  803. 'op': 'add',
  804. 'path': 'acl',
  805. 'value': True,
  806. },
  807. ])
  808. self.assertEqual(response.status_code, 403)
  809. class EventAddAclApiTests(ThreadEventPatchApiTestCase):
  810. def test_add_acl_true(self):
  811. """api adds current event's acl to response"""
  812. response = self.patch(self.api_link, [
  813. {
  814. 'op': 'add',
  815. 'path': 'acl',
  816. 'value': True,
  817. },
  818. ])
  819. self.assertEqual(response.status_code, 200)
  820. response_json = response.json()
  821. self.assertTrue(response_json['acl'])
  822. def test_add_acl_false(self):
  823. """if value is false, api won't add acl to the response, but will set empty key"""
  824. response = self.patch(self.api_link, [
  825. {
  826. 'op': 'add',
  827. 'path': 'acl',
  828. 'value': False,
  829. },
  830. ])
  831. self.assertEqual(response.status_code, 200)
  832. response_json = response.json()
  833. self.assertIsNone(response_json['acl'])
  834. response = self.patch(self.api_link, [
  835. {
  836. 'op': 'add',
  837. 'path': 'acl',
  838. 'value': True,
  839. },
  840. ])
  841. self.assertEqual(response.status_code, 200)
  842. class EventHideApiTests(ThreadEventPatchApiTestCase):
  843. def test_hide_event(self):
  844. """api makes it possible to hide event"""
  845. self.override_acl({'can_hide_events': 1})
  846. response = self.patch(
  847. self.api_link, [
  848. {
  849. 'op': 'replace',
  850. 'path': 'is-hidden',
  851. 'value': True,
  852. },
  853. ]
  854. )
  855. self.assertEqual(response.status_code, 200)
  856. self.refresh_event()
  857. self.assertTrue(self.event.is_hidden)
  858. def test_show_event(self):
  859. """api makes it possible to unhide event"""
  860. self.event.is_hidden = True
  861. self.event.save()
  862. self.refresh_event()
  863. self.assertTrue(self.event.is_hidden)
  864. self.override_acl({'can_hide_events': 1})
  865. response = self.patch(
  866. self.api_link, [
  867. {
  868. 'op': 'replace',
  869. 'path': 'is-hidden',
  870. 'value': False,
  871. },
  872. ]
  873. )
  874. self.assertEqual(response.status_code, 200)
  875. self.refresh_event()
  876. self.assertFalse(self.event.is_hidden)
  877. def test_hide_event_no_permission(self):
  878. """api hide event with no permission fails"""
  879. self.override_acl({'can_hide_events': 0})
  880. response = self.patch(
  881. self.api_link, [
  882. {
  883. 'op': 'replace',
  884. 'path': 'is-hidden',
  885. 'value': True,
  886. },
  887. ]
  888. )
  889. self.assertEqual(response.status_code, 400)
  890. response_json = response.json()
  891. self.assertEqual(
  892. response_json['detail'][0], "You don't have permission to hide this event."
  893. )
  894. self.refresh_event()
  895. self.assertFalse(self.event.is_hidden)
  896. def test_show_event_no_permission(self):
  897. """api unhide event with no permission fails"""
  898. self.event.is_hidden = True
  899. self.event.save()
  900. self.refresh_event()
  901. self.assertTrue(self.event.is_hidden)
  902. self.override_acl({'can_hide_events': 0})
  903. response = self.patch(
  904. self.api_link, [
  905. {
  906. 'op': 'replace',
  907. 'path': 'is-hidden',
  908. 'value': False,
  909. },
  910. ]
  911. )
  912. self.assertEqual(response.status_code, 404)