1557 lines
43 KiB
Python
1557 lines
43 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
import struct
|
|
import sys
|
|
import warnings
|
|
|
|
if sys.version_info < (2, 7):
|
|
import unittest2 as unittest # pragma: nocover
|
|
else:
|
|
import unittest # pragma: nocover
|
|
|
|
try:
|
|
from simplejson import dumps, loads
|
|
except:
|
|
from json import dumps, loads # noqa
|
|
|
|
from six import b as b_, PY3
|
|
from webtest import TestApp
|
|
|
|
from pecan import abort, expose, make_app, response, redirect
|
|
from pecan.rest import RestController
|
|
from pecan.tests import PecanTestCase
|
|
|
|
|
|
class TestRestController(PecanTestCase):
|
|
|
|
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)]
|
|
|
|
@expose('json')
|
|
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'
|
|
|
|
@expose()
|
|
def post(self, value):
|
|
self.data.append(value)
|
|
response.status = 302
|
|
return 'CREATED'
|
|
|
|
@expose()
|
|
def edit(self, id):
|
|
return 'EDIT %s' % self.data[int(id)]
|
|
|
|
@expose()
|
|
def put(self, id, value):
|
|
self.data[int(id)] = value
|
|
return 'UPDATED'
|
|
|
|
@expose()
|
|
def get_delete(self, id):
|
|
return 'DELETE %s' % self.data[int(id)]
|
|
|
|
@expose()
|
|
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()
|
|
|
|
# create the app
|
|
app = TestApp(make_app(RootController()))
|
|
|
|
# test get_all
|
|
r = app.get('/things')
|
|
assert r.status_int == 200
|
|
assert r.body == b_(dumps(dict(items=ThingsController.data)))
|
|
|
|
# test get_one
|
|
for i, value in enumerate(ThingsController.data):
|
|
r = app.get('/things/%d' % i)
|
|
assert r.status_int == 200
|
|
assert r.body == b_(value)
|
|
|
|
# test post
|
|
r = app.post('/things', {'value': 'four'})
|
|
assert r.status_int == 302
|
|
assert r.body == b_('CREATED')
|
|
|
|
# make sure it works
|
|
r = app.get('/things/4')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('four')
|
|
|
|
# test edit
|
|
r = app.get('/things/3/edit')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('EDIT three')
|
|
|
|
# test put
|
|
r = app.put('/things/4', {'value': 'FOUR'})
|
|
assert r.status_int == 200
|
|
assert r.body == b_('UPDATED')
|
|
|
|
# make sure it works
|
|
r = app.get('/things/4')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('FOUR')
|
|
|
|
# test put with _method parameter and GET
|
|
r = app.get('/things/4?_method=put', {'value': 'FOUR!'}, status=405)
|
|
assert r.status_int == 405
|
|
|
|
# make sure it works
|
|
r = app.get('/things/4')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('FOUR')
|
|
|
|
# test put with _method parameter and POST
|
|
r = app.post('/things/4?_method=put', {'value': 'FOUR!'})
|
|
assert r.status_int == 200
|
|
assert r.body == b_('UPDATED')
|
|
|
|
# make sure it works
|
|
r = app.get('/things/4')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('FOUR!')
|
|
|
|
# test get delete
|
|
r = app.get('/things/4/delete')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('DELETE FOUR!')
|
|
|
|
# test delete
|
|
r = app.delete('/things/4')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('DELETED')
|
|
|
|
# make sure it works
|
|
r = app.get('/things')
|
|
assert r.status_int == 200
|
|
assert len(loads(r.body.decode())['items']) == 4
|
|
|
|
# test delete with _method parameter and GET
|
|
r = app.get('/things/3?_method=DELETE', status=405)
|
|
assert r.status_int == 405
|
|
|
|
# make sure it works
|
|
r = app.get('/things')
|
|
assert r.status_int == 200
|
|
assert len(loads(r.body.decode())['items']) == 4
|
|
|
|
# test delete with _method parameter and POST
|
|
r = app.post('/things/3?_method=DELETE')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('DELETED')
|
|
|
|
# make sure it works
|
|
r = app.get('/things')
|
|
assert r.status_int == 200
|
|
assert len(loads(r.body.decode())['items']) == 3
|
|
|
|
# test "RESET" custom action
|
|
r = app.request('/things', method='RESET')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('RESET')
|
|
|
|
# test "RESET" custom action with _method parameter
|
|
r = app.get('/things?_method=RESET')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('RESET')
|
|
|
|
# test the "OPTIONS" custom action
|
|
r = app.request('/things', method='OPTIONS')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('OPTIONS')
|
|
|
|
# test the "OPTIONS" custom action with the _method parameter
|
|
r = app.post('/things', {'_method': 'OPTIONS'})
|
|
assert r.status_int == 200
|
|
assert r.body == b_('OPTIONS')
|
|
|
|
# test the "other" custom action
|
|
with warnings.catch_warnings():
|
|
warnings.simplefilter("ignore")
|
|
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
|
|
with warnings.catch_warnings():
|
|
warnings.simplefilter("ignore")
|
|
r = app.request('/things/others/', method='MISC')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('OTHERS')
|
|
|
|
# test the "others" custom action missing trailing slash
|
|
with warnings.catch_warnings():
|
|
warnings.simplefilter("ignore")
|
|
r = app.request('/things/others', method='MISC', status=302)
|
|
assert r.status_int == 302
|
|
|
|
# test the "others" custom action with the _method parameter
|
|
r = app.get('/things/others/?_method=MISC')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('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 == b_('3')
|
|
|
|
# test custom "GET" request "length"
|
|
r = app.get('/things/1/length')
|
|
assert r.status_int == 200
|
|
assert r.body == b_(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 == b_('test')
|
|
|
|
# test custom "POST" request "length"
|
|
r = app.post('/things/1/length', {'value': 'test'})
|
|
assert r.status_int == 200
|
|
assert r.body == b_(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 == b_('test')
|
|
|
|
def test_getall_with_trailing_slash(self):
|
|
|
|
class ThingsController(RestController):
|
|
|
|
data = ['zero', 'one', 'two', 'three']
|
|
|
|
@expose('json')
|
|
def get_all(self):
|
|
return dict(items=self.data)
|
|
|
|
class RootController(object):
|
|
things = ThingsController()
|
|
|
|
# create the app
|
|
app = TestApp(make_app(RootController()))
|
|
|
|
# test get_all
|
|
r = app.get('/things/')
|
|
assert r.status_int == 200
|
|
assert r.body == b_(dumps(dict(items=ThingsController.data)))
|
|
|
|
def test_404_with_lookup(self):
|
|
|
|
class LookupController(RestController):
|
|
|
|
def __init__(self, _id):
|
|
self._id = _id
|
|
|
|
@expose()
|
|
def get_all(self):
|
|
return 'ID: %s' % self._id
|
|
|
|
class ThingsController(RestController):
|
|
|
|
@expose()
|
|
def _lookup(self, _id, *remainder):
|
|
return LookupController(_id), remainder
|
|
|
|
class RootController(object):
|
|
things = ThingsController()
|
|
|
|
# create the app
|
|
app = TestApp(make_app(RootController()))
|
|
|
|
# these should 404
|
|
for path in ('/things', '/things/'):
|
|
r = app.get(path, expect_errors=True)
|
|
assert r.status_int == 404
|
|
|
|
r = app.get('/things/foo')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('ID: foo')
|
|
|
|
def test_getall_with_lookup(self):
|
|
|
|
class LookupController(RestController):
|
|
|
|
def __init__(self, _id):
|
|
self._id = _id
|
|
|
|
@expose()
|
|
def get_all(self):
|
|
return 'ID: %s' % self._id
|
|
|
|
class ThingsController(RestController):
|
|
|
|
data = ['zero', 'one', 'two', 'three']
|
|
|
|
@expose()
|
|
def _lookup(self, _id, *remainder):
|
|
return LookupController(_id), remainder
|
|
|
|
@expose('json')
|
|
def get_all(self):
|
|
return dict(items=self.data)
|
|
|
|
class RootController(object):
|
|
things = ThingsController()
|
|
|
|
# create the app
|
|
app = TestApp(make_app(RootController()))
|
|
|
|
# test get_all
|
|
for path in ('/things', '/things/'):
|
|
r = app.get(path)
|
|
assert r.status_int == 200
|
|
assert r.body == b_(dumps(dict(items=ThingsController.data)))
|
|
|
|
r = app.get('/things/foo')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('ID: foo')
|
|
|
|
def test_simple_nested_rest(self):
|
|
|
|
class BarController(RestController):
|
|
|
|
@expose()
|
|
def post(self):
|
|
return "BAR-POST"
|
|
|
|
@expose()
|
|
def delete(self, id_):
|
|
return "BAR-%s" % id_
|
|
|
|
class FooController(RestController):
|
|
|
|
bar = BarController()
|
|
|
|
@expose()
|
|
def post(self):
|
|
return "FOO-POST"
|
|
|
|
@expose()
|
|
def delete(self, id_):
|
|
return "FOO-%s" % id_
|
|
|
|
class RootController(object):
|
|
foo = FooController()
|
|
|
|
# create the app
|
|
app = TestApp(make_app(RootController()))
|
|
|
|
r = app.post('/foo')
|
|
assert r.status_int == 200
|
|
assert r.body == b_("FOO-POST")
|
|
|
|
r = app.delete('/foo/1')
|
|
assert r.status_int == 200
|
|
assert r.body == b_("FOO-1")
|
|
|
|
r = app.post('/foo/bar')
|
|
assert r.status_int == 200
|
|
assert r.body == b_("BAR-POST")
|
|
|
|
r = app.delete('/foo/bar/2')
|
|
assert r.status_int == 200
|
|
assert r.body == b_("BAR-2")
|
|
|
|
def test_complicated_nested_rest(self):
|
|
|
|
class BarsController(RestController):
|
|
|
|
data = [['zero-zero', 'zero-one'], ['one-zero', 'one-one']]
|
|
|
|
@expose()
|
|
def get_one(self, foo_id, id):
|
|
return self.data[int(foo_id)][int(id)]
|
|
|
|
@expose('json')
|
|
def get_all(self, foo_id):
|
|
return dict(items=self.data[int(foo_id)])
|
|
|
|
@expose()
|
|
def new(self, foo_id):
|
|
return 'NEW FOR %s' % foo_id
|
|
|
|
@expose()
|
|
def post(self, foo_id, value):
|
|
foo_id = int(foo_id)
|
|
if len(self.data) < foo_id + 1:
|
|
self.data.extend([[]] * (foo_id - len(self.data) + 1))
|
|
self.data[foo_id].append(value)
|
|
response.status = 302
|
|
return 'CREATED FOR %s' % foo_id
|
|
|
|
@expose()
|
|
def edit(self, foo_id, id):
|
|
return 'EDIT %s' % self.data[int(foo_id)][int(id)]
|
|
|
|
@expose()
|
|
def put(self, foo_id, id, value):
|
|
self.data[int(foo_id)][int(id)] = value
|
|
return 'UPDATED'
|
|
|
|
@expose()
|
|
def get_delete(self, foo_id, id):
|
|
return 'DELETE %s' % self.data[int(foo_id)][int(id)]
|
|
|
|
@expose()
|
|
def delete(self, foo_id, id):
|
|
del self.data[int(foo_id)][int(id)]
|
|
return 'DELETED'
|
|
|
|
class FoosController(RestController):
|
|
|
|
data = ['zero', 'one']
|
|
|
|
bars = BarsController()
|
|
|
|
@expose()
|
|
def get_one(self, id):
|
|
return self.data[int(id)]
|
|
|
|
@expose('json')
|
|
def get_all(self):
|
|
return dict(items=self.data)
|
|
|
|
@expose()
|
|
def new(self):
|
|
return 'NEW'
|
|
|
|
@expose()
|
|
def edit(self, id):
|
|
return 'EDIT %s' % self.data[int(id)]
|
|
|
|
@expose()
|
|
def post(self, value):
|
|
self.data.append(value)
|
|
response.status = 302
|
|
return 'CREATED'
|
|
|
|
@expose()
|
|
def put(self, id, value):
|
|
self.data[int(id)] = value
|
|
return 'UPDATED'
|
|
|
|
@expose()
|
|
def get_delete(self, id):
|
|
return 'DELETE %s' % self.data[int(id)]
|
|
|
|
@expose()
|
|
def delete(self, id):
|
|
del self.data[int(id)]
|
|
return 'DELETED'
|
|
|
|
class RootController(object):
|
|
foos = FoosController()
|
|
|
|
# create the app
|
|
app = TestApp(make_app(RootController()))
|
|
|
|
# test get_all
|
|
r = app.get('/foos')
|
|
assert r.status_int == 200
|
|
assert r.body == b_(dumps(dict(items=FoosController.data)))
|
|
|
|
# test nested get_all
|
|
r = app.get('/foos/1/bars')
|
|
assert r.status_int == 200
|
|
assert r.body == b_(dumps(dict(items=BarsController.data[1])))
|
|
|
|
# test get_one
|
|
for i, value in enumerate(FoosController.data):
|
|
r = app.get('/foos/%d' % i)
|
|
assert r.status_int == 200
|
|
assert r.body == b_(value)
|
|
|
|
# test nested get_one
|
|
for i, value in enumerate(FoosController.data):
|
|
for j, value in enumerate(BarsController.data[i]):
|
|
r = app.get('/foos/%s/bars/%s' % (i, j))
|
|
assert r.status_int == 200
|
|
assert r.body == b_(value)
|
|
|
|
# test post
|
|
r = app.post('/foos', {'value': 'two'})
|
|
assert r.status_int == 302
|
|
assert r.body == b_('CREATED')
|
|
|
|
# make sure it works
|
|
r = app.get('/foos/2')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('two')
|
|
|
|
# test nested post
|
|
r = app.post('/foos/2/bars', {'value': 'two-zero'})
|
|
assert r.status_int == 302
|
|
assert r.body == b_('CREATED FOR 2')
|
|
|
|
# make sure it works
|
|
r = app.get('/foos/2/bars/0')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('two-zero')
|
|
|
|
# test edit
|
|
r = app.get('/foos/1/edit')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('EDIT one')
|
|
|
|
# test nested edit
|
|
r = app.get('/foos/1/bars/1/edit')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('EDIT one-one')
|
|
|
|
# test put
|
|
r = app.put('/foos/2', {'value': 'TWO'})
|
|
assert r.status_int == 200
|
|
assert r.body == b_('UPDATED')
|
|
|
|
# make sure it works
|
|
r = app.get('/foos/2')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('TWO')
|
|
|
|
# test nested put
|
|
r = app.put('/foos/2/bars/0', {'value': 'TWO-ZERO'})
|
|
assert r.status_int == 200
|
|
assert r.body == b_('UPDATED')
|
|
|
|
# make sure it works
|
|
r = app.get('/foos/2/bars/0')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('TWO-ZERO')
|
|
|
|
# test put with _method parameter and GET
|
|
r = app.get('/foos/2?_method=put', {'value': 'TWO!'}, status=405)
|
|
assert r.status_int == 405
|
|
|
|
# make sure it works
|
|
r = app.get('/foos/2')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('TWO')
|
|
|
|
# test nested put with _method parameter and GET
|
|
r = app.get(
|
|
'/foos/2/bars/0?_method=put',
|
|
{'value': 'ZERO-TWO!'}, status=405
|
|
)
|
|
assert r.status_int == 405
|
|
|
|
# make sure it works
|
|
r = app.get('/foos/2/bars/0')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('TWO-ZERO')
|
|
|
|
# test put with _method parameter and POST
|
|
r = app.post('/foos/2?_method=put', {'value': 'TWO!'})
|
|
assert r.status_int == 200
|
|
assert r.body == b_('UPDATED')
|
|
|
|
# make sure it works
|
|
r = app.get('/foos/2')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('TWO!')
|
|
|
|
# test nested put with _method parameter and POST
|
|
r = app.post('/foos/2/bars/0?_method=put', {'value': 'TWO-ZERO!'})
|
|
assert r.status_int == 200
|
|
assert r.body == b_('UPDATED')
|
|
|
|
# make sure it works
|
|
r = app.get('/foos/2/bars/0')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('TWO-ZERO!')
|
|
|
|
# test get delete
|
|
r = app.get('/foos/2/delete')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('DELETE TWO!')
|
|
|
|
# test nested get delete
|
|
r = app.get('/foos/2/bars/0/delete')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('DELETE TWO-ZERO!')
|
|
|
|
# test nested delete
|
|
r = app.delete('/foos/2/bars/0')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('DELETED')
|
|
|
|
# make sure it works
|
|
r = app.get('/foos/2/bars')
|
|
assert r.status_int == 200
|
|
assert len(loads(r.body.decode())['items']) == 0
|
|
|
|
# test delete
|
|
r = app.delete('/foos/2')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('DELETED')
|
|
|
|
# make sure it works
|
|
r = app.get('/foos')
|
|
assert r.status_int == 200
|
|
assert len(loads(r.body.decode())['items']) == 2
|
|
|
|
# test nested delete with _method parameter and GET
|
|
r = app.get('/foos/1/bars/1?_method=DELETE', status=405)
|
|
assert r.status_int == 405
|
|
|
|
# make sure it works
|
|
r = app.get('/foos/1/bars')
|
|
assert r.status_int == 200
|
|
assert len(loads(r.body.decode())['items']) == 2
|
|
|
|
# test delete with _method parameter and GET
|
|
r = app.get('/foos/1?_method=DELETE', status=405)
|
|
assert r.status_int == 405
|
|
|
|
# make sure it works
|
|
r = app.get('/foos')
|
|
assert r.status_int == 200
|
|
assert len(loads(r.body.decode())['items']) == 2
|
|
|
|
# test nested delete with _method parameter and POST
|
|
r = app.post('/foos/1/bars/1?_method=DELETE')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('DELETED')
|
|
|
|
# make sure it works
|
|
r = app.get('/foos/1/bars')
|
|
assert r.status_int == 200
|
|
assert len(loads(r.body.decode())['items']) == 1
|
|
|
|
# test delete with _method parameter and POST
|
|
r = app.post('/foos/1?_method=DELETE')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('DELETED')
|
|
|
|
# make sure it works
|
|
r = app.get('/foos')
|
|
assert r.status_int == 200
|
|
assert len(loads(r.body.decode())['items']) == 1
|
|
|
|
def test_nested_get_all(self):
|
|
|
|
class BarsController(RestController):
|
|
|
|
@expose()
|
|
def get_one(self, foo_id, id):
|
|
return '4'
|
|
|
|
@expose()
|
|
def get_all(self, foo_id):
|
|
return '3'
|
|
|
|
class FoosController(RestController):
|
|
|
|
bars = BarsController()
|
|
|
|
@expose()
|
|
def get_one(self, id):
|
|
return '2'
|
|
|
|
@expose()
|
|
def get_all(self):
|
|
return '1'
|
|
|
|
class RootController(object):
|
|
foos = FoosController()
|
|
|
|
# create the app
|
|
app = TestApp(make_app(RootController()))
|
|
|
|
r = app.get('/foos/')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('1')
|
|
|
|
r = app.get('/foos/1/')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('2')
|
|
|
|
r = app.get('/foos/1/bars/')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('3')
|
|
|
|
r = app.get('/foos/1/bars/2/')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('4')
|
|
|
|
r = app.get('/foos/bars/', status=404)
|
|
assert r.status_int == 404
|
|
|
|
r = app.get('/foos/bars/1', status=404)
|
|
assert r.status_int == 404
|
|
|
|
def test_nested_get_all_with_lookup(self):
|
|
|
|
class BarsController(RestController):
|
|
|
|
@expose()
|
|
def get_one(self, foo_id, id):
|
|
return '4'
|
|
|
|
@expose()
|
|
def get_all(self, foo_id):
|
|
return '3'
|
|
|
|
@expose('json')
|
|
def _lookup(self, id, *remainder):
|
|
redirect('/lookup-hit/')
|
|
|
|
class FoosController(RestController):
|
|
|
|
bars = BarsController()
|
|
|
|
@expose()
|
|
def get_one(self, id):
|
|
return '2'
|
|
|
|
@expose()
|
|
def get_all(self):
|
|
return '1'
|
|
|
|
class RootController(object):
|
|
foos = FoosController()
|
|
|
|
# create the app
|
|
app = TestApp(make_app(RootController()))
|
|
|
|
r = app.get('/foos/')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('1')
|
|
|
|
r = app.get('/foos/1/')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('2')
|
|
|
|
r = app.get('/foos/1/bars/')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('3')
|
|
|
|
r = app.get('/foos/1/bars/2/')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('4')
|
|
|
|
r = app.get('/foos/bars/')
|
|
assert r.status_int == 302
|
|
assert r.headers['Location'].endswith('/lookup-hit/')
|
|
|
|
r = app.get('/foos/bars/1')
|
|
assert r.status_int == 302
|
|
assert r.headers['Location'].endswith('/lookup-hit/')
|
|
|
|
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
|
|
with warnings.catch_warnings():
|
|
warnings.simplefilter("ignore")
|
|
r = app.request('/things', method='RESET', status=404)
|
|
assert r.status_int == 404
|
|
|
|
def test_nested_rest_with_missing_intermediate_id(self):
|
|
|
|
class BarsController(RestController):
|
|
|
|
data = [['zero-zero', 'zero-one'], ['one-zero', 'one-one']]
|
|
|
|
@expose('json')
|
|
def get_all(self, foo_id):
|
|
return dict(items=self.data[int(foo_id)])
|
|
|
|
class FoosController(RestController):
|
|
|
|
data = ['zero', 'one']
|
|
|
|
bars = BarsController()
|
|
|
|
@expose()
|
|
def get_one(self, id):
|
|
return self.data[int(id)]
|
|
|
|
@expose('json')
|
|
def get_all(self):
|
|
return dict(items=self.data)
|
|
|
|
class RootController(object):
|
|
foos = FoosController()
|
|
|
|
# create the app
|
|
app = TestApp(make_app(RootController()))
|
|
|
|
# test get_all
|
|
r = app.get('/foos')
|
|
self.assertEqual(r.status_int, 200)
|
|
self.assertEqual(r.body, b_(dumps(dict(items=FoosController.data))))
|
|
|
|
# test nested get_all
|
|
r = app.get('/foos/1/bars')
|
|
self.assertEqual(r.status_int, 200)
|
|
self.assertEqual(r.body, b_(dumps(dict(items=BarsController.data[1]))))
|
|
|
|
r = app.get('/foos/bars', expect_errors=True)
|
|
self.assertEqual(r.status_int, 404)
|
|
|
|
def test_custom_with_trailing_slash(self):
|
|
|
|
class CustomController(RestController):
|
|
|
|
_custom_actions = {
|
|
'detail': ['GET'],
|
|
'create': ['POST'],
|
|
'update': ['PUT'],
|
|
'remove': ['DELETE'],
|
|
}
|
|
|
|
@expose()
|
|
def detail(self):
|
|
return 'DETAIL'
|
|
|
|
@expose()
|
|
def create(self):
|
|
return 'CREATE'
|
|
|
|
@expose()
|
|
def update(self, id):
|
|
return id
|
|
|
|
@expose()
|
|
def remove(self, id):
|
|
return id
|
|
|
|
app = TestApp(make_app(CustomController()))
|
|
|
|
r = app.get('/detail')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('DETAIL')
|
|
|
|
r = app.get('/detail/')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('DETAIL')
|
|
|
|
r = app.post('/create')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('CREATE')
|
|
|
|
r = app.post('/create/')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('CREATE')
|
|
|
|
r = app.put('/update/123')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('123')
|
|
|
|
r = app.put('/update/123/')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('123')
|
|
|
|
r = app.delete('/remove/456')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('456')
|
|
|
|
r = app.delete('/remove/456/')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('456')
|
|
|
|
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 == b_('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 == b_('DELETE')
|
|
|
|
# test custom delete with ID
|
|
r = app.delete('/things/others/reset/1')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('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 == b_('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 == b_('one, two, three')
|
|
|
|
# test nested get request
|
|
r = app.get('/things/one/two/three/others/')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('NESTED: one, two, three')
|
|
|
|
def test_sub_nested_rest(self):
|
|
|
|
class BazsController(RestController):
|
|
|
|
data = [[['zero-zero-zero']]]
|
|
|
|
@expose()
|
|
def get_one(self, foo_id, bar_id, id):
|
|
return self.data[int(foo_id)][int(bar_id)][int(id)]
|
|
|
|
class BarsController(RestController):
|
|
|
|
data = [['zero-zero']]
|
|
|
|
bazs = BazsController()
|
|
|
|
@expose()
|
|
def get_one(self, foo_id, id):
|
|
return self.data[int(foo_id)][int(id)]
|
|
|
|
class FoosController(RestController):
|
|
|
|
data = ['zero']
|
|
|
|
bars = BarsController()
|
|
|
|
@expose()
|
|
def get_one(self, id):
|
|
return self.data[int(id)]
|
|
|
|
class RootController(object):
|
|
foos = FoosController()
|
|
|
|
# create the app
|
|
app = TestApp(make_app(RootController()))
|
|
|
|
# test sub-nested get_one
|
|
r = app.get('/foos/0/bars/0/bazs/0')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('zero-zero-zero')
|
|
|
|
def test_sub_nested_rest_with_overwrites(self):
|
|
|
|
class FinalController(object):
|
|
|
|
@expose()
|
|
def index(self):
|
|
return 'FINAL'
|
|
|
|
@expose()
|
|
def named(self):
|
|
return 'NAMED'
|
|
|
|
class BazsController(RestController):
|
|
|
|
data = [[['zero-zero-zero']]]
|
|
|
|
final = FinalController()
|
|
|
|
@expose()
|
|
def get_one(self, foo_id, bar_id, id):
|
|
return self.data[int(foo_id)][int(bar_id)][int(id)]
|
|
|
|
@expose()
|
|
def post(self):
|
|
return 'POST-GRAND-CHILD'
|
|
|
|
@expose()
|
|
def put(self, id):
|
|
return 'PUT-GRAND-CHILD'
|
|
|
|
class BarsController(RestController):
|
|
|
|
data = [['zero-zero']]
|
|
|
|
bazs = BazsController()
|
|
|
|
@expose()
|
|
def get_one(self, foo_id, id):
|
|
return self.data[int(foo_id)][int(id)]
|
|
|
|
@expose()
|
|
def post(self):
|
|
return 'POST-CHILD'
|
|
|
|
@expose()
|
|
def put(self, id):
|
|
return 'PUT-CHILD'
|
|
|
|
class FoosController(RestController):
|
|
|
|
data = ['zero']
|
|
|
|
bars = BarsController()
|
|
|
|
@expose()
|
|
def get_one(self, id):
|
|
return self.data[int(id)]
|
|
|
|
@expose()
|
|
def post(self):
|
|
return 'POST'
|
|
|
|
@expose()
|
|
def put(self, id):
|
|
return 'PUT'
|
|
|
|
class RootController(object):
|
|
foos = FoosController()
|
|
|
|
# create the app
|
|
app = TestApp(make_app(RootController()))
|
|
|
|
r = app.post('/foos')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('POST')
|
|
|
|
r = app.put('/foos/0')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('PUT')
|
|
|
|
r = app.post('/foos/bars')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('POST-CHILD')
|
|
|
|
r = app.put('/foos/bars/0')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('PUT-CHILD')
|
|
|
|
r = app.post('/foos/bars/bazs')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('POST-GRAND-CHILD')
|
|
|
|
r = app.put('/foos/bars/bazs/0')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('PUT-GRAND-CHILD')
|
|
|
|
r = app.get('/foos/bars/bazs/final/')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('FINAL')
|
|
|
|
r = app.get('/foos/bars/bazs/final/named')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('NAMED')
|
|
|
|
def test_post_with_kwargs_only(self):
|
|
|
|
class RootController(RestController):
|
|
|
|
@expose()
|
|
def get_all(self):
|
|
return 'INDEX'
|
|
|
|
@expose('json')
|
|
def post(self, **kw):
|
|
return kw
|
|
|
|
# create the app
|
|
app = TestApp(make_app(RootController()))
|
|
|
|
r = app.get('/')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('INDEX')
|
|
|
|
kwargs = {'foo': 'bar', 'spam': 'eggs'}
|
|
r = app.post('/', kwargs)
|
|
assert r.status_int == 200
|
|
assert r.namespace['foo'] == 'bar'
|
|
assert r.namespace['spam'] == 'eggs'
|
|
|
|
def test_nested_rest_with_lookup(self):
|
|
|
|
class SubController(RestController):
|
|
|
|
@expose()
|
|
def get_all(self):
|
|
return "SUB"
|
|
|
|
class FinalController(RestController):
|
|
|
|
def __init__(self, id_):
|
|
self.id_ = id_
|
|
|
|
@expose()
|
|
def get_all(self):
|
|
return "FINAL-%s" % self.id_
|
|
|
|
@expose()
|
|
def post(self):
|
|
return "POST-%s" % self.id_
|
|
|
|
class LookupController(RestController):
|
|
|
|
sub = SubController()
|
|
|
|
def __init__(self, id_):
|
|
self.id_ = id_
|
|
|
|
@expose()
|
|
def _lookup(self, id_, *remainder):
|
|
return FinalController(id_), remainder
|
|
|
|
@expose()
|
|
def get_all(self):
|
|
raise AssertionError("Never Reached")
|
|
|
|
@expose()
|
|
def post(self):
|
|
return "POST-LOOKUP-%s" % self.id_
|
|
|
|
@expose()
|
|
def put(self, id_):
|
|
return "PUT-LOOKUP-%s-%s" % (self.id_, id_)
|
|
|
|
@expose()
|
|
def delete(self, id_):
|
|
return "DELETE-LOOKUP-%s-%s" % (self.id_, id_)
|
|
|
|
class FooController(RestController):
|
|
|
|
@expose()
|
|
def _lookup(self, id_, *remainder):
|
|
return LookupController(id_), remainder
|
|
|
|
@expose()
|
|
def get_one(self, id_):
|
|
return "GET ONE"
|
|
|
|
@expose()
|
|
def get_all(self):
|
|
return "INDEX"
|
|
|
|
@expose()
|
|
def post(self):
|
|
return "POST"
|
|
|
|
@expose()
|
|
def put(self, id_):
|
|
return "PUT-%s" % id_
|
|
|
|
@expose()
|
|
def delete(self, id_):
|
|
return "DELETE-%s" % id_
|
|
|
|
class RootController(RestController):
|
|
foo = FooController()
|
|
|
|
app = TestApp(make_app(RootController()))
|
|
|
|
r = app.get('/foo')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('INDEX')
|
|
|
|
r = app.post('/foo')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('POST')
|
|
|
|
r = app.get('/foo/1')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('GET ONE')
|
|
|
|
r = app.post('/foo/1')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('POST-LOOKUP-1')
|
|
|
|
r = app.put('/foo/1')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('PUT-1')
|
|
|
|
r = app.delete('/foo/1')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('DELETE-1')
|
|
|
|
r = app.put('/foo/1/2')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('PUT-LOOKUP-1-2')
|
|
|
|
r = app.delete('/foo/1/2')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('DELETE-LOOKUP-1-2')
|
|
|
|
r = app.get('/foo/1/2')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('FINAL-2')
|
|
|
|
r = app.post('/foo/1/2')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('POST-2')
|
|
|
|
def test_nested_rest_with_default(self):
|
|
|
|
class FooController(RestController):
|
|
|
|
@expose()
|
|
def _default(self, *remainder):
|
|
return "DEFAULT %s" % remainder
|
|
|
|
class RootController(RestController):
|
|
foo = FooController()
|
|
|
|
app = TestApp(make_app(RootController()))
|
|
|
|
r = app.get('/foo/missing')
|
|
assert r.status_int == 200
|
|
assert r.body == b_("DEFAULT missing")
|
|
|
|
def test_rest_with_non_utf_8_body(self):
|
|
if PY3:
|
|
# webob+PY3 doesn't suffer from this bug; the POST parsing in PY3
|
|
# seems to more gracefully detect the bytestring
|
|
return
|
|
|
|
class FooController(RestController):
|
|
|
|
@expose()
|
|
def post(self):
|
|
return "POST"
|
|
|
|
class RootController(RestController):
|
|
foo = FooController()
|
|
|
|
app = TestApp(make_app(RootController()))
|
|
|
|
data = struct.pack('255h', *range(0, 255))
|
|
r = app.post('/foo/', data, expect_errors=True)
|
|
assert r.status_int == 400
|
|
|
|
def test_dynamic_rest_lookup(self):
|
|
class BarController(RestController):
|
|
@expose()
|
|
def get_all(self):
|
|
return "BAR"
|
|
|
|
@expose()
|
|
def put(self):
|
|
return "PUT_BAR"
|
|
|
|
@expose()
|
|
def delete(self):
|
|
return "DELETE_BAR"
|
|
|
|
class BarsController(RestController):
|
|
@expose()
|
|
def _lookup(self, id_, *remainder):
|
|
return BarController(), remainder
|
|
|
|
@expose()
|
|
def get_all(self):
|
|
return "BARS"
|
|
|
|
@expose()
|
|
def post(self):
|
|
return "POST_BARS"
|
|
|
|
class FooController(RestController):
|
|
bars = BarsController()
|
|
|
|
@expose()
|
|
def get_all(self):
|
|
return "FOO"
|
|
|
|
@expose()
|
|
def put(self):
|
|
return "PUT_FOO"
|
|
|
|
@expose()
|
|
def delete(self):
|
|
return "DELETE_FOO"
|
|
|
|
class FoosController(RestController):
|
|
@expose()
|
|
def _lookup(self, id_, *remainder):
|
|
return FooController(), remainder
|
|
|
|
@expose()
|
|
def get_all(self):
|
|
return "FOOS"
|
|
|
|
@expose()
|
|
def post(self):
|
|
return "POST_FOOS"
|
|
|
|
class RootController(RestController):
|
|
foos = FoosController()
|
|
|
|
app = TestApp(make_app(RootController()))
|
|
|
|
r = app.get('/foos')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('FOOS')
|
|
|
|
r = app.post('/foos')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('POST_FOOS')
|
|
|
|
r = app.get('/foos/foo')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('FOO')
|
|
|
|
r = app.put('/foos/foo')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('PUT_FOO')
|
|
|
|
r = app.delete('/foos/foo')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('DELETE_FOO')
|
|
|
|
r = app.get('/foos/foo/bars')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('BARS')
|
|
|
|
r = app.post('/foos/foo/bars')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('POST_BARS')
|
|
|
|
r = app.get('/foos/foo/bars/bar')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('BAR')
|
|
|
|
r = app.put('/foos/foo/bars/bar')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('PUT_BAR')
|
|
|
|
r = app.delete('/foos/foo/bars/bar')
|
|
assert r.status_int == 200
|
|
assert r.body == b_('DELETE_BAR')
|
|
|
|
def test_rest_with_utf8_uri(self):
|
|
|
|
class FooController(RestController):
|
|
key = chr(0x1F330) if PY3 else unichr(0x1F330)
|
|
data = {key: 'Success!'}
|
|
|
|
@expose()
|
|
def get_one(self, id_):
|
|
return self.data[id_]
|
|
|
|
@expose()
|
|
def get_all(self):
|
|
return "Hello, World!"
|
|
|
|
@expose()
|
|
def put(self, id_, value):
|
|
return self.data[id_]
|
|
|
|
@expose()
|
|
def delete(self, id_):
|
|
return self.data[id_]
|
|
|
|
class RootController(RestController):
|
|
foo = FooController()
|
|
|
|
app = TestApp(make_app(RootController()))
|
|
|
|
r = app.get('/foo/%F0%9F%8C%B0')
|
|
assert r.status_int == 200
|
|
assert r.body == b'Success!'
|
|
|
|
r = app.put('/foo/%F0%9F%8C%B0', {'value': 'pecans'})
|
|
assert r.status_int == 200
|
|
assert r.body == b'Success!'
|
|
|
|
r = app.delete('/foo/%F0%9F%8C%B0')
|
|
assert r.status_int == 200
|
|
assert r.body == b'Success!'
|
|
|
|
r = app.get('/foo/')
|
|
assert r.status_int == 200
|
|
assert r.body == b'Hello, World!'
|
|
|
|
@unittest.skipIf(not PY3, "test is Python3 specific")
|
|
def test_rest_with_utf8_endpoint(self):
|
|
class ChildController(object):
|
|
@expose()
|
|
def index(self):
|
|
return 'Hello, World!'
|
|
|
|
class FooController(RestController):
|
|
pass
|
|
|
|
# okay, so it's technically a chestnut, but close enough...
|
|
setattr(FooController, '🌰', ChildController())
|
|
|
|
class RootController(RestController):
|
|
foo = FooController()
|
|
|
|
app = TestApp(make_app(RootController()))
|
|
|
|
r = app.get('/foo/%F0%9F%8C%B0/')
|
|
assert r.status_int == 200
|
|
assert r.body == b'Hello, World!'
|