1e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved.
2e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
3e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)# found in the LICENSE file.
4e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
5e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)import alerts
6e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)import json
7e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)import random
8e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)import string
9e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)import unittest
10e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)import webtest
11e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
12e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)from google.appengine.api import memcache
13e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)from google.appengine.ext import testbed
14e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
15e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
16e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)class AlertsTest(unittest.TestCase):
17e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)    def setUp(self):
18e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.testbed = testbed.Testbed()
19e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.testbed.activate()
20e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.testbed.init_memcache_stub()
21e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.testapp = webtest.TestApp(alerts.app)
22e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
23e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)    def tearDown(self):
24e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.testbed.deactivate()
25e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
26e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)    def check_json_headers(self, res):
27e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.assertEqual(res.content_type, 'application/json')
28e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        # This is necessary for cross-site tools to retrieve alerts
29e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.assertEqual(res.headers['access-control-allow-origin'], '*')
30e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
31e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)    def test_get_no_data_cached(self):
32e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        res = self.testapp.get('/alerts')
33e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.check_json_headers(res)
34e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.assertEqual(res.body, '')
35e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
36e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)    def test_happy_path(self):
37e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        # Set it.
38e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        params = {'content': '{"alerts": ["hello", "world"]}'}
39e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.testapp.post('/alerts', params)
40e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
41e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        # Get it.
42e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        res = self.testapp.get('/alerts')
43e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.check_json_headers(res)
44e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        alerts = json.loads(res.body)
45e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
46e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        # The server should have stuck a 'date' on there.
47e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.assertTrue('date' in alerts)
48e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.assertEqual(type(alerts['date']), int)
49e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
50e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.assertEqual(alerts['alerts'], ['hello', 'world'])
51e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
52e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)    def test_post_invalid_data_not_reflected(self):
53e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        params = {'content': '[{"this is not valid JSON'}
54e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.testapp.post('/alerts', params, status=400)
55e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        res = self.testapp.get('/alerts')
56e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.assertEqual(res.body, '')
57e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
58e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)    def test_post_invalid_data_does_not_overwrite_valid_data(self):
59e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        # Populate the cache with something valid
60e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        params = {'content': '{"alerts": "everything is OK"}'}
61e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.testapp.post('/alerts', params)
62e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.testapp.post('/alerts', {'content': 'woozlwuzl'}, status=400)
63e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        res = self.testapp.get('/alerts')
64e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.check_json_headers(res)
65e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        alerts = json.loads(res.body)
66e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.assertEqual(alerts['alerts'], 'everything is OK')
67e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
68e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)    def test_large_number_of_alerts(self):
69e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        # This generates ~2.5MB of JSON that compresses to ~750K. Real
70e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        # data compresses about 6x better.
71e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        random.seed(0xf00f00)
72e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        put_alerts = self.generate_fake_alerts(4000)
73e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
74e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        params = {'content': json.dumps(put_alerts)}
75e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.testapp.post('/alerts', params)
76e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
77e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        res = self.testapp.get('/alerts')
78e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        got_alerts = json.loads(res.body)
79e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        self.assertEquals(got_alerts['alerts'], put_alerts['alerts'])
80e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
81e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)    def generate_fake_alerts(self, n):
82e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        return {'alerts': [self.generate_fake_alert() for _ in range(n)]}
83e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
84e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)    def generate_fake_alert(self):
85e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        # fake labels
86e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        labels = [['', 'last_', 'latest_', 'failing_', 'passing_'],
87e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)                  ['build', 'builder', 'revision'],
88e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)                  ['', 's', '_url', '_reason', '_name']]
89e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
90e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        def label():
91e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)            return string.join(map(random.choice, labels), '')
92e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
93e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        # fake values
94e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        def time():
95e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)            return random.randint(1407976107614, 1408076107614) / 101.0
96e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
97e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        def build():
98e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)            return random.randint(2737, 2894)
99e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
100e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        def revision():
101e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)            return random.randint(288849, 289415)
102e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
103e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        tests = [['Activity', 'Async', 'Browser', 'Content', 'Input'],
104e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)                 ['Manager', 'Card', 'Sandbox', 'Container'],
105e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)                 ['Test.'],
106e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)                 ['', 'Basic', 'Empty', 'More'],
107e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)                 ['Mouse', 'App', 'Selection', 'Network', 'Grab'],
108e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)                 ['Input', 'Click', 'Failure', 'Capture']]
109e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
110e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        def test():
111e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)            return string.join(map(random.choice, tests), '')
112e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
113e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        def literal_array():
114e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)            generator = random.choice([time, build, revision])
115e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)            return [generator() for _ in range(random.randint(0, 10))]
116e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
117e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        def literal_map():
118e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)            generators = [build, revision, test, literal_array]
119e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)            obj = {}
120e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)            for _ in range(random.randint(3, 9)):
121e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)                obj[label()] = random.choice(generators)()
122e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)            return obj
123e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
124e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        def value():
125e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)            generators = [time, build, revision, test, literal_array,
126e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)                          literal_map]
127e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)            return random.choice(generators)()
128e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
129e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        alert = {}
130e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        for _ in range(random.randint(6, 9)):
131e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)            alert[label()] = value()
132e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        return alert
133