test_apipatch.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  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 mock_function():
  17. pass
  18. patch.add('test-add', mock_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'], mock_function)
  23. def test_remove(self):
  24. """remove method adds function to patch object"""
  25. patch = ApiPatch()
  26. def mock_function():
  27. pass
  28. patch.remove('test-remove', mock_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'], mock_function)
  33. def test_replace(self):
  34. """replace method adds function to patch object"""
  35. patch = ApiPatch()
  36. def mock_function():
  37. pass
  38. patch.replace('test-replace', mock_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'], mock_function)
  43. def test_validate_action(self):
  44. """validate_action method validates action dict"""
  45. patch = ApiPatch()
  46. VALID_ACTIONS = ({
  47. 'op': 'add',
  48. 'path': 'test',
  49. 'value': 42
  50. }, {
  51. 'op': 'remove',
  52. 'path': 'other-test',
  53. 'value': 'Lorem'
  54. }, {
  55. 'op': 'replace',
  56. 'path': 'false-test',
  57. 'value': None
  58. }, )
  59. for action in VALID_ACTIONS:
  60. patch.validate_action(action)
  61. # undefined op
  62. UNSUPPORTED_ACTIONS = ({}, {'op': ''}, {'no': 'op'}, )
  63. for action in UNSUPPORTED_ACTIONS:
  64. try:
  65. patch.validate_action(action)
  66. except InvalidAction as e:
  67. self.assertEqual(e.args[0], u"undefined op")
  68. # unsupported op
  69. try:
  70. patch.validate_action({'op': 'nope'})
  71. except InvalidAction as e:
  72. self.assertEqual(e.args[0], u'"nope" op is unsupported')
  73. # op lacking patch
  74. try:
  75. patch.validate_action({'op': 'add'})
  76. except InvalidAction as e:
  77. self.assertEqual(e.args[0], u'"add" op has to specify path')
  78. # op lacking value
  79. try:
  80. patch.validate_action({'op': 'add', 'path': 'yolo'})
  81. except InvalidAction as e:
  82. self.assertEqual(e.args[0], u'"add" op has to specify value')
  83. # empty value is allowed
  84. try:
  85. patch.validate_action({'op': 'add', 'path': 'yolo', 'value': ''})
  86. except InvalidAction as e:
  87. self.assertEqual(e.args[0], u'"add" op has to specify value')
  88. def test_dispatch_action(self):
  89. """dispatch_action calls specified actions"""
  90. patch = ApiPatch()
  91. mock_target = MockObject(13)
  92. def action_a(request, target, value):
  93. self.assertEqual(request, 'request')
  94. self.assertEqual(target, mock_target)
  95. return {'a': value * 2, 'b': 111}
  96. patch.replace('abc', action_a)
  97. def action_b(request, target, value):
  98. self.assertEqual(request, 'request')
  99. self.assertEqual(target, mock_target)
  100. return {'b': value * 10}
  101. patch.replace('abc', action_b)
  102. def action_fail(request, target, value):
  103. self.fail("unrequired action was called")
  104. patch.add('c', action_fail)
  105. patch.remove('c', action_fail)
  106. patch.replace('c', action_fail)
  107. patch_dict = {'id': 123}
  108. patch.dispatch_action(
  109. patch_dict, 'request', mock_target, {
  110. 'op': 'replace',
  111. 'path': 'abc',
  112. 'value': 5,
  113. }
  114. )
  115. self.assertEqual(len(patch_dict), 3)
  116. self.assertEqual(patch_dict['id'], 123)
  117. self.assertEqual(patch_dict['a'], 10)
  118. self.assertEqual(patch_dict['b'], 50)
  119. def test_dispatch(self):
  120. """dispatch calls actions and returns response"""
  121. patch = ApiPatch()
  122. def action_error(request, target, value):
  123. if value == '404':
  124. raise Http404()
  125. if value == 'perm':
  126. raise PermissionDenied("yo ain't doing that!")
  127. patch.replace('error', action_error)
  128. def action_mutate(request, target, value):
  129. return {'value': value * 2}
  130. patch.replace('mutate', action_mutate)
  131. # dispatch requires list as an argument
  132. response = patch.dispatch(MockRequest({}), {})
  133. self.assertEqual(response.status_code, 400)
  134. self.assertEqual(response.data['detail'], "PATCH request should be list of operations")
  135. # valid dispatch
  136. response = patch.dispatch(
  137. MockRequest([
  138. {
  139. 'op': 'replace',
  140. 'path': 'mutate',
  141. 'value': 2
  142. },
  143. {
  144. 'op': 'replace',
  145. 'path': 'mutate',
  146. 'value': 6
  147. },
  148. {
  149. 'op': 'replace',
  150. 'path': 'mutate',
  151. 'value': 7
  152. },
  153. ]), MockObject(13)
  154. )
  155. self.assertEqual(response.status_code, 200)
  156. self.assertEqual(len(response.data['detail']), 3)
  157. self.assertEqual(response.data['detail'][0], 'ok')
  158. self.assertEqual(response.data['detail'][1], 'ok')
  159. self.assertEqual(response.data['detail'][2], 'ok')
  160. self.assertEqual(response.data['id'], 13)
  161. self.assertEqual(response.data['value'], 14)
  162. # invalid action in dispatch
  163. response = patch.dispatch(
  164. MockRequest([
  165. {
  166. 'op': 'replace',
  167. 'path': 'mutate',
  168. 'value': 2
  169. },
  170. {
  171. 'op': 'replace',
  172. 'path': 'mutate',
  173. 'value': 6
  174. },
  175. {
  176. 'op': 'replace'
  177. },
  178. {
  179. 'op': 'replace',
  180. 'path': 'mutate',
  181. 'value': 7
  182. },
  183. ]), MockObject(13)
  184. )
  185. self.assertEqual(response.status_code, 400)
  186. self.assertEqual(len(response.data['detail']), 3)
  187. self.assertEqual(response.data['detail'][0], 'ok')
  188. self.assertEqual(response.data['detail'][1], 'ok')
  189. self.assertEqual(response.data['detail'][2], '"replace" op has to specify path')
  190. self.assertEqual(response.data['id'], 13)
  191. self.assertEqual(response.data['value'], 12)
  192. # action in dispatch raised 404
  193. response = patch.dispatch(
  194. MockRequest([
  195. {
  196. 'op': 'replace',
  197. 'path': 'mutate',
  198. 'value': 2
  199. },
  200. {
  201. 'op': 'replace',
  202. 'path': 'error',
  203. 'value': '404'
  204. },
  205. {
  206. 'op': 'replace',
  207. 'path': 'mutate',
  208. 'value': 6
  209. },
  210. {
  211. 'op': 'replace',
  212. 'path': 'mutate',
  213. 'value': 7
  214. },
  215. ]), MockObject(13)
  216. )
  217. self.assertEqual(response.status_code, 400)
  218. self.assertEqual(len(response.data['detail']), 2)
  219. self.assertEqual(response.data['detail'][0], 'ok')
  220. self.assertEqual(response.data['detail'][1], "NOT FOUND")
  221. self.assertEqual(response.data['id'], 13)
  222. self.assertEqual(response.data['value'], 4)
  223. # action in dispatch raised perm denied
  224. response = patch.dispatch(
  225. MockRequest([
  226. {
  227. 'op': 'replace',
  228. 'path': 'mutate',
  229. 'value': 2
  230. },
  231. {
  232. 'op': 'replace',
  233. 'path': 'mutate',
  234. 'value': 6
  235. },
  236. {
  237. 'op': 'replace',
  238. 'path': 'mutate',
  239. 'value': 9
  240. },
  241. {
  242. 'op': 'replace',
  243. 'path': 'error',
  244. 'value': 'perm'
  245. },
  246. ]), MockObject(13)
  247. )
  248. self.assertEqual(response.status_code, 400)
  249. self.assertEqual(len(response.data['detail']), 4)
  250. self.assertEqual(response.data['detail'][0], 'ok')
  251. self.assertEqual(response.data['detail'][1], 'ok')
  252. self.assertEqual(response.data['detail'][2], 'ok')
  253. self.assertEqual(response.data['detail'][3], "yo ain't doing that!")
  254. self.assertEqual(response.data['id'], 13)
  255. self.assertEqual(response.data['value'], 18)