import pytest import json from webob.request import Request from webob.dec import wsgify from webob import exc as webob_exc @wsgify def method_not_allowed_app(req): if req.method != 'GET': raise webob_exc.HTTPMethodNotAllowed() return 'hello!' def test_noescape_null(): assert webob_exc.no_escape(None) == '' def test_noescape_not_basestring(): assert webob_exc.no_escape(42) == '42' def test_noescape_unicode(): class DummyUnicodeObject(object): def __unicode__(self): return '42' duo = DummyUnicodeObject() assert webob_exc.no_escape(duo) == '42' def test_strip_tags_empty(): assert webob_exc.strip_tags('') == '' def test_strip_tags_newline_to_space(): assert webob_exc.strip_tags('a\nb') == 'a b' def test_strip_tags_zaps_carriage_return(): assert webob_exc.strip_tags('a\rb') == 'ab' def test_strip_tags_br_to_newline(): assert webob_exc.strip_tags('a
b') == 'a\nb' def test_strip_tags_zaps_comments(): assert webob_exc.strip_tags('a') == 'ab' def test_strip_tags_zaps_tags(): assert webob_exc.strip_tags('foobaz') == 'foobaz' def test_HTTPException(): _called = [] _result = object() def _response(environ, start_response): _called.append((environ, start_response)) return _result environ = {} start_response = object() exc = webob_exc.HTTPException('testing', _response) assert exc.wsgi_response is _response result = exc(environ, start_response) assert result is result assert _called == [(environ, start_response)] def test_exception_with_unicode_data(): req = Request.blank('/', method='POST') res = req.get_response(method_not_allowed_app) assert res.status_code == 405 def test_WSGIHTTPException_headers(): exc = webob_exc.WSGIHTTPException(headers=[('Set-Cookie', 'a=1'), ('Set-Cookie', 'a=2')]) mixed = exc.headers.mixed() assert mixed['set-cookie'] == ['a=1', 'a=2'] def test_WSGIHTTPException_w_body_template(): from string import Template TEMPLATE = '$foo: $bar' exc = webob_exc.WSGIHTTPException(body_template=TEMPLATE) assert exc.body_template == TEMPLATE assert isinstance(exc.body_template_obj, Template) assert exc.body_template_obj.substitute({'foo': 'FOO', 'bar': 'BAR'}) == 'FOO: BAR' def test_WSGIHTTPException_w_empty_body(): class EmptyOnly(webob_exc.WSGIHTTPException): empty_body = True exc = EmptyOnly(content_type='text/plain', content_length=234) assert 'content_type' not in exc.__dict__ assert 'content_length' not in exc.__dict__ def test_WSGIHTTPException___str__(): exc1 = webob_exc.WSGIHTTPException(detail='Detail') assert str(exc1) == 'Detail' class Explain(webob_exc.WSGIHTTPException): explanation = 'Explanation' assert str(Explain()) == 'Explanation' def test_WSGIHTTPException_plain_body_no_comment(): class Explain(webob_exc.WSGIHTTPException): code = '999' title = 'Testing' explanation = 'Explanation' exc = Explain(detail='Detail') assert exc.plain_body({}) == '999 Testing\n\nExplanation\n\n Detail ' def test_WSGIHTTPException_html_body_w_comment(): class Explain(webob_exc.WSGIHTTPException): code = '999' title = 'Testing' explanation = 'Explanation' exc = Explain(detail='Detail', comment='Comment') assert exc.html_body({}) == ( '\n' ' \n' ' 999 Testing\n' ' \n' ' \n' '

999 Testing

\n' ' Explanation

\n' 'Detail\n' '\n\n' ' \n' '' ) def test_WSGIHTTPException_json_body_no_comment(): class ValidationError(webob_exc.WSGIHTTPException): code = '422' title = 'Validation Failed' explanation = 'Validation of an attribute failed.' exc = ValidationError(detail='Attribute "xyz" is invalid.') body = exc.json_body({}) assert json.loads(body) == { "code": "422 Validation Failed", "title": "Validation Failed", "message": "Validation of an attribute failed.

\nAttribute" ' "xyz" is invalid.\n\n', } def test_WSGIHTTPException_respects_application_json(): class ValidationError(webob_exc.WSGIHTTPException): code = '422' title = 'Validation Failed' explanation = 'Validation of an attribute failed.' def start_response(status, headers, exc_info=None): # check that json doesn't contain a charset assert ('Content-Type', 'application/json') in headers pass exc = ValidationError(detail='Attribute "xyz" is invalid.') resp = exc.generate_response(environ={ 'wsgi.url_scheme': 'HTTP', 'SERVER_NAME': 'localhost', 'SERVER_PORT': '80', 'REQUEST_METHOD': 'PUT', 'HTTP_ACCEPT': 'application/json', }, start_response=start_response) assert json.loads(resp[0].decode('utf-8')) == { "code": "422 Validation Failed", "title": "Validation Failed", "message": "Validation of an attribute failed.

\nAttribute" ' "xyz" is invalid.\n\n', } def test_WSGIHTTPException_respects_accept_text_html(): def start_response(status, headers, exc_info=None): for header in headers: if header[0] == 'Content-Type': assert header[1].startswith('text/html') exc = webob_exc.WSGIHTTPException() resp = exc.generate_response(environ={ 'wsgi.url_scheme': 'HTTP', 'SERVER_NAME': 'localhost', 'SERVER_PORT': '80', 'REQUEST_METHOD': 'GET', 'HTTP_ACCEPT': 'text/html', }, start_response=start_response) def test_WSGIHTTPException_respects_accept_text_plain(): def start_response(status, headers, exc_info=None): for header in headers: if header[0] == 'Content-Type': assert header[1].startswith('text/plain') exc = webob_exc.WSGIHTTPException() resp = exc.generate_response(environ={ 'wsgi.url_scheme': 'HTTP', 'SERVER_NAME': 'localhost', 'SERVER_PORT': '80', 'REQUEST_METHOD': 'GET', 'HTTP_ACCEPT': 'text/plain', }, start_response=start_response) def test_WSGIHTTPException_respects_accept_star_star(): def start_response(status, headers, exc_info=None): for header in headers: if header[0] == 'Content-Type': assert header[1].startswith('text/html') exc = webob_exc.WSGIHTTPException() resp = exc.generate_response(environ={ 'wsgi.url_scheme': 'HTTP', 'SERVER_NAME': 'localhost', 'SERVER_PORT': '80', 'REQUEST_METHOD': 'GET', 'HTTP_ACCEPT': '*/*', }, start_response=start_response) def test_WSGIHTTPException_allows_custom_json_formatter(): def json_formatter(body, status, title, environ): return {"fake": True} class ValidationError(webob_exc.WSGIHTTPException): code = '422' title = 'Validation Failed' explanation = 'Validation of an attribute failed.' exc = ValidationError(detail='Attribute "xyz" is invalid.', json_formatter=json_formatter) body = exc.json_body({}) assert json.loads(body) == {"fake": True} def test_WSGIHTTPException_generate_response(): def start_response(status, headers, exc_info=None): assert ('Content-Type', 'text/html; charset=UTF-8') in headers pass environ = { 'wsgi.url_scheme': 'HTTP', 'SERVER_NAME': 'localhost', 'SERVER_PORT': '80', 'REQUEST_METHOD': 'PUT', 'HTTP_ACCEPT': 'text/html' } excep = webob_exc.WSGIHTTPException() assert excep(environ, start_response) == [ b'\n' b' \n' b' 500 Internal Server Error\n' b' \n' b' \n' b'

500 Internal Server Error

\n' b'

\n' b'\n' b'\n\n' b' \n' b''] def test_WSGIHTTPException_call_w_body(): def start_response(status, headers, exc_info=None): pass environ = { 'wsgi.url_scheme': 'HTTP', 'SERVER_NAME': 'localhost', 'SERVER_PORT': '80', 'REQUEST_METHOD': 'PUT' } excep = webob_exc.WSGIHTTPException() excep.body = b'test' assert excep(environ,start_response) == [b'test'] def test_WSGIHTTPException_wsgi_response(): def start_response(status, headers, exc_info=None): pass environ = { 'wsgi.url_scheme': 'HTTP', 'SERVER_NAME': 'localhost', 'SERVER_PORT': '80', 'REQUEST_METHOD': 'HEAD' } excep = webob_exc.WSGIHTTPException() assert excep.wsgi_response(environ,start_response) == [] def test_WSGIHTTPException_exception_newstyle(): def start_response(status, headers, exc_info=None): pass environ = { 'wsgi.url_scheme': 'HTTP', 'SERVER_NAME': 'localhost', 'SERVER_PORT': '80', 'REQUEST_METHOD': 'HEAD' } excep = webob_exc.WSGIHTTPException() webob_exc.newstyle_exceptions = True assert excep(environ,start_response) == [] def test_WSGIHTTPException_exception_no_newstyle(): def start_response(status, headers, exc_info=None): pass environ = { 'wsgi.url_scheme': 'HTTP', 'SERVER_NAME': 'localhost', 'SERVER_PORT': '80', 'REQUEST_METHOD': 'HEAD' } excep = webob_exc.WSGIHTTPException() webob_exc.newstyle_exceptions = False assert excep(environ,start_response) == [] def test_HTTPOk_head_of_proxied_head(): # first set up a response to a HEAD request HELLO_WORLD = "Hi!\n" CONTENT_TYPE = "application/hello" def head_app(environ, start_response): """An application object that understands HEAD""" status = '200 OK' response_headers = [('Content-Type', CONTENT_TYPE), ('Content-Length', len(HELLO_WORLD))] start_response(status, response_headers) if environ['REQUEST_METHOD'] == 'HEAD': return [] else: return [HELLO_WORLD] def verify_response(resp, description): assert resp.content_type == CONTENT_TYPE, description assert resp.content_length == len(HELLO_WORLD), description assert resp.body == b'', description req = Request.blank('/', method='HEAD') resp1 = req.get_response(head_app) verify_response(resp1, "first response") # Copy the response like a proxy server would. # Copying an empty body has set content_length # so copy the headers only afterwards. resp2 = webob_exc.status_map[resp1.status_int](request=req) resp2.body = resp1.body resp2.headerlist = resp1.headerlist verify_response(resp2, "copied response") # evaluate it again resp3 = req.get_response(resp2) verify_response(resp3, "evaluated copy") def test_HTTPMove(): def start_response(status, headers, exc_info=None): pass environ = { 'wsgi.url_scheme': 'HTTP', 'SERVER_NAME': 'localhost', 'SERVER_PORT': '80', 'REQUEST_METHOD': 'HEAD', 'PATH_INFO': '/', } m = webob_exc._HTTPMove() assert m( environ, start_response ) == [] def test_HTTPMove_location_not_none(): def start_response(status, headers, exc_info=None): pass environ = { 'wsgi.url_scheme': 'HTTP', 'SERVER_NAME': 'localhost', 'SERVER_PORT': '80', 'REQUEST_METHOD': 'HEAD', 'PATH_INFO': '/', } m = webob_exc._HTTPMove(location='http://example.com') assert m( environ, start_response ) == [] def test_HTTPMove_location_newlines(): environ = { 'wsgi.url_scheme': 'HTTP', 'SERVER_NAME': 'localhost', 'SERVER_PORT': '80', 'REQUEST_METHOD': 'HEAD', 'PATH_INFO': '/', } with pytest.raises(ValueError): webob_exc._HTTPMove(location='http://example.com\r\nX-Test: false') def test_HTTPMove_add_slash_and_location(): def start_response(status, headers, exc_info=None): pass with pytest.raises(TypeError): webob_exc._HTTPMove(location='http://example.com', add_slash=True) def test_HTTPMove_call_add_slash(): def start_response(status, headers, exc_info=None): pass environ = { 'wsgi.url_scheme': 'HTTP', 'SERVER_NAME': 'localhost', 'SERVER_PORT': '80', 'REQUEST_METHOD': 'HEAD', 'PATH_INFO': '/', } m = webob_exc._HTTPMove() m.add_slash = True assert m( environ, start_response ) == [] def test_HTTPMove_call_query_string(): def start_response(status, headers, exc_info=None): pass environ = { 'wsgi.url_scheme': 'HTTP', 'SERVER_NAME': 'localhost', 'SERVER_PORT': '80', 'REQUEST_METHOD': 'HEAD' } m = webob_exc._HTTPMove() m.add_slash = True environ[ 'QUERY_STRING' ] = 'querystring' environ['PATH_INFO'] = '/' assert m( environ, start_response ) == [] def test_HTTPFound_unused_environ_variable(): class Crashy(object): def __str__(self): raise Exception('I crashed!') def start_response(status, headers, exc_info=None): pass environ = { 'wsgi.url_scheme': 'HTTP', 'SERVER_NAME': 'localhost', 'SERVER_PORT': '80', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/', 'HTTP_ACCEPT': 'text/html', 'crashy': Crashy() } m = webob_exc._HTTPMove(location='http://www.example.com') assert m(environ, start_response) == [ b'\n' b' \n' b' 500 Internal Server Error\n' b' \n' b' \n' b'

500 Internal Server Error

\n' b' The resource has been moved to ' b'' b'http://www.example.com;\n' b'you should be redirected automatically.\n' b'\n\n' b' \n' b''] def test_HTTPExceptionMiddleware_ok(): def app(environ, start_response): return '123' application = app m = webob_exc.HTTPExceptionMiddleware(application) environ = {} start_response = None res = m(environ, start_response) assert res == '123' def test_HTTPExceptionMiddleware_exception(): def wsgi_response(environ, start_response): return '123' def app(environ, start_response): raise webob_exc.HTTPException(None, wsgi_response) application = app m = webob_exc.HTTPExceptionMiddleware(application) environ = {} start_response = None res = m(environ, start_response) assert res == '123' def test_HTTPExceptionMiddleware_exception_exc_info_none(): class DummySys: def exc_info(self): return None def wsgi_response(environ, start_response): return start_response('200 OK', [], exc_info=None) def app(environ, start_response): raise webob_exc.HTTPException(None, wsgi_response) application = app m = webob_exc.HTTPExceptionMiddleware(application) environ = {} def start_response(status, headers, exc_info): pass try: old_sys = webob_exc.sys sys = DummySys() res = m(environ, start_response) assert res is None finally: webob_exc.sys = old_sys def test_status_map_is_deterministic(): for code, cls in ( (200, webob_exc.HTTPOk), (201, webob_exc.HTTPCreated), (202, webob_exc.HTTPAccepted), (203, webob_exc.HTTPNonAuthoritativeInformation), (204, webob_exc.HTTPNoContent), (205, webob_exc.HTTPResetContent), (206, webob_exc.HTTPPartialContent), (300, webob_exc.HTTPMultipleChoices), (301, webob_exc.HTTPMovedPermanently), (302, webob_exc.HTTPFound), (303, webob_exc.HTTPSeeOther), (304, webob_exc.HTTPNotModified), (305, webob_exc.HTTPUseProxy), (307, webob_exc.HTTPTemporaryRedirect), (308, webob_exc.HTTPPermanentRedirect), (400, webob_exc.HTTPBadRequest), (401, webob_exc.HTTPUnauthorized), (402, webob_exc.HTTPPaymentRequired), (403, webob_exc.HTTPForbidden), (404, webob_exc.HTTPNotFound), (405, webob_exc.HTTPMethodNotAllowed), (406, webob_exc.HTTPNotAcceptable), (407, webob_exc.HTTPProxyAuthenticationRequired), (408, webob_exc.HTTPRequestTimeout), (409, webob_exc.HTTPConflict), (410, webob_exc.HTTPGone), (411, webob_exc.HTTPLengthRequired), (412, webob_exc.HTTPPreconditionFailed), (413, webob_exc.HTTPRequestEntityTooLarge), (414, webob_exc.HTTPRequestURITooLong), (415, webob_exc.HTTPUnsupportedMediaType), (416, webob_exc.HTTPRequestRangeNotSatisfiable), (417, webob_exc.HTTPExpectationFailed), (422, webob_exc.HTTPUnprocessableEntity), (423, webob_exc.HTTPLocked), (424, webob_exc.HTTPFailedDependency), (428, webob_exc.HTTPPreconditionRequired), (429, webob_exc.HTTPTooManyRequests), (431, webob_exc.HTTPRequestHeaderFieldsTooLarge), (451, webob_exc.HTTPUnavailableForLegalReasons), (500, webob_exc.HTTPInternalServerError), (501, webob_exc.HTTPNotImplemented), (502, webob_exc.HTTPBadGateway), (503, webob_exc.HTTPServiceUnavailable), (504, webob_exc.HTTPGatewayTimeout), (505, webob_exc.HTTPVersionNotSupported), (511, webob_exc.HTTPNetworkAuthenticationRequired), ): assert webob_exc.status_map[code] == cls