| LEFT | RIGHT | 
|    1 # coding: utf-8 |    1 # coding: utf-8 | 
|    2  |    2  | 
|    3 # This file is part of the Adblock Plus web scripts, |    3 # This file is part of the Adblock Plus web scripts, | 
|    4 # Copyright (C) 2006-2015 Eyeo GmbH |    4 # Copyright (C) 2006-2015 Eyeo GmbH | 
|    5 # |    5 # | 
|    6 # Adblock Plus is free software: you can redistribute it and/or modify |    6 # Adblock Plus is free software: you can redistribute it and/or modify | 
|    7 # it under the terms of the GNU General Public License version 3 as |    7 # it under the terms of the GNU General Public License version 3 as | 
|    8 # published by the Free Software Foundation. |    8 # published by the Free Software Foundation. | 
|    9 # |    9 # | 
|   10 # Adblock Plus is distributed in the hope that it will be useful, |   10 # Adblock Plus is distributed in the hope that it will be useful, | 
|   11 # but WITHOUT ANY WARRANTY; without even the implied warranty of |   11 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|   12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |   12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|   13 # GNU General Public License for more details. |   13 # GNU General Public License for more details. | 
|   14 # |   14 # | 
|   15 # You should have received a copy of the GNU General Public License |   15 # You should have received a copy of the GNU General Public License | 
|   16 # along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. |   16 # along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
|   17  |   17  | 
|   18 import MySQLdb, StringIO, json, sys, unittest |   18 import json | 
 |   19 import sys | 
 |   20 import StringIO | 
 |   21 import unittest | 
|   19 from urllib import urlencode |   22 from urllib import urlencode | 
|   20  |   23  | 
 |   24 from sitescripts.filterhits.test import test_helpers | 
|   21 from sitescripts.filterhits import db |   25 from sitescripts.filterhits import db | 
|   22 from sitescripts.filterhits.web.query import query_handler |   26 from sitescripts.filterhits.web.query import query_handler | 
|   23 from sitescripts.filterhits.web.submit import submit as submit_handler |   27 from sitescripts.filterhits.web.submit import submit as submit_handler | 
|   24  |   28  | 
|   25 valid_data = """{ |   29 valid_data = """{ | 
|   26   "version": 1, |   30   "version": 1, | 
|   27   "timeSincePush": 12345, |   31   "timeSincePush": 12345, | 
|   28   "addonName": "adblockplus", |   32   "addonName": "adblockplus", | 
|   29   "addonVersion": "2.3.4", |   33   "addonVersion": "2.3.4", | 
|   30   "application": "firefox", |   34   "application": "firefox", | 
|   31   "applicationVersion": "31", |   35   "applicationVersion": "31", | 
|   32   "platform": "gecko", |   36   "platform": "gecko", | 
|   33   "platformVersion": "31", |   37   "platformVersion": "31", | 
|   34   "filters": { |   38   "filters": { | 
|   35     "||example.com^": { |   39     "||example.com^": { | 
|   36       "firstParty": { |   40       "firstParty": { | 
|   37         "example.com": {"hits": 12, "latest": 1414817340948}, |   41         "example.com": {"hits": 12, "latest": 1414817340948}, | 
|   38         "example.org": {"hits": 4, "latest": 1414859271125} |   42         "example.org": {"hits": 4, "latest": 1414859271125} | 
|   39       }, |   43       }, | 
|   40       "thirdParty": { |   44       "thirdParty": { | 
|   41         "example.com": {"hits": 5, "latest": 1414916268769} |   45         "example.com": {"hits": 5, "latest": 1414916268769} | 
|   42       }, |   46       }, | 
|   43       "subscriptions": ["EasyList", "EasyList Germany+EasyList"] |   47       "subscriptions": ["EasyList", "EasyList Germany+EasyList"] | 
|   44     } |   48     } | 
|   45   } |   49   } | 
|   46 }""" |   50 }""" | 
|   47  |   51  | 
|   48 class APITestCase(unittest.TestCase): |   52 class APITestCase(test_helpers.FilterhitsTestCase): | 
|   49   def clear_rows(self): |  | 
|   50     if self.db: |  | 
|   51       db.write(self.db, (("DELETE FROM filters",), |  | 
|   52                          ("DELETE FROM geometrical_mean",))) |  | 
|   53  |  | 
|   54   def setUp(self): |  | 
|   55     try: |  | 
|   56       db.testing = True |  | 
|   57       self.db = db.connect() |  | 
|   58     except MySQLdb.Error: |  | 
|   59       self.db = None |  | 
|   60     self.clear_rows() |  | 
|   61  |  | 
|   62   def tearDown(self): |  | 
|   63     if self.db: |  | 
|   64       self.clear_rows() |  | 
|   65       self.db.close() |  | 
|   66       self.db = None |  | 
|   67  |  | 
|   68   def assertResponse(self, handler, expected_response, expected_result=None, exp
     ected_headers=None, **environ): |   53   def assertResponse(self, handler, expected_response, expected_result=None, exp
     ected_headers=None, **environ): | 
