test_patch_dispatch_bulk.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. from __future__ import unicode_literals
  2. from rest_framework.exceptions import ValidationError as ApiValidationError
  3. from django.core.exceptions import PermissionDenied, ValidationError
  4. from django.http import Http404
  5. from django.test import TestCase
  6. from misago.api.patch import ApiPatch, InvalidAction
  7. class MockRequest(object):
  8. def __init__(self, ops):
  9. self.data = {'ops': ops}
  10. class MockObject(object):
  11. def __init__(self, pk):
  12. self.id = pk
  13. self.pk = pk
  14. class ApiPatchDispatchBulkTests(TestCase):
  15. def test_dispatch_bulk(self):
  16. """dispatch_bulk calls actions and returns response"""
  17. patch = ApiPatch()
  18. def action_error(request, target, value):
  19. if value == '404':
  20. raise Http404()
  21. if value == '404_reason':
  22. raise Http404("something was removed")
  23. if value == 'perm':
  24. raise PermissionDenied("yo ain't doing that!")
  25. if value == 'invalid':
  26. raise ValidationError("invalid data here!")
  27. if value == 'api_invalid':
  28. raise ApiValidationError("invalid api data here!")
  29. patch.replace('error', action_error)
  30. def action_custom_path_error(request, target, value):
  31. if value == 'invalid':
  32. raise ValidationError("invalid data here!")
  33. if value == 'api_invalid':
  34. raise ApiValidationError("invalid api data here!")
  35. patch.replace('path-error', action_custom_path_error)
  36. def action_mutate(request, target, value):
  37. return {'value': value * 2}
  38. patch.replace('mutate', action_mutate)
  39. # valid bulk dispatch
  40. response = patch.dispatch_bulk(
  41. MockRequest([
  42. {
  43. 'op': 'replace',
  44. 'path': 'mutate',
  45. 'value': 6,
  46. },
  47. ]),
  48. [MockObject(5), MockObject(7)],
  49. )
  50. self.assertEqual(response.status_code, 200)
  51. self.assertEqual(response.data, [
  52. {'id': '5', 'status': '200', 'patch': {'value': 12}},
  53. {'id': '7', 'status': '200', 'patch': {'value': 12}},
  54. ])
  55. # dispatch requires list as an argument
  56. response = patch.dispatch_bulk(MockRequest({}), [MockObject(5), MockObject(7)])
  57. self.assertEqual(response.status_code, 400)
  58. self.assertEqual(response.data, {
  59. 'non_field_errors': ["PATCH request should be a list of operations."],
  60. })
  61. # invalid action in bulk dispatch
  62. response = patch.dispatch_bulk(
  63. MockRequest([
  64. {
  65. 'op': 'replace',
  66. 'path': 'mutate',
  67. 'value': 6,
  68. },
  69. {
  70. 'op': 'replace',
  71. },
  72. ]),
  73. [MockObject(5), MockObject(7)],
  74. )
  75. self.assertEqual(response.status_code, 400)
  76. self.assertEqual(response.data, {
  77. 'non_field_errors': ['"replace" op has to specify path.'],
  78. })
  79. # repeated action in dispatch
  80. response = patch.dispatch_bulk(
  81. MockRequest([
  82. {
  83. 'op': 'replace',
  84. 'path': 'mutate',
  85. 'value': 6,
  86. },
  87. {
  88. 'op': 'replace',
  89. 'path': 'mutate',
  90. 'value': 12,
  91. },
  92. ]),
  93. [MockObject(5), MockObject(7)],
  94. )
  95. self.assertEqual(response.status_code, 400)
  96. self.assertEqual(response.data, {
  97. 'non_field_errors': ['"replace" op for "mutate" path is repeated.'],
  98. })
  99. # op raised validation error
  100. response = patch.dispatch_bulk(
  101. MockRequest([
  102. {
  103. 'op': 'replace',
  104. 'path': 'mutate',
  105. 'value': 6,
  106. },
  107. {
  108. 'op': 'replace',
  109. 'path': 'error',
  110. 'value': 'invalid',
  111. },
  112. ]),
  113. [MockObject(5), MockObject(7)],
  114. )
  115. self.assertEqual(response.status_code, 200)
  116. self.assertEqual(response.data, [
  117. {'id': '5', 'status': '400', 'invalid': {'error': ["invalid data here!"]}},
  118. {'id': '7', 'status': '400', 'invalid': {'error': ["invalid data here!"]}},
  119. ])
  120. # op raised api validation error dict in custom path
  121. response = patch.dispatch_bulk(
  122. MockRequest([
  123. {
  124. 'op': 'replace',
  125. 'path': 'mutate',
  126. 'value': 6,
  127. },
  128. {
  129. 'op': 'replace',
  130. 'path': 'path-error',
  131. 'value': 'api_invalid',
  132. },
  133. ]),
  134. [MockObject(5), MockObject(7)],
  135. )
  136. self.assertEqual(response.status_code, 200)
  137. self.assertEqual(response.data, [
  138. {'id': '5', 'status': '400', 'invalid': {'path_error': ["invalid api data here!"]}},
  139. {'id': '7', 'status': '400', 'invalid': {'path_error': ["invalid api data here!"]}},
  140. ])
  141. # op raised api validation error
  142. response = patch.dispatch_bulk(
  143. MockRequest([
  144. {
  145. 'op': 'replace',
  146. 'path': 'mutate',
  147. 'value': 6,
  148. },
  149. {
  150. 'op': 'replace',
  151. 'path': 'error',
  152. 'value': 'api_invalid',
  153. },
  154. ]),
  155. [MockObject(5), MockObject(7)],
  156. )
  157. self.assertEqual(response.status_code, 200)
  158. self.assertEqual(response.data, [
  159. {'id': '5', 'status': '400', 'invalid': {'error': ["invalid api data here!"]}},
  160. {'id': '7', 'status': '400', 'invalid': {'error': ["invalid api data here!"]}},
  161. ])
  162. # op raised api validation error in custom path
  163. response = patch.dispatch_bulk(
  164. MockRequest([
  165. {
  166. 'op': 'replace',
  167. 'path': 'mutate',
  168. 'value': 6,
  169. },
  170. {
  171. 'op': 'replace',
  172. 'path': 'path-error',
  173. 'value': 'api_invalid',
  174. },
  175. ]),
  176. [MockObject(5), MockObject(7)],
  177. )
  178. self.assertEqual(response.status_code, 200)
  179. self.assertEqual(response.data, [
  180. {'id': '5', 'status': '400', 'invalid': {'path_error': ["invalid api data here!"]}},
  181. {'id': '7', 'status': '400', 'invalid': {'path_error': ["invalid api data here!"]}},
  182. ])
  183. # action in bulk dispatch raised perm denied
  184. response = patch.dispatch_bulk(
  185. MockRequest([
  186. {
  187. 'op': 'replace',
  188. 'path': 'mutate',
  189. 'value': 6,
  190. },
  191. {
  192. 'op': 'replace',
  193. 'path': 'error',
  194. 'value': 'perm',
  195. },
  196. ]),
  197. [MockObject(5), MockObject(7)],
  198. )
  199. self.assertEqual(response.status_code, 200)
  200. self.assertEqual(response.data, [
  201. {'id': '5', 'status': '403', 'detail': "yo ain't doing that!"},
  202. {'id': '7', 'status': '403', 'detail': "yo ain't doing that!"},
  203. ])
  204. # action in bulk dispatch raised 404
  205. response = patch.dispatch_bulk(
  206. MockRequest([
  207. {
  208. 'op': 'replace',
  209. 'path': 'mutate',
  210. 'value': 6,
  211. },
  212. {
  213. 'op': 'replace',
  214. 'path': 'error',
  215. 'value': '404',
  216. },
  217. ]),
  218. [MockObject(5), MockObject(7)],
  219. )
  220. self.assertEqual(response.status_code, 200)
  221. self.assertEqual(response.data, [
  222. {'id': '5', 'status': '404', 'detail': 'NOT FOUND'},
  223. {'id': '7', 'status': '404', 'detail': 'NOT FOUND'},
  224. ])
  225. # action in dispatch raised 404 with message but didn't expose it
  226. response = patch.dispatch_bulk(
  227. MockRequest([
  228. {
  229. 'op': 'replace',
  230. 'path': 'mutate',
  231. 'value': 2,
  232. },
  233. {
  234. 'op': 'replace',
  235. 'path': 'error',
  236. 'value': '404_reason',
  237. },
  238. ]),
  239. [MockObject(5), MockObject(7)],
  240. )
  241. self.assertEqual(response.status_code, 200)
  242. self.assertEqual(response.data, [
  243. {'id': '5', 'status': '404', 'detail': 'NOT FOUND'},
  244. {'id': '7', 'status': '404', 'detail': 'NOT FOUND'},
  245. ])