From 5b2ac224bdac7a341e461ccaf7c48fb6fa36cfed Mon Sep 17 00:00:00 2001 From: Chris Dent Date: Wed, 8 Apr 2015 20:04:48 +0100 Subject: [PATCH] Wrap the http request to trap WSGIAppError and reraise To create a strong boundary between the client and the server, wsgi-intercept wraps exceptions raised in the intercepted app in a WSGIAppError. In most situations this is a good thing. However if gabbi is being used to do TDD against a nascent API this behavior can obscure useful traceback information when there is a catastrophic and untrapped exception in the server code. To deal with this the WSGIAppError is caught and the contained exception is reraised in all its glory to spew into STDERR for debugging purposes. --- gabbi/case.py | 26 +++++++++++++++++++------- gabbi/gabbits_intercept/self.yaml | 10 ++++++++-- gabbi/simple_wsgi.py | 3 +++ test-failskip.sh | 2 +- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/gabbi/case.py b/gabbi/case.py index 570a272..d623e79 100644 --- a/gabbi/case.py +++ b/gabbi/case.py @@ -27,7 +27,9 @@ import os import re import sys +import wsgi_intercept import jsonpath_rw +import six from six.moves.urllib import parse as urlparse from testtools import testcase @@ -243,14 +245,24 @@ class HTTPTestCase(testcase.TestCase): self._json_replacer, message) def _run_request(self, url, method, headers, body): - """Run the http request and decode output.""" + """Run the http request and decode output. - response, content = self.http.request( - url, - method=method, - headers=headers, - body=body - ) + The call to make the request will catch a WSGIAppError from + wsgi_intercept so that the real traceback from a catastrophic + error in the intercepted app can be examined. + """ + + try: + response, content = self.http.request( + url, + method=method, + headers=headers, + body=body + ) + except wsgi_intercept.WSGIAppError as exc: + # Extract and re-raise the wrapped exception. + six.reraise(exc.exception_type, exc.exception_value, + exc.traceback) # Set headers and location attributes for follow on requests self.response = response diff --git a/gabbi/gabbits_intercept/self.yaml b/gabbi/gabbits_intercept/self.yaml index b996be2..83b085a 100644 --- a/gabbi/gabbits_intercept/self.yaml +++ b/gabbi/gabbits_intercept/self.yaml @@ -19,11 +19,11 @@ tests: - name: bogus method url: / - method: DIE + method: UNREAL status: 405 response_headers: allow: GET, PUT, POST, DELETE, PATCH - x-gabbi-method: DIE + x-gabbi-method: UNREAL x-gabbi-url: $SCHEME://$NETLOC/ - name: query returned @@ -105,3 +105,9 @@ tests: xfail: true response_test: - 'CO"alpha": ["1"]' + +- name: test exception wrapper + desc: simple wsgi will raise exception + url: / + xfail: true + method: DIE diff --git a/gabbi/simple_wsgi.py b/gabbi/simple_wsgi.py index 18b1a6d..8d15c81 100644 --- a/gabbi/simple_wsgi.py +++ b/gabbi/simple_wsgi.py @@ -51,6 +51,9 @@ class SimpleWsgi(object): ('X-Gabbi-url', request_url), ] + if request_method == 'DIE': + raise Exception('because you asked me to') + if request_method not in METHODS: headers.append( ('Allow', ', '.join(METHODS))) diff --git a/test-failskip.sh b/test-failskip.sh index 373c9e2..6f2daf7 100755 --- a/test-failskip.sh +++ b/test-failskip.sh @@ -2,7 +2,7 @@ # Run the tests and confirm that the stuff we expect to skip or fail # does. -GREP_FAIL_MATCH='expected failures=3' +GREP_FAIL_MATCH='expected failures=4' GREP_SKIP_MATCH='skips=2' python setup.py testr && \