|   69     def check_response(response, headers): |   54     def check_response(response, headers): | 
|   70       self.assertEqual(response, expected_response) |   55       self.assertEqual(response, expected_response) | 
|   71       if not expected_headers is None: |   56       if not expected_headers is None: | 
|   72         self.assertEqual(headers, expected_headers) |   57         self.assertEqual(headers, expected_headers) | 
|   73  |   58  | 
|   74     if "body" in environ: |   59     if "body" in environ: | 
|   75       environ["CONTENT_LENGTH"] = len(environ["body"]) |   60       environ["CONTENT_LENGTH"] = len(environ["body"]) | 
|   76       environ["wsgi.input"] = StringIO.StringIO(environ["body"]) |   61       environ["wsgi.input"] = StringIO.StringIO(environ["body"]) | 
|   77       del environ["body"] |   62       del environ["body"] | 
|   78  |   63  | 
|   79     if "get_params" in environ: |   64     if "get_params" in environ: | 
|   80       environ["QUERY_STRING"] = urlencode(environ["get_params"]) |   65       environ["QUERY_STRING"] = urlencode(environ["get_params"]) | 
|   81       del environ["get_params"] |   66       del environ["get_params"] | 
|   82  |   67  | 
|   83     environ["wsgi.errors"] = sys.stderr |   68     environ["wsgi.errors"] = sys.stderr | 
|   84     result = handler(environ, check_response) |   69     result = handler(environ, check_response) | 
|   85     if not expected_result is None: |   70     if not expected_result is None: | 
|   86       self.assertEqual(json.loads("".join(result)), expected_result) |   71       self.assertEqual(json.loads("".join(result)), expected_result) | 
|   87  |   72  | 
|   88   def test_submit(self): |   73   def test_submit(self): | 
|   89     self.assertEqual(len(db.query(self.db, "SELECT * FROM geometrical_mean")), 0
     ) |   74     self.assertEqual(len(db.query(self.db, "SELECT * FROM frequencies")), 0) | 
|   90     self.assertEqual(len(db.query(self.db, "SELECT * FROM filters")), 0) |   75     self.assertEqual(len(db.query(self.db, "SELECT * FROM filters")), 0) | 
|   91     # Ensure missing or invalid JSON results in an error |   76     # Ensure missing or invalid JSON results in an error | 
|   92     self.assertResponse(submit_handler, "400 Processing Error", |   77     self.assertResponse(submit_handler, "400 Processing Error", | 
|   93                         REQUEST_METHOD="POST", body="") |   78                         REQUEST_METHOD="POST", body="") | 
|   94     self.assertResponse(submit_handler, "400 Processing Error", |   79     self.assertResponse(submit_handler, "400 Processing Error", | 
|   95                         REQUEST_METHOD="POST", body="Oops...") |   80                         REQUEST_METHOD="POST", body="Oops...") | 
|   96     self.assertResponse(submit_handler, "400 Processing Error", |   81     self.assertResponse(submit_handler, "400 Processing Error", | 
|   97                         REQUEST_METHOD="POST", body="{123:]") |   82                         REQUEST_METHOD="POST", body="{123:]") | 
|   98     self.assertResponse(submit_handler, "400 Processing Error", |   83     self.assertResponse(submit_handler, "400 Processing Error", | 
|   99                         REQUEST_METHOD="POST", body="1") |   84                         REQUEST_METHOD="POST", body="1") | 
|  100     self.assertEqual(len(db.query(self.db, "SELECT * FROM geometrical_mean")), 0
     ) |   85     self.assertEqual(len(db.query(self.db, "SELECT * FROM frequencies")), 0) | 
