Fixing bugs and returning RestController to 100% test coverage
This commit is contained in:
@@ -7,11 +7,13 @@ from routing import iscontroller, lookup_controller
|
||||
|
||||
class RestController(object):
|
||||
|
||||
_custom_actions = {}
|
||||
|
||||
@expose()
|
||||
def _route(self, args):
|
||||
|
||||
# convention uses "_method" to handle browser-unsupported methods
|
||||
method = request.GET.get('_method', request.method).lower()
|
||||
method = request.params.get('_method', request.method).lower()
|
||||
|
||||
# make sure DELETE/PUT requests don't use GET
|
||||
if request.method == 'GET' and method in ('delete', 'put'):
|
||||
@@ -108,13 +110,13 @@ class RestController(object):
|
||||
return controller, remainder[:-1]
|
||||
|
||||
# check for custom GET requests
|
||||
if method_name in getattr(self, '_custom_actions', []):
|
||||
controller = self._find_controller(method_name, 'get_%s' % method_name)
|
||||
if method.upper() in self._custom_actions.get(method_name, []):
|
||||
controller = self._find_controller('get_%s' % method_name, method_name)
|
||||
if controller:
|
||||
return controller, remainder[:-1]
|
||||
controller = getattr(self, remainder[0], None)
|
||||
if controller and not ismethod(controller):
|
||||
return controller, remainder[1:]
|
||||
return lookup_controller(controller, remainder[1:])
|
||||
|
||||
# finally, check for the regular get_one/get requests
|
||||
controller = self._find_controller('get_one', 'get')
|
||||
@@ -142,20 +144,23 @@ class RestController(object):
|
||||
abort(404)
|
||||
|
||||
def _handle_post(self, method, remainder):
|
||||
|
||||
# check for custom controllers or sub-controllers
|
||||
|
||||
# check for custom POST/PUT requests
|
||||
if remainder:
|
||||
controller = self._find_controller(remainder[0])
|
||||
if controller:
|
||||
return controller, remainder[1:]
|
||||
sub_controller = getattr(self, remainder[0], None)
|
||||
if sub_controller and not ismethod(sub_controller):
|
||||
return lookup_controller(sub_controller, remainder[1:])
|
||||
method_name = remainder[-1]
|
||||
if method.upper() in self._custom_actions.get(method_name, []):
|
||||
controller = self._find_controller('%s_%s' % (method, method_name), method_name)
|
||||
if controller:
|
||||
return controller, remainder[:-1]
|
||||
controller = getattr(self, remainder[0], None)
|
||||
if controller and not ismethod(controller):
|
||||
return lookup_controller(controller, remainder[1:])
|
||||
|
||||
# check for regular post/put requests
|
||||
# check for regular POST/PUT requests
|
||||
controller = self._find_controller(method)
|
||||
if controller:
|
||||
return controller, remainder
|
||||
|
||||
abort(404)
|
||||
|
||||
_handle_put = _handle_post
|
||||
|
||||
@@ -1,17 +1,30 @@
|
||||
from pecan import expose, make_app, request, response
|
||||
from pecan import abort, expose, make_app, request, response
|
||||
from pecan.rest import RestController
|
||||
from webob import exc
|
||||
from webtest import TestApp, AppError
|
||||
from py.test import raises
|
||||
from webtest import TestApp
|
||||
from json import dumps, loads
|
||||
|
||||
|
||||
class TestRestController(object):
|
||||
|
||||
def test_basic_rest(self):
|
||||
|
||||
class OthersController(object):
|
||||
|
||||
@expose()
|
||||
def index(self):
|
||||
return 'OTHERS'
|
||||
|
||||
@expose()
|
||||
def echo(self, value):
|
||||
return str(value)
|
||||
|
||||
class ThingsController(RestController):
|
||||
data = ['zero', 'one', 'two', 'three']
|
||||
|
||||
_custom_actions = {'count': ['GET'], 'length': ['GET', 'POST']}
|
||||
|
||||
others = OthersController()
|
||||
|
||||
@expose()
|
||||
def get_one(self, id):
|
||||
return self.data[int(id)]
|
||||
@@ -20,6 +33,17 @@ class TestRestController(object):
|
||||
def get_all(self):
|
||||
return dict(items=self.data)
|
||||
|
||||
@expose()
|
||||
def length(self, id, value=None):
|
||||
length = len(self.data[int(id)])
|
||||
if value:
|
||||
length += len(value)
|
||||
return str(length)
|
||||
|
||||
@expose()
|
||||
def get_count(self):
|
||||
return str(len(self.data))
|
||||
|
||||
@expose()
|
||||
def new(self):
|
||||
return 'NEW'
|
||||
@@ -47,6 +71,22 @@ class TestRestController(object):
|
||||
def delete(self, id):
|
||||
del self.data[int(id)]
|
||||
return 'DELETED'
|
||||
|
||||
@expose()
|
||||
def reset(self):
|
||||
return 'RESET'
|
||||
|
||||
@expose()
|
||||
def post_options(self):
|
||||
return 'OPTIONS'
|
||||
|
||||
@expose()
|
||||
def options(self):
|
||||
abort(500)
|
||||
|
||||
@expose()
|
||||
def other(self):
|
||||
abort(500)
|
||||
|
||||
class RootController(object):
|
||||
things = ThingsController()
|
||||
@@ -142,6 +182,73 @@ class TestRestController(object):
|
||||
r = app.get('/things')
|
||||
assert r.status_int == 200
|
||||
assert len(loads(r.body)['items']) == 3
|
||||
|
||||
# test "RESET" custom action
|
||||
r = app.request('/things', method='RESET')
|
||||
assert r.status_int == 200
|
||||
assert r.body == 'RESET'
|
||||
|
||||
# test "RESET" custom action with _method parameter
|
||||
r = app.get('/things?_method=RESET')
|
||||
assert r.status_int == 200
|
||||
assert r.body == 'RESET'
|
||||
|
||||
# test the "OPTIONS" custom action
|
||||
r = app.request('/things', method='OPTIONS')
|
||||
assert r.status_int == 200
|
||||
assert r.body == 'OPTIONS'
|
||||
|
||||
# test the "OPTIONS" custom action with the _method parameter
|
||||
r = app.post('/things', {'_method': 'OPTIONS'})
|
||||
assert r.status_int == 200
|
||||
assert r.body == 'OPTIONS'
|
||||
|
||||
# test the "other" custom action
|
||||
r = app.request('/things/other', method='MISC', status=405)
|
||||
assert r.status_int == 405
|
||||
|
||||
# test the "other" custom action with the _method parameter
|
||||
r = app.post('/things/other', {'_method': 'MISC'}, status=405)
|
||||
assert r.status_int == 405
|
||||
|
||||
# test the "others" custom action
|
||||
r = app.request('/things/others', method='MISC')
|
||||
assert r.status_int == 200
|
||||
assert r.body == 'OTHERS'
|
||||
|
||||
# test the "others" custom action with the _method parameter
|
||||
r = app.get('/things/others?_method=MISC')
|
||||
assert r.status_int == 200
|
||||
assert r.body == 'OTHERS'
|
||||
|
||||
# test an invalid custom action
|
||||
r = app.get('/things?_method=BAD', status=404)
|
||||
assert r.status_int == 404
|
||||
|
||||
# test custom "GET" request "count"
|
||||
r = app.get('/things/count')
|
||||
assert r.status_int == 200
|
||||
assert r.body == '3'
|
||||
|
||||
# test custom "GET" request "length"
|
||||
r = app.get('/things/1/length')
|
||||
assert r.status_int == 200
|
||||
assert r.body == str(len('one'))
|
||||
|
||||
# test custom "GET" request through subcontroller
|
||||
r = app.get('/things/others/echo?value=test')
|
||||
assert r.status_int == 200
|
||||
assert r.body == 'test'
|
||||
|
||||
# test custom "POST" request "length"
|
||||
r = app.post('/things/1/length', {'value':'test'})
|
||||
assert r.status_int == 200
|
||||
assert r.body == str(len('onetest'))
|
||||
|
||||
# test custom "POST" request through subcontroller
|
||||
r = app.post('/things/others/echo', {'value':'test'})
|
||||
assert r.status_int == 200
|
||||
assert r.body == 'test'
|
||||
|
||||
def test_nested_rest(self):
|
||||
|
||||
@@ -414,3 +521,159 @@ class TestRestController(object):
|
||||
r = app.get('/foos')
|
||||
assert r.status_int == 200
|
||||
assert len(loads(r.body)['items']) == 1
|
||||
|
||||
def test_bad_rest(self):
|
||||
|
||||
class ThingsController(RestController):
|
||||
pass
|
||||
|
||||
class RootController(object):
|
||||
things = ThingsController()
|
||||
|
||||
# create the app
|
||||
app = TestApp(make_app(RootController()))
|
||||
|
||||
# test get_all
|
||||
r = app.get('/things', status=404)
|
||||
assert r.status_int == 404
|
||||
|
||||
# test get_one
|
||||
r = app.get('/things/1', status=404)
|
||||
assert r.status_int == 404
|
||||
|
||||
# test post
|
||||
r = app.post('/things', {'value':'one'}, status=404)
|
||||
assert r.status_int == 404
|
||||
|
||||
# test edit
|
||||
r = app.get('/things/1/edit', status=404)
|
||||
assert r.status_int == 404
|
||||
|
||||
# test put
|
||||
r = app.put('/things/1', {'value':'ONE'}, status=404)
|
||||
|
||||
# test put with _method parameter and GET
|
||||
r = app.get('/things/1?_method=put', {'value':'ONE!'}, status=405)
|
||||
assert r.status_int == 405
|
||||
|
||||
# test put with _method parameter and POST
|
||||
r = app.post('/things/1?_method=put', {'value':'ONE!'}, status=404)
|
||||
assert r.status_int == 404
|
||||
|
||||
# test get delete
|
||||
r = app.get('/things/1/delete', status=404)
|
||||
assert r.status_int == 404
|
||||
|
||||
# test delete
|
||||
r = app.delete('/things/1', status=404)
|
||||
assert r.status_int == 404
|
||||
|
||||
# test delete with _method parameter and GET
|
||||
r = app.get('/things/1?_method=DELETE', status=405)
|
||||
assert r.status_int == 405
|
||||
|
||||
# test delete with _method parameter and POST
|
||||
r = app.post('/things/1?_method=DELETE', status=404)
|
||||
assert r.status_int == 404
|
||||
|
||||
# test "RESET" custom action
|
||||
r = app.request('/things', method='RESET', status=404)
|
||||
assert r.status_int == 404
|
||||
|
||||
def test_custom_delete(self):
|
||||
|
||||
class OthersController(object):
|
||||
|
||||
@expose()
|
||||
def index(self):
|
||||
return 'DELETE'
|
||||
|
||||
@expose()
|
||||
def reset(self, id):
|
||||
return str(id)
|
||||
|
||||
class ThingsController(RestController):
|
||||
|
||||
others = OthersController()
|
||||
|
||||
@expose()
|
||||
def delete_fail(self):
|
||||
abort(500)
|
||||
|
||||
class RootController(object):
|
||||
things = ThingsController()
|
||||
|
||||
# create the app
|
||||
app = TestApp(make_app(RootController()))
|
||||
|
||||
# test bad delete
|
||||
r = app.delete('/things/delete_fail', status=405)
|
||||
assert r.status_int == 405
|
||||
|
||||
# test bad delete with _method parameter and GET
|
||||
r = app.get('/things/delete_fail?_method=delete', status=405)
|
||||
assert r.status_int == 405
|
||||
|
||||
# test bad delete with _method parameter and POST
|
||||
r = app.post('/things/delete_fail', {'_method':'delete'}, status=405)
|
||||
assert r.status_int == 405
|
||||
|
||||
# test custom delete without ID
|
||||
r = app.delete('/things/others')
|
||||
assert r.status_int == 200
|
||||
assert r.body == 'DELETE'
|
||||
|
||||
# test custom delete without ID with _method parameter and GET
|
||||
r = app.get('/things/others?_method=delete', status=405)
|
||||
assert r.status_int == 405
|
||||
|
||||
# test custom delete without ID with _method parameter and POST
|
||||
r = app.post('/things/others', {'_method':'delete'})
|
||||
assert r.status_int == 200
|
||||
assert r.body == 'DELETE'
|
||||
|
||||
# test custom delete with ID
|
||||
r = app.delete('/things/others/reset/1')
|
||||
assert r.status_int == 200
|
||||
assert r.body == '1'
|
||||
|
||||
# test custom delete with ID with _method parameter and GET
|
||||
r = app.get('/things/others/reset/1?_method=delete', status=405)
|
||||
assert r.status_int == 405
|
||||
|
||||
# test custom delete with ID with _method parameter and POST
|
||||
r = app.post('/things/others/reset/1', {'_method':'delete'})
|
||||
assert r.status_int == 200
|
||||
assert r.body == '1'
|
||||
|
||||
def test_get_with_var_args(self):
|
||||
|
||||
class OthersController(object):
|
||||
|
||||
@expose()
|
||||
def index(self, one, two, three):
|
||||
return 'NESTED: %s, %s, %s' % (one, two, three)
|
||||
|
||||
class ThingsController(RestController):
|
||||
|
||||
others = OthersController()
|
||||
|
||||
@expose()
|
||||
def get_one(self, *args):
|
||||
return ', '.join(args)
|
||||
|
||||
class RootController(object):
|
||||
things = ThingsController()
|
||||
|
||||
# create the app
|
||||
app = TestApp(make_app(RootController()))
|
||||
|
||||
# test get request
|
||||
r = app.get('/things/one/two/three')
|
||||
assert r.status_int == 200
|
||||
assert r.body == 'one, two, three'
|
||||
|
||||
# test nested get request
|
||||
r = app.get('/things/one/two/three/others')
|
||||
assert r.status_int == 200
|
||||
assert r.body == 'NESTED: one, two, three'
|
||||
|
||||
Reference in New Issue
Block a user