From 05db3aa05ebb34769491d56a2699497890f42b9a Mon Sep 17 00:00:00 2001 From: Brian Rue Date: Thu, 14 May 2015 19:15:53 -0700 Subject: [PATCH] Add tests for Flask, testing against 0.9 and 0.10.1 - Flask 0.9 is tested against python 2.6 and 2.7. - Flask 0.10.1 is tested against 2.6, 2.7, 3.3, and 3.4. - Python 3.2 does not work at all with flask. --- .gitignore | 1 + .travis.yml | 25 ++++- rollbar/__init__.py | 6 +- rollbar/test/__init__.py | 23 ++++ rollbar/test/contrib/__init__.py | 0 rollbar/test/contrib/flask/__init__.py | 0 rollbar/test/contrib/flask/test_flask.py | 133 +++++++++++++++++++++++ setup.py | 2 + 8 files changed, 183 insertions(+), 7 deletions(-) create mode 100644 rollbar/test/contrib/__init__.py create mode 100644 rollbar/test/contrib/flask/__init__.py create mode 100644 rollbar/test/contrib/flask/test_flask.py diff --git a/.gitignore b/.gitignore index 48f23780..b35cc152 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ dist/ rollbar.egg-info/ *.egg +.eggs/ .idea/ diff --git a/.travis.yml b/.travis.yml index 781aca3f..8e379898 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,30 @@ sudo: false language: python + python: - "2.6" - "2.7" - "3.2" - "3.3" - "3.4" -before_script: - - pip install codecov + +env: + - FLASK_VERSION=0.9 + - FLASK_VERSION=0.10.1 + +matrix: + exclude: + - python: "3.2" + env: FLASK_VERSION=0.9 + - python: "3.3" + env: FLASK_VERSION=0.9 + - python: "3.4" + env: FLASK_VERSION=0.9 + +install: + - pip install Flask==$FLASK_VERSION + script: - - coverage run setup.py test - - codecov + - python setup.py test + + diff --git a/rollbar/__init__.py b/rollbar/__init__.py index 30a3eb1b..79715de4 100644 --- a/rollbar/__init__.py +++ b/rollbar/__init__.py @@ -61,12 +61,12 @@ try: from werkzeug.wrappers import Request as WerkzeugRequest -except ImportError: +except (ImportError, SyntaxError): WerkzeugRequest = None try: from werkzeug.local import LocalProxy as WerkzeugLocalProxy -except ImportError: +except (ImportError, SyntaxError): WerkzeugLocalProxy = None try: @@ -978,7 +978,7 @@ def _build_werkzeug_request_data(request): 'files_keys': request.files.keys(), } - if request.get_json(): + if request.json: request_data['body'] = json.dumps(_scrub_obj(request.json)) return request_data diff --git a/rollbar/test/__init__.py b/rollbar/test/__init__.py index bd75e3fe..18c5cf89 100644 --- a/rollbar/test/__init__.py +++ b/rollbar/test/__init__.py @@ -20,6 +20,29 @@ class BaseTest(unittest.TestCase): # from http://hg.python.org/cpython/file/67ada6ab7fe2/Lib/unittest/case.py # for Python 2.6 support + longMessage = False + + def _formatMessage(self, msg, standardMsg): + """Honour the longMessage attribute when generating failure messages. + If longMessage is False this means: + * Use only an explicit message if it is provided + * Otherwise use the standard message for the assert + + If longMessage is True: + * Use the standard message + * If an explicit message is provided, plus ' : ' and the explicit message + """ + if not self.longMessage: + return msg or standardMsg + if msg is None: + return standardMsg + try: + # don't switch to '{}' formatting in Python 2.X + # it changes the way unicode input is handled + return '%s : %s' % (standardMsg, msg) + except UnicodeDecodeError: + return '%s : %s' % (safe_repr(standardMsg), safe_repr(msg)) + def assertIsInstance(self, obj, cls, msg=None): """Same as self.assertTrue(isinstance(obj, cls)), with a nicer default message.""" diff --git a/rollbar/test/contrib/__init__.py b/rollbar/test/contrib/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/rollbar/test/contrib/flask/__init__.py b/rollbar/test/contrib/flask/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/rollbar/test/contrib/flask/test_flask.py b/rollbar/test/contrib/flask/test_flask.py new file mode 100644 index 00000000..03761240 --- /dev/null +++ b/rollbar/test/contrib/flask/test_flask.py @@ -0,0 +1,133 @@ +""" +Tests for Flask instrumentation +""" + +import json +import sys +import os + +import mock +import rollbar + +from rollbar.test import BaseTest + +# access token for https://rollbar.com/rollbar/pyrollbar +TOKEN = '92c10f5616944b81a2e6f3c6493a0ec2' + +# Flask doesn't work on python 3.2, so don't test there. +ALLOWED_PYTHON_VERSION = not (sys.version_info[0] == 3 and sys.version_info[1] == 2) + + +def create_app(): + from flask import Flask, Request, got_request_exception + import rollbar.contrib.flask + app = Flask(__name__) + + @app.route('/') + def index(): + return 'Index page' + + @app.route('/cause_error', methods=['GET', 'POST']) + def cause_error(): + raise Exception("Uh oh") + + @app.before_first_request + def init_rollbar(): + rollbar.init(TOKEN, 'flasktest', + root=os.path.dirname(os.path.realpath(__file__)), + allow_logging_basic_config=True) + got_request_exception.connect(rollbar.contrib.flask.report_exception, app) + + class CustomRequest(Request): + @property + def rollbar_person(self): + return {'id': '123', 'username': 'testuser', 'email': 'test@example.com'} + + app.request_class = CustomRequest + + return app + + +class FlaskTest(BaseTest): + def setUp(self): + super(FlaskTest, self).setUp() + if not ALLOWED_PYTHON_VERSION: + return + self.app = create_app() + self.client = self.app.test_client() + + def test_index(self): + if not ALLOWED_PYTHON_VERSION: + return + + resp = self.client.get('/') + self.assertEqual(resp.status_code, 200) + + def assertStringEqual(self, left, right): + if sys.version_info[0] > 2: + if hasattr(left, 'decode'): + left = left.decode('ascii') + if hasattr(right, 'decode'): + right = right.decode('ascii') + + return self.assertEqual(left, right) + else: + return self.assertEqual(left, right) + + @mock.patch('rollbar.send_payload') + def test_uncaught(self, send_payload): + if not ALLOWED_PYTHON_VERSION: + return + + resp = self.client.get('/cause_error?foo=bar', + headers={'X-Real-Ip': '1.2.3.4', 'User-Agent': 'Flask Test'}) + self.assertEquals(resp.status_code, 500) + + self.assertEqual(send_payload.called, True) + payload = send_payload.call_args[0][0] + data = payload['data'] + + self.assertIn('body', data) + self.assertEqual(data['body']['trace']['exception']['class'], 'Exception') + self.assertStringEqual(data['body']['trace']['exception']['message'], 'Uh oh') + + self.assertIn('person', data) + self.assertDictEqual(data['person'], + {'id': '123', 'username': 'testuser', 'email': 'test@example.com'}) + + self.assertIn('request', data) + self.assertEqual(data['request']['url'], 'http://localhost/cause_error?foo=bar') + self.assertDictEqual(data['request']['GET'], {'foo': ['bar']}) + self.assertEqual(data['request']['user_ip'], '1.2.3.4') + self.assertEqual(data['request']['method'], 'GET') + self.assertEqual(data['request']['headers']['User-Agent'], 'Flask Test') + + @mock.patch('rollbar.send_payload') + def test_uncaught_json_request(self, send_payload): + if not ALLOWED_PYTHON_VERSION: + return + + json_body = json.dumps({"hello": "world"}) + resp = self.client.post('/cause_error', data=json_body, + headers={'Content-Type': 'application/json', 'X-Forwarded-For': '5.6.7.8'}) + + self.assertEquals(resp.status_code, 500) + + self.assertEqual(send_payload.called, True) + payload = send_payload.call_args[0][0] + data = payload['data'] + + self.assertIn('body', data) + self.assertEqual(data['body']['trace']['exception']['class'], 'Exception') + self.assertStringEqual(data['body']['trace']['exception']['message'], 'Uh oh') + + self.assertIn('person', data) + self.assertDictEqual(data['person'], + {'id': '123', 'username': 'testuser', 'email': 'test@example.com'}) + + self.assertIn('request', data) + self.assertEqual(data['request']['url'], 'http://localhost/cause_error') + self.assertEqual(data['request']['body'], json_body) + self.assertEqual(data['request']['user_ip'], '5.6.7.8') + self.assertEqual(data['request']['method'], 'POST') + diff --git a/setup.py b/setup.py index ee3eb98f..ef76df74 100644 --- a/setup.py +++ b/setup.py @@ -53,6 +53,8 @@ tests_require=[ 'mock', 'webob', + 'Flask', + 'blinker' ], )