|  101     self.assertEqual(len(db.query(self.db, "SELECT * FROM filters")), 0) |   86     self.assertEqual(len(db.query(self.db, "SELECT * FROM filters")), 0) | 
|  102     # Ensure even an empty object, or one with the wrong fields returns OK |   87     # Ensure even an empty object, or one with the wrong fields returns successf
     ully | 
|  103     self.assertResponse(submit_handler, "200 OK", |   88     self.assertResponse(submit_handler, "204 No Content", | 
|  104                         REQUEST_METHOD="POST", body="{}") |   89                         REQUEST_METHOD="POST", body="{}") | 
|  105     self.assertResponse(submit_handler, "200 OK", |   90     self.assertResponse(submit_handler, "204 No Content", | 
|  106                         REQUEST_METHOD="POST", body="{\"hello\": \"world\"}") |   91                         REQUEST_METHOD="POST", body="{\"hello\": \"world\"}") | 
|  107     self.assertEqual(len(db.query(self.db, "SELECT * FROM geometrical_mean")), 0
     ) |   92     self.assertEqual(len(db.query(self.db, "SELECT * FROM frequencies")), 0) | 
|  108     self.assertEqual(len(db.query(self.db, "SELECT * FROM filters")), 0) |   93     self.assertEqual(len(db.query(self.db, "SELECT * FROM filters")), 0) | 
|  109     # Now some actually valid data |   94     # Now some actually valid data | 
|  110     self.assertResponse(submit_handler, "200 OK", |   95     self.assertResponse(submit_handler, "204 No Content", | 
|  111                         REQUEST_METHOD="POST", body=valid_data) |   96                         REQUEST_METHOD="POST", body=valid_data) | 
|  112     self.assertEqual(len(db.query(self.db, "SELECT * FROM geometrical_mean")), 2
     ) |   97     self.assertEqual(len(db.query(self.db, "SELECT * FROM frequencies")), 2) | 
