test_patch_dispatch_bulk.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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_mutate(request, target, value):
  31. return {'value': value * 2}
  32. patch.replace('mutate', action_mutate)
  33. # valid bulk dispatch
  34. response = patch.dispatch_bulk(
  35. MockRequest([
  36. {
  37. 'op': 'replace',
  38. 'path': 'mutate',
  39. 'value': 6,
  40. },
  41. ]),
  42. [MockObject(5), MockObject(7)],
  43. )
  44. self.assertEqual(response.status_code, 200)
  45. self.assertEqual(response.data, [
  46. {'id': '5', 'status': '200', 'patch': {'value': 12}},
  47. {'id': '7', 'status': '200', 'patch': {'value': 12}},
  48. ])
  49. # dispatch requires list as an argument
  50. response = patch.dispatch_bulk(MockRequest({}), [MockObject(5), MockObject(7)])
  51. self.assertEqual(response.status_code, 400)
  52. self.assertEqual(response.data, {
  53. 'non_field_errors': ["PATCH request should be a list of operations."],
  54. })
  55. # invalid action in bulk dispatch
  56. response = patch.dispatch_bulk(
  57. MockRequest([
  58. {
  59. 'op': 'replace',
  60. 'path': 'mutate',
  61. 'value': 6,
  62. },
  63. {
  64. 'op': 'replace',
  65. },
  66. ]),
  67. [MockObject(5), MockObject(7)],
  68. )
  69. self.assertEqual(response.status_code, 400)
  70. self.assertEqual(response.data, {
  71. 'non_field_errors': ['"replace" op has to specify path.'],
  72. })
  73. # repeated action in dispatch
  74. response = patch.dispatch_bulk(
  75. MockRequest([
  76. {
  77. 'op': 'replace',
  78. 'path': 'mutate',
  79. 'value': 6,
  80. },
  81. {
  82. 'op': 'replace',
  83. 'path': 'mutate',
  84. 'value': 12,
  85. },
  86. ]),
  87. [MockObject(5), MockObject(7)],
  88. )
  89. self.assertEqual(response.status_code, 400)
  90. self.assertEqual(response.data, {
  91. 'non_field_errors': ['"replace" op for "mutate" path is repeated.'],
  92. })
  93. # op raised validation error
  94. response = patch.dispatch_bulk(
  95. MockRequest([
  96. {
  97. 'op': 'replace',
  98. 'path': 'mutate',
  99. 'value': 6,
  100. },
  101. {
  102. 'op': 'replace',
  103. 'path': 'error',
  104. 'value': 'invalid',
  105. },
  106. ]),
  107. [MockObject(5), MockObject(7)],
  108. )
  109. self.assertEqual(response.status_code, 200)
  110. self.assertEqual(response.data, [
  111. {'id': '5', 'status': '400', 'value': ["invalid data here!"]},
  112. {'id': '7', 'status': '400', 'value': ["invalid data here!"]},
  113. ])
  114. # op raised api validation error
  115. response = patch.dispatch_bulk(
  116. MockRequest([
  117. {
  118. 'op': 'replace',
  119. 'path': 'mutate',
  120. 'value': 6,
  121. },
  122. {
  123. 'op': 'replace',
  124. 'path': 'error',
  125. 'value': 'api_invalid',
  126. },
  127. ]),
  128. [MockObject(5), MockObject(7)],
  129. )
  130. self.assertEqual(response.status_code, 200)
  131. self.assertEqual(response.data, [
  132. {'id': '5', 'status': '400', 'value': ["invalid api data here!"]},
  133. {'id': '7', 'status': '400', 'value': ["invalid api data here!"]},
  134. ])
  135. # action in bulk dispatch raised perm denied
  136. response = patch.dispatch_bulk(
  137. MockRequest([
  138. {
  139. 'op': 'replace',
  140. 'path': 'mutate',
  141. 'value': 6,
  142. },
  143. {
  144. 'op': 'replace',
  145. 'path': 'error',
  146. 'value': 'perm',
  147. },
  148. ]),
  149. [MockObject(5), MockObject(7)],
  150. )
  151. self.assertEqual(response.status_code, 200)
  152. self.assertEqual(response.data, [
  153. {'id': '5', 'status': '403', 'detail': "yo ain't doing that!"},
  154. {'id': '7', 'status': '403', 'detail': "yo ain't doing that!"},
  155. ])
  156. # action in bulk dispatch raised 404
  157. response = patch.dispatch_bulk(
  158. MockRequest([
  159. {
  160. 'op': 'replace',
  161. 'path': 'mutate',
  162. 'value': 6,
  163. },
  164. {
  165. 'op': 'replace',
  166. 'path': 'error',
  167. 'value': '404',
  168. },
  169. ]),
  170. [MockObject(5), MockObject(7)],
  171. )
  172. self.assertEqual(response.status_code, 200)
  173. self.assertEqual(response.data, [
  174. {'id': '5', 'status': '404', 'detail': 'NOT FOUND'},
  175. {'id': '7', 'status': '404', 'detail': 'NOT FOUND'},
  176. ])
  177. # action in dispatch raised 404 with message but didn't expose it
  178. response = patch.dispatch_bulk(
  179. MockRequest([
  180. {
  181. 'op': 'replace',
  182. 'path': 'mutate',
  183. 'value': 2,
  184. },
  185. {
  186. 'op': 'replace',
  187. 'path': 'error',
  188. 'value': '404_reason',
  189. },
  190. ]),
  191. [MockObject(5), MockObject(7)],
  192. )
  193. self.assertEqual(response.status_code, 200)
  194. self.assertEqual(response.data, [
  195. {'id': '5', 'status': '404', 'detail': 'NOT FOUND'},
  196. {'id': '7', 'status': '404', 'detail': 'NOT FOUND'},
  197. ])