from django.core.exceptions import PermissionDenied from django.http import Http404 from django.test import TestCase from misago.core.apipatch import ApiPatch, InvalidAction class MockRequest(object): def __init__(self, data=None): self.data = data class MockObject(object): def __init__(self, pk): self.id = pk self.pk = pk class ApiPatchTests(TestCase): def test_add(self): """add method adds function to patch object""" patch = ApiPatch() def mock_function(): pass patch.add('test-add', mock_function) self.assertEqual(len(patch._actions), 1) self.assertEqual(patch._actions[0]['op'], 'add') self.assertEqual(patch._actions[0]['path'], 'test-add') self.assertEqual(patch._actions[0]['handler'], mock_function) def test_remove(self): """remove method adds function to patch object""" patch = ApiPatch() def mock_function(): pass patch.remove('test-remove', mock_function) self.assertEqual(len(patch._actions), 1) self.assertEqual(patch._actions[0]['op'], 'remove') self.assertEqual(patch._actions[0]['path'], 'test-remove') self.assertEqual(patch._actions[0]['handler'], mock_function) def test_replace(self): """replace method adds function to patch object""" patch = ApiPatch() def mock_function(): pass patch.replace('test-replace', mock_function) self.assertEqual(len(patch._actions), 1) self.assertEqual(patch._actions[0]['op'], 'replace') self.assertEqual(patch._actions[0]['path'], 'test-replace') self.assertEqual(patch._actions[0]['handler'], mock_function) def test_validate_action(self): """validate_action method validates action dict""" patch = ApiPatch() VALID_ACTIONS = [ { 'op': 'add', 'path': 'test', 'value': 42 }, { 'op': 'remove', 'path': 'other-test', 'value': 'Lorem' }, { 'op': 'replace', 'path': 'false-test', 'value': None }, ] for action in VALID_ACTIONS: patch.validate_action(action) # undefined op UNSUPPORTED_ACTIONS = ({}, {'op': ''}, {'no': 'op'}, ) for action in UNSUPPORTED_ACTIONS: try: patch.validate_action(action) except InvalidAction as e: self.assertEqual(e.args[0], u"undefined op") # unsupported op try: patch.validate_action({'op': 'nope'}) except InvalidAction as e: self.assertEqual(e.args[0], u'"nope" op is unsupported') # op lacking patch try: patch.validate_action({'op': 'add'}) except InvalidAction as e: self.assertEqual(e.args[0], u'"add" op has to specify path') # op lacking value try: patch.validate_action({ 'op': 'add', 'path': 'yolo', }) except InvalidAction as e: self.assertEqual(e.args[0], u'"add" op has to specify value') # empty value is allowed try: patch.validate_action({ 'op': 'add', 'path': 'yolo', 'value': '', }) except InvalidAction as e: self.assertEqual(e.args[0], u'"add" op has to specify value') def test_dispatch_action(self): """dispatch_action calls specified actions""" patch = ApiPatch() mock_target = MockObject(13) def action_a(request, target, value): self.assertEqual(request, 'request') self.assertEqual(target, mock_target) return {'a': value * 2, 'b': 111} patch.replace('abc', action_a) def action_b(request, target, value): self.assertEqual(request, 'request') self.assertEqual(target, mock_target) return {'b': value * 10} patch.replace('abc', action_b) def action_fail(request, target, value): self.fail("unrequired action was called") patch.add('c', action_fail) patch.remove('c', action_fail) patch.replace('c', action_fail) patch_dict = {'id': 123} patch.dispatch_action( patch_dict, 'request', mock_target, { 'op': 'replace', 'path': 'abc', 'value': 5, } ) self.assertEqual(len(patch_dict), 3) self.assertEqual(patch_dict['id'], 123) self.assertEqual(patch_dict['a'], 10) self.assertEqual(patch_dict['b'], 50) def test_dispatch(self): """dispatch calls actions and returns response""" patch = ApiPatch() def action_error(request, target, value): if value == '404': raise Http404() if value == 'perm': raise PermissionDenied("yo ain't doing that!") patch.replace('error', action_error) def action_mutate(request, target, value): return {'value': value * 2} patch.replace('mutate', action_mutate) # dispatch requires list as an argument response = patch.dispatch(MockRequest({}), {}) self.assertEqual(response.status_code, 400) self.assertEqual(response.data['detail'], "PATCH request should be list of operations") # valid dispatch response = patch.dispatch( MockRequest([ { 'op': 'replace', 'path': 'mutate', 'value': 2, }, { 'op': 'replace', 'path': 'mutate', 'value': 6, }, { 'op': 'replace', 'path': 'mutate', 'value': 7, }, ]), MockObject(13) ) self.assertEqual(response.status_code, 200) self.assertEqual(len(response.data['detail']), 3) self.assertEqual(response.data['detail'][0], 'ok') self.assertEqual(response.data['detail'][1], 'ok') self.assertEqual(response.data['detail'][2], 'ok') self.assertEqual(response.data['id'], 13) self.assertEqual(response.data['value'], 14) # invalid action in dispatch response = patch.dispatch( MockRequest([ { 'op': 'replace', 'path': 'mutate', 'value': 2, }, { 'op': 'replace', 'path': 'mutate', 'value': 6, }, { 'op': 'replace', }, { 'op': 'replace', 'path': 'mutate', 'value': 7, }, ]), MockObject(13) ) self.assertEqual(response.status_code, 400) self.assertEqual(len(response.data['detail']), 3) self.assertEqual(response.data['detail'][0], 'ok') self.assertEqual(response.data['detail'][1], 'ok') self.assertEqual(response.data['detail'][2], '"replace" op has to specify path') self.assertEqual(response.data['id'], 13) self.assertEqual(response.data['value'], 12) # action in dispatch raised 404 response = patch.dispatch( MockRequest([ { 'op': 'replace', 'path': 'mutate', 'value': 2, }, { 'op': 'replace', 'path': 'error', 'value': '404', }, { 'op': 'replace', 'path': 'mutate', 'value': 6, }, { 'op': 'replace', 'path': 'mutate', 'value': 7, }, ]), MockObject(13) ) self.assertEqual(response.status_code, 400) self.assertEqual(len(response.data['detail']), 2) self.assertEqual(response.data['detail'][0], 'ok') self.assertEqual(response.data['detail'][1], "NOT FOUND") self.assertEqual(response.data['id'], 13) self.assertEqual(response.data['value'], 4) # action in dispatch raised perm denied response = patch.dispatch( MockRequest([ { 'op': 'replace', 'path': 'mutate', 'value': 2, }, { 'op': 'replace', 'path': 'mutate', 'value': 6, }, { 'op': 'replace', 'path': 'mutate', 'value': 9, }, { 'op': 'replace', 'path': 'error', 'value': 'perm', }, ]), MockObject(13) ) self.assertEqual(response.status_code, 400) self.assertEqual(len(response.data['detail']), 4) self.assertEqual(response.data['detail'][0], 'ok') self.assertEqual(response.data['detail'][1], 'ok') self.assertEqual(response.data['detail'][2], 'ok') self.assertEqual(response.data['detail'][3], "yo ain't doing that!") self.assertEqual(response.data['id'], 13) self.assertEqual(response.data['value'], 18)