|  113     self.assertEqual(len(db.query(self.db, "SELECT * FROM filters")), 1) |   98     self.assertEqual(len(db.query(self.db, "SELECT * FROM filters")), 1) | 
|  114     # Now make sure apparently valid data with timestamps that cause geometrical |   99     # Now make sure apparently valid data with timestamps that cause geometrical | 
|  115     # mean calculations to fail with MySQL errors return OK but don't change DB |  100     # mean calculations to fail with MySQL errors return OK but don't change DB | 
|  116     invalid_data = json.loads(valid_data) |  101     invalid_data = json.loads(valid_data) | 
|  117     invalid_data["filters"]["||example.com^"]["firstParty"]["example.com"]["late
     st"] = 3 |  102     invalid_data["filters"]["||example.com^"]["firstParty"]["example.com"]["late
     st"] = 3 | 
|  118     invalid_data = json.dumps(invalid_data) |  103     invalid_data = json.dumps(invalid_data) | 
|  119     self.assertResponse(submit_handler, "200 OK", |  104     self.assertResponse(submit_handler, "204 No Content", | 
|  120                         REQUEST_METHOD="POST", body=invalid_data) |  105                         REQUEST_METHOD="POST", body=invalid_data) | 
|  121     self.assertEqual(len(db.query(self.db, "SELECT * FROM geometrical_mean")), 2
     ) |  106     self.assertEqual(len(db.query(self.db, "SELECT * FROM frequencies")), 2) | 
|  122     self.assertEqual(len(db.query(self.db, "SELECT * FROM filters")), 1) |  107     self.assertEqual(len(db.query(self.db, "SELECT * FROM filters")), 1) | 
|  123  |  108  | 
|  124   def test_query(self): |  109   def test_query(self): | 
|  125     # Basic query with no data, should return OK |  110     # Basic query with no data, should return successfully | 
|  126     self.assertResponse(query_handler, "200 OK", {"count": 0, "total": 0, "resul
     ts": [], "echo": 0}) |  111     self.assertResponse(query_handler, "200 OK", {"count": 0, "total": 0, "resul
     ts": [], "echo": 0}) | 
|  127     # If echo parameter is passed and is integer it should be returned |  112     # If echo parameter is passed and is integer it should be returned | 
|  128     self.assertResponse(query_handler, "200 OK", {"count": 0, "total": 0, "resul
     ts": [], "echo": 1337}, |  113     self.assertResponse(query_handler, "200 OK", {"count": 0, "total": 0, "resul
     ts": [], "echo": 1337}, | 
|  129                         get_params={ "echo": 1337 }) |  114                         get_params={ "echo": 1337 }) | 
|  130     self.assertResponse(query_handler, "200 OK", {"count": 0, "total": 0, "resul
     ts": [], "echo": 0}, |  115     self.assertResponse(query_handler, "200 OK", {"count": 0, "total": 0, "resul
     ts": [], "echo": 0}, | 
|  131                         get_params={ "echo": "naughty" }) |  116                         get_params={ "echo": "naughty" }) | 
|  132     # Now let's submit some data so we can query it back out |  117     # Now let's submit some data so we can query it back out | 
|  133     test_data = [json.loads(valid_data), json.loads(valid_data), json.loads(vali
     d_data)] |  118     test_data = [json.loads(valid_data), json.loads(valid_data), json.loads(vali
     d_data)] | 
|  134     test_data[1]["filters"]["##Second-Filter|"] = test_data[1]["filters"].pop("|
     |example.com^") |  119     test_data[1]["filters"]["##Second-Filter|"] = test_data[1]["filters"].pop("|
     |example.com^") | 
|  135     test_data[2]["filters"]["##Third-Filter|"] = test_data[2]["filters"].pop("||
     example.com^") |  120     test_data[2]["filters"]["##Third-Filter|"] = test_data[2]["filters"].pop("||
     example.com^") | 
|  136     for data in test_data: |  121     for data in test_data: | 
|  137       self.assertResponse(submit_handler, "200 OK", |  122       self.assertResponse(submit_handler, "204 No Content", | 
|  138                           REQUEST_METHOD="POST", body=json.dumps(data)) |  123                           REQUEST_METHOD="POST", body=json.dumps(data)) | 
|  139     # Ordering parameters should be respected |  124     # Ordering parameters should be respected | 
|  140     self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 6, |  125     self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 6, | 
|  141                                                   "results": [{'domain': 'exampl
     e.com', |  126                                                   "results": [{"domain": "exampl
     e.com", | 
|  142                                                                'filter': '||exam
     ple.com^', |  127                                                                "filter": "||exam
     ple.com^", | 
|  143                                                                'hits': 0}], "ech
     o": 0}, |  128                                                                "frequency": 0}],
      "echo": 0}, | 
|  144                         get_params={ "order_by": "filter", "order": "desc", "tak
     e": "1" }) |  129                         get_params={ "order_by": "filter", "order": "desc", "tak
     e": "1" }) | 
|  145     self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 6, |  130     self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 6, | 
|  146                                                   "results": [{'domain': 'exampl
     e.com', |  131                                                   "results": [{"domain": "exampl
     e.com", | 
|  147                                                                'filter': '##Seco
     nd-Filter|', |  132                                                                "filter": "##Seco
     nd-Filter|", | 
|  148                                                                'hits': 0}], "ech
     o": 0}, |  133                                                                "frequency": 0}],
      "echo": 0}, | 
|  149                         get_params={ "order_by": "filter", "order": "asc", "take
     ": "1" }) |  134                         get_params={ "order_by": "filter", "order": "asc", "take
     ": "1" }) | 
|  150     # As should filtering parameters |  135     # As should filtering parameters | 
|  151     self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 3, |  136     self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 3, | 
|  152                                                   "results": [{'domain': 'exampl
     e.com', |  137                                                   "results": [{"domain": "exampl
     e.com", | 
|  153                                                                'filter': '##Thir
     d-Filter|', |  138                                                                "filter": "##Thir
     d-Filter|", | 
|  154                                                                'hits': 0}], "ech
     o": 0}, |  139                                                                "frequency": 0}],
      "echo": 0}, | 
|  155                         get_params={ "domain": "example.com", "take": "1" }) |  140                         get_params={ "domain": "example.com", "take": "1" }) | 
|  156     self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 2, |  141     self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 2, | 
|  157                                                   "results": [{'domain': 'exampl
     e.org', |  142                                                   "results": [{"domain": "exampl
     e.org", | 
|  158                                                                'filter': '##Thir
     d-Filter|', |  143                                                                "filter": "##Thir
     d-Filter|", | 
|  159                                                                'hits': 4}], "ech
     o": 0}, |  144                                                                "frequency": 4}],
      "echo": 0}, | 
|  160                         get_params={ "filter": "Third", "take": 1 }) |  145                         get_params={ "filter": "Third", "take": 1 }) | 
|  161     self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 1, |  146     self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 1, | 
|  162                                                   "results": [{'domain': 'exampl
     e.com', |  147                                                   "results": [{"domain": "exampl
     e.com", | 
|  163                                                                'filter': '##Thir
     d-Filter|', |  148                                                                "filter": "##Thir
     d-Filter|", | 
|  164                                                                'hits': 0}], "ech
     o": 0}, |  149                                                                "frequency": 0}],
      "echo": 0}, | 
