Merged default into py3k

--HG--
branch : py3k
master
Dustin C. Hatch 2012-11-30 13:19:55 -06:00
commit dc79fea9db
2 changed files with 400 additions and 3 deletions

View File

@ -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

396
src/milla/tests/test_app.py Normal file
View File

@ -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