| LEFT | RIGHT | 
|    1 # This file is part of the Adblock Plus web scripts, |    1 # This file is part of the Adblock Plus web scripts, | 
|    2 # Copyright (C) 2017-present eyeo GmbH |    2 # Copyright (C) 2017-present eyeo GmbH | 
|    3 # |    3 # | 
|    4 # Adblock Plus is free software: you can redistribute it and/or modify |    4 # Adblock Plus is free software: you can redistribute it and/or modify | 
|    5 # it under the terms of the GNU General Public License version 3 as |    5 # it under the terms of the GNU General Public License version 3 as | 
|    6 # published by the Free Software Foundation. |    6 # published by the Free Software Foundation. | 
|    7 # |    7 # | 
|    8 # Adblock Plus is distributed in the hope that it will be useful, |    8 # Adblock Plus is distributed in the hope that it will be useful, | 
|    9 # but WITHOUT ANY WARRANTY; without even the implied warranty of |    9 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|   10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |   10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|   11 # GNU General Public License for more details. |   11 # GNU General Public License for more details. | 
|   12 # |   12 # | 
|   13 # You should have received a copy of the GNU General Public License |   13 # You should have received a copy of the GNU General Public License | 
|   14 # along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. |   14 # along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
|   15  |   15  | 
 |   16 import sys | 
|   16 from urllib import urlencode |   17 from urllib import urlencode | 
|   17 from urllib2 import urlopen, HTTPError |   18 from urllib2 import urlopen, HTTPError | 
|   18  |   19  | 
|   19 import pytest |   20 import pytest | 
|   20 from wsgi_intercept import (urllib_intercept, add_wsgi_intercept, |   21 from wsgi_intercept import (urllib_intercept, add_wsgi_intercept, | 
|   21                             remove_wsgi_intercept) |   22                             remove_wsgi_intercept) | 
|   22  |   23  | 
 |   24 # We are mocking the functions that use MySQLdb, so it is not needed. This | 
 |   25 # is to prevent the tests from crashing when they try to import it. | 
 |   26 sys.modules['MySQLdb'] = sys | 
|   23 from sitescripts.reports.web.updateReport import handleRequest |   27 from sitescripts.reports.web.updateReport import handleRequest | 
|   24  |   28  | 
|   25 LOCAL_HOST = 'test.local' |   29 LOCAL_HOST = 'test.local' | 
|   26 REMOTE_HOST = 'reports.adblockplus.org' |   30 REMOTE_HOST = 'reports.adblockplus.org' | 
|   27 PORT = 80 |   31 PORT = 80 | 
|   28 PLAINTEXT_GUID = '12345678-1234-1234-1234-123456789abc' |   32 PLAINTEXT_GUID = '12345678-1234-1234-1234-123456789abc' | 
 |   33 UR_PATH = 'sitescripts.reports.web.updateReport.' | 
