commit
dc79fea9db
|
@ -47,7 +47,7 @@ class Application(object):
|
|||
configuration is copied and assigned to ``request.config``.
|
||||
'''
|
||||
|
||||
DEFAULT_ALLOWED_METHODS = ['GET', 'HEAD', 'OPTIONS']
|
||||
DEFAULT_ALLOWED_METHODS = ['GET', 'HEAD']
|
||||
|
||||
def __init__(self, dispatcher):
|
||||
self.dispatcher = dispatcher
|
||||
|
@ -83,8 +83,9 @@ class Application(object):
|
|||
response.headers = allow_header
|
||||
return response
|
||||
func = options_response
|
||||
return HTTPMethodNotAllowed(headers=allow_header)(environ,
|
||||
start_response)
|
||||
else:
|
||||
func = HTTPMethodNotAllowed(headers=allow_header)
|
||||
return func(environ, start_response)
|
||||
|
||||
start_response_wrapper = StartResponseWrapper(start_response)
|
||||
request.start_response = start_response_wrapper
|
||||
|
|
|
@ -0,0 +1,396 @@
|
|||
'''Tests for the main Application logic
|
||||
|
||||
:Created: Nov 27, 2012
|
||||
:Author: dustin
|
||||
'''
|
||||
import functools
|
||||
import milla.app
|
||||
import milla.dispatch
|
||||
import nose.tools
|
||||
import wsgiref.util
|
||||
import webob.exc
|
||||
|
||||
class StubResolver(object):
|
||||
'''Stub resolver for testing purposes'''
|
||||
|
||||
def __init__(self, controller=None):
|
||||
if not controller:
|
||||
def controller(request):
|
||||
return 'success'
|
||||
self.controller = controller
|
||||
|
||||
def resolve(self, path_info):
|
||||
return self.controller
|
||||
|
||||
class StubResolverUnresolved(object):
|
||||
'''Stub resolver that always raises UnresolvedPath'''
|
||||
|
||||
def resolve(self, path_info):
|
||||
raise milla.dispatch.UnresolvedPath()
|
||||
|
||||
class ResponseMaker(object):
|
||||
|
||||
def __init__(self, http_version=1.1):
|
||||
self.http_version = http_version
|
||||
self.headers = ''
|
||||
self.body = ''
|
||||
|
||||
def start_response(self, status, response_headers):
|
||||
self.headers += 'HTTP/{0} {1}\r\n'.format(self.http_version, status)
|
||||
for header, value in response_headers:
|
||||
self.headers += '{0}: {1}\r\n'.format(header, value)
|
||||
|
||||
def finish_response(self, app_iter):
|
||||
for data in app_iter:
|
||||
self.body += data
|
||||
|
||||
def testing_environ():
|
||||
environ = {}
|
||||
wsgiref.util.setup_testing_defaults(environ)
|
||||
return environ
|
||||
|
||||
class AfterCalled(Exception):
|
||||
'''Raised in tests for the __after__ method'''
|
||||
|
||||
class BeforeCalled(Exception):
|
||||
'''Raised in tests for the __before__ method'''
|
||||
|
||||
|
||||
def test_notfound():
|
||||
'''Application returns a 404 response for unresolved paths
|
||||
'''
|
||||
|
||||
app = milla.app.Application(StubResolverUnresolved())
|
||||
environ = testing_environ()
|
||||
response = ResponseMaker()
|
||||
app_iter = app(environ, response.start_response)
|
||||
response.finish_response(app_iter)
|
||||
assert response.headers.startswith('HTTP/1.1 404'), response.headers
|
||||
|
||||
def test_favicon():
|
||||
'''Application returns the default favicon image when requested
|
||||
'''
|
||||
|
||||
app = milla.app.Application(StubResolverUnresolved())
|
||||
environ = testing_environ()
|
||||
environ.update({'PATH_INFO': '/favicon.ico'})
|
||||
response = ResponseMaker()
|
||||
app_iter = app(environ, response.start_response)
|
||||
response.finish_response(app_iter)
|
||||
assert response.headers.startswith('HTTP/1.1 200'), response.headers
|
||||
assert response.body.startswith('\x00\x00\x01\x00'), response.body
|
||||
|
||||
def test_allow_header_disallowed():
|
||||
'''HTTP 405 is returned for disallowed HTTP request methods
|
||||
'''
|
||||
|
||||
app = milla.app.Application(StubResolver())
|
||||
environ = testing_environ()
|
||||
environ.update({'REQUEST_METHOD': 'POST'})
|
||||
response = ResponseMaker()
|
||||
app_iter = app(environ, response.start_response)
|
||||
response.finish_response(app_iter)
|
||||
assert response.headers.startswith('HTTP/1.1 405'), response.headers
|
||||
|
||||
def test_allow_header_allowed():
|
||||
'''HTTP 405 is not returned for explicitly allowed HTTP request methods
|
||||
'''
|
||||
|
||||
resolver = StubResolver()
|
||||
resolver.controller.allowed_methods = ('POST',)
|
||||
app = milla.app.Application(resolver)
|
||||
environ = testing_environ()
|
||||
environ.update({'REQUEST_METHOD': 'POST'})
|
||||
response = ResponseMaker()
|
||||
app_iter = app(environ, response.start_response)
|
||||
response.finish_response(app_iter)
|
||||
assert response.headers.startswith('HTTP/1.1 200'), response.headers
|
||||
|
||||
def test_allow_header_options():
|
||||
'''HTTP OPTIONS requests returns HTTP 200
|
||||
'''
|
||||
|
||||
resolver = StubResolver()
|
||||
resolver.controller.allowed_methods = ('GET',)
|
||||
app = milla.app.Application(resolver)
|
||||
environ = testing_environ()
|
||||
environ.update({'REQUEST_METHOD': 'OPTIONS'})
|
||||
response = ResponseMaker()
|
||||
app_iter = app(environ, response.start_response)
|
||||
response.finish_response(app_iter)
|
||||
assert response.headers.startswith('HTTP/1.1 200'), response.headers
|
||||
|
||||
def test_emulated_method():
|
||||
'''Emulated HTTP methods are interpreted correctly
|
||||
|
||||
For applications that cannot use the proper HTTP method and instead
|
||||
use HTTP POST with an ``_method`` parameter
|
||||
'''
|
||||
|
||||
resolver = StubResolver()
|
||||
resolver.controller.allowed_methods = ('PUT',)
|
||||
app = milla.app.Application(resolver)
|
||||
environ = testing_environ()
|
||||
environ.update({
|
||||
'REQUEST_METHOD': 'POST',
|
||||
'CONTENT_TYPE': 'application/x-www-form-urlencoded',
|
||||
'CONTENT_LENGTH': '11'
|
||||
})
|
||||
body = environ['wsgi.input']
|
||||
body.seek(0)
|
||||
body.write('_method=PUT')
|
||||
body.seek(0)
|
||||
response = ResponseMaker()
|
||||
app_iter = app(environ, response.start_response)
|
||||
response.finish_response(app_iter)
|
||||
assert response.headers.startswith('HTTP/1.1 200'), response.headers
|
||||
|
||||
@nose.tools.raises(BeforeCalled)
|
||||
def test_function_before():
|
||||
'''__before__ attribute is called for controller functions
|
||||
'''
|
||||
|
||||
def before(request):
|
||||
raise BeforeCalled()
|
||||
|
||||
resolver = StubResolver()
|
||||
resolver.controller.__before__ = before
|
||||
app = milla.app.Application(resolver)
|
||||
environ = testing_environ()
|
||||
response = ResponseMaker()
|
||||
app(environ, response.start_response)
|
||||
|
||||
@nose.tools.raises(BeforeCalled)
|
||||
def test_instance_before():
|
||||
'''Class's __before__ is called for controller instances
|
||||
'''
|
||||
|
||||
class Controller(object):
|
||||
def __before__(self, request):
|
||||
raise BeforeCalled()
|
||||
def __call__(self, request):
|
||||
return 'success'
|
||||
|
||||
app = milla.app.Application(StubResolver(Controller()))
|
||||
environ = testing_environ()
|
||||
response = ResponseMaker()
|
||||
app(environ, response.start_response)
|
||||
|
||||
@nose.tools.raises(BeforeCalled)
|
||||
def test_instancemethod_before():
|
||||
'''Class's __before__ is called for controller instance methods
|
||||
'''
|
||||
|
||||
class Controller(object):
|
||||
def __before__(self, request):
|
||||
raise BeforeCalled()
|
||||
def foo(self, request):
|
||||
return 'success'
|
||||
|
||||
app = milla.app.Application(StubResolver(Controller().foo))
|
||||
environ = testing_environ()
|
||||
response = ResponseMaker()
|
||||
app(environ, response.start_response)
|
||||
|
||||
@nose.tools.raises(BeforeCalled)
|
||||
def test_partial_function_before():
|
||||
'''__before__ attribute is called for wrapped controller functions
|
||||
'''
|
||||
|
||||
def before(request):
|
||||
raise BeforeCalled()
|
||||
def controller(request, text):
|
||||
return text
|
||||
controller.__before__ = before
|
||||
|
||||
resolver = StubResolver()
|
||||
resolver.controller = functools.partial(controller, text='success')
|
||||
app = milla.app.Application(resolver)
|
||||
environ = testing_environ()
|
||||
response = ResponseMaker()
|
||||
app(environ, response.start_response)
|
||||
|
||||
@nose.tools.raises(BeforeCalled)
|
||||
def test_partial_instance_before():
|
||||
'''Class's __before__ is called for wrapped controller instances
|
||||
'''
|
||||
|
||||
class Controller(object):
|
||||
def __before__(self, request):
|
||||
raise BeforeCalled()
|
||||
def __call__(self, request, text):
|
||||
return text
|
||||
|
||||
resolver = StubResolver()
|
||||
resolver.controller = functools.partial(Controller(), text='success')
|
||||
app = milla.app.Application(resolver)
|
||||
environ = testing_environ()
|
||||
response = ResponseMaker()
|
||||
app(environ, response.start_response)
|
||||
|
||||
@nose.tools.raises(BeforeCalled)
|
||||
def test_partial_instancemethod_before():
|
||||
'''Class's __before__ is called for wrapped controller instance methods
|
||||
'''
|
||||
|
||||
class Controller(object):
|
||||
def __before__(self, request):
|
||||
raise BeforeCalled()
|
||||
def foo(self, request, text):
|
||||
if not hasattr(request, 'before_called'):
|
||||
return 'before not called'
|
||||
else:
|
||||
return text
|
||||
|
||||
resolver = StubResolver()
|
||||
resolver.controller = functools.partial(Controller().foo, text='success')
|
||||
app = milla.app.Application(resolver)
|
||||
environ = testing_environ()
|
||||
response = ResponseMaker()
|
||||
app(environ, response.start_response)
|
||||
|
||||
@nose.tools.raises(AfterCalled)
|
||||
def test_function_after():
|
||||
'''__after__ attribute is called for controller functions
|
||||
'''
|
||||
|
||||
def after(request):
|
||||
raise AfterCalled()
|
||||
|
||||
resolver = StubResolver()
|
||||
resolver.controller.__after__ = after
|
||||
app = milla.app.Application(resolver)
|
||||
environ = testing_environ()
|
||||
response = ResponseMaker()
|
||||
app(environ, response.start_response)
|
||||
|
||||
@nose.tools.raises(AfterCalled)
|
||||
def test_instance_after():
|
||||
'''Class's __after__ is called for controller instances
|
||||
'''
|
||||
|
||||
class Controller(object):
|
||||
def __after__(self, request):
|
||||
raise AfterCalled()
|
||||
def __call__(self, request):
|
||||
return 'success'
|
||||
|
||||
app = milla.app.Application(StubResolver(Controller()))
|
||||
environ = testing_environ()
|
||||
response = ResponseMaker()
|
||||
app(environ, response.start_response)
|
||||
|
||||
@nose.tools.raises(AfterCalled)
|
||||
def test_instancemethod_after():
|
||||
'''Class's __after__ is called for controller instance methods
|
||||
'''
|
||||
|
||||
class Controller(object):
|
||||
def __after__(self, request):
|
||||
raise AfterCalled()
|
||||
def foo(self, request):
|
||||
return 'success'
|
||||
|
||||
app = milla.app.Application(StubResolver(Controller().foo))
|
||||
environ = testing_environ()
|
||||
response = ResponseMaker()
|
||||
app(environ, response.start_response)
|
||||
|
||||
@nose.tools.raises(AfterCalled)
|
||||
def test_partial_function_after():
|
||||
'''__after__ attribute is called for wrapped controller functions
|
||||
'''
|
||||
|
||||
def after(request):
|
||||
raise AfterCalled()
|
||||
def controller(request, text):
|
||||
return text
|
||||
controller.__after__ = after
|
||||
|
||||
resolver = StubResolver()
|
||||
resolver.controller = functools.partial(controller, text='success')
|
||||
app = milla.app.Application(resolver)
|
||||
environ = testing_environ()
|
||||
response = ResponseMaker()
|
||||
app(environ, response.start_response)
|
||||
|
||||
@nose.tools.raises(AfterCalled)
|
||||
def test_partial_instance_after():
|
||||
'''Class's __after__ is called for wrapped controller instances
|
||||
'''
|
||||
|
||||
class Controller(object):
|
||||
def __after__(self, request):
|
||||
raise AfterCalled()
|
||||
def __call__(self, request, text):
|
||||
return text
|
||||
|
||||
resolver = StubResolver()
|
||||
resolver.controller = functools.partial(Controller(), text='success')
|
||||
app = milla.app.Application(resolver)
|
||||
environ = testing_environ()
|
||||
response = ResponseMaker()
|
||||
app(environ, response.start_response)
|
||||
|
||||
@nose.tools.raises(AfterCalled)
|
||||
def test_partial_instancemethod_after():
|
||||
'''Class's __after__ is called for wrapped controller instance methods
|
||||
'''
|
||||
|
||||
class Controller(object):
|
||||
def __after__(self, request):
|
||||
raise AfterCalled()
|
||||
def foo(self, request, text):
|
||||
if not hasattr(request, 'after_called'):
|
||||
return 'after not called'
|
||||
else:
|
||||
return text
|
||||
|
||||
resolver = StubResolver()
|
||||
resolver.controller = functools.partial(Controller().foo, text='success')
|
||||
app = milla.app.Application(resolver)
|
||||
environ = testing_environ()
|
||||
response = ResponseMaker()
|
||||
app(environ, response.start_response)
|
||||
|
||||
def test_httperror_response():
|
||||
'''HTTPErrors raised by controllers should used as the response
|
||||
'''
|
||||
|
||||
def controller(request):
|
||||
raise webob.exc.HTTPClientError()
|
||||
|
||||
app = milla.app.Application(StubResolver(controller))
|
||||
environ = testing_environ()
|
||||
response = ResponseMaker()
|
||||
app_iter = app(environ, response.start_response)
|
||||
response.finish_response(app_iter)
|
||||
assert response.headers.startswith('HTTP/1.1 400'), response.headers
|
||||
assert webob.exc.HTTPClientError.explanation in response.body, response.body
|
||||
|
||||
def test_single_start_response():
|
||||
'''Ensure start_response is only called once'''
|
||||
|
||||
class TestStartResponse(object):
|
||||
def __init__(self, func):
|
||||
self.call_count = 0
|
||||
self.func = func
|
||||
def __call__(self, *args, **kwargs):
|
||||
self.call_count += 1
|
||||
return self.func(*args, **kwargs)
|
||||
|
||||
def controller(request):
|
||||
status = '200 OK'
|
||||
headers = [('Content-Type', 'text/plain')]
|
||||
request.start_response(status, headers)
|
||||
return 'test'
|
||||
|
||||
app = milla.app.Application(StubResolver(controller))
|
||||
environ = testing_environ()
|
||||
response = ResponseMaker()
|
||||
start_response = TestStartResponse(response.start_response)
|
||||
app_iter = app(environ, start_response)
|
||||
response.finish_response(app_iter)
|
||||
assert start_response.call_count == 1, start_response.call_count
|
||||
assert response.headers.startswith('HTTP/1.1 200 OK'), response.headers
|
||||
assert response.body == 'test', response.body
|
Loading…
Reference in New Issue