510 lines
16 KiB
Python
510 lines
16 KiB
Python
import os
|
|
from pecan import Pecan, expose, request, response, redirect, abort
|
|
from pecan.templating import _builtin_renderers as builtin_renderers
|
|
from webtest import TestApp
|
|
from formencode import Schema, validators
|
|
|
|
|
|
class TestBase(object):
|
|
|
|
def test_simple_app(self):
|
|
class RootController(object):
|
|
@expose()
|
|
def index(self):
|
|
return 'Hello, World!'
|
|
|
|
app = TestApp(Pecan(RootController()))
|
|
r = app.get('/')
|
|
assert r.status_int == 200
|
|
assert r.body == 'Hello, World!'
|
|
|
|
r = app.get('/index')
|
|
assert r.status_int == 200
|
|
assert r.body == 'Hello, World!'
|
|
|
|
r = app.get('/index.html')
|
|
assert r.status_int == 200
|
|
assert r.body == 'Hello, World!'
|
|
|
|
def test_object_dispatch(self):
|
|
class SubSubController(object):
|
|
@expose()
|
|
def index(self):
|
|
return '/sub/sub'
|
|
|
|
@expose()
|
|
def deeper(self):
|
|
return '/sub/sub/deeper'
|
|
|
|
class SubController(object):
|
|
@expose()
|
|
def index(self):
|
|
return '/sub'
|
|
|
|
@expose()
|
|
def deeper(self):
|
|
return '/sub/deeper'
|
|
|
|
sub = SubSubController()
|
|
|
|
class RootController(object):
|
|
@expose()
|
|
def index(self):
|
|
return '/'
|
|
|
|
@expose()
|
|
def deeper(self):
|
|
return '/deeper'
|
|
|
|
sub = SubController()
|
|
|
|
app = TestApp(Pecan(RootController()))
|
|
for path in ('/', '/deeper', '/sub', '/sub/deeper', '/sub/sub', '/sub/sub/deeper'):
|
|
r = app.get(path)
|
|
assert r.status_int == 200
|
|
assert r.body == path
|
|
|
|
def test_lookup(self):
|
|
class LookupController(object):
|
|
def __init__(self, someID):
|
|
self.someID = someID
|
|
|
|
@expose()
|
|
def index(self):
|
|
return '/%s' % self.someID
|
|
|
|
@expose()
|
|
def name(self):
|
|
return '/%s/name' % self.someID
|
|
|
|
class RootController(object):
|
|
@expose()
|
|
def index(self):
|
|
return '/'
|
|
|
|
@expose()
|
|
def _lookup(self, someID, *remainder):
|
|
return LookupController(someID), remainder
|
|
|
|
app = TestApp(Pecan(RootController()))
|
|
r = app.get('/')
|
|
assert r.status_int == 200
|
|
assert r.body == '/'
|
|
|
|
r = app.get('/100')
|
|
assert r.status_int == 200
|
|
assert r.body == '/100'
|
|
|
|
r = app.get('/100/name')
|
|
assert r.status_int == 200
|
|
assert r.body == '/100/name'
|
|
|
|
def test_controller_args(self):
|
|
class RootController(object):
|
|
@expose()
|
|
def index(self, id):
|
|
return 'index: %s' % id
|
|
|
|
@expose()
|
|
def multiple(self, one, two):
|
|
return 'multiple: %s, %s' % (one, two)
|
|
|
|
@expose()
|
|
def optional(self, id=None):
|
|
return 'optional: %s' % str(id)
|
|
|
|
@expose()
|
|
def variable_args(self, *args):
|
|
return 'variable_args: %s' % ', '.join(args)
|
|
|
|
@expose()
|
|
def variable_kwargs(self, **kwargs):
|
|
data = ['%s=%s' % (key, kwargs[key]) for key in sorted(kwargs.keys())]
|
|
return 'variable_kwargs: %s' % ', '.join(data)
|
|
|
|
@expose()
|
|
def variable_all(self, *args, **kwargs):
|
|
data = ['%s=%s' % (key, kwargs[key]) for key in sorted(kwargs.keys())]
|
|
return 'variable_all: %s' % ', '.join(list(args) + data)
|
|
|
|
@expose()
|
|
def eater(self, id, dummy=None, *args, **kwargs):
|
|
data = ['%s=%s' % (key, kwargs[key]) for key in sorted(kwargs.keys())]
|
|
return 'eater: %s, %s, %s' % (id, dummy, ', '.join(list(args) + data))
|
|
|
|
@expose()
|
|
def _route(self, args):
|
|
if hasattr(self, args[0]):
|
|
return getattr(self, args[0]), args[1:]
|
|
else:
|
|
return self.index, args
|
|
|
|
app = TestApp(Pecan(RootController()))
|
|
|
|
# required arg
|
|
|
|
try:
|
|
r = app.get('/')
|
|
assert r.status_int != 200
|
|
except Exception, ex:
|
|
assert type(ex) == TypeError
|
|
assert ex.args[0] == 'index() takes exactly 2 arguments (1 given)'
|
|
|
|
r = app.get('/1')
|
|
assert r.status_int == 200
|
|
assert r.body == 'index: 1'
|
|
|
|
r = app.get('/1/dummy', status=404)
|
|
assert r.status_int == 404
|
|
|
|
r = app.get('/?id=2')
|
|
assert r.status_int == 200
|
|
assert r.body == 'index: 2'
|
|
|
|
r = app.get('/3?id=three')
|
|
assert r.status_int == 200
|
|
assert r.body == 'index: 3'
|
|
|
|
r = app.post('/', {'id': '4'})
|
|
assert r.status_int == 200
|
|
assert r.body == 'index: 4'
|
|
|
|
r = app.post('/4', {'id': 'four'})
|
|
assert r.status_int == 200
|
|
assert r.body == 'index: 4'
|
|
|
|
r = app.get('/?id=5&dummy=dummy')
|
|
assert r.status_int == 200
|
|
assert r.body == 'index: 5'
|
|
|
|
r = app.post('/', {'id': '6', 'dummy': 'dummy'})
|
|
assert r.status_int == 200
|
|
assert r.body == 'index: 6'
|
|
|
|
# multiple args
|
|
|
|
r = app.get('/multiple/one/two')
|
|
assert r.status_int == 200
|
|
assert r.body == 'multiple: one, two'
|
|
|
|
r = app.get('/multiple?one=three&two=four')
|
|
assert r.status_int == 200
|
|
assert r.body == 'multiple: three, four'
|
|
|
|
r = app.post('/multiple', {'one': 'five', 'two': 'six'})
|
|
assert r.status_int == 200
|
|
assert r.body == 'multiple: five, six'
|
|
|
|
# optional arg
|
|
|
|
r = app.get('/optional')
|
|
assert r.status_int == 200
|
|
assert r.body == 'optional: None'
|
|
|
|
r = app.get('/optional/1')
|
|
assert r.status_int == 200
|
|
assert r.body == 'optional: 1'
|
|
|
|
r = app.get('/optional/2/dummy', status=404)
|
|
assert r.status_int == 404
|
|
|
|
r = app.get('/optional?id=2')
|
|
assert r.status_int == 200
|
|
assert r.body == 'optional: 2'
|
|
|
|
r = app.get('/optional/3?id=three')
|
|
assert r.status_int == 200
|
|
assert r.body == 'optional: 3'
|
|
|
|
r = app.post('/optional', {'id': '4'})
|
|
assert r.status_int == 200
|
|
assert r.body == 'optional: 4'
|
|
|
|
r = app.post('/optional/5', {'id': 'five'})
|
|
assert r.status_int == 200
|
|
assert r.body == 'optional: 5'
|
|
|
|
r = app.get('/optional?id=6&dummy=dummy')
|
|
assert r.status_int == 200
|
|
assert r.body == 'optional: 6'
|
|
|
|
r = app.post('/optional', {'id': '7', 'dummy': 'dummy'})
|
|
assert r.status_int == 200
|
|
assert r.body == 'optional: 7'
|
|
|
|
# variable args
|
|
|
|
r = app.get('/variable_args')
|
|
assert r.status_int == 200
|
|
assert r.body == 'variable_args: '
|
|
|
|
r = app.get('/variable_args/1/dummy')
|
|
assert r.status_int == 200
|
|
assert r.body == 'variable_args: 1, dummy'
|
|
|
|
r = app.get('/variable_args?id=2&dummy=dummy')
|
|
assert r.status_int == 200
|
|
assert r.body == 'variable_args: '
|
|
|
|
r = app.post('/variable_args', {'id': '3', 'dummy': 'dummy'})
|
|
assert r.status_int == 200
|
|
assert r.body == 'variable_args: '
|
|
|
|
# variable keyword args
|
|
|
|
r = app.get('/variable_kwargs')
|
|
assert r.status_int == 200
|
|
assert r.body == 'variable_kwargs: '
|
|
|
|
r = app.get('/variable_kwargs/1/dummy', status=404)
|
|
assert r.status_int == 404
|
|
|
|
r = app.get('/variable_kwargs?id=2&dummy=dummy')
|
|
assert r.status_int == 200
|
|
assert r.body == 'variable_kwargs: dummy=dummy, id=2'
|
|
|
|
r = app.post('/variable_kwargs', {'id': '3', 'dummy': 'dummy'})
|
|
assert r.status_int == 200
|
|
assert r.body == 'variable_kwargs: dummy=dummy, id=3'
|
|
|
|
# variable args & keyword args
|
|
|
|
r = app.get('/variable_all')
|
|
assert r.status_int == 200
|
|
assert r.body == 'variable_all: '
|
|
|
|
r = app.get('/variable_all/1')
|
|
assert r.status_int == 200
|
|
assert r.body == 'variable_all: 1'
|
|
|
|
r = app.get('/variable_all/2/dummy')
|
|
assert r.status_int == 200
|
|
assert r.body == 'variable_all: 2, dummy'
|
|
|
|
r = app.get('/variable_all/3?month=1&day=12')
|
|
assert r.status_int == 200
|
|
assert r.body == 'variable_all: 3, day=12, month=1'
|
|
|
|
r = app.get('/variable_all/4?id=four&month=1&day=12')
|
|
assert r.status_int == 200
|
|
assert r.body == 'variable_all: 4, day=12, id=four, month=1'
|
|
|
|
r = app.post('/variable_all/5/dummy')
|
|
assert r.status_int == 200
|
|
assert r.body == 'variable_all: 5, dummy'
|
|
|
|
r = app.post('/variable_all/6', {'month': '1', 'day': '12'})
|
|
assert r.status_int == 200
|
|
assert r.body == 'variable_all: 6, day=12, month=1'
|
|
|
|
r = app.post('/variable_all/7', {'id': 'seven', 'month': '1', 'day': '12'})
|
|
assert r.status_int == 200
|
|
assert r.body == 'variable_all: 7, day=12, id=seven, month=1'
|
|
|
|
# the "everything" controller
|
|
|
|
try:
|
|
r = app.get('/eater')
|
|
assert r.status_int != 200
|
|
except Exception, ex:
|
|
assert type(ex) == TypeError
|
|
assert ex.args[0] == 'eater() takes at least 2 arguments (1 given)'
|
|
|
|
r = app.get('/eater/1')
|
|
assert r.status_int == 200
|
|
assert r.body == 'eater: 1, None, '
|
|
|
|
r = app.get('/eater/2/dummy')
|
|
assert r.status_int == 200
|
|
assert r.body == 'eater: 2, dummy, '
|
|
|
|
r = app.get('/eater/3/dummy/foo/bar')
|
|
assert r.status_int == 200
|
|
assert r.body == 'eater: 3, dummy, foo, bar'
|
|
|
|
r = app.get('/eater/4?month=1&day=12')
|
|
assert r.status_int == 200
|
|
assert r.body == 'eater: 4, None, day=12, month=1'
|
|
|
|
r = app.get('/eater/5?id=five&month=1&day=12&dummy=dummy')
|
|
assert r.status_int == 200
|
|
assert r.body == 'eater: 5, dummy, day=12, month=1'
|
|
|
|
r = app.post('/eater/6')
|
|
assert r.status_int == 200
|
|
assert r.body == 'eater: 6, None, '
|
|
|
|
r = app.post('/eater/7/dummy')
|
|
assert r.status_int == 200
|
|
assert r.body == 'eater: 7, dummy, '
|
|
|
|
r = app.post('/eater/8/dummy/foo/bar')
|
|
assert r.status_int == 200
|
|
assert r.body == 'eater: 8, dummy, foo, bar'
|
|
|
|
r = app.post('/eater/9', {'month': '1', 'day': '12'})
|
|
assert r.status_int == 200
|
|
assert r.body == 'eater: 9, None, day=12, month=1'
|
|
|
|
r = app.post('/eater/10', {'id': 'ten', 'month': '1', 'day': '12', 'dummy': 'dummy'})
|
|
assert r.status_int == 200
|
|
assert r.body == 'eater: 10, dummy, day=12, month=1'
|
|
|
|
def test_abort(self):
|
|
class RootController(object):
|
|
@expose()
|
|
def index(self):
|
|
abort(404)
|
|
|
|
app = TestApp(Pecan(RootController()))
|
|
r = app.get('/', status=404)
|
|
assert r.status_int == 404
|
|
|
|
def test_redirect(self):
|
|
class RootController(object):
|
|
@expose()
|
|
def index(self):
|
|
redirect('/testing')
|
|
|
|
@expose()
|
|
def permanent(self):
|
|
redirect('/testing', code=301)
|
|
|
|
@expose()
|
|
def testing(self):
|
|
return 'it worked!'
|
|
|
|
app = TestApp(Pecan(RootController()))
|
|
r = app.get('/')
|
|
assert r.status_int == 302
|
|
r = r.follow()
|
|
assert r.status_int == 200
|
|
assert r.body == 'it worked!'
|
|
|
|
r = app.get('/permanent')
|
|
assert r.status_int == 301
|
|
r = r.follow()
|
|
assert r.status_int == 200
|
|
assert r.body == 'it worked!'
|
|
|
|
def test_streaming_response(self):
|
|
import StringIO
|
|
class RootController(object):
|
|
@expose(content_type='text/plain')
|
|
def test(self, foo):
|
|
if foo == 'stream':
|
|
# mimic large file
|
|
contents = StringIO.StringIO('stream')
|
|
response.content_type='application/octet-stream'
|
|
contents.seek(0, os.SEEK_END)
|
|
response.content_length = contents.tell()
|
|
contents.seek(0, os.SEEK_SET)
|
|
response.app_iter = contents
|
|
return response
|
|
else:
|
|
return 'plain text'
|
|
|
|
app = TestApp(Pecan(RootController()))
|
|
r = app.get('/test/stream')
|
|
assert r.content_type == 'application/octet-stream'
|
|
assert r.body == 'stream'
|
|
|
|
r = app.get('/test/plain')
|
|
assert r.content_type == 'text/plain'
|
|
assert r.body == 'plain text'
|
|
|
|
def test_request_state_cleanup(self):
|
|
"""
|
|
After a request, the state local() should be totally clean
|
|
except for state.app (so that objects don't leak between requests)
|
|
"""
|
|
from pecan.core import state
|
|
|
|
class RootController(object):
|
|
@expose()
|
|
def index(self):
|
|
return '/'
|
|
|
|
app = TestApp(Pecan(RootController()))
|
|
r = app.get('/')
|
|
assert r.status_int == 200
|
|
assert r.body == '/'
|
|
|
|
assert state.__dict__.keys() == ['app']
|
|
|
|
class TestEngines(object):
|
|
|
|
template_path = os.path.join(os.path.dirname(__file__), 'templates')
|
|
|
|
def test_genshi(self):
|
|
if 'genshi' not in builtin_renderers:
|
|
return
|
|
|
|
class RootController(object):
|
|
@expose('genshi:genshi.html')
|
|
def index(self, name='Jonathan'):
|
|
return dict(name=name)
|
|
|
|
app = TestApp(Pecan(RootController(), template_path=self.template_path))
|
|
r = app.get('/')
|
|
assert r.status_int == 200
|
|
assert "<h1>Hello, Jonathan!</h1>" in r.body
|
|
|
|
app = TestApp(Pecan(RootController(), template_path=self.template_path))
|
|
r = app.get('/index.html?name=World')
|
|
assert r.status_int == 200
|
|
assert "<h1>Hello, World!</h1>" in r.body
|
|
|
|
def test_kajiki(self):
|
|
if 'kajiki' not in builtin_renderers:
|
|
return
|
|
|
|
class RootController(object):
|
|
@expose('kajiki:kajiki.html')
|
|
def index(self, name='Jonathan'):
|
|
return dict(name=name)
|
|
|
|
app = TestApp(Pecan(RootController(), template_path=self.template_path))
|
|
r = app.get('/')
|
|
assert r.status_int == 200
|
|
assert "<h1>Hello, Jonathan!</h1>" in r.body
|
|
|
|
app = TestApp(Pecan(RootController(), template_path=self.template_path))
|
|
r = app.get('/index.html?name=World')
|
|
assert r.status_int == 200
|
|
assert "<h1>Hello, World!</h1>" in r.body
|
|
|
|
def test_mako(self):
|
|
if 'mako' not in builtin_renderers:
|
|
return
|
|
class RootController(object):
|
|
@expose('mako:mako.html')
|
|
def index(self, name='Jonathan'):
|
|
return dict(name=name)
|
|
|
|
app = TestApp(Pecan(RootController(), template_path=self.template_path))
|
|
r = app.get('/')
|
|
assert r.status_int == 200
|
|
assert "<h1>Hello, Jonathan!</h1>" in r.body
|
|
|
|
app = TestApp(Pecan(RootController(), template_path=self.template_path))
|
|
r = app.get('/index.html?name=World')
|
|
assert r.status_int == 200
|
|
assert "<h1>Hello, World!</h1>" in r.body
|
|
|
|
def test_json(self):
|
|
from json import loads
|
|
|
|
expected_result = dict(name='Jonathan', age=30, nested=dict(works=True))
|
|
|
|
class RootController(object):
|
|
@expose('json')
|
|
def index(self):
|
|
return expected_result
|
|
|
|
app = TestApp(Pecan(RootController()))
|
|
r = app.get('/')
|
|
assert r.status_int == 200
|
|
result = dict(loads(r.body))
|
|
assert result == expected_result
|