import json
import pytest
from webob import exc as webob_exc
from webob.dec import wsgify
from webob.request import Request
@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:
def __str__(self):
return "42"
duo = DummyUnicodeObject()
assert webob_exc.no_escape(duo) == "42"
def test_noescape_bytes():
assert webob_exc.no_escape(b"test") == "test"
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()
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()
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()
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():
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:
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
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