|  165                         get_params={ "domain": "example.com", "filter": "Third",
      "take": "1" }) |  150                         get_params={ "domain": "example.com", "filter": "Third",
      "take": "1" }) | 
|  166     # ... and pagination parameters |  151     # ... and pagination parameters | 
|  167     self.maxDiff = None |  152     self.maxDiff = None | 
|  168     self.assertResponse(query_handler, "200 OK", {"count": 2, "total": 6, |  153     self.assertResponse(query_handler, "200 OK", {"count": 2, "total": 6, | 
|  169                                                   "results": [{'domain': 'exampl
     e.org', |  154                                                   "results": [{"domain": "exampl
     e.org", | 
|  170                                                                'filter': '||exam
     ple.com^', |  155                                                                "filter": "||exam
     ple.com^", | 
|  171                                                                'hits': 4}, |  156                                                                "frequency": 4}, | 
|  172                                                               {'domain': 'exampl
     e.org', |  157                                                               {"domain": "exampl
     e.org", | 
|  173                                                                'filter': '##Seco
     nd-Filter|', |  158                                                                "filter": "##Seco
     nd-Filter|", | 
|  174                                                                'hits': 4}], "ech
     o": 0}, |  159                                                                "frequency": 4}],
      "echo": 0}, | 
|  175                         get_params={ "skip": "1", "take": "2" }) |  160                         get_params={ "skip": "1", "take": "2" }) | 
|  176     self.assertResponse(query_handler, "200 OK", {"count": 2, "total": 6, |  161     self.assertResponse(query_handler, "200 OK", {"count": 2, "total": 6, | 
|  177                                                   "results": [{'domain': 'exampl
     e.org', |  162                                                   "results": [{"domain": "exampl
     e.org", | 
|  178                                                                'filter': '##Seco
     nd-Filter|', |  163                                                                "filter": "##Seco
     nd-Filter|", | 
|  179                                                                'hits': 4}, |  164                                                                "frequency": 4}, | 
|  180                                                               {'domain': 'exampl
     e.com', |  165                                                               {"domain": "exampl
     e.com", | 
|  181                                                                'filter': '##Thir
     d-Filter|', |  166                                                                "filter": "##Thir
     d-Filter|", | 
|  182                                                                'hits': 0}], "ech
     o": 0}, |  167                                                                "frequency": 0}],
      "echo": 0}, | 
|  183                         get_params={ "skip": "2", "take": "2" }) |  168                         get_params={ "skip": "2", "take": "2" }) | 
|  184  |  169  | 
|  185 if __name__ == '__main__': |  170 if __name__ == "__main__": | 
|  186   unittest.main() |  171   unittest.main() | 
| LEFT | RIGHT |