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