def test_sample_rate(): # de-DE has 4 cohorts. each one should represent 1% of the users # we're going to make 1000 calls and see if we're around those percentages app = get_app() counts = defaultdict(int) # get the cohort 1000 times path = '/1/firefox/39/beta/de-DE/de/default/default' for i in range(1000): res = app.get(path) if 'cohort' not in res.json: counts['default'] += 1 else: counts[res.json['cohort']] += 1 # we should have around 10 users per cohort # and around 970 for the default assert 0 < counts['one'] <= 20, counts assert 0 < counts['two'] <= 20, counts assert 0 < counts['three'] <= 20, counts assert 955 <= counts['default'] <= 985, counts # verifying redis counters counters = list(dump_counters()) assert 'de-de:de:one:%d' % counts['one'] in counters assert 'de-de:de:two:%d' % counts['two'] in counters assert 'de-de:de:three:%d' % counts['three'] in counters assert 'de-de:de:default:%d' % counts['default'] in counters
def test_metrics(): app = get_app() # get a cohort path = '/1/firefox/39/beta/en-US/US/default/default' res = app.get(path) assert 'cohort' not in res.json assert res.json['settings'] == {'searchDefault': 'Yahoo'} assert res.json['interval'] == 31536000 # lets verify our metrics stats = app.app._statsd # we got one cohort counter assert stats.counters['enrolled'] == 0 # we called add_user_to_cohort once assert len(stats.timers['add_user_to_cohort']), 1 # we read two files in AWS assert len(stats.timers['get_s3_file']), 2 # we incremented on redis the counter for the default cohort assert len(stats.timers['redis.incr']) == 1
def test_max_cohort(): flush_redis() # check that we can have at the most 3 users in the 'foo' cohort # that cohort is at 100% sampleRate for the fr territory under fr-FR app = get_app() # the counters should all be empty counters = list(dump_counters()) assert len(counters) == 0 # get the cohort 3 times path = '/1/firefox/39/beta/fr-FR/fr/default/default' for i in range(3): res = app.get(path) assert res.json.get('cohort') == 'fooBaz', i # should be exausthed now, let's check we get a default now res = app.get(path) # when default we don't have the cohort key in the response assert 'cohort' not in res.json, res.json # the counters should be 1 for the default, 3 for foo counters = list(dump_counters()) counters.sort() assert counters == ['fr-fr:fr:default:1', 'fr-fr:fr:fooBaz:3']
def test_swagger(): app = get_app() # test the APIs res = app.get('/__api__') # make sure it's compliant parser = SwaggerParser(swagger_dict=yaml.load(res.body)) spec = parser.specification assert spec['info']['version'] == __version__ assert spec['schemes'] == ['https'] assert spec['host'] == 'localhost:80' _values = {'prod': 'firefox', 'channel': 'beta', 'locale': 'fr', 'territory': 'fr', 'dist': 'dist', 'distver': 'distver', 'cohort': 'default', 'ver': '34'} # now testing that every GET endpoint is present for path, items in spec['paths'].items(): for verb, options in items.items(): if verb.upper() != 'GET': continue statuses = [int(st) for st in options['responses'].keys() if st != '404'] app.get(path.format(**_values), status=statuses)
def test_unexistant_locale(): app = get_app() # check that an unexistant locale sends back and interval path = '/1/firefox/39/beta/hh-FR/uz/default/default' res = app.get(path).json assert res.keys() == ['interval']
def test_unexistant_territory(): app = get_app() # check that an unexistant territory sends back the default # from the locale path = '/1/firefox/39/beta/fr-FR/uz/default/default' res = app.get(path).json assert res['settings']['searchDefault'] == 'GoogleD'
def test_enrolled(): app = get_app() stats = app.app._statsd # get a cohort cohort = 'default' while cohort == 'default': path = '/1/firefox/39/beta/cs-CZ/cz/default/default' res = app.get(path) cohort = res.json.get('cohort', 'default') # we got one cohort counter assert stats.counters['enrolled'] == 1 # we called add_user_to_cohort once assert len(stats.timers['add_user_to_cohort']), 1 # now that we have a cohort let's check back the settings path = '/1/firefox/39/beta/cs-CZ/cz/default/default/' + cohort res = app.get(path) assert stats.counters['refreshed'] == 1 # also, an unexistant cohort should be counted as a discard path = '/1/firefox/39/beta/cs-CZ/cz/default/default/meh' app.get(path) assert stats.counters['discarded'] == 1
def test_aws_dies(): # life is good, we get some cohorts app = get_app() path = '/1/firefox/39/beta/en-US/US/default/default' app.get(path) # now aws goes down because life is hard moto_process = _P[0] try: os.killpg(moto_process.pid, signal.SIGTERM) moto_process.kill() except OSError: pass moto_process.wait() # what happens to our app ? # our app should be ok because it has a memory cache. app.get(path) # but sometimes the cache has to be updated app.app.settings.max_age = 10 app.app.settings._last_loaded = time.time() - 10 # in that case we want the server to try to # call S3 - fail after a specified timeout # and fallback to the existing memory # but also emit a warning app.get(path) # and... redis is back! _P.insert(0, run_moto()) time.sleep(.1) # our app should be ok. app.get(path)
def test_swagger(): app = get_app() # test the APIs res = app.get('/__api__') # make sure it's compliant parser = SwaggerParser(swagger_dict=yaml.load(res.body)) spec = parser.specification assert spec['info']['version'] == __version__ assert spec['schemes'] == ['https'] assert spec['host'] == 'localhost:80' _values = { 'prod': 'firefox', 'channel': 'beta', 'locale': 'fr', 'territory': 'fr', 'dist': 'dist', 'distver': 'distver', 'cohort': 'default', 'ver': '34' } # now testing that every GET endpoint is present for path, items in spec['paths'].items(): for verb, options in items.items(): if verb.upper() != 'GET': continue statuses = [ int(st) for st in options['responses'].keys() if st != '404' ] app.get(path.format(**_values), status=statuses)
def _test_set_cohort2(): app = get_app() # get a cohort path = '/1/firefox/39/beta/cs-CZ/cz/default/default' res = app.get(path) cohort = res.json.get('cohort', 'default') assert cohort in ('default', 'foo23542', 'bar34234') settings = res.json['settings'] # now that we have a cohort let's check back the settings path = '/1/firefox/39/beta/cs-CZ/cz/default/default/' + cohort res = app.get(path) assert res.json['settings'] == settings if 'cohort' not in res.json: wanted = ('Google1', ) else: wanted = ('Google2', 'Google3') assert res.json['settings']['searchDefault'] in wanted # also, an unexistant cohort should fall back to the default # settings for the territory path = '/1/firefox/39/beta/cs-CZ/cz/default/default/meh' res = app.get(path) assert res.json['settings']['searchDefault'] == 'Google1' # and we should not see the cohort key in there anymore assert 'cohort' not in res.json
def test_lbheartbeat(): app = get_app() # test the APIs resp = app.get('/__lbheartbeat__') assert resp.headers["Cache-Control"] == "max-age=300" assert resp.status_code == 200
def test_info(): app = get_app() # test the APIs resp = app.get('/__info__') info = resp.json assert resp.headers["Cache-Control"] == "max-age=300" assert info['version'] == __version__
def test_excluded(): app = get_app() # make sure an excluded distribution falls back to # sending back just a 200 + interval path = '/1/firefox/39/beta/de-DE/de/ayeah/default' res = app.get(path).json assert res.keys() == ['interval']
def test_just_3_keys(): app = get_app() path = '/1/Firefoox/39/release/de-DE/DE/default/default' res = app.get(path) keys = list(res.json.keys()).sort() wanted = ['cohort', 'interval', 'settings'] wanted2 = ['interval', 'settings'] assert keys == wanted or wanted2, keys
def test_default_interval(): # en-US/FR does not exists. we fallback to the default in en-US # and if it does not contain an interval we want to add the default # interval app = get_app() path = '/1/Firefox/39/release/en-US/FR/default/default' res = app.get(path) assert 'cohort' not in res.json assert res.json['interval'] == 31536000
def test_just_3_keys(): app = get_app() path = '/1/Firefoox/39/release/de-DE/DE/default/default' res = app.get(path) keys = res.json.keys() keys.sort() wanted = ['cohort', 'interval', 'settings'] wanted2 = ['interval', 'settings'] assert keys == wanted or wanted2, keys
def test_set_cohort(): app = get_app() # get a cohort path = '/1/firefox/39/beta/en-US/US/default/default' res = app.get(path) assert 'cohort' not in res.json assert res.json['settings'] == {'searchDefault': 'Yahoo'} assert res.json['interval'] == 31536000
def test_weird_locale_name(): # we want to make sure a cohort with a weird locale fallsback # to a local we have. see issue #5 app = get_app() # get a cohort path = '/1/firefox/39/beta/cs-WAAAT/cz/default/default' res = app.get(path) # we get one of those and not 'Google' because the territory # falled back to 'cz' wanted = ('Google1', 'Google2', 'Google3') assert res.json['settings']['searchDefault'] in wanted
def test_version_filter(): # check that we are filtering by product app = get_app() # get the cohort for fr-FR+fr path = '/1/firefox/39/beta/fr-FR/fr/default/default' res = app.get(path) assert res.json.get('cohort') == 'fooBaz', res.json # if the version is < 39, we should bypass the foo cohort path = '/1/firefox/38/beta/fr-FR/fr/default/default' res = app.get(path) assert 'cohort' not in res.json, res.json
def test_start_time(): app = get_app() # bar34234 or default # gets picked because foo23542 has not started yet path = '/1/firefox/39/beta/fr-BE/BE/default/default' res = app.get(path).json assert res['settings']['searchDefault'] != 'Google2' # also, any attempt to get foo23542 directly should # fallback to the defaults because it's not active yet res = app.get(path + '/foo23542').json assert res['settings']['searchDefault'] != 'Google2' # let's change the data config = app.app._config datadir = os.path.join(os.path.dirname(__file__), '..', '..', 'data') datafile = os.path.join(datadir, config['absearch']['config']) # save a copy shutil.copyfile(datafile, datafile + '.saved') with open(datafile) as f: data = json.loads(f.read()) # change the start time so it's activated now filters = data['locales']['fr-BE']['BE']['tests']['foo23542']['filters'] filters['startTime'] = time.time() - 10 try: # save the new data with open(datafile, 'w') as f: f.write(json.dumps(data)) with capture(): # reload S3 populate_S3() # reload the app reload() # now it has to be foo23542 res = app.get(path).json assert res['settings']['searchDefault'] == 'Google2', res finally: # back to original os.rename(datafile + '.saved', datafile) with capture(): # reload S3 populate_S3() # reload the app reload()
def test_channel_filter(): flush_redis() # check that we are filtering by product app = get_app() # get the cohort for fr-FR+fr path = '/1/firefox/39/beta/fr-FR/fr/default/default' res = app.get(path) assert res.json.get('cohort') == 'fooBaz', res.json # if the channel is not listed, we should bypass the foo cohort path = '/1/firefox/39/alpha/fr-FR/fr/default/default' res = app.get(path) assert 'cohort' not in res.json, res.json
def test_product_filter(): flush_redis() # check that we are filtering by product app = get_app() # get the cohort for fr-FR+fr path = '/1/firefox/39/beta/fr-FR/fr/default/default' res = app.get(path) assert res.json.get('cohort') == 'fooBaz', res.json # if the product it not firefox, we should bypass the foo cohort path = '/1/thunderbird/39/beta/fr-FR/fr/default/default' res = app.get(path) assert 'cohort' not in res.json, res.json
def test_version_filter(): flush_redis() # check that we are filtering by product app = get_app() # get the cohort for fr-FR+fr path = '/1/firefox/39/beta/fr-FR/fr/default/default' res = app.get(path) assert res.json.get('cohort') == 'fooBaz', res.json # if the version is < 39, we should bypass the foo cohort path = '/1/firefox/38/beta/fr-FR/fr/default/default' res = app.get(path) assert 'cohort' not in res.json, res.json
def test_redis_dies(): # life is good, we get some cohorts app = get_app() path = '/1/firefox/39/beta/en-US/US/default/default' res = app.get(path) # now redis goes down because life is hard redis_process = _P[1] try: os.killpg(redis_process.pid, signal.SIGTERM) redis_process.kill() except OSError: pass redis_process.wait() # what happens to our app ? class MySentry(object): exceptions = 0 def get_ident(self, something): self.exceptions += 1 return 'id' def captureException(self): return 'yeah' old_sentry = app.app._sentry app.app._sentry = MySentry() app.app.catchall = True with capture(): try: res = app.get(path) except AppError as e: # that's what we want assert '500' in str(e) else: raise Exception(res) finally: app.app._sentry = old_sentry app.app.catchall = False # and... redis is back! _P.append(run_redis()) time.sleep(.1) # our app should be ok. app.get(path)
def test_max_cohort(): # check that we can have at the most 3 users in the 'foo' cohort # that cohort is at 100% sampleRate for the fr territory under fr-FR app = get_app() # get the cohort 3 times path = '/1/firefox/39/beta/fr-FR/fr/default/default' for i in range(3): res = app.get(path) assert res.json.get('cohort') == 'fooBaz', i # should be exausthed now, let's check we get a default now res = app.get(path) # when default we don't have the cohort key in the response assert 'cohort' not in res.json, res.json
def test_string_max_version(): app = get_app() path = '/1/firefox/45.3/esr/en-GB/GB/default/default' res = app.get(path) assert res.json['settings'] == {'searchDefault': 'Google'} path = '/1/firefox/45.4/esr/en-GB/GB/default/default' res = app.get(path) assert res.json['settings'] == {'searchDefault': 'Google'} path = '/1/firefox/45.5/esr/en-GB/GB/default/default' res = app.get(path) assert res.json['settings'] == {'searchDefault': 'Yahoo'}
def test_pick_test_cohort_and_ask_again(): app = get_app() # get a cohort path = '/1/firefox/39/beta/fr-FR/fr/default/default' res = app.get(path) res = res.json cohort = res.get('cohort', 'default') assert res['cohort'] == 'fooBaz' settings = res['settings'] # now that we have a cohort let's check back the settings path += '/' + cohort res = app.get(path) assert res.json['settings'] == settings assert res.json['cohort'] == 'fooBaz'
def test_version(): app = get_app() try: os.remove("./version.json") except OSError: pass try: app.get('/__version__') assert False, "should crash if version.json is missing." except IOError: pass json.dump({'project': 'absearch'}, open('./version.json', 'w')) version = app.get('/__version__').json assert version['project'] == 'absearch'
def test_all_locales(): # We want to test that the locale defaults are set correctly, if they are # not this test fails to alert the config changes in absearchdata are bad locale_configs = { "en-US": { "US": ("Yahoo") }, "fr-FR": { "FR": ("Google", "Yahoo") } } app = get_app() for locale, config in locale_configs.items(): for territory, search_providers in config.items(): path = "/1/firefox/39/beta/{0}/{1}/default/default" path = path.format(locale, territory) res = app.get(path) assert res.json["settings"]["searchDefault"] in search_providers
def test_all_locales(): # We want to test that the locale defaults are set correctly, if they are # not this test fails to alert the config changes in absearchdata are bad locale_configs = { "en-US": { "US": ("Yahoo") }, "fr-FR": { "FR": ("Google", "Yahoo") } } app = get_app() for locale, config in locale_configs.iteritems(): for territory, search_providers in config.iteritems(): path = "/1/firefox/39/beta/{0}/{1}/default/default" path = path.format(locale, territory) res = app.get(path) assert res.json["settings"]["searchDefault"] in search_providers
def test_version(): app = get_app() try: os.remove("./version.json") except OSError: pass try: app.get('/__version__') assert False, "should crash if version.json is missing." except IOError: pass json.dump({'project': 'absearch'}, open('./version.json', 'w')) resp = app.get('/__version__') version = resp.json assert resp.headers["Cache-Control"] == "max-age=300" assert version['project'] == 'absearch'
def test_reload(): # change some things in the config app = get_app() config_file = app.app._config_file config = app.app._config # save current config with open(config_file + '.saved', 'w') as f: config.write(f) # change the configuration to alternatives # so we actually test them config['statsd']['prefix'] = 'meh' config['sentry']['enabled'] = '1' config['absearch']['backend'] = 'directory' config['absearch']['counter'] = 'memory' datadir = os.path.join(os.path.dirname(__file__), '..', '..', 'data') config.add_section('directory') config['directory']['path'] = datadir # save new config with open(config_file, 'w') as f: config.write(f) try: # make sure that reload grabs the config with capture(): reload() assert app.app._config['statsd']['prefix'] == 'meh' # doing a call with sentry disabled path = '/1/firefox/39/beta/hh-FR/uz/default/default' res = app.get(path).json assert res.keys() == ['interval'] finally: # restore old config os.rename(config_file + '.saved', config_file)
def test_channel_filter(): # check that we are filtering by product app = get_app() # get the cohort for fr-FR+fr path = '/1/firefox/39/beta/fr-FR/fr/default/default' res = app.get(path) assert res.json.get('cohort') == 'fooBaz', res.json # if the channel is not listed, we should bypass the foo cohort path = '/1/firefox/39/alpha/fr-FR/fr/default/default' res = app.get(path) assert 'cohort' not in res.json, res.json # cdntest should map to regular channel path = '/1/firefox/39/beta-cdntest/fr-FR/fr/default/default' res = app.get(path) assert res.json.get('cohort') == 'fooBaz', res.json # localtest should map to regular channel path = '/1/firefox/39/release-localtest/fr-FR/fr/default/default' res = app.get(path) assert res.json.get('cohort') == 'fooBaz', res.json
def test_channel_filter(): flush_redis() # check that we are filtering by product app = get_app() # get the cohort for fr-FR+fr path = '/1/firefox/39/beta/fr-FR/fr/default/default' res = app.get(path) assert res.json.get('cohort') == 'fooBaz', res.json # if the channel is not listed, we should bypass the foo cohort path = '/1/firefox/39/alpha/fr-FR/fr/default/default' res = app.get(path) assert 'cohort' not in res.json, res.json # cdntest should map to regular channel path = '/1/firefox/39/beta-cdntest/fr-FR/fr/default/default' res = app.get(path) assert res.json.get('cohort') == 'fooBaz', res.json # localtest should map to regular channel path = '/1/firefox/39/release-localtest/fr-FR/fr/default/default' res = app.get(path) assert res.json.get('cohort') == 'fooBaz', res.json
def test_set_cohort2_datadog(): app = get_app() return _test_set_cohort2(app)
def test_info(): app = get_app() # test the APIs info = app.get("/__info__").json assert info["version"] == __version__
def test_root(): app = get_app() res = app.get('/') assert res.json.keys() == ['description']
def test_hb(): app = get_app() res = app.get('/__heartbeat__') assert 'schema_md5' in res.json, res.json assert 'config_md5' in res.json, res.json
def test_invalid_url(): app = get_app() # these should return 404 for url in ('/1/firefox/.*/release/.*/.*/default/default/web.xml', '/1/firefox/.*/release/.*/.*/default/default'): app.get(url, status=404)
def test_info(): app = get_app() # test the APIs info = app.get('/__info__').json assert info['version'] == __version__
def test_set_cohort2_statsd(): app = get_app(datadog=False) return _test_set_cohort2(app)