| OLD | NEW |
| (Empty) |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 import alerts | |
| 6 import json | |
| 7 import random | |
| 8 import string | |
| 9 import unittest | |
| 10 import webtest | |
| 11 | |
| 12 from google.appengine.api import memcache | |
| 13 from google.appengine.ext import ndb | |
| 14 from google.appengine.ext import testbed | |
| 15 | |
| 16 | |
| 17 class AlertsTest(unittest.TestCase): | |
| 18 def setUp(self): | |
| 19 self.testbed = testbed.Testbed() | |
| 20 self.testbed.activate() | |
| 21 self.testbed.init_user_stub() | |
| 22 self.testbed.init_memcache_stub() | |
| 23 self.testbed.init_datastore_v3_stub() | |
| 24 self.testapp = webtest.TestApp(alerts.app) | |
| 25 | |
| 26 def tearDown(self): | |
| 27 self.testbed.deactivate() | |
| 28 | |
| 29 def check_json_headers(self, res): | |
| 30 self.assertEqual(res.content_type, 'application/json') | |
| 31 # This is necessary for cross-site tools to retrieve alerts | |
| 32 self.assertEqual(res.headers['access-control-allow-origin'], '*') | |
| 33 | |
| 34 def test_get_no_data_cached(self): | |
| 35 res = self.testapp.get('/alerts') | |
| 36 self.check_json_headers(res) | |
| 37 self.assertEqual(res.body, '') | |
| 38 | |
| 39 def test_happy_path(self): | |
| 40 # Set it. | |
| 41 params = {'content': '{"alerts": ["hello", "world"]}'} | |
| 42 self.testapp.post('/alerts', params) | |
| 43 | |
| 44 # Get it. | |
| 45 res = self.testapp.get('/alerts') | |
| 46 self.check_json_headers(res) | |
| 47 alerts = json.loads(res.body) | |
| 48 | |
| 49 # The server should have stuck a 'date' on there. | |
| 50 self.assertTrue('date' in alerts) | |
| 51 self.assertEqual(type(alerts['date']), int) | |
| 52 | |
| 53 self.assertEqual(alerts['alerts'], ['hello', 'world']) | |
| 54 | |
| 55 def test_post_invalid_data_not_reflected(self): | |
| 56 params = {'content': '[{"this is not valid JSON'} | |
| 57 self.testapp.post('/alerts', params, status=400) | |
| 58 res = self.testapp.get('/alerts') | |
| 59 self.assertEqual(res.body, '') | |
| 60 | |
| 61 def test_post_invalid_data_does_not_overwrite_valid_data(self): | |
| 62 # Populate the cache with something valid | |
| 63 params = {'content': '{"alerts": "everything is OK"}'} | |
| 64 self.testapp.post('/alerts', params) | |
| 65 self.testapp.post('/alerts', {'content': 'woozlwuzl'}, status=400) | |
| 66 res = self.testapp.get('/alerts') | |
| 67 self.check_json_headers(res) | |
| 68 alerts = json.loads(res.body) | |
| 69 self.assertEqual(alerts['alerts'], 'everything is OK') | |
| 70 | |
| 71 def test_alerts_jsons_are_stored_in_history(self): | |
| 72 test_alerts1 = {'alerts': ['hello', 'world', '1']} | |
| 73 test_alerts2 = {'alerts': ['hello', 'world', '2']} | |
| 74 self.testapp.post('/alerts', {'content': json.dumps(test_alerts1)}) | |
| 75 self.testapp.post('/alerts', {'content': json.dumps(test_alerts2)}) | |
| 76 alerts_query = alerts.AlertsJSON.query().order(alerts.AlertsJSON.date) | |
| 77 stored_alerts = alerts_query.fetch(limit=3) | |
| 78 self.assertEqual(2, len(stored_alerts)) | |
| 79 self.assertEqual(stored_alerts[0].type, 'alerts') | |
| 80 self.assertEqual(stored_alerts[1].type, 'alerts') | |
| 81 stored_alerts1 = json.loads(stored_alerts[0].json) | |
| 82 stored_alerts2 = json.loads(stored_alerts[1].json) | |
| 83 self.assertEqual(test_alerts1['alerts'], stored_alerts1['alerts']) | |
| 84 self.assertEqual(test_alerts2['alerts'], stored_alerts2['alerts']) | |
| 85 self.assertTrue('date' in stored_alerts1) | |
| 86 self.assertTrue('date' in stored_alerts2) | |
| 87 self.assertEqual(type(stored_alerts1['date']), int) | |
| 88 self.assertEqual(type(stored_alerts2['date']), int) | |
| 89 | |
| 90 def test_repeating_alerts_are_not_stored_to_history(self): | |
| 91 test_alerts = {'alerts': ['hello', 'world']} | |
| 92 self.testapp.post('/alerts', {'content': json.dumps(test_alerts)}) | |
| 93 test_alerts['last_builder_info'] = {'some': 'info'} | |
| 94 self.testapp.post('/alerts', {'content': json.dumps(test_alerts)}) | |
| 95 stored_alerts = alerts.AlertsJSON.query().fetch(limit=2) | |
| 96 self.assertEqual(1, len(stored_alerts)) | |
| 97 | |
| 98 def test_alerts_jsons_are_retrieved_from_history(self): | |
| 99 test_alert = {'alerts': ['hello', 'world', '1']} | |
| 100 alerts.AlertsJSON(json=json.dumps(test_alert), type='alerts').put() | |
| 101 response = self.testapp.get('/alerts-history') | |
| 102 self.assertEqual(response.status_int, 200) | |
| 103 self.assertEqual(response.content_type, 'application/json') | |
| 104 parsed_json = json.loads(response.normal_body) | |
| 105 self.assertEqual(len(parsed_json['history']), 1) | |
| 106 | |
| 107 entry_id = parsed_json['history'][0] | |
| 108 response = self.testapp.get('/alerts-history/%s' % entry_id) | |
| 109 self.assertEqual(response.status_int, 200) | |
| 110 self.assertEqual(response.content_type, 'application/json') | |
| 111 parsed_json = json.loads(response.normal_body) | |
| 112 self.assertEqual(parsed_json['alerts'], test_alert['alerts']) | |
| 113 | |
| 114 def test_provides_login_url(self): | |
| 115 response = self.testapp.get('/alerts-history') | |
| 116 self.assertIn('login-url', response) | |
| 117 | |
| 118 def test_invalid_keys_return_400(self): | |
| 119 response = self.testapp.get('/alerts-history/kjhg$%T', | |
| 120 expect_errors=True) | |
| 121 self.assertEqual(response.status_int, 400) | |
| 122 self.assertEqual(response.content_type, 'application/json') | |
| 123 | |
| 124 def test_non_existing_keys_return_404(self): | |
| 125 response = self.testapp.get('/alerts-history/5348024557502464', | |
| 126 expect_errors=True) | |
| 127 self.assertEqual(response.status_int, 404) | |
| 128 self.assertEqual(response.content_type, 'application/json') | |
| 129 | |
| 130 def test_internal_alerts_can_only_retrieved_by_internal_users(self): | |
| 131 test_alert = {'alerts': ['hello', 'world', '1']} | |
| 132 internal_alert = alerts.AlertsJSON(json=json.dumps(test_alert), | |
| 133 type='internal-alerts') | |
| 134 internal_alert_key = internal_alert.put().integer_id() | |
| 135 | |
| 136 # No signed-in user. | |
| 137 response = self.testapp.get('/alerts-history/%s' % internal_alert_key, | |
| 138 expect_errors=True) | |
| 139 self.assertEqual(response.status_int, 404) | |
| 140 self.assertEqual(response.content_type, 'application/json') | |
| 141 parsed_json = json.loads(response.normal_body) | |
| 142 self.assertNotIn('alerts', parsed_json) | |
| 143 | |
| 144 # Non-internal user. | |
| 145 self.testbed.setup_env(USER_EMAIL='test@example.com', USER_ID='1', | |
| 146 USER_IS_ADMIN='1', overwrite=True) | |
| 147 response = self.testapp.get('/alerts-history/%s' % internal_alert_key, | |
| 148 expect_errors=True) | |
| 149 self.assertEqual(response.status_int, 404) | |
| 150 self.assertEqual(response.content_type, 'application/json') | |
| 151 parsed_json = json.loads(response.normal_body) | |
| 152 self.assertNotIn('alerts', parsed_json) | |
| 153 | |
| 154 def test_lists_internal_alerts_to_internal_users_only(self): | |
| 155 test_alert = {'alerts': ['hello', 'world', '1']} | |
| 156 alerts.AlertsJSON(json=json.dumps(test_alert), | |
| 157 type='internal-alerts').put() | |
| 158 | |
| 159 # No signed-in user. | |
| 160 response = self.testapp.get('/alerts-history') | |
| 161 self.assertEqual(response.status_int, 200) | |
| 162 self.assertEqual(response.content_type, 'application/json') | |
| 163 response_json = json.loads(response.normal_body) | |
| 164 self.assertEqual(len(response_json['history']), 0) | |
| 165 | |
| 166 # Non-internal user. | |
| 167 self.testbed.setup_env(USER_EMAIL='test@example.com', USER_ID='1', | |
| 168 USER_IS_ADMIN='1', overwrite=True) | |
| 169 response = self.testapp.get('/alerts-history') | |
| 170 self.assertEqual(response.status_int, 200) | |
| 171 self.assertEqual(response.content_type, 'application/json') | |
| 172 response_json = json.loads(response.normal_body) | |
| 173 self.assertEqual(len(response_json['history']), 0) | |
| 174 | |
| 175 # Internal user. | |
| 176 self.testbed.setup_env(USER_EMAIL='test@google.com', USER_ID='2', | |
| 177 USER_IS_ADMIN='1', overwrite=True) | |
| 178 response = self.testapp.get('/alerts-history') | |
| 179 self.assertEqual(response.status_int, 200) | |
| 180 self.assertEqual(response.content_type, 'application/json') | |
| 181 response_json = json.loads(response.normal_body) | |
| 182 self.assertEqual(len(response_json['history']), 1) | |
| 183 | |
| 184 def test_returned_alerts_from_history_are_paged(self): | |
| 185 for i in range(20): | |
| 186 test_alert = {'alerts': ['hello', 'world', i]} | |
| 187 alerts.AlertsJSON(json=json.dumps(test_alert), type='alerts').put() | |
| 188 | |
| 189 response = self.testapp.get('/alerts-history?limit=15') | |
| 190 self.assertEqual(response.status_int, 200) | |
| 191 self.assertEqual(response.content_type, 'application/json') | |
| 192 response_json = json.loads(response.normal_body) | |
| 193 self.assertEqual(len(response_json['history']), 15) | |
| 194 self.assertEqual(response_json['has_more'], True) | |
| 195 | |
| 196 url = '/alerts-history?limit=15&cursor=%s' % response_json['cursor'] | |
| 197 response = self.testapp.get(url) | |
| 198 self.assertEqual(response.status_int, 200) | |
| 199 self.assertEqual(response.content_type, 'application/json') | |
| 200 response_json = json.loads(response.normal_body) | |
| 201 self.assertEqual(len(response_json['history']), 5) | |
| 202 self.assertEqual(response_json['has_more'], False) | |
| 203 | |
| 204 def test_large_number_of_alerts(self): | |
| 205 # This generates ~2.5MB of JSON that compresses to ~750K. Real | |
| 206 # data compresses about 6x better. | |
| 207 random.seed(0xf00f00) | |
| 208 put_alerts = self.generate_fake_alerts(4000) | |
| 209 | |
| 210 params = {'content': json.dumps(put_alerts)} | |
| 211 self.testapp.post('/alerts', params) | |
| 212 | |
| 213 res = self.testapp.get('/alerts') | |
| 214 got_alerts = json.loads(res.body) | |
| 215 self.assertEquals(got_alerts['alerts'], put_alerts['alerts']) | |
| 216 | |
| 217 def generate_fake_alerts(self, n): | |
| 218 return {'alerts': [self.generate_fake_alert() for _ in range(n)]} | |
| 219 | |
| 220 def generate_fake_alert(self): | |
| 221 # fake labels | |
| 222 labels = [['', 'last_', 'latest_', 'failing_', 'passing_'], | |
| 223 ['build', 'builder', 'revision'], | |
| 224 ['', 's', '_url', '_reason', '_name']] | |
| 225 | |
| 226 def label(): | |
| 227 return string.join(map(random.choice, labels), '') | |
| 228 | |
| 229 # fake values | |
| 230 def time(): | |
| 231 return random.randint(1407976107614, 1408076107614) / 101.0 | |
| 232 | |
| 233 def build(): | |
| 234 return random.randint(2737, 2894) | |
| 235 | |
| 236 def revision(): | |
| 237 return random.randint(288849, 289415) | |
| 238 | |
| 239 tests = [['Activity', 'Async', 'Browser', 'Content', 'Input'], | |
| 240 ['Manager', 'Card', 'Sandbox', 'Container'], | |
| 241 ['Test.'], | |
| 242 ['', 'Basic', 'Empty', 'More'], | |
| 243 ['Mouse', 'App', 'Selection', 'Network', 'Grab'], | |
| 244 ['Input', 'Click', 'Failure', 'Capture']] | |
| 245 | |
| 246 def test(): | |
| 247 return string.join(map(random.choice, tests), '') | |
| 248 | |
| 249 def literal_array(): | |
| 250 generator = random.choice([time, build, revision]) | |
| 251 return [generator() for _ in range(random.randint(0, 10))] | |
| 252 | |
| 253 def literal_map(): | |
| 254 generators = [build, revision, test, literal_array] | |
| 255 obj = {} | |
| 256 for _ in range(random.randint(3, 9)): | |
| 257 obj[label()] = random.choice(generators)() | |
| 258 return obj | |
| 259 | |
| 260 def value(): | |
| 261 generators = [time, build, revision, test, literal_array, | |
| 262 literal_map] | |
| 263 return random.choice(generators)() | |
| 264 | |
| 265 alert = {} | |
| 266 for _ in range(random.randint(6, 9)): | |
| 267 alert[label()] = value() | |
| 268 return alert | |
| OLD | NEW |