test_thread_postpatch_api.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969
  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(
  344. response_json['detail'][0], "This post is protected. You can't reveal it."
  345. )
  346. self.refresh_post()
  347. self.assertTrue(self.post.is_hidden)
  348. def test_hide_other_user_post(self):
  349. """api validates post ownership when hiding"""
  350. self.post.poster = None
  351. self.post.save()
  352. self.override_acl({'can_hide_own_posts': 1})
  353. response = self.patch(
  354. self.api_link, [{
  355. 'op': 'replace',
  356. 'path': 'is-hidden',
  357. 'value': True
  358. }]
  359. )
  360. self.assertEqual(response.status_code, 400)
  361. response_json = response.json()
  362. self.assertEqual(
  363. response_json['detail'][0], "You can't hide other users posts in this category."
  364. )
  365. self.refresh_post()
  366. self.assertFalse(self.post.is_hidden)
  367. def test_show_other_user_post(self):
  368. """api validates post ownership when revealing"""
  369. self.post.is_hidden = True
  370. self.post.poster = None
  371. self.post.save()
  372. self.override_acl({'can_hide_own_posts': 1})
  373. response = self.patch(
  374. self.api_link, [{
  375. 'op': 'replace',
  376. 'path': 'is-hidden',
  377. 'value': False
  378. }]
  379. )
  380. self.assertEqual(response.status_code, 400)
  381. response_json = response.json()
  382. self.assertEqual(
  383. response_json['detail'][0], "You can't reveal other users posts in this category."
  384. )
  385. self.refresh_post()
  386. self.assertTrue(self.post.is_hidden)
  387. def test_hide_own_post_after_edit_time(self):
  388. """api validates if we are trying to hide post after edit time"""
  389. self.post.posted_on = timezone.now() - timedelta(minutes=10)
  390. self.post.save()
  391. self.override_acl({'post_edit_time': 1, 'can_hide_own_posts': 1})
  392. response = self.patch(
  393. self.api_link, [{
  394. 'op': 'replace',
  395. 'path': 'is-hidden',
  396. 'value': True
  397. }]
  398. )
  399. self.assertEqual(response.status_code, 400)
  400. response_json = response.json()
  401. self.assertEqual(
  402. response_json['detail'][0], "You can't hide posts that are older than 1 minute."
  403. )
  404. self.refresh_post()
  405. self.assertFalse(self.post.is_hidden)
  406. def test_show_own_post_after_edit_time(self):
  407. """api validates if we are trying to reveal post after edit time"""
  408. self.post.is_hidden = True
  409. self.post.posted_on = timezone.now() - timedelta(minutes=10)
  410. self.post.save()
  411. self.override_acl({'post_edit_time': 1, 'can_hide_own_posts': 1})
  412. response = self.patch(
  413. self.api_link, [{
  414. 'op': 'replace',
  415. 'path': 'is-hidden',
  416. 'value': False
  417. }]
  418. )
  419. self.assertEqual(response.status_code, 400)
  420. response_json = response.json()
  421. self.assertEqual(
  422. response_json['detail'][0], "You can't reveal posts that are older than 1 minute."
  423. )
  424. self.refresh_post()
  425. self.assertTrue(self.post.is_hidden)
  426. def test_hide_post_in_closed_thread(self):
  427. """api validates if we are trying to hide post in closed thread"""
  428. self.thread.is_closed = True
  429. self.thread.save()
  430. self.override_acl({'can_hide_own_posts': 1})
  431. response = self.patch(
  432. self.api_link, [{
  433. 'op': 'replace',
  434. 'path': 'is-hidden',
  435. 'value': True
  436. }]
  437. )
  438. self.assertEqual(response.status_code, 400)
  439. response_json = response.json()
  440. self.assertEqual(
  441. response_json['detail'][0], "This thread is closed. You can't hide posts in it."
  442. )
  443. self.refresh_post()
  444. self.assertFalse(self.post.is_hidden)
  445. def test_show_post_in_closed_thread(self):
  446. """api validates if we are trying to reveal post in closed thread"""
  447. self.thread.is_closed = True
  448. self.thread.save()
  449. self.post.is_hidden = True
  450. self.post.save()
  451. self.override_acl({'can_hide_own_posts': 1})
  452. response = self.patch(
  453. self.api_link, [{
  454. 'op': 'replace',
  455. 'path': 'is-hidden',
  456. 'value': False
  457. }]
  458. )
  459. self.assertEqual(response.status_code, 400)
  460. response_json = response.json()
  461. self.assertEqual(
  462. response_json['detail'][0], "This thread is closed. You can't reveal posts in it."
  463. )
  464. self.refresh_post()
  465. self.assertTrue(self.post.is_hidden)
  466. def test_hide_post_in_closed_category(self):
  467. """api validates if we are trying to hide post in closed category"""
  468. self.category.is_closed = True
  469. self.category.save()
  470. self.override_acl({'can_hide_own_posts': 1})
  471. response = self.patch(
  472. self.api_link, [{
  473. 'op': 'replace',
  474. 'path': 'is-hidden',
  475. 'value': True
  476. }]
  477. )
  478. self.assertEqual(response.status_code, 400)
  479. response_json = response.json()
  480. self.assertEqual(
  481. response_json['detail'][0], "This category is closed. You can't hide posts in it."
  482. )
  483. self.refresh_post()
  484. self.assertFalse(self.post.is_hidden)
  485. def test_show_post_in_closed_category(self):
  486. """api validates if we are trying to reveal post in closed category"""
  487. self.category.is_closed = True
  488. self.category.save()
  489. self.post.is_hidden = True
  490. self.post.save()
  491. self.override_acl({'can_hide_own_posts': 1})
  492. response = self.patch(
  493. self.api_link, [{
  494. 'op': 'replace',
  495. 'path': 'is-hidden',
  496. 'value': False
  497. }]
  498. )
  499. self.assertEqual(response.status_code, 400)
  500. response_json = response.json()
  501. self.assertEqual(
  502. response_json['detail'][0], "This category is closed. You can't reveal posts in it."
  503. )
  504. self.refresh_post()
  505. self.assertTrue(self.post.is_hidden)
  506. def test_hide_first_post(self):
  507. """api hide first post fails"""
  508. self.thread.set_first_post(self.post)
  509. self.thread.save()
  510. self.override_acl({'can_hide_posts': 1})
  511. response = self.patch(
  512. self.api_link, [{
  513. 'op': 'replace',
  514. 'path': 'is-hidden',
  515. 'value': True
  516. }]
  517. )
  518. self.assertEqual(response.status_code, 400)
  519. response_json = response.json()
  520. self.assertEqual(response_json['detail'][0], "You can't hide thread's first post.")
  521. def test_show_first_post(self):
  522. """api unhide first post fails"""
  523. self.thread.set_first_post(self.post)
  524. self.thread.save()
  525. self.override_acl({'can_hide_posts': 1})
  526. response = self.patch(
  527. self.api_link, [{
  528. 'op': 'replace',
  529. 'path': 'is-hidden',
  530. 'value': False
  531. }]
  532. )
  533. self.assertEqual(response.status_code, 400)
  534. response_json = response.json()
  535. self.assertEqual(response_json['detail'][0], "You can't reveal thread's first post.")
  536. class PostLikeApiTests(ThreadPostPatchApiTestCase):
  537. def test_like_no_see_permission(self):
  538. """api validates user's permission to see posts likes"""
  539. self.override_acl({'can_see_posts_likes': 0})
  540. response = self.patch(
  541. self.api_link, [{
  542. 'op': 'replace',
  543. 'path': 'is-liked',
  544. 'value': True
  545. }]
  546. )
  547. self.assertContains(response, "You can't like posts in this category.", status_code=400)
  548. def test_like_no_like_permission(self):
  549. """api validates user's permission to see posts likes"""
  550. self.override_acl({'can_like_posts': False})
  551. response = self.patch(
  552. self.api_link, [{
  553. 'op': 'replace',
  554. 'path': 'is-liked',
  555. 'value': True
  556. }]
  557. )
  558. self.assertContains(response, "You can't like posts in this category.", status_code=400)
  559. def test_like_post(self):
  560. """api adds user like to post"""
  561. response = self.patch(
  562. self.api_link, [{
  563. 'op': 'replace',
  564. 'path': 'is-liked',
  565. 'value': True
  566. }]
  567. )
  568. self.assertEqual(response.status_code, 200)
  569. response_json = response.json()
  570. self.assertEqual(response_json['likes'], 1)
  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. )
  578. post = Post.objects.get(pk=self.post.pk)
  579. self.assertEqual(post.likes, response_json['likes'])
  580. self.assertEqual(post.last_likes, response_json['last_likes'])
  581. def test_like_liked_post(self):
  582. """api adds user like to post"""
  583. testutils.like_post(self.post, username='Myo')
  584. testutils.like_post(self.post, username='Mugi')
  585. testutils.like_post(self.post, username='Bob')
  586. testutils.like_post(self.post, username='Miku')
  587. response = self.patch(
  588. self.api_link, [{
  589. 'op': 'replace',
  590. 'path': 'is-liked',
  591. 'value': True
  592. }]
  593. )
  594. self.assertEqual(response.status_code, 200)
  595. response_json = response.json()
  596. self.assertEqual(response_json['likes'], 5)
  597. self.assertEqual(response_json['is_liked'], True)
  598. self.assertEqual(
  599. response_json['last_likes'], [{
  600. 'id': self.user.id,
  601. 'username': self.user.username
  602. }, {
  603. 'id': None,
  604. 'username': 'Miku'
  605. }, {
  606. 'id': None,
  607. 'username': 'Bob'
  608. }, {
  609. 'id': None,
  610. 'username': 'Mugi'
  611. }]
  612. )
  613. post = Post.objects.get(pk=self.post.pk)
  614. self.assertEqual(post.likes, response_json['likes'])
  615. self.assertEqual(post.last_likes, response_json['last_likes'])
  616. def test_unlike_post(self):
  617. """api removes user like from post"""
  618. testutils.like_post(self.post, self.user)
  619. response = self.patch(
  620. self.api_link, [{
  621. 'op': 'replace',
  622. 'path': 'is-liked',
  623. 'value': False
  624. }]
  625. )
  626. self.assertEqual(response.status_code, 200)
  627. response_json = response.json()
  628. self.assertEqual(response_json['likes'], 0)
  629. self.assertEqual(response_json['is_liked'], False)
  630. self.assertEqual(response_json['last_likes'], [])
  631. post = Post.objects.get(pk=self.post.pk)
  632. self.assertEqual(post.likes, response_json['likes'])
  633. self.assertEqual(post.last_likes, response_json['last_likes'])
  634. def test_like_post_no_change(self):
  635. """api does no state change if we are linking liked post"""
  636. testutils.like_post(self.post, self.user)
  637. response = self.patch(
  638. self.api_link, [{
  639. 'op': 'replace',
  640. 'path': 'is-liked',
  641. 'value': True
  642. }]
  643. )
  644. self.assertEqual(response.status_code, 200)
  645. response_json = response.json()
  646. self.assertEqual(response_json['likes'], 1)
  647. self.assertEqual(response_json['is_liked'], True)
  648. self.assertEqual(
  649. response_json['last_likes'], [{
  650. 'id': self.user.id,
  651. 'username': self.user.username
  652. }]
  653. )
  654. post = Post.objects.get(pk=self.post.pk)
  655. self.assertEqual(post.likes, response_json['likes'])
  656. self.assertEqual(post.last_likes, response_json['last_likes'])
  657. def test_unlike_post_no_change(self):
  658. """api does no state change if we are unlinking unliked post"""
  659. response = self.patch(
  660. self.api_link, [{
  661. 'op': 'replace',
  662. 'path': 'is-liked',
  663. 'value': False
  664. }]
  665. )
  666. self.assertEqual(response.status_code, 200)
  667. response_json = response.json()
  668. self.assertEqual(response_json['likes'], 0)
  669. self.assertEqual(response_json['is_liked'], False)
  670. self.assertEqual(response_json['last_likes'], [])
  671. class ThreadEventPatchApiTestCase(ThreadPostPatchApiTestCase):
  672. def setUp(self):
  673. super(ThreadEventPatchApiTestCase, self).setUp()
  674. self.event = testutils.reply_thread(self.thread, poster=self.user, is_event=True)
  675. self.api_link = reverse(
  676. 'misago:api:thread-post-detail',
  677. kwargs={'thread_pk': self.thread.pk,
  678. 'pk': self.event.pk}
  679. )
  680. def refresh_event(self):
  681. self.event = self.thread.post_set.get(pk=self.event.pk)
  682. class EventAnonPatchApiTests(ThreadEventPatchApiTestCase):
  683. def test_anonymous_user(self):
  684. """anonymous users can't change event state"""
  685. self.logout_user()
  686. response = self.patch(self.api_link, [{'op': 'add', 'path': 'acl', 'value': True}])
  687. self.assertEqual(response.status_code, 403)
  688. class EventAddAclApiTests(ThreadEventPatchApiTestCase):
  689. def test_add_acl_true(self):
  690. """api adds current event's acl to response"""
  691. response = self.patch(self.api_link, [{'op': 'add', 'path': 'acl', 'value': True}])
  692. self.assertEqual(response.status_code, 200)
  693. response_json = response.json()
  694. self.assertTrue(response_json['acl'])
  695. def test_add_acl_false(self):
  696. """if value is false, api won't add acl to the response, but will set empty key"""
  697. response = self.patch(self.api_link, [{'op': 'add', 'path': 'acl', 'value': False}])
  698. self.assertEqual(response.status_code, 200)
  699. response_json = response.json()
  700. self.assertIsNone(response_json['acl'])
  701. response = self.patch(self.api_link, [{'op': 'add', 'path': 'acl', 'value': True}])
  702. self.assertEqual(response.status_code, 200)
  703. class EventHideApiTests(ThreadEventPatchApiTestCase):
  704. def test_hide_event(self):
  705. """api makes it possible to hide event"""
  706. self.override_acl({'can_hide_events': 1})
  707. response = self.patch(
  708. self.api_link, [{
  709. 'op': 'replace',
  710. 'path': 'is-hidden',
  711. 'value': True
  712. }]
  713. )
  714. self.assertEqual(response.status_code, 200)
  715. self.refresh_event()
  716. self.assertTrue(self.event.is_hidden)
  717. def test_show_event(self):
  718. """api makes it possible to unhide event"""
  719. self.event.is_hidden = True
  720. self.event.save()
  721. self.refresh_event()
  722. self.assertTrue(self.event.is_hidden)
  723. self.override_acl({'can_hide_events': 1})
  724. response = self.patch(
  725. self.api_link, [{
  726. 'op': 'replace',
  727. 'path': 'is-hidden',
  728. 'value': False
  729. }]
  730. )
  731. self.assertEqual(response.status_code, 200)
  732. self.refresh_event()
  733. self.assertFalse(self.event.is_hidden)
  734. def test_hide_event_no_permission(self):
  735. """api hide event with no permission fails"""
  736. self.override_acl({'can_hide_events': 0})
  737. response = self.patch(
  738. self.api_link, [{
  739. 'op': 'replace',
  740. 'path': 'is-hidden',
  741. 'value': True
  742. }]
  743. )
  744. self.assertEqual(response.status_code, 400)
  745. response_json = response.json()
  746. self.assertEqual(
  747. response_json['detail'][0], "You don't have permission to hide this event."
  748. )
  749. self.refresh_event()
  750. self.assertFalse(self.event.is_hidden)
  751. def test_show_event_no_permission(self):
  752. """api unhide event with no permission fails"""
  753. self.event.is_hidden = True
  754. self.event.save()
  755. self.refresh_event()
  756. self.assertTrue(self.event.is_hidden)
  757. self.override_acl({'can_hide_events': 0})
  758. response = self.patch(
  759. self.api_link, [{
  760. 'op': 'replace',
  761. 'path': 'is-hidden',
  762. 'value': False
  763. }]
  764. )
  765. self.assertEqual(response.status_code, 404)