summaryrefslogtreecommitdiff
path: root/tests/test_exceptions
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_exceptions')
-rw-r--r--tests/test_exceptions/__init__.py1
-rw-r--r--tests/test_exceptions/test_error_middleware.py109
-rw-r--r--tests/test_exceptions/test_formatter.py183
-rw-r--r--tests/test_exceptions/test_httpexceptions.py97
-rw-r--r--tests/test_exceptions/test_reporter.py50
5 files changed, 440 insertions, 0 deletions
diff --git a/tests/test_exceptions/__init__.py b/tests/test_exceptions/__init__.py
new file mode 100644
index 0000000..792d600
--- /dev/null
+++ b/tests/test_exceptions/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/tests/test_exceptions/test_error_middleware.py b/tests/test_exceptions/test_error_middleware.py
new file mode 100644
index 0000000..95ab177
--- /dev/null
+++ b/tests/test_exceptions/test_error_middleware.py
@@ -0,0 +1,109 @@
+from paste.fixture import *
+from paste.exceptions.errormiddleware import ErrorMiddleware
+from paste import lint
+from paste.util.quoting import strip_html
+#
+# For some strange reason, these 4 lines cannot be removed or the regression
+# test breaks; is it counting the number of lines in the file somehow?
+#
+
+def do_request(app, expect_status=500):
+ app = lint.middleware(app)
+ app = ErrorMiddleware(app, {}, debug=True)
+ app = clear_middleware(app)
+ testapp = TestApp(app)
+ res = testapp.get('', status=expect_status,
+ expect_errors=True)
+ return res
+
+def clear_middleware(app):
+ """
+ The fixture sets paste.throw_errors, which suppresses exactly what
+ we want to test in this case. This wrapper also strips exc_info
+ on the *first* call to start_response (but not the second, or
+ subsequent calls.
+ """
+ def clear_throw_errors(environ, start_response):
+ headers_sent = []
+ def replacement(status, headers, exc_info=None):
+ if headers_sent:
+ return start_response(status, headers, exc_info)
+ headers_sent.append(True)
+ return start_response(status, headers)
+ if 'paste.throw_errors' in environ:
+ del environ['paste.throw_errors']
+ return app(environ, replacement)
+ return clear_throw_errors
+
+
+############################################################
+## Applications that raise exceptions
+############################################################
+
+def bad_app():
+ "No argument list!"
+ return None
+
+def unicode_bad_app(environ, start_response):
+ raise ValueError(u"\u1000")
+
+def start_response_app(environ, start_response):
+ "raise error before start_response"
+ raise ValueError("hi")
+
+def after_start_response_app(environ, start_response):
+ start_response("200 OK", [('Content-type', 'text/plain')])
+ raise ValueError('error2')
+
+def iter_app(environ, start_response):
+ start_response("200 OK", [('Content-type', 'text/plain')])
+ return yielder([b'this', b' is ', b' a', None])
+
+def yielder(args):
+ for arg in args:
+ if arg is None:
+ raise ValueError("None raises error")
+ yield arg
+
+############################################################
+## Tests
+############################################################
+
+def test_makes_exception():
+ res = do_request(bad_app)
+ assert '<html' in res
+ res = strip_html(str(res))
+ if six.PY3:
+ assert 'bad_app() takes 0 positional arguments but 2 were given' in res
+ else:
+ assert 'bad_app() takes no arguments (2 given' in res, repr(res)
+ assert 'iterator = application(environ, start_response_wrapper)' in res
+ assert 'paste.lint' in res
+ assert 'paste.exceptions.errormiddleware' in res
+
+def test_unicode_exception():
+ res = do_request(unicode_bad_app)
+
+
+def test_start_res():
+ res = do_request(start_response_app)
+ res = strip_html(str(res))
+ assert 'ValueError: hi' in res
+ assert 'test_error_middleware' in res
+ assert ':52 in start_response_app' in res
+
+def test_after_start():
+ res = do_request(after_start_response_app, 200)
+ res = strip_html(str(res))
+ #print res
+ assert 'ValueError: error2' in res
+
+def test_iter_app():
+ res = do_request(lint.middleware(iter_app), 200)
+ #print res
+ assert 'None raises error' in res
+ assert 'yielder' in res
+
+
+
+
diff --git a/tests/test_exceptions/test_formatter.py b/tests/test_exceptions/test_formatter.py
new file mode 100644
index 0000000..9c53a9a
--- /dev/null
+++ b/tests/test_exceptions/test_formatter.py
@@ -0,0 +1,183 @@
+from paste.exceptions import formatter
+from paste.exceptions import collector
+import sys
+import os
+import difflib
+
+class Mock(object):
+ def __init__(self, **kw):
+ for name, value in kw.items():
+ setattr(self, name, value)
+
+class Supplement(Mock):
+
+ object = 'test_object'
+ source_url = 'http://whatever.com'
+ info = 'This is some supplemental information'
+ args = ()
+ def getInfo(self):
+ return self.info
+
+ def __call__(self, *args):
+ self.args = args
+ return self
+
+class BadSupplement(Supplement):
+
+ def getInfo(self):
+ raise ValueError("This supplemental info is buggy")
+
+def call_error(sup):
+ 1 + 2
+ __traceback_supplement__ = (sup, ())
+ assert 0, "I am an error"
+
+def raise_error(sup='default'):
+ if sup == 'default':
+ sup = Supplement()
+ for i in range(10):
+ __traceback_info__ = i
+ if i == 5:
+ call_error(sup=sup)
+
+def hide(t, inner, *args, **kw):
+ __traceback_hide__ = t
+ return inner(*args, **kw)
+
+def pass_through(info, inner, *args, **kw):
+ """
+ To add another frame to the call; detectable because
+ __tracback_info__ is set to `info`
+ """
+ __traceback_info__ = info
+ return inner(*args, **kw)
+
+def format(type='html', **ops):
+ data = collector.collect_exception(*sys.exc_info())
+ report = getattr(formatter, 'format_' + type)(data, **ops)
+ return report
+
+formats = ('text', 'html')
+
+def test_excersize():
+ for f in formats:
+ try:
+ raise_error()
+ except:
+ format(f)
+
+def test_content():
+ for f in formats:
+ try:
+ raise_error()
+ except:
+ result = format(f)
+ print(result)
+ assert 'test_object' in result
+ assert 'http://whatever.com' in result
+ assert 'This is some supplemental information' in result
+ assert 'raise_error' in result
+ assert 'call_error' in result
+ assert '5' in result
+ assert 'test_content' in result
+ else:
+ assert 0
+
+def test_trim():
+ current = os.path.abspath(os.getcwd())
+ for f in formats:
+ try:
+ raise_error()
+ except:
+ result = format(f, trim_source_paths=[(current, '.')])
+ assert current not in result
+ assert ('%stest_formatter.py' % os.sep) in result, ValueError(repr(result))
+ else:
+ assert 0
+
+def test_hide():
+ for f in formats:
+ try:
+ hide(True, raise_error)
+ except:
+ result = format(f)
+ print(result)
+ assert 'in hide_inner' not in result
+ assert 'inner(*args, **kw)' not in result
+ else:
+ assert 0
+
+def print_diff(s1, s2):
+ differ = difflib.Differ()
+ result = list(differ.compare(s1.splitlines(), s2.splitlines()))
+ print('\n'.join(result))
+
+def test_hide_supppressed():
+ """
+ When an error occurs and __traceback_stop__ is true for the
+ erroneous frame, then that setting should be ignored.
+ """
+ for f in ['html']: #formats:
+ results = []
+ for hide_value in (False, 'after'):
+ try:
+ pass_through(
+ 'a',
+ hide,
+ hide_value,
+ pass_through,
+ 'b',
+ raise_error)
+ except:
+ results.append(format(f))
+ else:
+ assert 0
+ if results[0] != results[1]:
+ print_diff(results[0], results[1])
+ assert 0
+
+def test_hide_after():
+ for f in formats:
+ try:
+ pass_through(
+ 'AABB',
+ hide, 'after',
+ pass_through, 'CCDD',
+ # A little whitespace to keep this line out of the
+ # content part of the report
+
+
+ hide, 'reset',
+ raise_error)
+ except:
+ result = format(f)
+ assert 'AABB' in result
+ assert 'CCDD' not in result
+ assert 'raise_error' in result
+ else:
+ assert 0
+
+def test_hide_before():
+ for f in formats:
+ try:
+ pass_through(
+ 'AABB',
+ hide, 'before',
+ raise_error)
+ except:
+ result = format(f)
+ print(result)
+ assert 'AABB' not in result
+ assert 'raise_error' in result
+ else:
+ assert 0
+
+def test_make_wrappable():
+ assert '<wbr>' in formatter.make_wrappable('x'*1000)
+ # I'm just going to test that this doesn't excede the stack limit:
+ formatter.make_wrappable(';'*2000)
+ assert (formatter.make_wrappable('this that the other')
+ == 'this that the other')
+ assert (formatter.make_wrappable('this that ' + ('x'*50) + ';' + ('y'*50) + ' and the other')
+ == 'this that '+('x'*50) + ';<wbr>' + ('y'*50) + ' and the other')
+
diff --git a/tests/test_exceptions/test_httpexceptions.py b/tests/test_exceptions/test_httpexceptions.py
new file mode 100644
index 0000000..24e00dd
--- /dev/null
+++ b/tests/test_exceptions/test_httpexceptions.py
@@ -0,0 +1,97 @@
+# (c) 2005 Ian Bicking, Clark C. Evans and contributors
+# This module is part of the Python Paste Project and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+"""
+WSGI Exception Middleware
+
+Regression Test Suite
+"""
+from nose.tools import assert_raises
+from paste.httpexceptions import *
+from paste.response import header_value
+import six
+
+
+def test_HTTPMove():
+ """ make sure that location is a mandatory attribute of Redirects """
+ assert_raises(AssertionError, HTTPFound)
+ assert_raises(AssertionError, HTTPTemporaryRedirect,
+ headers=[('l0cation','/bing')])
+ assert isinstance(HTTPMovedPermanently("This is a message",
+ headers=[('Location','/bing')])
+ ,HTTPRedirection)
+ assert isinstance(HTTPUseProxy(headers=[('LOCATION','/bing')])
+ ,HTTPRedirection)
+ assert isinstance(HTTPFound('/foobar'),HTTPRedirection)
+
+def test_badapp():
+ """ verify that the middleware handles previously-started responses """
+ def badapp(environ, start_response):
+ start_response("200 OK",[])
+ raise HTTPBadRequest("Do not do this at home.")
+ newapp = HTTPExceptionHandler(badapp)
+ assert b'Bad Request' in b''.join(newapp({'HTTP_ACCEPT': 'text/html'},
+ (lambda a, b, c=None: None)))
+
+def test_unicode():
+ """ verify unicode output """
+ tstr = u"\0xCAFE"
+ def badapp(environ, start_response):
+ start_response("200 OK",[])
+ raise HTTPBadRequest(tstr)
+ newapp = HTTPExceptionHandler(badapp)
+ assert tstr.encode("utf-8") in b''.join(newapp({'HTTP_ACCEPT':
+ 'text/html'},
+ (lambda a, b, c=None: None)))
+ assert tstr.encode("utf-8") in b''.join(newapp({'HTTP_ACCEPT':
+ 'text/plain'},
+ (lambda a, b, c=None: None)))
+
+def test_template():
+ """ verify that html() and plain() output methods work """
+ e = HTTPInternalServerError()
+ e.template = 'A %(ping)s and <b>%(pong)s</b> message.'
+ assert str(e).startswith("500 Internal Server Error")
+ assert e.plain({'ping': 'fun', 'pong': 'happy'}) == (
+ '500 Internal Server Error\r\n'
+ 'A fun and happy message.\r\n')
+ assert '<p>A fun and <b>happy</b> message.</p>' in \
+ e.html({'ping': 'fun', 'pong': 'happy'})
+
+def test_redapp():
+ """ check that redirect returns the correct, expected results """
+ saved = []
+ def saveit(status, headers, exc_info = None):
+ saved.append((status,headers))
+ def redapp(environ, start_response):
+ raise HTTPFound("/bing/foo")
+ app = HTTPExceptionHandler(redapp)
+ result = list(app({'HTTP_ACCEPT': 'text/html'},saveit))
+ assert b'<a href="/bing/foo">' in result[0]
+ assert "302 Found" == saved[0][0]
+ if six.PY3:
+ assert "text/html; charset=utf8" == header_value(saved[0][1], 'content-type')
+ else:
+ assert "text/html" == header_value(saved[0][1], 'content-type')
+ assert "/bing/foo" == header_value(saved[0][1],'location')
+ result = list(app({'HTTP_ACCEPT': 'text/plain'},saveit))
+ assert "text/plain; charset=utf8" == header_value(saved[1][1],'content-type')
+ assert "/bing/foo" == header_value(saved[1][1],'location')
+
+def test_misc():
+ assert get_exception(301) == HTTPMovedPermanently
+ redirect = HTTPFound("/some/path")
+ assert isinstance(redirect,HTTPException)
+ assert isinstance(redirect,HTTPRedirection)
+ assert not isinstance(redirect,HTTPError)
+ notfound = HTTPNotFound()
+ assert isinstance(notfound,HTTPException)
+ assert isinstance(notfound,HTTPError)
+ assert isinstance(notfound,HTTPClientError)
+ assert not isinstance(notfound,HTTPServerError)
+ notimpl = HTTPNotImplemented()
+ assert isinstance(notimpl,HTTPException)
+ assert isinstance(notimpl,HTTPError)
+ assert isinstance(notimpl,HTTPServerError)
+ assert not isinstance(notimpl,HTTPClientError)
+
diff --git a/tests/test_exceptions/test_reporter.py b/tests/test_exceptions/test_reporter.py
new file mode 100644
index 0000000..a40666e
--- /dev/null
+++ b/tests/test_exceptions/test_reporter.py
@@ -0,0 +1,50 @@
+import sys
+import os
+from paste.exceptions.reporter import *
+from paste.exceptions import collector
+
+def setup_file(fn, content=None):
+ dir = os.path.join(os.path.dirname(__file__), 'reporter_output')
+ fn = os.path.join(dir, fn)
+ if os.path.exists(dir):
+ if os.path.exists(fn):
+ os.unlink(fn)
+ else:
+ os.mkdir(dir)
+ if content is not None:
+ f = open(fn, 'wb')
+ f.write(content)
+ f.close()
+ return fn
+
+def test_logger():
+ fn = setup_file('test_logger.log')
+ rep = LogReporter(
+ filename=fn,
+ show_hidden_frames=False)
+ try:
+ int('a')
+ except:
+ exc_data = collector.collect_exception(*sys.exc_info())
+ else:
+ assert 0
+ rep.report(exc_data)
+ content = open(fn).read()
+ assert len(content.splitlines()) == 4, len(content.splitlines())
+ assert 'ValueError' in content
+ assert 'int' in content
+ assert 'test_reporter.py' in content
+ assert 'test_logger' in content
+
+ try:
+ 1 / 0
+ except:
+ exc_data = collector.collect_exception(*sys.exc_info())
+ else:
+ assert 0
+ rep.report(exc_data)
+ content = open(fn).read()
+ print(content)
+ assert len(content.splitlines()) == 8
+ assert 'ZeroDivisionError' in content
+