|   29  |   34  | 
|   30  |   35  | 
|   31 def intercept_fn(environ, start_response): |   36 def intercept_fn(environ, start_response): | 
|   32     assert environ['SERVER_NAME'] == REMOTE_HOST |   37     assert environ['SERVER_NAME'] == REMOTE_HOST | 
|   33     assert PLAINTEXT_GUID in environ['PATH_INFO'] |   38     assert PLAINTEXT_GUID in environ['PATH_INFO'] | 
|   34     return 'Intercepted!' |   39     return 'Intercepted!' | 
|   35  |   40  | 
|   36  |   41  | 
|   37 @pytest.fixture |   42 @pytest.fixture | 
|   38 def response_for(): |   43 def response_for(): | 
|   39     """Register two intercepts, and returns responses for them.""" |   44     """Register two intercepts, and return responses for them.""" | 
|   40     urllib_intercept.install_opener() |   45     urllib_intercept.install_opener() | 
|   41     add_wsgi_intercept(LOCAL_HOST, PORT, lambda: handleRequest) |   46     add_wsgi_intercept(LOCAL_HOST, PORT, lambda: handleRequest) | 
|   42     add_wsgi_intercept(REMOTE_HOST, 443, lambda: intercept_fn) |   47     add_wsgi_intercept(REMOTE_HOST, 443, lambda: intercept_fn) | 
|   43  |   48  | 
|   44     def response_for(data): |   49     def response_for(data): | 
|   45         url = 'http://{}:{}'.format(LOCAL_HOST, PORT) |   50         url = 'http://{}:{}'.format(LOCAL_HOST, PORT) | 
|   46         if data is None: |   51         response = urlopen(url, urlencode(data)) | 
|   47             response = urlopen(url) |  | 
|   48         else: |  | 
|   49             response = urlopen(url, urlencode(data)) |  | 
|   50         return response.code, response.read() |   52         return response.code, response.read() | 
|   51  |   53  | 
|   52     yield response_for |   54     yield response_for | 
|   53     remove_wsgi_intercept() |   55     remove_wsgi_intercept() | 
|   54  |   56  | 
|   55  |   57  | 
|   56 @pytest.fixture |   58 @pytest.fixture | 
|   57 def form_data(): |   59 def form_data(): | 
|   58     return { |   60     return { | 
|   59         'email': 'john_doe@gmail.com', |   61         'email': 'jane_doe@example.com', | 
|   60         'secret': '92b3e705f2abe74c20c1c5ea9abd9ba2', |   62         'secret': '92b3e705f2abe74c20c1c5ea9abd9ba2', | 
|   61         'guid': PLAINTEXT_GUID, |   63         'guid': PLAINTEXT_GUID, | 
|   62         'status': 'test STATUS', |   64         'status': 'x' * 1025, | 
|   63         'usefulness': 'test USEFULNESS', |   65         'usefulness': 0, | 
|   64         'notify': 'test NOTIFY', |   66         'notify': 'test NOTIFY', | 
|   65         'test_mode': True, |   67         'message': 'test MESSAGE', | 
 |   68         'subject': 'test SUBJECT', | 
 |   69         'name': 'test NAME', | 
|   66     } |   70     } | 
|   67  |   71  | 
|   68  |   72  | 
|   69 @pytest.mark.parametrize('field,message', [ |   73 @pytest.mark.parametrize('field,message', [ | 
|   70     (('guid', ''), 'Invalid or missing report GUID'), |   74     (('guid', 'badGUID'), 'Invalid or missing report GUID'), | 
|   71     (('secret', ''), 'Wrong secret value'), |   75     (('secret', 'badSECRET'), 'Wrong secret value'), | 
|   72 ]) |   76 ]) | 
|   73 def test_http_errs(field, message, response_for, form_data): |   77 def test_http_errs(field, message, response_for, form_data, mocker): | 
 |   78     mocker.patch(UR_PATH + 'getReport', new=lambda *args: {'usefulness': 1}) | 
|   74     key, value = field |   79     key, value = field | 
|   75     form_data[key] = value |   80     form_data[key] = value | 
|   76     with pytest.raises(HTTPError) as error: |   81     with pytest.raises(HTTPError) as error: | 
|   77         response_for(form_data) |   82         response_for(form_data) | 
|   78  |   83  | 
|   79     assert message in error.value.read() |   84     assert message in error.value.read() | 
|   80  |   85  | 
|   81  |   86  | 
|   82 def test_success(response_for, form_data): |   87 def test_success(response_for, form_data, mocker): | 
 |   88     # These methods are patched to avoid the need for a MySQL database | 
 |   89     mocker.patch(UR_PATH + 'getReport', new=lambda *args: {'usefulness': 1, | 
 |   90                  'email': 'jane_doe@example.com'}) | 
 |   91     sr_mock = mocker.patch(UR_PATH + 'saveReport') | 
 |   92     uuu_mock = mocker.patch(UR_PATH + 'updateUserUsefulness') | 
 |   93     sun_mock = mocker.patch(UR_PATH + 'sendUpdateNotification') | 
 |   94  | 
|   83     assert response_for(form_data) == (200, '\nIntercepted!') |   95     assert response_for(form_data) == (200, '\nIntercepted!') | 
 |   96  | 
 |   97     assert sr_mock.call_count == 1 | 
 |   98     for key in ['usefulness', 'email']: | 
 |   99         assert key in sr_mock.call_args[0][1] | 
 |  100         assert sr_mock.call_args[0][1][key] == str(form_data[key]) | 
 |  101  | 
 |  102     assert '0' in uuu_mock.call_args[0] and 1 in uuu_mock.call_args[0] | 
 |  103  | 
 |  104     for key in ['email', 'status']: | 
 |  105         assert key in sun_mock.call_args[0][0] | 
 |  106     assert sun_mock.call_args[0][0]['email'] == form_data['email'] | 
 |  107  | 
 |  108     # These should not be equal, because updateReport.py strips characters | 
 |  109     # over 1024, and form_data['status'] has 1025. | 
 |  110     assert str(sr_mock.call_args[0][1]['status']) != form_data['status'] | 
 |  111     assert str(sun_mock.call_args[0][0]['status']) != form_data['status'] | 
 |  112  | 
 |  113  | 
 |  114 def test_get_report_error(response_for, form_data, mocker): | 
 |  115     mocker.patch(UR_PATH + 'getReport', new=lambda *args: None) | 
 |  116     with pytest.raises(HTTPError) as error: | 
 |  117         response_for(form_data) | 
 |  118  | 
 |  119     assert 'Report does not exist' in error.value.read() | 
| LEFT | RIGHT |