test_thread_postpatch_api.py 32 KB

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