Remove WIP performance tests - they were never used and are outdated (#20471)
This commit is contained in:
Родитель
1f247e0d8f
Коммит
37f65c46b8
|
@ -1,11 +0,0 @@
|
|||
FROM python:2.7-alpine
|
||||
|
||||
COPY . /code
|
||||
WORKDIR /code
|
||||
|
||||
RUN apk --no-cache add --virtual=.build-dep build-base git \
|
||||
&& pip install --no-cache-dir -r /code/requirements/perftests.txt \
|
||||
&& apk del .build-dep \
|
||||
&& rm -f /tmp/* /etc/apk/cache/*
|
||||
|
||||
EXPOSE 8089 5557 5558
|
|
@ -1,7 +0,0 @@
|
|||
# Required by locust, not using hashes for now. We'll use them once we can
|
||||
# use a tagged locust release
|
||||
pyzmq==25.0.0
|
||||
|
||||
# We need this specific commit until there is a new locust release. Once this happens
|
||||
# This can be pinned to a specific version.
|
||||
-e git+https://github.com/locustio/locust@524ab5203ebc7c4c5c108b641773262ae53fbc40#egg=locustio
|
Двоичные данные
tests/performance/fixtures/small_webextension.xpi
Двоичные данные
tests/performance/fixtures/small_webextension.xpi
Двоичный файл не отображается.
|
@ -1,146 +0,0 @@
|
|||
import logging
|
||||
import random
|
||||
import collections
|
||||
import os
|
||||
import string
|
||||
import re
|
||||
import tempfile
|
||||
import uuid
|
||||
from contextlib import contextmanager
|
||||
from shutil import make_archive, rmtree
|
||||
from zipfile import ZipFile
|
||||
|
||||
import lxml
|
||||
|
||||
from fxa.constants import ENVIRONMENT_URLS
|
||||
from fxa.core import Client
|
||||
from fxa.tests.utils import TestEmailAccount
|
||||
|
||||
NAME_REGEX = re.compile('THIS_IS_THE_NAME')
|
||||
|
||||
root_path = os.path.dirname(__file__)
|
||||
data_dir = os.path.join(root_path, 'fixtures')
|
||||
xpis = [os.path.join(data_dir, xpi) for xpi in os.listdir(data_dir)]
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_random():
|
||||
return str(uuid.uuid4())
|
||||
|
||||
|
||||
def submit_url(step):
|
||||
return f'/en-US/developers/addon/submit/{step}/'
|
||||
|
||||
|
||||
def get_xpi():
|
||||
return uniqueify_xpi(random.choice(xpis))
|
||||
|
||||
|
||||
@contextmanager
|
||||
def uniqueify_xpi(path):
|
||||
output_dir = tempfile.mkdtemp()
|
||||
try:
|
||||
data_dir = os.path.join(output_dir, 'xpi')
|
||||
output_path = os.path.join(output_dir, 'addon')
|
||||
xpi_name = os.path.basename(path)
|
||||
xpi_path = os.path.join(output_dir, xpi_name)
|
||||
|
||||
with ZipFile(path) as original:
|
||||
original.extractall(data_dir)
|
||||
|
||||
with open(os.path.join(data_dir, 'manifest.json')) as f:
|
||||
manifest_json = f.read()
|
||||
|
||||
manifest_json = NAME_REGEX.sub(get_random(), manifest_json)
|
||||
|
||||
with open(os.path.join(data_dir, 'manifest.json'), 'w') as f:
|
||||
f.write(manifest_json)
|
||||
|
||||
archive_path = make_archive(output_path, 'zip', data_dir)
|
||||
os.rename(archive_path, xpi_path)
|
||||
with open(xpi_path) as f:
|
||||
yield f
|
||||
finally:
|
||||
rmtree(output_dir)
|
||||
|
||||
|
||||
class EventMarker:
|
||||
"""
|
||||
Simple event marker that logs on every call.
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def _generate_log_message(self):
|
||||
log.info(f'locust event: {self.name}')
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
self._generate_log_message()
|
||||
|
||||
|
||||
def install_event_markers():
|
||||
# "import locust" within this scope so that this module is importable by
|
||||
# code running in environments which do not have locust installed.
|
||||
import locust
|
||||
|
||||
# The locust logging format is not necessarily stable, so we use the event
|
||||
# hooks API to implement our own "stable" logging for later programmatic
|
||||
# reference.
|
||||
|
||||
# The events are:
|
||||
|
||||
# * locust_start_hatching
|
||||
# * master_start_hatching
|
||||
# * quitting
|
||||
# * hatch_complete
|
||||
|
||||
# install simple event markers
|
||||
locust.events.locust_start_hatching += EventMarker('locust_start_hatching')
|
||||
locust.events.master_start_hatching += EventMarker('master_start_hatching')
|
||||
locust.events.quitting += EventMarker('quitting')
|
||||
locust.events.hatch_complete += EventMarker('hatch_complete')
|
||||
|
||||
|
||||
def get_fxa_client():
|
||||
fxa_env = os.getenv('FXA_ENV', 'stable')
|
||||
return Client(ENVIRONMENT_URLS[fxa_env]['authentication'])
|
||||
|
||||
|
||||
def get_fxa_account():
|
||||
fxa_client = get_fxa_client()
|
||||
|
||||
account = TestEmailAccount()
|
||||
password = ''.join([random.choice(string.ascii_letters) for i in range(8)])
|
||||
FxAccount = collections.namedtuple('FxAccount', 'email password')
|
||||
fxa_account = FxAccount(email=account.email, password=password)
|
||||
session = fxa_client.create_account(fxa_account.email, fxa_account.password)
|
||||
account.fetch()
|
||||
message = account.wait_for_email(lambda m: 'x-verify-code' in m['headers'])
|
||||
session.verify_email_code(message['headers']['x-verify-code'])
|
||||
return fxa_account, account
|
||||
|
||||
|
||||
def destroy_fxa_account(fxa_account, email_account):
|
||||
email_account.clear()
|
||||
get_fxa_client().destroy_account(fxa_account.email, fxa_account.password)
|
||||
|
||||
|
||||
def get_the_only_form_without_id(response_content):
|
||||
"""
|
||||
Gets the only form on the page that doesn't have an ID.
|
||||
|
||||
A lot of pages (login, registration) have a single form without an ID.
|
||||
This is the one we want. The other forms on the page have IDs so we
|
||||
can ignore them. I'm sure this will break one day.
|
||||
"""
|
||||
html = lxml.html.fromstring(response_content)
|
||||
target_form = None
|
||||
for form in html.forms:
|
||||
if not form.attrib.get('id'):
|
||||
target_form = form
|
||||
if target_form is None:
|
||||
raise ValueError(
|
||||
f'Could not find only one form without an ID; found: {html.forms}'
|
||||
)
|
||||
return target_form
|
|
@ -1,34 +0,0 @@
|
|||
import logging
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
# due to locust sys.path manipulation, we need to re-add the project root.
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings')
|
||||
import olympia # noqa
|
||||
|
||||
from locust import HttpLocust # noqa
|
||||
|
||||
import tasks.user # noqa
|
||||
import helpers # noqa
|
||||
|
||||
|
||||
logging.Formatter.converter = time.gmtime
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
helpers.install_event_markers()
|
||||
|
||||
|
||||
class WebsiteUser(HttpLocust):
|
||||
weight = 1
|
||||
task_set = tasks.user.UserTaskSet
|
||||
min_wait = 120
|
||||
max_wait = 240
|
||||
|
||||
|
||||
# class Developer(HttpLocust):
|
||||
# weight = 10
|
||||
# task_set = tasks.developer.DeveloperTaskSet
|
||||
# min_wait = 120
|
||||
# max_wait = 240
|
|
@ -1,45 +0,0 @@
|
|||
#!/bin/sh
|
||||
# Example usage:
|
||||
#
|
||||
# $ docker build -t amoloadtests:latest -f Dockerfile.perftests
|
||||
# $ docker run -ti -e LOCUST_OPTS="-c 1 --no-web" \
|
||||
# -e ATTACKED_HOST="https://addons.allizom.org" \
|
||||
# amoloadtests:latest
|
||||
# /code/tests/performance/run-locust.sh
|
||||
#
|
||||
# If you're running this against addons.allizom.org make sure to configure
|
||||
# the correct FxA environment variables:
|
||||
#
|
||||
# -e FXA_CLIENT_ID="..." -e FXA_CLIENT_SECRET="..."
|
||||
#
|
||||
# To run this locally, use
|
||||
# $ LOCUST_OPTS="-c 3 --no-web" ATTACKED_HOST="https://addons.allizom.org" tests/performance/run-locust.sh
|
||||
|
||||
set -e
|
||||
LOCUST_MODE=${LOCUST_MODE:-standalone}
|
||||
LOCUST_MASTER_BIND_PORT=${LOCUST_MASTER_BIND_PORT:-5557}
|
||||
CURRENT_FOLDER=$(dirname $(realpath $0))
|
||||
DEFAULT_LOCUST_FILE="$CURRENT_FOLDER/locustfile.py"
|
||||
LOCUST_FILE=${LOCUST_FILE:-$DEFAULT_LOCUST_FILE}
|
||||
|
||||
if [ -z ${ATTACKED_HOST+x} ] ; then
|
||||
echo "You need to set the URL of the host to be tested (ATTACKED_HOST)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LOCUST_OPTS="-f ${LOCUST_FILE} --host=${ATTACKED_HOST} --no-reset-stats $LOCUST_OPTS"
|
||||
|
||||
case `echo ${LOCUST_MODE} | tr 'a-z' 'A-Z'` in
|
||||
"MASTER")
|
||||
LOCUST_OPTS="--master --master-bind-port=${LOCUST_MASTER_BIND_PORT} $LOCUST_OPTS"
|
||||
;;
|
||||
"SLAVE")
|
||||
LOCUST_OPTS="--slave --master-host=${LOCUST_MASTER} --master-port=${LOCUST_MASTER_BIND_PORT} $LOCUST_OPTS"
|
||||
if [ -z ${LOCUST_MASTER+x} ] ; then
|
||||
echo "You need to set LOCUST_MASTER."
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
locust ${LOCUST_OPTS}
|
|
@ -1 +0,0 @@
|
|||
from . import developer, user # noqa
|
|
@ -1,130 +0,0 @@
|
|||
import os
|
||||
import logging
|
||||
|
||||
import gevent
|
||||
from django.conf import settings
|
||||
from locust import task
|
||||
import lxml.html
|
||||
from lxml.html import submit_form
|
||||
|
||||
import helpers
|
||||
from .user import BaseUserTaskSet
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
MAX_UPLOAD_POLL_ATTEMPTS = 200
|
||||
FXA_CONFIG = settings.FXA_CONFIG[settings.DEFAULT_FXA_CONFIG_NAME]
|
||||
|
||||
|
||||
class DeveloperTaskSet(BaseUserTaskSet):
|
||||
def submit_form(self, form=None, url=None, extra_values=None):
|
||||
if form is None:
|
||||
raise ValueError(f'form cannot be None; url={url}')
|
||||
|
||||
def submit(method, form_action_url, values):
|
||||
values = dict(values)
|
||||
if 'csrfmiddlewaretoken' not in values:
|
||||
raise ValueError(
|
||||
'Possibly the wrong form. Could not find '
|
||||
'csrfmiddlewaretoken: {}'.format(repr(values))
|
||||
)
|
||||
|
||||
response = self.client.post(
|
||||
url or form_action_url,
|
||||
values,
|
||||
allow_redirects=False,
|
||||
catch_response=True,
|
||||
)
|
||||
|
||||
if response.status_code not in (301, 302):
|
||||
# This probably means the form failed and is displaying
|
||||
# errors.
|
||||
response.failure(
|
||||
'Form submission did not redirect; status={}'.format(
|
||||
response.status_code
|
||||
)
|
||||
)
|
||||
|
||||
return submit_form(form, open_http=submit, extra_values=extra_values)
|
||||
|
||||
def load_upload_form(self):
|
||||
url = helpers.submit_url('upload-unlisted')
|
||||
response = self.client.get(url, allow_redirects=False, catch_response=True)
|
||||
|
||||
if response.status_code == 200:
|
||||
response.success()
|
||||
html = lxml.html.fromstring(response.content)
|
||||
return html.get_element_by_id('create-addon')
|
||||
else:
|
||||
more_info = ''
|
||||
if response.status_code in (301, 302):
|
||||
more_info = 'Location: {}'.format(response.headers['Location'])
|
||||
response.failure(f'Unexpected status: {response.status_code}; {more_info}')
|
||||
|
||||
def upload_addon(self, form):
|
||||
url = helpers.submit_url('upload-unlisted')
|
||||
csrfmiddlewaretoken = form.fields['csrfmiddlewaretoken']
|
||||
|
||||
with helpers.get_xpi() as addon_file:
|
||||
response = self.client.post(
|
||||
'/en-US/developers/upload/',
|
||||
{'csrfmiddlewaretoken': csrfmiddlewaretoken},
|
||||
files={'upload': addon_file},
|
||||
name=f'devhub.upload {os.path.basename(addon_file.name)}',
|
||||
allow_redirects=False,
|
||||
catch_response=True,
|
||||
)
|
||||
|
||||
if response.status_code == 302:
|
||||
poll_url = response.headers['location']
|
||||
upload_uuid = gevent.spawn(self.poll_upload_until_ready, poll_url).get()
|
||||
if upload_uuid:
|
||||
form.fields['upload'] = upload_uuid
|
||||
self.submit_form(form=form, url=url)
|
||||
else:
|
||||
response.failure(f'Unexpected status: {response.status_code}')
|
||||
|
||||
@task(1)
|
||||
def upload(self):
|
||||
self.login(self.fxa_account)
|
||||
|
||||
form = self.load_upload_form()
|
||||
if form:
|
||||
self.upload_addon(form)
|
||||
|
||||
self.logout(self.fxa_account)
|
||||
|
||||
def poll_upload_until_ready(self, url):
|
||||
for i in range(MAX_UPLOAD_POLL_ATTEMPTS):
|
||||
response = self.client.get(
|
||||
url,
|
||||
allow_redirects=False,
|
||||
name='/en-US/developers/upload/:uuid',
|
||||
catch_response=True,
|
||||
)
|
||||
|
||||
try:
|
||||
data = response.json()
|
||||
except ValueError:
|
||||
return response.failure(
|
||||
'Failed to parse JSON when polling. '
|
||||
'Status: {} content: {}'.format(
|
||||
response.status_code, response.content
|
||||
)
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
if data['error']:
|
||||
return response.failure(
|
||||
'Unexpected error: {}'.format(data['error'])
|
||||
)
|
||||
elif data['validation']:
|
||||
response.success()
|
||||
return data['upload']
|
||||
else:
|
||||
return response.failure(f'Unexpected status: {response.status_code}')
|
||||
gevent.sleep(1)
|
||||
else:
|
||||
response.failure(
|
||||
f'Upload did not complete in {MAX_UPLOAD_POLL_ATTEMPTS} tries'
|
||||
)
|
|
@ -1,271 +0,0 @@
|
|||
import logging
|
||||
import urlparse
|
||||
import random
|
||||
|
||||
from django.conf import settings
|
||||
from locust import TaskSet, task
|
||||
import lxml.html
|
||||
from fxa import oauth as fxa_oauth
|
||||
|
||||
import helpers
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
MAX_UPLOAD_POLL_ATTEMPTS = 200
|
||||
FXA_CONFIG = settings.FXA_CONFIG[settings.DEFAULT_FXA_CONFIG_NAME]
|
||||
|
||||
|
||||
class BaseUserTaskSet(TaskSet):
|
||||
def on_start(self):
|
||||
self.fxa_account, self.email_account = helpers.get_fxa_account()
|
||||
|
||||
log.info(f'Created {self.fxa_account} for load-tests')
|
||||
|
||||
def is_legacy_page(self, app):
|
||||
return app in ('thunderbird', 'seamonkey')
|
||||
|
||||
def get_app(self):
|
||||
# Slightly weighted
|
||||
app = random.choice(['firefox'] * 20 + ['thunderbird'] * 5 + ['seamonkey'] * 1)
|
||||
return app
|
||||
|
||||
def get_url(self, url, app):
|
||||
# Only take a sub-set of languages, doesn't really matter only
|
||||
# increases variance and may circumvent some caches here and there
|
||||
user_language = random.choice(
|
||||
('af', 'de', 'dsb', 'en-US', 'hsb', 'ru', 'tr', 'zh-CN', 'zh-TW')
|
||||
)
|
||||
|
||||
return url.format(app=app, language=user_language)
|
||||
|
||||
def on_stop(self):
|
||||
log.info(f'Cleaning up and destroying {self.fxa_account}')
|
||||
helpers.destroy_fxa_account(self.fxa_account, self.email_account)
|
||||
|
||||
def login(self, fxa_account):
|
||||
log.info('calling login/start to generate fxa_state')
|
||||
response = self.client.get(
|
||||
'/api/v3/accounts/login/start/', allow_redirects=True
|
||||
)
|
||||
|
||||
params = dict(urlparse.parse_qsl(response.url))
|
||||
fxa_state = params['state']
|
||||
|
||||
log.info('Get browser id session token')
|
||||
fxa_session = helpers.get_fxa_client().login(
|
||||
email=fxa_account.email, password=fxa_account.password
|
||||
)
|
||||
|
||||
oauth_client = fxa_oauth.Client(
|
||||
client_id=FXA_CONFIG['client_id'],
|
||||
client_secret=FXA_CONFIG['client_secret'],
|
||||
server_url=FXA_CONFIG['oauth_host'],
|
||||
)
|
||||
|
||||
log.info('convert browser id session token into oauth code')
|
||||
oauth_code = oauth_client.authorize_code(fxa_session, scope='profile')
|
||||
|
||||
# Now authenticate the user, this will verify the user on the server
|
||||
response = self.client.get(
|
||||
'/api/v3/accounts/authenticate/',
|
||||
params={
|
||||
'state': fxa_state,
|
||||
'code': oauth_code,
|
||||
},
|
||||
name='/api/v3/accounts/authenticate/?state=:state',
|
||||
)
|
||||
|
||||
def logout(self, account):
|
||||
log.info(f'Logging out {account}')
|
||||
self.client.get('/en-US/firefox/users/logout/')
|
||||
|
||||
|
||||
class UserTaskSet(BaseUserTaskSet):
|
||||
def _browse_listing_and_click_detail(
|
||||
self,
|
||||
listing_url,
|
||||
app,
|
||||
detail_selector,
|
||||
legacy_selector=None,
|
||||
name=None,
|
||||
force_legacy=False,
|
||||
):
|
||||
# TODO: This should hit pagination automatically if there is any
|
||||
response = self.client.get(
|
||||
self.get_url(listing_url, app), allow_redirects=False, catch_response=True
|
||||
)
|
||||
|
||||
if (self.is_legacy_page(app) or force_legacy) and not legacy_selector:
|
||||
log.warning(
|
||||
'Received legacy url without legacy selector. {} :: {}'.format(
|
||||
listing_url, detail_selector
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
if response.status_code == 200:
|
||||
html = lxml.html.fromstring(response.content)
|
||||
selector = (
|
||||
detail_selector
|
||||
if not (self.is_legacy_page(app) or force_legacy)
|
||||
else legacy_selector
|
||||
)
|
||||
collection_links = html.cssselect(selector)
|
||||
|
||||
if not collection_links:
|
||||
log.warning(
|
||||
'No selectable links on page. {} :: {}'.format(
|
||||
listing_url, selector
|
||||
)
|
||||
)
|
||||
|
||||
url = random.choice(collection_links).get('href')
|
||||
|
||||
kwargs = {}
|
||||
if name is not None:
|
||||
if self.is_legacy_page(app) or force_legacy:
|
||||
name = name.replace(':app', ':legacy_app')
|
||||
kwargs['name'] = name
|
||||
|
||||
self.client.get(url, **kwargs)
|
||||
response.success()
|
||||
else:
|
||||
response.failure(f'Unexpected status code {response.status_code}')
|
||||
|
||||
@task(1)
|
||||
def browse(self):
|
||||
app = self.get_app()
|
||||
self.client.get(self.get_url('/{language}/{app}/', app))
|
||||
|
||||
self._browse_listing_and_click_detail(
|
||||
listing_url='/{language}/{app}/extensions/',
|
||||
app=app,
|
||||
detail_selector='a.SearchResult-link',
|
||||
legacy_selector='.items .item.addon a',
|
||||
)
|
||||
|
||||
@task(10)
|
||||
def search(self):
|
||||
app = self.get_app()
|
||||
|
||||
term_choices = ('Spam', 'Privacy', 'Download')
|
||||
term = random.choice(term_choices)
|
||||
self.client.get(
|
||||
self.get_url('/{language}/{app}/search/?platform=linux&q=' + term, app)
|
||||
)
|
||||
|
||||
@task(6)
|
||||
def browse_and_download_addon(self):
|
||||
app = self.get_app()
|
||||
|
||||
self._browse_listing_and_click_detail(
|
||||
listing_url='/{language}/{app}/extensions/',
|
||||
app=app,
|
||||
detail_selector='a.SearchResult-link',
|
||||
legacy_selector='.items .item.addon a',
|
||||
)
|
||||
|
||||
@task(5)
|
||||
def browse_collections(self):
|
||||
app = self.get_app()
|
||||
|
||||
# detail and legacy selector match both, themes and regular add-ons
|
||||
self._browse_listing_and_click_detail(
|
||||
listing_url='/{language}/{app}/',
|
||||
app=app,
|
||||
detail_selector='a.Home-SubjectShelf-link',
|
||||
legacy_selector='.listing-grid .hovercard .summary>a',
|
||||
)
|
||||
|
||||
@task(4)
|
||||
def browse_categories(self):
|
||||
app = self.get_app()
|
||||
|
||||
self._browse_listing_and_click_detail(
|
||||
listing_url='/{language}/{app}/extensions/',
|
||||
app=app,
|
||||
detail_selector='a.Categories-link',
|
||||
legacy_selector='ul#side-categories li a',
|
||||
)
|
||||
|
||||
@task(4)
|
||||
def browse_reviews(self):
|
||||
app = self.get_app()
|
||||
|
||||
# TODO: Get add-ons more generalized by looking at collections
|
||||
# pages but for now that'll suffice.
|
||||
addons = (
|
||||
'grammarly-spell-checker',
|
||||
'clip-to-onenote',
|
||||
'evernote-web-clipper',
|
||||
'reader',
|
||||
'fractal-summer-colors',
|
||||
'abstract-splash',
|
||||
'colorful-fractal',
|
||||
'tab-mix-plus',
|
||||
)
|
||||
|
||||
for addon in addons:
|
||||
self.client.get(
|
||||
self.get_url('/{language}/{app}/addon/%s/reviews/' % addon, app)
|
||||
)
|
||||
|
||||
@task(4)
|
||||
def browse_theme_categories(self):
|
||||
app = self.get_app()
|
||||
self._browse_listing_and_click_detail(
|
||||
listing_url='/{language}/{app}/complete-themes/',
|
||||
app=app,
|
||||
detail_selector=None,
|
||||
legacy_selector='.listing-grid .hovercard>a',
|
||||
name='/:lang/:app/complete-themes/:slug/',
|
||||
force_legacy=True,
|
||||
)
|
||||
|
||||
self._browse_listing_and_click_detail(
|
||||
listing_url='/{language}/{app}/themes/',
|
||||
app=app,
|
||||
detail_selector='a.SearchResult-link',
|
||||
legacy_selector='ul#side-categories li a',
|
||||
name='/:lang/:app/themes/:slug/',
|
||||
)
|
||||
|
||||
@task(3)
|
||||
def test_user_profile(self):
|
||||
app = self.get_app()
|
||||
|
||||
# TODO: Generalize by actually creating a user-profile and uploading
|
||||
# some data.
|
||||
usernames = (
|
||||
'giorgio-maone',
|
||||
'wot-services',
|
||||
'onemen',
|
||||
'gary-reyes',
|
||||
'mozilla-labs5133025',
|
||||
'gregglind',
|
||||
# Has many ratings
|
||||
'daveg',
|
||||
)
|
||||
|
||||
for user in usernames:
|
||||
self.client.get(self.get_url('/{language}/{app}/user/%s/' % user, app))
|
||||
|
||||
@task(2)
|
||||
def test_rss_feeds(self):
|
||||
app = self.get_app()
|
||||
|
||||
urls = (
|
||||
# Add-on Category RSS Feed
|
||||
'/{language}/firefox/extensions/alerts-updates/format:rss',
|
||||
'/{language}/firefox/extensions/appearance/format:rss',
|
||||
'/{language}/firefox/extensions/bookmarks/format:rss',
|
||||
'/{language}/firefox/extensions/language-support/format:rss',
|
||||
# Collection RSS Feed
|
||||
'/{language}/firefox/collections/Vivre/ploaia/format:rss',
|
||||
# Featured Add-ons
|
||||
'/{language}/{app}/featured/format:rss',
|
||||
# Search tools RSS Feed
|
||||
'/{language}/{app}/search-tools/format:rss',
|
||||
)
|
||||
|
||||
self.client.get(self.get_url(random.choice(urls), app))
|
Загрузка…
Ссылка в новой задаче