import time from json import dumps, loads import warnings from webtest import TestApp from six import b as b_ from six import u as u_ import webob import mock from pecan import Pecan, expose, abort, Request, Response from pecan.rest import RestController from pecan.hooks import PecanHook, HookController from pecan.tests import PecanTestCase class TestThreadingLocalUsage(PecanTestCase): @property def root(self): class RootController(object): @expose() def index(self, req, resp): assert isinstance(req, webob.BaseRequest) assert isinstance(resp, webob.Response) return 'Hello, World!' @expose() def warning(self): return ("This should be unroutable because (req, resp) are not" " arguments. It should raise a TypeError.") @expose(generic=True) def generic(self): return ("This should be unroutable because (req, resp) are not" " arguments. It should raise a TypeError.") @generic.when(method='PUT') def generic_put(self, _id): return ("This should be unroutable because (req, resp) are not" " arguments. It should raise a TypeError.") return RootController def test_locals_are_not_used(self): with mock.patch('threading.local', side_effect=AssertionError()): app = TestApp(Pecan(self.root(), use_context_locals=False)) r = app.get('/') assert r.status_int == 200 assert r.body == b_('Hello, World!') self.assertRaises(AssertionError, Pecan, self.root) def test_threadlocal_argument_warning(self): with mock.patch('threading.local', side_effect=AssertionError()): app = TestApp(Pecan(self.root(), use_context_locals=False)) self.assertRaises( TypeError, app.get, '/warning/' ) def test_threadlocal_argument_warning_on_generic(self): with mock.patch('threading.local', side_effect=AssertionError()): app = TestApp(Pecan(self.root(), use_context_locals=False)) self.assertRaises( TypeError, app.get, '/generic/' ) def test_threadlocal_argument_warning_on_generic_delegate(self): with mock.patch('threading.local', side_effect=AssertionError()): app = TestApp(Pecan(self.root(), use_context_locals=False)) self.assertRaises( TypeError, app.put, '/generic/' ) class TestIndexRouting(PecanTestCase): @property def app_(self): class RootController(object): @expose() def index(self, req, resp): assert isinstance(req, webob.BaseRequest) assert isinstance(resp, webob.Response) return 'Hello, World!' return TestApp(Pecan(RootController(), use_context_locals=False)) def test_empty_root(self): r = self.app_.get('/') assert r.status_int == 200 assert r.body == b_('Hello, World!') def test_index(self): r = self.app_.get('/index') assert r.status_int == 200 assert r.body == b_('Hello, World!') def test_index_html(self): r = self.app_.get('/index.html') assert r.status_int == 200 assert r.body == b_('Hello, World!') class TestManualResponse(PecanTestCase): def test_manual_response(self): class RootController(object): @expose() def index(self, req, resp): resp = webob.Response(resp.environ) resp.body = b_('Hello, World!') return resp app = TestApp(Pecan(RootController(), use_context_locals=False)) r = app.get('/') assert r.body == b_('Hello, World!'), r.body class TestDispatch(PecanTestCase): @property def app_(self): class SubSubController(object): @expose() def index(self, req, resp): assert isinstance(req, webob.BaseRequest) assert isinstance(resp, webob.Response) return '/sub/sub/' @expose() def deeper(self, req, resp): assert isinstance(req, webob.BaseRequest) assert isinstance(resp, webob.Response) return '/sub/sub/deeper' class SubController(object): @expose() def index(self, req, resp): assert isinstance(req, webob.BaseRequest) assert isinstance(resp, webob.Response) return '/sub/' @expose() def deeper(self, req, resp): assert isinstance(req, webob.BaseRequest) assert isinstance(resp, webob.Response) return '/sub/deeper' sub = SubSubController() class RootController(object): @expose() def index(self, req, resp): assert isinstance(req, webob.BaseRequest) assert isinstance(resp, webob.Response) return '/' @expose() def deeper(self, req, resp): assert isinstance(req, webob.BaseRequest) assert isinstance(resp, webob.Response) return '/deeper' sub = SubController() return TestApp(Pecan(RootController(), use_context_locals=False)) def test_index(self): r = self.app_.get('/') assert r.status_int == 200 assert r.body == b_('/') def test_one_level(self): r = self.app_.get('/deeper') assert r.status_int == 200 assert r.body == b_('/deeper') def test_one_level_with_trailing(self): r = self.app_.get('/sub/') assert r.status_int == 200 assert r.body == b_('/sub/') def test_two_levels(self): r = self.app_.get('/sub/deeper') assert r.status_int == 200 assert r.body == b_('/sub/deeper') def test_two_levels_with_trailing(self): r = self.app_.get('/sub/sub/') assert r.status_int == 200 def test_three_levels(self): r = self.app_.get('/sub/sub/deeper') assert r.status_int == 200 assert r.body == b_('/sub/sub/deeper') class TestLookups(PecanTestCase): @property def app_(self): class LookupController(object): def __init__(self, someID): self.someID = someID @expose() def index(self, req, resp): return '/%s' % self.someID @expose() def name(self, req, resp): return '/%s/name' % self.someID class RootController(object): @expose() def index(self, req, resp): return '/' @expose() def _lookup(self, someID, *remainder): return LookupController(someID), remainder return TestApp(Pecan(RootController(), use_context_locals=False)) def test_index(self): r = self.app_.get('/') assert r.status_int == 200 assert r.body == b_('/') def test_lookup(self): r = self.app_.get('/100/') assert r.status_int == 200 assert r.body == b_('/100') def test_lookup_with_method(self): r = self.app_.get('/100/name') assert r.status_int == 200 assert r.body == b_('/100/name') def test_lookup_with_wrong_argspec(self): class RootController(object): @expose() def _lookup(self, someID): return 'Bad arg spec' # pragma: nocover with warnings.catch_warnings(): warnings.simplefilter("ignore") app = TestApp(Pecan(RootController(), use_context_locals=False)) r = app.get('/foo/bar', expect_errors=True) assert r.status_int == 404 class TestCanonicalLookups(PecanTestCase): @property def app_(self): class LookupController(object): def __init__(self, someID): self.someID = someID @expose() def index(self, req, resp): return self.someID class UserController(object): @expose() def _lookup(self, someID, *remainder): return LookupController(someID), remainder class RootController(object): users = UserController() return TestApp(Pecan(RootController(), use_context_locals=False)) def test_canonical_lookup(self): assert self.app_.get('/users', expect_errors=404).status_int == 404 assert self.app_.get('/users/', expect_errors=404).status_int == 404 assert self.app_.get('/users/100').status_int == 302 assert self.app_.get('/users/100/').body == b_('100') class TestControllerArguments(PecanTestCase): @property def app_(self): class RootController(object): @expose() def index(self, req, resp, id): return 'index: %s' % id @expose() def multiple(self, req, resp, one, two): return 'multiple: %s, %s' % (one, two) @expose() def optional(self, req, resp, id=None): return 'optional: %s' % str(id) @expose() def multiple_optional(self, req, resp, one=None, two=None, three=None): return 'multiple_optional: %s, %s, %s' % (one, two, three) @expose() def variable_args(self, req, resp, *args): return 'variable_args: %s' % ', '.join(args) @expose() def variable_kwargs(self, req, resp, **kwargs): data = [ '%s=%s' % (key, kwargs[key]) for key in sorted(kwargs.keys()) ] return 'variable_kwargs: %s' % ', '.join(data) @expose() def variable_all(self, req, resp, *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, req, resp, 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, request): if hasattr(self, args[0]): return getattr(self, args[0]), args[1:] else: return self.index, args return TestApp(Pecan(RootController(), use_context_locals=False)) def test_required_argument(self): try: r = self.app_.get('/') assert r.status_int != 200 # pragma: nocover except Exception as ex: assert type(ex) == TypeError assert ex.args[0] in ( "index() takes exactly 4 arguments (3 given)", "index() missing 1 required positional argument: 'id'" ) # this messaging changed in Python 3.3 def test_single_argument(self): r = self.app_.get('/1') assert r.status_int == 200 assert r.body == b_('index: 1') def test_single_argument_with_encoded_url(self): r = self.app_.get('/This%20is%20a%20test%21') assert r.status_int == 200 assert r.body == b_('index: This is a test!') def test_two_arguments(self): r = self.app_.get('/1/dummy', status=404) assert r.status_int == 404 def test_keyword_argument(self): r = self.app_.get('/?id=2') assert r.status_int == 200 assert r.body == b_('index: 2') def test_keyword_argument_with_encoded_url(self): r = self.app_.get('/?id=This%20is%20a%20test%21') assert r.status_int == 200 assert r.body == b_('index: This is a test!') def test_argument_and_keyword_argument(self): r = self.app_.get('/3?id=three') assert r.status_int == 200 assert r.body == b_('index: 3') def test_encoded_argument_and_keyword_argument(self): r = self.app_.get('/This%20is%20a%20test%21?id=three') assert r.status_int == 200 assert r.body == b_('index: This is a test!') def test_explicit_kwargs(self): r = self.app_.post('/', {'id': '4'}) assert r.status_int == 200 assert r.body == b_('index: 4') def test_path_with_explicit_kwargs(self): r = self.app_.post('/4', {'id': 'four'}) assert r.status_int == 200 assert r.body == b_('index: 4') def test_multiple_kwargs(self): r = self.app_.get('/?id=5&dummy=dummy') assert r.status_int == 200 assert r.body == b_('index: 5') def test_kwargs_from_root(self): r = self.app_.post('/', {'id': '6', 'dummy': 'dummy'}) assert r.status_int == 200 assert r.body == b_('index: 6') # multiple args def test_multiple_positional_arguments(self): r = self.app_.get('/multiple/one/two') assert r.status_int == 200 assert r.body == b_('multiple: one, two') def test_multiple_positional_arguments_with_url_encode(self): r = self.app_.get('/multiple/One%20/Two%21') assert r.status_int == 200 assert r.body == b_('multiple: One , Two!') def test_multiple_positional_arguments_with_kwargs(self): r = self.app_.get('/multiple?one=three&two=four') assert r.status_int == 200 assert r.body == b_('multiple: three, four') def test_multiple_positional_arguments_with_url_encoded_kwargs(self): r = self.app_.get('/multiple?one=Three%20&two=Four%20%21') assert r.status_int == 200 assert r.body == b_('multiple: Three , Four !') def test_positional_args_with_dictionary_kwargs(self): r = self.app_.post('/multiple', {'one': 'five', 'two': 'six'}) assert r.status_int == 200 assert r.body == b_('multiple: five, six') def test_positional_args_with_url_encoded_dictionary_kwargs(self): r = self.app_.post('/multiple', {'one': 'Five%20', 'two': 'Six%20%21'}) assert r.status_int == 200 assert r.body == b_('multiple: Five%20, Six%20%21') # optional arg def test_optional_arg(self): r = self.app_.get('/optional') assert r.status_int == 200 assert r.body == b_('optional: None') def test_multiple_optional(self): r = self.app_.get('/optional/1') assert r.status_int == 200 assert r.body == b_('optional: 1') def test_multiple_optional_url_encoded(self): r = self.app_.get('/optional/Some%20Number') assert r.status_int == 200 assert r.body == b_('optional: Some Number') def test_multiple_optional_missing(self): r = self.app_.get('/optional/2/dummy', status=404) assert r.status_int == 404 def test_multiple_with_kwargs(self): r = self.app_.get('/optional?id=2') assert r.status_int == 200 assert r.body == b_('optional: 2') def test_multiple_with_url_encoded_kwargs(self): r = self.app_.get('/optional?id=Some%20Number') assert r.status_int == 200 assert r.body == b_('optional: Some Number') def test_multiple_args_with_url_encoded_kwargs(self): r = self.app_.get('/optional/3?id=three') assert r.status_int == 200 assert r.body == b_('optional: 3') def test_url_encoded_positional_args(self): r = self.app_.get('/optional/Some%20Number?id=three') assert r.status_int == 200 assert r.body == b_('optional: Some Number') def test_optional_arg_with_kwargs(self): r = self.app_.post('/optional', {'id': '4'}) assert r.status_int == 200 assert r.body == b_('optional: 4') def test_optional_arg_with_url_encoded_kwargs(self): r = self.app_.post('/optional', {'id': 'Some%20Number'}) assert r.status_int == 200 assert r.body == b_('optional: Some%20Number') def test_multiple_positional_arguments_with_dictionary_kwargs(self): r = self.app_.post('/optional/5', {'id': 'five'}) assert r.status_int == 200 assert r.body == b_('optional: 5') def test_multiple_positional_url_encoded_arguments_with_kwargs(self): r = self.app_.post('/optional/Some%20Number', {'id': 'five'}) assert r.status_int == 200 assert r.body == b_('optional: Some Number') def test_optional_arg_with_multiple_kwargs(self): r = self.app_.get('/optional?id=6&dummy=dummy') assert r.status_int == 200 assert r.body == b_('optional: 6') def test_optional_arg_with_multiple_url_encoded_kwargs(self): r = self.app_.get('/optional?id=Some%20Number&dummy=dummy') assert r.status_int == 200 assert r.body == b_('optional: Some Number') def test_optional_arg_with_multiple_dictionary_kwargs(self): r = self.app_.post('/optional', {'id': '7', 'dummy': 'dummy'}) assert r.status_int == 200 assert r.body == b_('optional: 7') def test_optional_arg_with_multiple_url_encoded_dictionary_kwargs(self): r = self.app_.post('/optional', { 'id': 'Some%20Number', 'dummy': 'dummy' }) assert r.status_int == 200 assert r.body == b_('optional: Some%20Number') # multiple optional args def test_multiple_optional_positional_args(self): r = self.app_.get('/multiple_optional') assert r.status_int == 200 assert r.body == b_('multiple_optional: None, None, None') def test_multiple_optional_positional_args_one_arg(self): r = self.app_.get('/multiple_optional/1') assert r.status_int == 200 assert r.body == b_('multiple_optional: 1, None, None') def test_multiple_optional_positional_args_one_url_encoded_arg(self): r = self.app_.get('/multiple_optional/One%21') assert r.status_int == 200 assert r.body == b_('multiple_optional: One!, None, None') def test_multiple_optional_positional_args_all_args(self): r = self.app_.get('/multiple_optional/1/2/3') assert r.status_int == 200 assert r.body == b_('multiple_optional: 1, 2, 3') def test_multiple_optional_positional_args_all_url_encoded_args(self): r = self.app_.get('/multiple_optional/One%21/Two%21/Three%21') assert r.status_int == 200 assert r.body == b_('multiple_optional: One!, Two!, Three!') def test_multiple_optional_positional_args_too_many_args(self): r = self.app_.get('/multiple_optional/1/2/3/dummy', status=404) assert r.status_int == 404 def test_multiple_optional_positional_args_with_kwargs(self): r = self.app_.get('/multiple_optional?one=1') assert r.status_int == 200 assert r.body == b_('multiple_optional: 1, None, None') def test_multiple_optional_positional_args_with_url_encoded_kwargs(self): r = self.app_.get('/multiple_optional?one=One%21') assert r.status_int == 200 assert r.body == b_('multiple_optional: One!, None, None') def test_multiple_optional_positional_args_with_string_kwargs(self): r = self.app_.get('/multiple_optional/1?one=one') assert r.status_int == 200 assert r.body == b_('multiple_optional: 1, None, None') def test_multiple_optional_positional_args_with_encoded_str_kwargs(self): r = self.app_.get('/multiple_optional/One%21?one=one') assert r.status_int == 200 assert r.body == b_('multiple_optional: One!, None, None') def test_multiple_optional_positional_args_with_dict_kwargs(self): r = self.app_.post('/multiple_optional', {'one': '1'}) assert r.status_int == 200 assert r.body == b_('multiple_optional: 1, None, None') def test_multiple_optional_positional_args_with_encoded_dict_kwargs(self): r = self.app_.post('/multiple_optional', {'one': 'One%21'}) assert r.status_int == 200 assert r.body == b_('multiple_optional: One%21, None, None') def test_multiple_optional_positional_args_and_dict_kwargs(self): r = self.app_.post('/multiple_optional/1', {'one': 'one'}) assert r.status_int == 200 assert r.body == b_('multiple_optional: 1, None, None') def test_multiple_optional_encoded_positional_args_and_dict_kwargs(self): r = self.app_.post('/multiple_optional/One%21', {'one': 'one'}) assert r.status_int == 200 assert r.body == b_('multiple_optional: One!, None, None') def test_multiple_optional_args_with_multiple_kwargs(self): r = self.app_.get('/multiple_optional?one=1&two=2&three=3&four=4') assert r.status_int == 200 assert r.body == b_('multiple_optional: 1, 2, 3') def test_multiple_optional_args_with_multiple_encoded_kwargs(self): r = self.app_.get( '/multiple_optional?one=One%21&two=Two%21&three=Three%21&four=4' ) assert r.status_int == 200 assert r.body == b_('multiple_optional: One!, Two!, Three!') def test_multiple_optional_args_with_multiple_dict_kwargs(self): r = self.app_.post( '/multiple_optional', {'one': '1', 'two': '2', 'three': '3', 'four': '4'} ) assert r.status_int == 200 assert r.body == b_('multiple_optional: 1, 2, 3') def test_multiple_optional_args_with_multiple_encoded_dict_kwargs(self): r = self.app_.post( '/multiple_optional', { 'one': 'One%21', 'two': 'Two%21', 'three': 'Three%21', 'four': '4' } ) assert r.status_int == 200 assert r.body == b_('multiple_optional: One%21, Two%21, Three%21') def test_multiple_optional_args_with_last_kwarg(self): r = self.app_.get('/multiple_optional?three=3') assert r.status_int == 200 assert r.body == b_('multiple_optional: None, None, 3') def test_multiple_optional_args_with_last_encoded_kwarg(self): r = self.app_.get('/multiple_optional?three=Three%21') assert r.status_int == 200 assert r.body == b_('multiple_optional: None, None, Three!') def test_multiple_optional_args_with_middle_arg(self): r = self.app_.get('/multiple_optional', {'two': '2'}) assert r.status_int == 200 assert r.body == b_('multiple_optional: None, 2, None') def test_variable_args(self): r = self.app_.get('/variable_args') assert r.status_int == 200 assert r.body == b_('variable_args: ') def test_multiple_variable_args(self): r = self.app_.get('/variable_args/1/dummy') assert r.status_int == 200 assert r.body == b_('variable_args: 1, dummy') def test_multiple_encoded_variable_args(self): r = self.app_.get('/variable_args/Testing%20One%20Two/Three%21') assert r.status_int == 200 assert r.body == b_('variable_args: Testing One Two, Three!') def test_variable_args_with_kwargs(self): r = self.app_.get('/variable_args?id=2&dummy=dummy') assert r.status_int == 200 assert r.body == b_('variable_args: ') def test_variable_args_with_dict_kwargs(self): r = self.app_.post('/variable_args', {'id': '3', 'dummy': 'dummy'}) assert r.status_int == 200 assert r.body == b_('variable_args: ') def test_variable_kwargs(self): r = self.app_.get('/variable_kwargs') assert r.status_int == 200 assert r.body == b_('variable_kwargs: ') def test_multiple_variable_kwargs(self): r = self.app_.get('/variable_kwargs/1/dummy', status=404) assert r.status_int == 404 def test_multiple_variable_kwargs_with_explicit_kwargs(self): r = self.app_.get('/variable_kwargs?id=2&dummy=dummy') assert r.status_int == 200 assert r.body == b_('variable_kwargs: dummy=dummy, id=2') def test_multiple_variable_kwargs_with_explicit_encoded_kwargs(self): r = self.app_.get( '/variable_kwargs?id=Two%21&dummy=This%20is%20a%20test' ) assert r.status_int == 200 assert r.body == b_('variable_kwargs: dummy=This is a test, id=Two!') def test_multiple_variable_kwargs_with_dict_kwargs(self): r = self.app_.post('/variable_kwargs', {'id': '3', 'dummy': 'dummy'}) assert r.status_int == 200 assert r.body == b_('variable_kwargs: dummy=dummy, id=3') def test_multiple_variable_kwargs_with_encoded_dict_kwargs(self): r = self.app_.post( '/variable_kwargs', {'id': 'Three%21', 'dummy': 'This%20is%20a%20test'} ) assert r.status_int == 200 result = 'variable_kwargs: dummy=This%20is%20a%20test, id=Three%21' assert r.body == b_(result) def test_variable_all(self): r = self.app_.get('/variable_all') assert r.status_int == 200 assert r.body == b_('variable_all: ') def test_variable_all_with_one_extra(self): r = self.app_.get('/variable_all/1') assert r.status_int == 200 assert r.body == b_('variable_all: 1') def test_variable_all_with_two_extras(self): r = self.app_.get('/variable_all/2/dummy') assert r.status_int == 200 assert r.body == b_('variable_all: 2, dummy') def test_variable_mixed(self): r = self.app_.get('/variable_all/3?month=1&day=12') assert r.status_int == 200 assert r.body == b_('variable_all: 3, day=12, month=1') def test_variable_mixed_explicit(self): r = self.app_.get('/variable_all/4?id=four&month=1&day=12') assert r.status_int == 200 assert r.body == b_('variable_all: 4, day=12, id=four, month=1') def test_variable_post(self): r = self.app_.post('/variable_all/5/dummy') assert r.status_int == 200 assert r.body == b_('variable_all: 5, dummy') def test_variable_post_with_kwargs(self): r = self.app_.post('/variable_all/6', {'month': '1', 'day': '12'}) assert r.status_int == 200 assert r.body == b_('variable_all: 6, day=12, month=1') def test_variable_post_mixed(self): r = self.app_.post( '/variable_all/7', {'id': 'seven', 'month': '1', 'day': '12'} ) assert r.status_int == 200 assert r.body == b_('variable_all: 7, day=12, id=seven, month=1') def test_no_remainder(self): try: r = self.app_.get('/eater') assert r.status_int != 200 # pragma: nocover except Exception as ex: assert type(ex) == TypeError assert ex.args[0] in ( "eater() takes at least 4 arguments (3 given)", "eater() missing 1 required positional argument: 'id'" ) # this messaging changed in Python 3.3 def test_one_remainder(self): r = self.app_.get('/eater/1') assert r.status_int == 200 assert r.body == b_('eater: 1, None, ') def test_two_remainders(self): r = self.app_.get('/eater/2/dummy') assert r.status_int == 200 assert r.body == b_('eater: 2, dummy, ') def test_many_remainders(self): r = self.app_.get('/eater/3/dummy/foo/bar') assert r.status_int == 200 assert r.body == b_('eater: 3, dummy, foo, bar') def test_remainder_with_kwargs(self): r = self.app_.get('/eater/4?month=1&day=12') assert r.status_int == 200 assert r.body == b_('eater: 4, None, day=12, month=1') def test_remainder_with_many_kwargs(self): r = self.app_.get('/eater/5?id=five&month=1&day=12&dummy=dummy') assert r.status_int == 200 assert r.body == b_('eater: 5, dummy, day=12, month=1') def test_post_remainder(self): r = self.app_.post('/eater/6') assert r.status_int == 200 assert r.body == b_('eater: 6, None, ') def test_post_three_remainders(self): r = self.app_.post('/eater/7/dummy') assert r.status_int == 200 assert r.body == b_('eater: 7, dummy, ') def test_post_many_remainders(self): r = self.app_.post('/eater/8/dummy/foo/bar') assert r.status_int == 200 assert r.body == b_('eater: 8, dummy, foo, bar') def test_post_remainder_with_kwargs(self): r = self.app_.post('/eater/9', {'month': '1', 'day': '12'}) assert r.status_int == 200 assert r.body == b_('eater: 9, None, day=12, month=1') def test_post_many_remainders_with_many_kwargs(self): r = self.app_.post( '/eater/10', {'id': 'ten', 'month': '1', 'day': '12', 'dummy': 'dummy'} ) assert r.status_int == 200 assert r.body == b_('eater: 10, dummy, day=12, month=1') class TestRestController(PecanTestCase): @property def app_(self): class OthersController(object): @expose() def index(self, req, resp): return 'OTHERS' @expose() def echo(self, req, resp, 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, req, resp, id): return self.data[int(id)] @expose('json') def get_all(self, req, resp): return dict(items=self.data) @expose() def length(self, req, resp, id, value=None): length = len(self.data[int(id)]) if value: length += len(value) return str(length) @expose() def post(self, req, resp, value): self.data.append(value) resp.status = 302 return 'CREATED' @expose() def edit(self, req, resp, id): return 'EDIT %s' % self.data[int(id)] @expose() def put(self, req, resp, id, value): self.data[int(id)] = value return 'UPDATED' @expose() def get_delete(self, req, resp, id): return 'DELETE %s' % self.data[int(id)] @expose() def delete(self, req, resp, id): del self.data[int(id)] return 'DELETED' @expose() def reset(self, req, resp): return 'RESET' @expose() def post_options(self, req, resp): return 'OPTIONS' @expose() def options(self, req, resp): abort(500) @expose() def other(self, req, resp): abort(500) class RootController(object): things = ThingsController() # create the app return TestApp(Pecan(RootController(), use_context_locals=False)) def test_get_all(self): r = self.app_.get('/things') assert r.status_int == 200 assert r.body == b_(dumps(dict(items=['zero', 'one', 'two', 'three']))) def test_get_one(self): for i, value in enumerate(['zero', 'one', 'two', 'three']): r = self.app_.get('/things/%d' % i) assert r.status_int == 200 assert r.body == b_(value) def test_post(self): r = self.app_.post('/things', {'value': 'four'}) assert r.status_int == 302 assert r.body == b_('CREATED') def test_custom_action(self): r = self.app_.get('/things/3/edit') assert r.status_int == 200 assert r.body == b_('EDIT three') def test_put(self): r = self.app_.put('/things/3', {'value': 'THREE!'}) assert r.status_int == 200 assert r.body == b_('UPDATED') def test_put_with_method_parameter_and_get(self): r = self.app_.get('/things/3?_method=put', {'value': 'X'}, status=405) assert r.status_int == 405 def test_put_with_method_parameter_and_post(self): r = self.app_.post('/things/3?_method=put', {'value': 'THREE!'}) assert r.status_int == 200 assert r.body == b_('UPDATED') def test_get_delete(self): r = self.app_.get('/things/3/delete') assert r.status_int == 200 assert r.body == b_('DELETE three') def test_delete_method(self): r = self.app_.delete('/things/3') assert r.status_int == 200 assert r.body == b_('DELETED') def test_delete_with_method_parameter(self): r = self.app_.get('/things/3?_method=DELETE', status=405) assert r.status_int == 405 def test_delete_with_method_parameter_and_post(self): r = self.app_.post('/things/3?_method=DELETE') assert r.status_int == 200 assert r.body == b_('DELETED') def test_custom_method_type(self): r = self.app_.request('/things', method='RESET') assert r.status_int == 200 assert r.body == b_('RESET') def test_custom_method_type_with_method_parameter(self): r = self.app_.get('/things?_method=RESET') assert r.status_int == 200 assert r.body == b_('RESET') def test_options(self): r = self.app_.request('/things', method='OPTIONS') assert r.status_int == 200 assert r.body == b_('OPTIONS') def test_options_with_method_parameter(self): r = self.app_.post('/things', {'_method': 'OPTIONS'}) assert r.status_int == 200 assert r.body == b_('OPTIONS') def test_other_custom_action(self): with warnings.catch_warnings(): warnings.simplefilter("ignore") r = self.app_.request('/things/other', method='MISC', status=405) assert r.status_int == 405 def test_other_custom_action_with_method_parameter(self): r = self.app_.post('/things/other', {'_method': 'MISC'}, status=405) assert r.status_int == 405 def test_nested_controller_with_trailing_slash(self): with warnings.catch_warnings(): warnings.simplefilter("ignore") r = self.app_.request('/things/others/', method='MISC') assert r.status_int == 200 assert r.body == b_('OTHERS') def test_nested_controller_without_trailing_slash(self): with warnings.catch_warnings(): warnings.simplefilter("ignore") r = self.app_.request('/things/others', method='MISC', status=302) assert r.status_int == 302 def test_invalid_custom_action(self): r = self.app_.get('/things?_method=BAD', status=404) assert r.status_int == 404 def test_named_action(self): # test custom "GET" request "length" r = self.app_.get('/things/1/length') assert r.status_int == 200 assert r.body == b_(str(len('one'))) def test_named_nested_action(self): # test custom "GET" request through subcontroller r = self.app_.get('/things/others/echo?value=test') assert r.status_int == 200 assert r.body == b_('test') def test_nested_post(self): # test custom "POST" request through subcontroller r = self.app_.post('/things/others/echo', {'value': 'test'}) assert r.status_int == 200 assert r.body == b_('test') class TestHooks(PecanTestCase): def test_basic_single_hook(self): run_hook = [] class RootController(object): @expose() def index(self, req, resp): run_hook.append('inside') return 'Hello, World!' class SimpleHook(PecanHook): def on_route(self, state): run_hook.append('on_route') def before(self, state): run_hook.append('before') def after(self, state): run_hook.append('after') def on_error(self, state, e): run_hook.append('error') app = TestApp(Pecan( RootController(), hooks=[SimpleHook()], use_context_locals=False )) response = app.get('/') assert response.status_int == 200 assert response.body == b_('Hello, World!') assert len(run_hook) == 4 assert run_hook[0] == 'on_route' assert run_hook[1] == 'before' assert run_hook[2] == 'inside' assert run_hook[3] == 'after' def test_basic_multi_hook(self): run_hook = [] class RootController(object): @expose() def index(self, req, resp): run_hook.append('inside') return 'Hello, World!' class SimpleHook(PecanHook): def __init__(self, id): self.id = str(id) def on_route(self, state): run_hook.append('on_route' + self.id) def before(self, state): run_hook.append('before' + self.id) def after(self, state): run_hook.append('after' + self.id) def on_error(self, state, e): run_hook.append('error' + self.id) app = TestApp(Pecan(RootController(), hooks=[ SimpleHook(1), SimpleHook(2), SimpleHook(3) ], use_context_locals=False)) response = app.get('/') assert response.status_int == 200 assert response.body == b_('Hello, World!') assert len(run_hook) == 10 assert run_hook[0] == 'on_route1' assert run_hook[1] == 'on_route2' assert run_hook[2] == 'on_route3' assert run_hook[3] == 'before1' assert run_hook[4] == 'before2' assert run_hook[5] == 'before3' assert run_hook[6] == 'inside' assert run_hook[7] == 'after3' assert run_hook[8] == 'after2' assert run_hook[9] == 'after1' def test_partial_hooks(self): run_hook = [] class RootController(object): @expose() def index(self, req, resp): run_hook.append('inside') return 'Hello World!' @expose() def causeerror(self, req, resp): return [][1] class ErrorHook(PecanHook): def on_error(self, state, e): run_hook.append('error') class OnRouteHook(PecanHook): def on_route(self, state): run_hook.append('on_route') app = TestApp(Pecan(RootController(), hooks=[ ErrorHook(), OnRouteHook() ], use_context_locals=False)) response = app.get('/') assert response.status_int == 200 assert response.body == b_('Hello World!') assert len(run_hook) == 2 assert run_hook[0] == 'on_route' assert run_hook[1] == 'inside' run_hook = [] try: response = app.get('/causeerror') except Exception as e: assert isinstance(e, IndexError) assert len(run_hook) == 2 assert run_hook[0] == 'on_route' assert run_hook[1] == 'error' def test_on_error_response_hook(self): run_hook = [] class RootController(object): @expose() def causeerror(self, req, resp): return [][1] class ErrorHook(PecanHook): def on_error(self, state, e): run_hook.append('error') r = webob.Response() r.text = u_('on_error') return r app = TestApp(Pecan(RootController(), hooks=[ ErrorHook() ], use_context_locals=False)) response = app.get('/causeerror') assert len(run_hook) == 1 assert run_hook[0] == 'error' assert response.text == 'on_error' def test_prioritized_hooks(self): run_hook = [] class RootController(object): @expose() def index(self, req, resp): run_hook.append('inside') return 'Hello, World!' class SimpleHook(PecanHook): def __init__(self, id, priority=None): self.id = str(id) if priority: self.priority = priority def on_route(self, state): run_hook.append('on_route' + self.id) def before(self, state): run_hook.append('before' + self.id) def after(self, state): run_hook.append('after' + self.id) def on_error(self, state, e): run_hook.append('error' + self.id) papp = Pecan(RootController(), hooks=[ SimpleHook(1, 3), SimpleHook(2, 2), SimpleHook(3, 1) ], use_context_locals=False) app = TestApp(papp) response = app.get('/') assert response.status_int == 200 assert response.body == b_('Hello, World!') assert len(run_hook) == 10 assert run_hook[0] == 'on_route3' assert run_hook[1] == 'on_route2' assert run_hook[2] == 'on_route1' assert run_hook[3] == 'before3' assert run_hook[4] == 'before2' assert run_hook[5] == 'before1' assert run_hook[6] == 'inside' assert run_hook[7] == 'after1' assert run_hook[8] == 'after2' assert run_hook[9] == 'after3' def test_basic_isolated_hook(self): run_hook = [] class SimpleHook(PecanHook): def on_route(self, state): run_hook.append('on_route') def before(self, state): run_hook.append('before') def after(self, state): run_hook.append('after') def on_error(self, state, e): run_hook.append('error') class SubSubController(object): @expose() def index(self, req, resp): run_hook.append('inside_sub_sub') return 'Deep inside here!' class SubController(HookController): __hooks__ = [SimpleHook()] @expose() def index(self, req, resp): run_hook.append('inside_sub') return 'Inside here!' sub = SubSubController() class RootController(object): @expose() def index(self, req, resp): run_hook.append('inside') return 'Hello, World!' sub = SubController() app = TestApp(Pecan(RootController(), use_context_locals=False)) response = app.get('/') assert response.status_int == 200 assert response.body == b_('Hello, World!') assert len(run_hook) == 1 assert run_hook[0] == 'inside' run_hook = [] response = app.get('/sub/') assert response.status_int == 200 assert response.body == b_('Inside here!') assert len(run_hook) == 3 assert run_hook[0] == 'before' assert run_hook[1] == 'inside_sub' assert run_hook[2] == 'after' run_hook = [] response = app.get('/sub/sub/') assert response.status_int == 200 assert response.body == b_('Deep inside here!') assert len(run_hook) == 3 assert run_hook[0] == 'before' assert run_hook[1] == 'inside_sub_sub' assert run_hook[2] == 'after' def test_isolated_hook_with_global_hook(self): run_hook = [] class SimpleHook(PecanHook): def __init__(self, id): self.id = str(id) def on_route(self, state): run_hook.append('on_route' + self.id) def before(self, state): run_hook.append('before' + self.id) def after(self, state): run_hook.append('after' + self.id) def on_error(self, state, e): run_hook.append('error' + self.id) class SubController(HookController): __hooks__ = [SimpleHook(2)] @expose() def index(self, req, resp): run_hook.append('inside_sub') return 'Inside here!' class RootController(object): @expose() def index(self, req, resp): run_hook.append('inside') return 'Hello, World!' sub = SubController() app = TestApp(Pecan( RootController(), hooks=[SimpleHook(1)], use_context_locals=False )) response = app.get('/') assert response.status_int == 200 assert response.body == b_('Hello, World!') assert len(run_hook) == 4 assert run_hook[0] == 'on_route1' assert run_hook[1] == 'before1' assert run_hook[2] == 'inside' assert run_hook[3] == 'after1' run_hook = [] response = app.get('/sub/') assert response.status_int == 200 assert response.body == b_('Inside here!') assert len(run_hook) == 6 assert run_hook[0] == 'on_route1' assert run_hook[1] == 'before2' assert run_hook[2] == 'before1' assert run_hook[3] == 'inside_sub' assert run_hook[4] == 'after1' assert run_hook[5] == 'after2' class TestGeneric(PecanTestCase): @property def root(self): class RootController(object): def __init__(self, unique): self.unique = unique @expose(generic=True, template='json') def index(self, req, resp): assert self.__class__.__name__ == 'RootController' assert isinstance(req, Request) assert isinstance(resp, Response) assert self.unique == req.headers.get('X-Unique') return {'hello': 'world'} @index.when(method='POST', template='json') def index_post(self, req, resp): assert self.__class__.__name__ == 'RootController' assert isinstance(req, Request) assert isinstance(resp, Response) assert self.unique == req.headers.get('X-Unique') return req.json @expose(template='json') def echo(self, req, resp): assert self.__class__.__name__ == 'RootController' assert isinstance(req, Request) assert isinstance(resp, Response) assert self.unique == req.headers.get('X-Unique') return req.json @expose(template='json') def extra(self, req, resp, first, second): assert self.__class__.__name__ == 'RootController' assert isinstance(req, Request) assert isinstance(resp, Response) assert self.unique == req.headers.get('X-Unique') return {'first': first, 'second': second} return RootController def test_generics_with_im_self_default(self): uniq = str(time.time()) with mock.patch('threading.local', side_effect=AssertionError()): app = TestApp(Pecan(self.root(uniq), use_context_locals=False)) r = app.get('/', headers={'X-Unique': uniq}) assert r.status_int == 200 json_resp = loads(r.body.decode()) assert json_resp['hello'] == 'world' def test_generics_with_im_self_with_method(self): uniq = str(time.time()) with mock.patch('threading.local', side_effect=AssertionError()): app = TestApp(Pecan(self.root(uniq), use_context_locals=False)) r = app.post_json('/', {'foo': 'bar'}, headers={'X-Unique': uniq}) assert r.status_int == 200 json_resp = loads(r.body.decode()) assert json_resp['foo'] == 'bar' def test_generics_with_im_self_with_path(self): uniq = str(time.time()) with mock.patch('threading.local', side_effect=AssertionError()): app = TestApp(Pecan(self.root(uniq), use_context_locals=False)) r = app.post_json('/echo/', {'foo': 'bar'}, headers={'X-Unique': uniq}) assert r.status_int == 200 json_resp = loads(r.body.decode()) assert json_resp['foo'] == 'bar' def test_generics_with_im_self_with_extra_args(self): uniq = str(time.time()) with mock.patch('threading.local', side_effect=AssertionError()): app = TestApp(Pecan(self.root(uniq), use_context_locals=False)) r = app.get('/extra/123/456', headers={'X-Unique': uniq}) assert r.status_int == 200 json_resp = loads(r.body.decode()) assert json_resp['first'] == '123' assert json_resp['second'] == '456'