test_thread_patchapi.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. import json
  2. from misago.acl.testutils import override_acl
  3. from misago.categories.models import Category
  4. from misago.threads.tests.test_thread_api import ThreadApiTestCase
  5. class ThreadPinGloballyApiTests(ThreadApiTestCase):
  6. def test_pin_thread(self):
  7. """api makes it possible to pin globally thread"""
  8. self.override_acl({
  9. 'can_pin_threads': 2
  10. })
  11. response = self.client.patch(self.api_link, json.dumps([
  12. {'op': 'replace', 'path': 'weight', 'value': 2}
  13. ]),
  14. content_type="application/json")
  15. self.assertEqual(response.status_code, 200)
  16. thread_json = self.get_thread_json()
  17. self.assertEqual(thread_json['weight'], 2)
  18. def test_unpin_thread(self):
  19. """api makes it possible to unpin thread"""
  20. self.thread.weight = 2
  21. self.thread.save()
  22. thread_json = self.get_thread_json()
  23. self.assertEqual(thread_json['weight'], 2)
  24. self.override_acl({
  25. 'can_pin_threads': 2
  26. })
  27. response = self.client.patch(self.api_link, json.dumps([
  28. {'op': 'replace', 'path': 'weight', 'value': 0}
  29. ]),
  30. content_type="application/json")
  31. self.assertEqual(response.status_code, 200)
  32. thread_json = self.get_thread_json()
  33. self.assertEqual(thread_json['weight'], 0)
  34. def test_pin_thread_no_permission(self):
  35. """api pin thread globally with no permission fails"""
  36. self.override_acl({
  37. 'can_pin_threads': 1
  38. })
  39. response = self.client.patch(self.api_link, json.dumps([
  40. {'op': 'replace', 'path': 'weight', 'value': 2}
  41. ]),
  42. content_type="application/json")
  43. self.assertEqual(response.status_code, 400)
  44. response_json = json.loads(response.content)
  45. self.assertEqual(response_json['detail'][0],
  46. "You don't have permission to pin this thread globally.")
  47. thread_json = self.get_thread_json()
  48. self.assertEqual(thread_json['weight'], 0)
  49. def test_unpin_thread_no_permission(self):
  50. """api unpin thread with no permission fails"""
  51. self.thread.weight = 2
  52. self.thread.save()
  53. thread_json = self.get_thread_json()
  54. self.assertEqual(thread_json['weight'], 2)
  55. self.override_acl({
  56. 'can_pin_threads': 1
  57. })
  58. response = self.client.patch(self.api_link, json.dumps([
  59. {'op': 'replace', 'path': 'weight', 'value': 1}
  60. ]),
  61. content_type="application/json")
  62. self.assertEqual(response.status_code, 400)
  63. response_json = json.loads(response.content)
  64. self.assertEqual(response_json['detail'][0],
  65. "You don't have permission to change this thread's weight.")
  66. thread_json = self.get_thread_json()
  67. self.assertEqual(thread_json['weight'], 2)
  68. class ThreadPinLocallyApiTests(ThreadApiTestCase):
  69. def test_pin_thread(self):
  70. """api makes it possible to pin locally thread"""
  71. self.override_acl({
  72. 'can_pin_threads': 1
  73. })
  74. response = self.client.patch(self.api_link, json.dumps([
  75. {'op': 'replace', 'path': 'weight', 'value': 1}
  76. ]),
  77. content_type="application/json")
  78. self.assertEqual(response.status_code, 200)
  79. thread_json = self.get_thread_json()
  80. self.assertEqual(thread_json['weight'], 1)
  81. def test_unpin_thread(self):
  82. """api makes it possible to unpin thread"""
  83. self.thread.weight = 1
  84. self.thread.save()
  85. thread_json = self.get_thread_json()
  86. self.assertEqual(thread_json['weight'], 1)
  87. self.override_acl({
  88. 'can_pin_threads': 1
  89. })
  90. response = self.client.patch(self.api_link, json.dumps([
  91. {'op': 'replace', 'path': 'weight', 'value': 0}
  92. ]),
  93. content_type="application/json")
  94. self.assertEqual(response.status_code, 200)
  95. thread_json = self.get_thread_json()
  96. self.assertEqual(thread_json['weight'], 0)
  97. def test_pin_thread_no_permission(self):
  98. """api pin thread locally with no permission fails"""
  99. self.override_acl({
  100. 'can_pin_threads': 0
  101. })
  102. response = self.client.patch(self.api_link, json.dumps([
  103. {'op': 'replace', 'path': 'weight', 'value': 1}
  104. ]),
  105. content_type="application/json")
  106. self.assertEqual(response.status_code, 400)
  107. response_json = json.loads(response.content)
  108. self.assertEqual(response_json['detail'][0],
  109. "You don't have permission to change this thread's weight.")
  110. thread_json = self.get_thread_json()
  111. self.assertEqual(thread_json['weight'], 0)
  112. def test_unpin_thread_no_permission(self):
  113. """api unpin thread with no permission fails"""
  114. self.thread.weight = 1
  115. self.thread.save()
  116. thread_json = self.get_thread_json()
  117. self.assertEqual(thread_json['weight'], 1)
  118. self.override_acl({
  119. 'can_pin_threads': 0
  120. })
  121. response = self.client.patch(self.api_link, json.dumps([
  122. {'op': 'replace', 'path': 'weight', 'value': 0}
  123. ]),
  124. content_type="application/json")
  125. self.assertEqual(response.status_code, 400)
  126. response_json = json.loads(response.content)
  127. self.assertEqual(response_json['detail'][0],
  128. "You don't have permission to change this thread's weight.")
  129. thread_json = self.get_thread_json()
  130. self.assertEqual(thread_json['weight'], 1)
  131. class ThreadMoveApiTests(ThreadApiTestCase):
  132. def setUp(self):
  133. super(ThreadMoveApiTests, self).setUp()
  134. Category(
  135. name='Category B',
  136. slug='category-b',
  137. ).insert_at(self.category, position='last-child', save=True)
  138. self.category_b = Category.objects.get(slug='category-b')
  139. def override_other_acl(self, acl):
  140. final_acl = {
  141. 'can_see': 1,
  142. 'can_browse': 1,
  143. 'can_see_all_threads': 1,
  144. 'can_see_own_threads': 0,
  145. 'can_hide_threads': 0,
  146. 'can_approve_content': 0,
  147. }
  148. final_acl.update(acl)
  149. categories_acl = self.user.acl['categories']
  150. categories_acl[self.category_b.pk] = final_acl
  151. visible_categories = [self.category.pk]
  152. if final_acl['can_see']:
  153. visible_categories.append(self.category_b.pk)
  154. override_acl(self.user, {
  155. 'visible_categories': visible_categories,
  156. 'categories': categories_acl,
  157. })
  158. def test_move_thread(self):
  159. """api moves thread to other category"""
  160. self.override_acl({
  161. 'can_move_threads': True
  162. })
  163. self.override_other_acl({})
  164. response = self.client.patch(self.api_link, json.dumps([
  165. {'op': 'replace', 'path': 'category', 'value': self.category_b.pk}
  166. ]),
  167. content_type="application/json")
  168. self.assertEqual(response.status_code, 200)
  169. self.override_other_acl({})
  170. thread_json = self.get_thread_json()
  171. self.assertEqual(thread_json['category']['id'], self.category_b.pk)
  172. def test_move_thread_no_permission(self):
  173. """api move thread to other category with no permission fails"""
  174. self.override_acl({
  175. 'can_move_threads': False
  176. })
  177. self.override_other_acl({})
  178. response = self.client.patch(self.api_link, json.dumps([
  179. {'op': 'replace', 'path': 'category', 'value': self.category_b.pk}
  180. ]),
  181. content_type="application/json")
  182. self.assertEqual(response.status_code, 400)
  183. response_json = json.loads(response.content)
  184. self.assertEqual(response_json['detail'][0],
  185. "You don't have permission to move this thread.")
  186. self.override_other_acl({})
  187. thread_json = self.get_thread_json()
  188. self.assertEqual(thread_json['category']['id'], self.category.pk)
  189. def test_move_thread_no_category_access(self):
  190. """api move thread to category with no access fails"""
  191. self.override_acl({
  192. 'can_move_threads': True
  193. })
  194. self.override_other_acl({
  195. 'can_see': False
  196. })
  197. response = self.client.patch(self.api_link, json.dumps([
  198. {'op': 'replace', 'path': 'category', 'value': self.category_b.pk}
  199. ]),
  200. content_type="application/json")
  201. self.assertEqual(response.status_code, 400)
  202. response_json = json.loads(response.content)
  203. self.assertEqual(response_json['detail'][0], 'NOT FOUND')
  204. self.override_other_acl({})
  205. thread_json = self.get_thread_json()
  206. self.assertEqual(thread_json['category']['id'], self.category.pk)
  207. def test_move_thread_no_category_browse(self):
  208. """api move thread to category with no browsing access fails"""
  209. self.override_acl({
  210. 'can_move_threads': True
  211. })
  212. self.override_other_acl({
  213. 'can_browse': False
  214. })
  215. response = self.client.patch(self.api_link, json.dumps([
  216. {'op': 'replace', 'path': 'category', 'value': self.category_b.pk}
  217. ]),
  218. content_type="application/json")
  219. self.assertEqual(response.status_code, 400)
  220. response_json = json.loads(response.content)
  221. self.assertEqual(response_json['detail'][0],
  222. 'You don\'t have permission to browse "Category B" contents.')
  223. self.override_other_acl({})
  224. thread_json = self.get_thread_json()
  225. self.assertEqual(thread_json['category']['id'], self.category.pk)
  226. def test_thread_flatten_categories(self):
  227. """api flatten thread categories"""
  228. response = self.client.patch(self.api_link, json.dumps([
  229. {'op': 'replace', 'path': 'flatten-categories', 'value': None}
  230. ]),
  231. content_type="application/json")
  232. self.assertEqual(response.status_code, 200)
  233. response_json = json.loads(response.content)
  234. self.assertEqual(response_json['category'], self.category.pk)
  235. def test_thread_top_flatten_categories(self):
  236. """api flatten thread with top category"""
  237. self.thread.category = self.category_b
  238. self.thread.save()
  239. self.override_other_acl({})
  240. response = self.client.patch(self.api_link, json.dumps([
  241. {
  242. 'op': 'add',
  243. 'path': 'top-category',
  244. 'value': Category.objects.root_category().pk,
  245. },
  246. {'op': 'replace', 'path': 'flatten-categories', 'value': None},
  247. ]),
  248. content_type="application/json")
  249. self.assertEqual(response.status_code, 200)
  250. response_json = json.loads(response.content)
  251. self.assertEqual(response_json['top_category'], self.category.pk)
  252. self.assertEqual(response_json['category'], self.category_b.pk)
  253. class ThreadCloseApiTests(ThreadApiTestCase):
  254. def test_close_thread(self):
  255. """api makes it possible to close thread"""
  256. self.override_acl({
  257. 'can_close_threads': True
  258. })
  259. response = self.client.patch(self.api_link, json.dumps([
  260. {'op': 'replace', 'path': 'is-closed', 'value': True}
  261. ]),
  262. content_type="application/json")
  263. self.assertEqual(response.status_code, 200)
  264. thread_json = self.get_thread_json()
  265. self.assertTrue(thread_json['is_closed'])
  266. def test_open_thread(self):
  267. """api makes it possible to open thread"""
  268. self.thread.is_closed = True
  269. self.thread.save()
  270. thread_json = self.get_thread_json()
  271. self.assertTrue(thread_json['is_closed'])
  272. self.override_acl({
  273. 'can_close_threads': True
  274. })
  275. response = self.client.patch(self.api_link, json.dumps([
  276. {'op': 'replace', 'path': 'is-closed', 'value': False}
  277. ]),
  278. content_type="application/json")
  279. self.assertEqual(response.status_code, 200)
  280. thread_json = self.get_thread_json()
  281. self.assertFalse(thread_json['is_closed'])
  282. def test_close_thread_no_permission(self):
  283. """api close thread with no permission fails"""
  284. self.override_acl({
  285. 'can_close_threads': False
  286. })
  287. response = self.client.patch(self.api_link, json.dumps([
  288. {'op': 'replace', 'path': 'is-closed', 'value': True}
  289. ]),
  290. content_type="application/json")
  291. self.assertEqual(response.status_code, 400)
  292. response_json = json.loads(response.content)
  293. self.assertEqual(response_json['detail'][0],
  294. "You don't have permission to close this thread.")
  295. thread_json = self.get_thread_json()
  296. self.assertFalse(thread_json['is_closed'])
  297. def test_open_thread_no_permission(self):
  298. """api open thread with no permission fails"""
  299. self.thread.is_closed = True
  300. self.thread.save()
  301. thread_json = self.get_thread_json()
  302. self.assertTrue(thread_json['is_closed'])
  303. self.override_acl({
  304. 'can_close_threads': False
  305. })
  306. response = self.client.patch(self.api_link, json.dumps([
  307. {'op': 'replace', 'path': 'is-closed', 'value': False}
  308. ]),
  309. content_type="application/json")
  310. self.assertEqual(response.status_code, 400)
  311. response_json = json.loads(response.content)
  312. self.assertEqual(response_json['detail'][0],
  313. "You don't have permission to open this thread.")
  314. thread_json = self.get_thread_json()
  315. self.assertTrue(thread_json['is_closed'])
  316. class ThreadHideApiTests(ThreadApiTestCase):
  317. def test_hide_thread(self):
  318. """api makes it possible to hide thread"""
  319. self.override_acl({
  320. 'can_hide_threads': 1
  321. })
  322. response = self.client.patch(self.api_link, json.dumps([
  323. {'op': 'replace', 'path': 'is-hidden', 'value': True}
  324. ]),
  325. content_type="application/json")
  326. self.assertEqual(response.status_code, 200)
  327. self.override_acl({
  328. 'can_hide_threads': 1
  329. })
  330. thread_json = self.get_thread_json()
  331. self.assertTrue(thread_json['is_hidden'])
  332. def test_show_thread(self):
  333. """api makes it possible to unhide thread"""
  334. self.thread.is_hidden = True
  335. self.thread.save()
  336. self.override_acl({
  337. 'can_hide_threads': 1
  338. })
  339. thread_json = self.get_thread_json()
  340. self.assertTrue(thread_json['is_hidden'])
  341. self.override_acl({
  342. 'can_hide_threads': 1
  343. })
  344. response = self.client.patch(self.api_link, json.dumps([
  345. {'op': 'replace', 'path': 'is-hidden', 'value': False}
  346. ]),
  347. content_type="application/json")
  348. self.assertEqual(response.status_code, 200)
  349. self.override_acl({
  350. 'can_hide_threads': 1
  351. })
  352. thread_json = self.get_thread_json()
  353. self.assertFalse(thread_json['is_hidden'])
  354. def test_hide_thread_no_permission(self):
  355. """api hide thread with no permission fails"""
  356. self.override_acl({
  357. 'can_hide_threads': 0
  358. })
  359. response = self.client.patch(self.api_link, json.dumps([
  360. {'op': 'replace', 'path': 'is-hidden', 'value': True}
  361. ]),
  362. content_type="application/json")
  363. self.assertEqual(response.status_code, 400)
  364. response_json = json.loads(response.content)
  365. self.assertEqual(response_json['detail'][0],
  366. "You don't have permission to hide this thread.")
  367. thread_json = self.get_thread_json()
  368. self.assertFalse(thread_json['is_hidden'])
  369. def test_show_thread_no_permission(self):
  370. """api unhide thread with no permission fails"""
  371. self.thread.is_hidden = True
  372. self.thread.save()
  373. self.override_acl({
  374. 'can_hide_threads': 1
  375. })
  376. thread_json = self.get_thread_json()
  377. self.assertTrue(thread_json['is_hidden'])
  378. self.override_acl({
  379. 'can_hide_threads': 0
  380. })
  381. response = self.client.patch(self.api_link, json.dumps([
  382. {'op': 'replace', 'path': 'is-hidden', 'value': False}
  383. ]),
  384. content_type="application/json")
  385. self.assertEqual(response.status_code, 404)
  386. class ThreadSubscribeApiTests(ThreadApiTestCase):
  387. def test_subscribe_thread(self):
  388. """api makes it possible to subscribe thread"""
  389. response = self.client.patch(self.api_link, json.dumps([
  390. {'op': 'replace', 'path': 'subscription', 'value': 'notify'}
  391. ]),
  392. content_type="application/json")
  393. self.assertEqual(response.status_code, 200)
  394. thread_json = self.get_thread_json()
  395. self.assertFalse(thread_json['subscription'])
  396. subscription = self.user.subscription_set.get(thread=self.thread)
  397. self.assertFalse(subscription.send_email)
  398. def test_subscribe_thread_with_email(self):
  399. """api makes it possible to subscribe thread with emails"""
  400. response = self.client.patch(self.api_link, json.dumps([
  401. {'op': 'replace', 'path': 'subscription', 'value': 'email'}
  402. ]),
  403. content_type="application/json")
  404. self.assertEqual(response.status_code, 200)
  405. thread_json = self.get_thread_json()
  406. self.assertTrue(thread_json['subscription'])
  407. subscription = self.user.subscription_set.get(thread=self.thread)
  408. self.assertTrue(subscription.send_email)
  409. def test_unsubscribe_thread(self):
  410. """api makes it possible to unsubscribe thread"""
  411. response = self.client.patch(self.api_link, json.dumps([
  412. {'op': 'replace', 'path': 'subscription', 'value': 'remove'}
  413. ]),
  414. content_type="application/json")
  415. self.assertEqual(response.status_code, 200)
  416. thread_json = self.get_thread_json()
  417. self.assertIsNone(thread_json['subscription'])
  418. self.assertEqual(self.user.subscription_set.count(), 0)
  419. def test_subscribe_as_guest(self):
  420. """api makes it impossible to subscribe thread"""
  421. self.logout_user()
  422. response = self.client.patch(self.api_link, json.dumps([
  423. {'op': 'replace', 'path': 'subscription', 'value': 'email'}
  424. ]),
  425. content_type="application/json")
  426. self.assertEqual(response.status_code, 403)
  427. def test_subscribe_nonexistant_thread(self):
  428. """api makes it impossible to subscribe nonexistant thread"""
  429. bad_api_link = self.api_link.replace(
  430. unicode(self.thread.pk), unicode(self.thread.pk + 9))
  431. response = self.client.patch(bad_api_link, json.dumps([
  432. {'op': 'replace', 'path': 'subscription', 'value': 'email'}
  433. ]),
  434. content_type="application/json")
  435. self.assertEqual(response.status_code, 404)