test_thread_postpatch_api.py 29 KB

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