Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: sitescripts/filterhits/test/api_tests.py

Issue 4615801646612480: Issue 395 - Filter hits statistics backend (Closed)
Left Patch Set: Addressed further comments from Wladimir. Created March 27, 2015, 10:12 p.m.
Right Patch Set: Addressed further comments from Sebastian. Created April 2, 2015, 10:13 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « sitescripts/filterhits/test/__init__.py ('k') | sitescripts/filterhits/test/db_tests.py » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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
19 import StringIO
20 import json 18 import json
21 import sys 19 import sys
20 import StringIO
22 import unittest 21 import unittest
23 from urllib import urlencode 22 from urllib import urlencode
24 23
24 from sitescripts.filterhits.test import test_helpers
25 from sitescripts.filterhits import db 25 from sitescripts.filterhits import db
26 from sitescripts.filterhits.web.query import query_handler 26 from sitescripts.filterhits.web.query import query_handler
27 from sitescripts.filterhits.web.submit import submit as submit_handler 27 from sitescripts.filterhits.web.submit import submit as submit_handler
28 28
29 valid_data = """{ 29 valid_data = """{
30 "version": 1, 30 "version": 1,
31 "timeSincePush": 12345, 31 "timeSincePush": 12345,
32 "addonName": "adblockplus", 32 "addonName": "adblockplus",
33 "addonVersion": "2.3.4", 33 "addonVersion": "2.3.4",
34 "application": "firefox", 34 "application": "firefox",
35 "applicationVersion": "31", 35 "applicationVersion": "31",
36 "platform": "gecko", 36 "platform": "gecko",
37 "platformVersion": "31", 37 "platformVersion": "31",
38 "filters": { 38 "filters": {
39 "||example.com^": { 39 "||example.com^": {
40 "firstParty": { 40 "firstParty": {
41 "example.com": {"hits": 12, "latest": 1414817340948}, 41 "example.com": {"hits": 12, "latest": 1414817340948},
42 "example.org": {"hits": 4, "latest": 1414859271125} 42 "example.org": {"hits": 4, "latest": 1414859271125}
43 }, 43 },
44 "thirdParty": { 44 "thirdParty": {
45 "example.com": {"hits": 5, "latest": 1414916268769} 45 "example.com": {"hits": 5, "latest": 1414916268769}
46 }, 46 },
47 "subscriptions": ["EasyList", "EasyList Germany+EasyList"] 47 "subscriptions": ["EasyList", "EasyList Germany+EasyList"]
48 } 48 }
49 } 49 }
50 }""" 50 }"""
51 51
52 class APITestCase(unittest.TestCase): 52 class APITestCase(test_helpers.FilterhitsTestCase):
53 def clear_rows(self):
54 if self.db:
55 db.write(self.db, (("DELETE FROM filters",),
56 ("DELETE FROM frequencies",)))
57
58 def setUp(self):
59 try:
60 db.testing = True
61 self.db = db.connect()
62 except MySQLdb.Error:
63 self.db = None
64 self.clear_rows()
65
66 def tearDown(self):
67 if self.db:
68 self.clear_rows()
69 self.db.close()
70 self.db = None
71
72 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):
73 def check_response(response, headers): 54 def check_response(response, headers):
74 self.assertEqual(response, expected_response) 55 self.assertEqual(response, expected_response)
75 if not expected_headers is None: 56 if not expected_headers is None:
76 self.assertEqual(headers, expected_headers) 57 self.assertEqual(headers, expected_headers)
77 58
78 if "body" in environ: 59 if "body" in environ:
79 environ["CONTENT_LENGTH"] = len(environ["body"]) 60 environ["CONTENT_LENGTH"] = len(environ["body"])
80 environ["wsgi.input"] = StringIO.StringIO(environ["body"]) 61 environ["wsgi.input"] = StringIO.StringIO(environ["body"])
81 del environ["body"] 62 del environ["body"]
(...skipping 14 matching lines...) Expand all
96 self.assertResponse(submit_handler, "400 Processing Error", 77 self.assertResponse(submit_handler, "400 Processing Error",
97 REQUEST_METHOD="POST", body="") 78 REQUEST_METHOD="POST", body="")
98 self.assertResponse(submit_handler, "400 Processing Error", 79 self.assertResponse(submit_handler, "400 Processing Error",
99 REQUEST_METHOD="POST", body="Oops...") 80 REQUEST_METHOD="POST", body="Oops...")
100 self.assertResponse(submit_handler, "400 Processing Error", 81 self.assertResponse(submit_handler, "400 Processing Error",
101 REQUEST_METHOD="POST", body="{123:]") 82 REQUEST_METHOD="POST", body="{123:]")
102 self.assertResponse(submit_handler, "400 Processing Error", 83 self.assertResponse(submit_handler, "400 Processing Error",
103 REQUEST_METHOD="POST", body="1") 84 REQUEST_METHOD="POST", body="1")
104 self.assertEqual(len(db.query(self.db, "SELECT * FROM frequencies")), 0) 85 self.assertEqual(len(db.query(self.db, "SELECT * FROM frequencies")), 0)
105 self.assertEqual(len(db.query(self.db, "SELECT * FROM filters")), 0) 86 self.assertEqual(len(db.query(self.db, "SELECT * FROM filters")), 0)
106 # 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
107 self.assertResponse(submit_handler, "200 OK", 88 self.assertResponse(submit_handler, "204 No Content",
108 REQUEST_METHOD="POST", body="{}") 89 REQUEST_METHOD="POST", body="{}")
109 self.assertResponse(submit_handler, "200 OK", 90 self.assertResponse(submit_handler, "204 No Content",
110 REQUEST_METHOD="POST", body="{\"hello\": \"world\"}") 91 REQUEST_METHOD="POST", body="{\"hello\": \"world\"}")
111 self.assertEqual(len(db.query(self.db, "SELECT * FROM frequencies")), 0) 92 self.assertEqual(len(db.query(self.db, "SELECT * FROM frequencies")), 0)
112 self.assertEqual(len(db.query(self.db, "SELECT * FROM filters")), 0) 93 self.assertEqual(len(db.query(self.db, "SELECT * FROM filters")), 0)
113 # Now some actually valid data 94 # Now some actually valid data
114 self.assertResponse(submit_handler, "200 OK", 95 self.assertResponse(submit_handler, "204 No Content",
115 REQUEST_METHOD="POST", body=valid_data) 96 REQUEST_METHOD="POST", body=valid_data)
116 self.assertEqual(len(db.query(self.db, "SELECT * FROM frequencies")), 2) 97 self.assertEqual(len(db.query(self.db, "SELECT * FROM frequencies")), 2)
117 self.assertEqual(len(db.query(self.db, "SELECT * FROM filters")), 1) 98 self.assertEqual(len(db.query(self.db, "SELECT * FROM filters")), 1)
118 # Now make sure apparently valid data with timestamps that cause geometrical 99 # Now make sure apparently valid data with timestamps that cause geometrical
119 # 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
120 invalid_data = json.loads(valid_data) 101 invalid_data = json.loads(valid_data)
121 invalid_data["filters"]["||example.com^"]["firstParty"]["example.com"]["late st"] = 3 102 invalid_data["filters"]["||example.com^"]["firstParty"]["example.com"]["late st"] = 3
122 invalid_data = json.dumps(invalid_data) 103 invalid_data = json.dumps(invalid_data)
123 self.assertResponse(submit_handler, "200 OK", 104 self.assertResponse(submit_handler, "204 No Content",
124 REQUEST_METHOD="POST", body=invalid_data) 105 REQUEST_METHOD="POST", body=invalid_data)
125 self.assertEqual(len(db.query(self.db, "SELECT * FROM frequencies")), 2) 106 self.assertEqual(len(db.query(self.db, "SELECT * FROM frequencies")), 2)
126 self.assertEqual(len(db.query(self.db, "SELECT * FROM filters")), 1) 107 self.assertEqual(len(db.query(self.db, "SELECT * FROM filters")), 1)
127 108
128 def test_query(self): 109 def test_query(self):
129 # Basic query with no data, should return OK 110 # Basic query with no data, should return successfully
130 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})
131 # 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
132 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},
133 get_params={ "echo": 1337 }) 114 get_params={ "echo": 1337 })
134 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},
135 get_params={ "echo": "naughty" }) 116 get_params={ "echo": "naughty" })
136 # 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
137 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)]
138 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^")
139 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^")
140 for data in test_data: 121 for data in test_data:
141 self.assertResponse(submit_handler, "200 OK", 122 self.assertResponse(submit_handler, "204 No Content",
142 REQUEST_METHOD="POST", body=json.dumps(data)) 123 REQUEST_METHOD="POST", body=json.dumps(data))
143 # Ordering parameters should be respected 124 # Ordering parameters should be respected
144 self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 6, 125 self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 6,
145 "results": [{'domain': 'exampl e.com', 126 "results": [{"domain": "exampl e.com",
146 'filter': '||exam ple.com^', 127 "filter": "||exam ple.com^",
147 'frequency': 0}], "echo": 0}, 128 "frequency": 0}], "echo": 0},
148 get_params={ "order_by": "filter", "order": "desc", "tak e": "1" }) 129 get_params={ "order_by": "filter", "order": "desc", "tak e": "1" })
149 self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 6, 130 self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 6,
150 "results": [{'domain': 'exampl e.com', 131 "results": [{"domain": "exampl e.com",
151 'filter': '##Seco nd-Filter|', 132 "filter": "##Seco nd-Filter|",
152 'frequency': 0}], "echo": 0}, 133 "frequency": 0}], "echo": 0},
153 get_params={ "order_by": "filter", "order": "asc", "take ": "1" }) 134 get_params={ "order_by": "filter", "order": "asc", "take ": "1" })
154 # As should filtering parameters 135 # As should filtering parameters
155 self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 3, 136 self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 3,
156 "results": [{'domain': 'exampl e.com', 137 "results": [{"domain": "exampl e.com",
157 'filter': '##Thir d-Filter|', 138 "filter": "##Thir d-Filter|",
158 'frequency': 0}], "echo": 0}, 139 "frequency": 0}], "echo": 0},
159 get_params={ "domain": "example.com", "take": "1" }) 140 get_params={ "domain": "example.com", "take": "1" })
160 self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 2, 141 self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 2,
161 "results": [{'domain': 'exampl e.org', 142 "results": [{"domain": "exampl e.org",
162 'filter': '##Thir d-Filter|', 143 "filter": "##Thir d-Filter|",
163 'frequency': 4}], "echo": 0}, 144 "frequency": 4}], "echo": 0},
164 get_params={ "filter": "Third", "take": 1 }) 145 get_params={ "filter": "Third", "take": 1 })
165 self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 1, 146 self.assertResponse(query_handler, "200 OK", {"count": 1, "total": 1,
166 "results": [{'domain': 'exampl e.com', 147 "results": [{"domain": "exampl e.com",
167 'filter': '##Thir d-Filter|', 148 "filter": "##Thir d-Filter|",
168 'frequency': 0}], "echo": 0}, 149 "frequency": 0}], "echo": 0},
169 get_params={ "domain": "example.com", "filter": "Third", "take": "1" }) 150 get_params={ "domain": "example.com", "filter": "Third", "take": "1" })
170 # ... and pagination parameters 151 # ... and pagination parameters
171 self.maxDiff = None 152 self.maxDiff = None
172 self.assertResponse(query_handler, "200 OK", {"count": 2, "total": 6, 153 self.assertResponse(query_handler, "200 OK", {"count": 2, "total": 6,
173 "results": [{'domain': 'exampl e.org', 154 "results": [{"domain": "exampl e.org",
174 'filter': '||exam ple.com^', 155 "filter": "||exam ple.com^",
175 'frequency': 4}, 156 "frequency": 4},
176 {'domain': 'exampl e.org', 157 {"domain": "exampl e.org",
177 'filter': '##Seco nd-Filter|', 158 "filter": "##Seco nd-Filter|",
178 'frequency': 4}], "echo": 0}, 159 "frequency": 4}], "echo": 0},
179 get_params={ "skip": "1", "take": "2" }) 160 get_params={ "skip": "1", "take": "2" })
180 self.assertResponse(query_handler, "200 OK", {"count": 2, "total": 6, 161 self.assertResponse(query_handler, "200 OK", {"count": 2, "total": 6,
181 "results": [{'domain': 'exampl e.org', 162 "results": [{"domain": "exampl e.org",
182 'filter': '##Seco nd-Filter|', 163 "filter": "##Seco nd-Filter|",
183 'frequency': 4}, 164 "frequency": 4},
184 {'domain': 'exampl e.com', 165 {"domain": "exampl e.com",
185 'filter': '##Thir d-Filter|', 166 "filter": "##Thir d-Filter|",
186 'frequency': 0}], "echo": 0}, 167 "frequency": 0}], "echo": 0},
187 get_params={ "skip": "2", "take": "2" }) 168 get_params={ "skip": "2", "take": "2" })
188 169
189 if __name__ == '__main__': 170 if __name__ == "__main__":
190 unittest.main() 171 unittest.main()
LEFTRIGHT

Powered by Google App Engine
This is Rietveld