test_apipatch.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. from django.core.exceptions import PermissionDenied
  2. from django.http import Http404
  3. from django.test import TestCase
  4. from misago.core.apipatch import ApiPatch, InvalidAction
  5. class MockRequest(object):
  6. def __init__(self, data=None):
  7. self.data = data
  8. class MockObject(object):
  9. def __init__(self, pk):
  10. self.id = pk
  11. self.pk = pk
  12. class ApiPatchTests(TestCase):
  13. def test_add(self):
  14. """add method adds function to patch object"""
  15. patch = ApiPatch()
  16. def test_function():
  17. pass
  18. patch.add('test-add', test_function)
  19. self.assertEqual(len(patch._actions), 1)
  20. self.assertEqual(patch._actions[0]['op'], 'add')
  21. self.assertEqual(patch._actions[0]['path'], 'test-add')
  22. self.assertEqual(patch._actions[0]['handler'], test_function)
  23. def test_remove(self):
  24. """remove method adds function to patch object"""
  25. patch = ApiPatch()
  26. def test_function():
  27. pass
  28. patch.remove('test-remove', test_function)
  29. self.assertEqual(len(patch._actions), 1)
  30. self.assertEqual(patch._actions[0]['op'], 'remove')
  31. self.assertEqual(patch._actions[0]['path'], 'test-remove')
  32. self.assertEqual(patch._actions[0]['handler'], test_function)
  33. def test_replace(self):
  34. """replace method adds function to patch object"""
  35. patch = ApiPatch()
  36. def test_function():
  37. pass
  38. patch.replace('test-replace', test_function)
  39. self.assertEqual(len(patch._actions), 1)
  40. self.assertEqual(patch._actions[0]['op'], 'replace')
  41. self.assertEqual(patch._actions[0]['path'], 'test-replace')
  42. self.assertEqual(patch._actions[0]['handler'], test_function)
  43. def test_validate_action(self):
  44. """validate_action method validates action dict"""
  45. patch = ApiPatch()
  46. VALID_ACTIONS = (
  47. {'op': 'add', 'path': 'test', 'value': 42},
  48. {'op': 'remove', 'path': 'other-test', 'value': 'Lorem'},
  49. {'op': 'replace', 'path': 'false-test', 'value': None},
  50. )
  51. for action in VALID_ACTIONS:
  52. patch.validate_action(action)
  53. # undefined op
  54. UNSUPPORTED_ACTIONS = (
  55. {},
  56. {'op': ''},
  57. {'no': 'op'},
  58. )
  59. for action in UNSUPPORTED_ACTIONS:
  60. try:
  61. patch.validate_action(action)
  62. except InvalidAction as e:
  63. self.assertEqual(e.args[0], u"undefined op")
  64. # unsupported op
  65. try:
  66. patch.validate_action({'op': 'nope'})
  67. except InvalidAction as e:
  68. self.assertEqual(e.args[0], u'"nope" op is unsupported')
  69. # op lacking patch
  70. try:
  71. patch.validate_action({'op': 'add'})
  72. except InvalidAction as e:
  73. self.assertEqual(e.args[0], u'"add" op has to specify path')
  74. # op lacking value
  75. try:
  76. patch.validate_action({'op': 'add', 'path': 'yolo'})
  77. except InvalidAction as e:
  78. self.assertEqual(e.args[0], u'"add" op has to specify value')
  79. # empty value is allowed
  80. try:
  81. patch.validate_action({'op': 'add', 'path': 'yolo', 'value': ''})
  82. except InvalidAction as e:
  83. self.assertEqual(e.args[0], u'"add" op has to specify value')
  84. def test_dispatch_action(self):
  85. """dispatch_action calls specified actions"""
  86. patch = ApiPatch()
  87. mock_target = MockObject(13)
  88. def action_a(request, target, value):
  89. self.assertEqual(request, 'request')
  90. self.assertEqual(target, mock_target)
  91. return {'a': value * 2, 'b': 111}
  92. patch.replace('abc', action_a)
  93. def action_b(request, target, value):
  94. self.assertEqual(request, 'request')
  95. self.assertEqual(target, mock_target)
  96. return {'b': value * 10}
  97. patch.replace('abc', action_b)
  98. def action_fail(request, target, value):
  99. self.fail("unrequired action was called")
  100. patch.add('c', action_fail)
  101. patch.remove('c', action_fail)
  102. patch.replace('c', action_fail)
  103. patch_dict = {
  104. 'id': 123
  105. }
  106. patch.dispatch_action(patch_dict, 'request', mock_target, {
  107. 'op': 'replace',
  108. 'path': 'abc',
  109. 'value': 5,
  110. })
  111. self.assertEqual(len(patch_dict), 3)
  112. self.assertEqual(patch_dict['id'], 123)
  113. self.assertEqual(patch_dict['a'], 10)
  114. self.assertEqual(patch_dict['b'], 50)
  115. def test_dispatch(self):
  116. """dispatch calls actions and returns response"""
  117. patch = ApiPatch()
  118. def action_error(request, target, value):
  119. if value == '404':
  120. raise Http404()
  121. if value == 'perm':
  122. raise PermissionDenied("yo ain't doing that!")
  123. patch.replace('error', action_error)
  124. def action_mutate(request, target, value):
  125. return {'value': value * 2}
  126. patch.replace('mutate', action_mutate)
  127. # dispatch requires list as an argument
  128. response = patch.dispatch(MockRequest({}), {})
  129. self.assertEqual(response.status_code, 400)
  130. self.assertEqual(
  131. response.data['detail'],
  132. "PATCH request should be list of operations")
  133. # valid dispatch
  134. response = patch.dispatch(MockRequest([
  135. {'op': 'replace', 'path': 'mutate', 'value': 2},
  136. {'op': 'replace', 'path': 'mutate', 'value': 6},
  137. {'op': 'replace', 'path': 'mutate', 'value': 7},
  138. ]), MockObject(13))
  139. self.assertEqual(response.status_code, 200)
  140. self.assertEqual(len(response.data['detail']), 3)
  141. self.assertEqual(response.data['detail'][0], 'ok')
  142. self.assertEqual(response.data['detail'][1], 'ok')
  143. self.assertEqual(response.data['detail'][2], 'ok')
  144. self.assertEqual(response.data['id'], 13)
  145. self.assertEqual(response.data['value'], 14)
  146. # invalid action in dispatch
  147. response = patch.dispatch(MockRequest([
  148. {'op': 'replace', 'path': 'mutate', 'value': 2},
  149. {'op': 'replace', 'path': 'mutate', 'value': 6},
  150. {'op': 'replace'},
  151. {'op': 'replace', 'path': 'mutate', 'value': 7},
  152. ]), MockObject(13))
  153. self.assertEqual(response.status_code, 400)
  154. self.assertEqual(len(response.data['detail']), 3)
  155. self.assertEqual(response.data['detail'][0], 'ok')
  156. self.assertEqual(response.data['detail'][1], 'ok')
  157. self.assertEqual(
  158. response.data['detail'][2], '"replace" op has to specify path')
  159. self.assertEqual(response.data['id'], 13)
  160. self.assertEqual(response.data['value'], 12)
  161. # action in dispatch raised 404
  162. response = patch.dispatch(MockRequest([
  163. {'op': 'replace', 'path': 'mutate', 'value': 2},
  164. {'op': 'replace', 'path': 'error', 'value': '404'},
  165. {'op': 'replace', 'path': 'mutate', 'value': 6},
  166. {'op': 'replace', 'path': 'mutate', 'value': 7},
  167. ]), MockObject(13))
  168. self.assertEqual(response.status_code, 400)
  169. self.assertEqual(len(response.data['detail']), 2)
  170. self.assertEqual(response.data['detail'][0], 'ok')
  171. self.assertEqual(
  172. response.data['detail'][1], "NOT FOUND")
  173. self.assertEqual(response.data['id'], 13)
  174. self.assertEqual(response.data['value'], 4)
  175. # action in dispatch raised perm denied
  176. response = patch.dispatch(MockRequest([
  177. {'op': 'replace', 'path': 'mutate', 'value': 2},
  178. {'op': 'replace', 'path': 'mutate', 'value': 6},
  179. {'op': 'replace', 'path': 'mutate', 'value': 9},
  180. {'op': 'replace', 'path': 'error', 'value': 'perm'},
  181. ]), MockObject(13))
  182. self.assertEqual(response.status_code, 400)
  183. self.assertEqual(len(response.data['detail']), 4)
  184. self.assertEqual(response.data['detail'][0], 'ok')
  185. self.assertEqual(response.data['detail'][1], 'ok')
  186. self.assertEqual(response.data['detail'][2], 'ok')
  187. self.assertEqual(
  188. response.data['detail'][3], "yo ain't doing that!")
  189. self.assertEqual(response.data['id'], 13)
  190. self.assertEqual(response.data['value'], 18)