def test_load_settings_missing_required_secrets(mock_resource_filename):
    """
    missing required setting in secrets document should raise an exception
    """
    mock_resource_filename.return_value = 'tests/foo.yml'

    with pytest.raises(settings.MissingRequiredSettingError):
        settings.init('loadtests.foo.locustfile',
                      required_secrets=['MISSING_REQUIRED_KEY'])
def test_load_settings_empty_secrets(mock_resource_filename):
    """
    load a valid settings file with an empty "secrets" yaml document.
    """
    mock_resource_filename.return_value = 'tests/foo_empty_secrets_doc.yml'
    settings.init('loadtests.foo.locustfile', ['REQUIRED_KEY'])
    with open('tests/foo_empty_secrets_doc.yml') as foo:
        foo_data, foo_secrets = yaml.load_all(foo)
    assert settings.data == foo_data
    assert settings.secrets == {}
    assert foo_secrets is None
def test_load_settings_no_secrets(mock_resource_filename):
    """
    load a valid settings file without a "secrets" yaml document.
    """
    mock_resource_filename.return_value = 'tests/foo_no_secrets_doc.yml'
    settings.init('loadtests.foo.locustfile', ['REQUIRED_KEY'])
    with open('tests/foo_no_secrets_doc.yml') as foo:
        settings_documents = yaml.load_all(foo)
        foo_data = settings_documents.next()
        with pytest.raises(StopIteration):
            settings_documents.next()
    assert settings.data == foo_data
    assert settings.secrets == {}
def test_load_settings(mock_resource_filename):
    """
    normal successful circumstances: load a valid settings file with a mix of
    optional/required/public/secret settings.
    """
    mock_resource_filename.return_value = 'tests/foo.yml'
    settings.init('loadtests.foo.locustfile', ['REQUIRED_KEY'],
                  ['REQUIRED_SECRET_KEY'])
    mock_resource_filename.assert_called_once_with('settings_files', 'foo.yml')
    with open('tests/foo.yml') as foo:
        foo_data, foo_secrets = yaml.load_all(foo)
    assert settings.data == foo_data
    assert settings.secrets == foo_secrets
# 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__))))

import random
import time

from locust import HttpLocust, TaskSet, task, events
from helpers import settings, markers

settings.init(
    __name__,
    required_data=[
        'CMS_USER_EMAIL',
        'TEST_FILE',
        'NUM_PARALLEL_COURSES',
    ],
    required_secrets=[
        'CMS_USER_PASSWORD',
    ],
)

markers.install_event_markers()


class CourseImport(TaskSet):
    "Course import task set -- creates course and imports tarballs."

    def on_start(self):
        "Setup method; log in to studio and create courses."
import urllib3
import sys
import os

# 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__))))
sys.path.append(os.path.dirname(os.path.dirname(__file__)))

from helpers import settings, markers
from locust import HttpLocust, TaskSet
settings.init(
    'ecommerce_deadlock.',  # This is a hack to allow the settings code to find the settings file.
    required_data=['ECOMMERCE_HOST', 'LMS_HOST'],
)
from loadtests.ecommerce_deadlock.tasks.LMS_user_tasks import ManualUserBasketTaskSet, AutoAuthUserBasketTaskSet
from loadtests.ecommerce_deadlock.tasks.ecommerce_user_tasks import EcommerceWorkerBasketTaskSet

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
markers.install_event_markers()


class EcommerceTaskSet(TaskSet):
    tasks = {
        ManualUserBasketTaskSet: 3,
        # AutoAuthUserBasketTaskSet: 1,
        EcommerceWorkerBasketTaskSet: 1
    }


class EcommerceLocust(HttpLocust):
    # Explicitly set host to avoid confusion since we are testing with multiple IDAs
Exemple #7
0
from locust import HttpLocust

from authentication_views import AuthenticationViewsTasks
from courseware_views import CoursewareViewsTasks
from forums import ForumsTasks, SeedForumsTasks
from base import LmsTasks
from proctoring import ProctoredExamTasks
from module_render import ModuleRenderTasks
from wiki_views import WikiViewTask
from tracking import TrackingTasks
from helpers import settings, markers

settings.init(__name__, required_data=[
    'courses',
    'LOCUST_TASK_SET',
    'LOCUST_MIN_WAIT',
    'LOCUST_MAX_WAIT',
])

markers.install_event_markers()


class LmsTest(LmsTasks):
    """
    TaskSet that pulls together all the LMS-related TaskSets into a single unified test.

    See util/lms_tx_distribution.sh for instructions on generating the data
    below.

    Traffic Distribution on courses.edx.org (last 7d as of 2016-11-16):
import sys

# 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__))))

import random

from locust import HttpLocust, task, TaskSet
from locust.clients import HttpSession

from helpers import settings
from helpers.api import LocustEdxRestApiClient

settings.init(
    __name__,
    required_data=['programs'],
    required_secrets=['oauth'],
)


class SelfInterruptingTaskSet(TaskSet):
    @task(1)
    def stop(self):
        self.interrupt()


class CatalogTaskSet(SelfInterruptingTaskSet):
    catalog_id = 1

    @task(20)
    def get_catalog_courses(self):
Exemple #9
0
# 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__))))

from collections import deque
import json
import random
import string

from locust import HttpLocust, task

from helpers.auto_auth_tasks import AutoAuthTasks

from helpers import settings
settings.init(__name__, required=[
    'COURSE_ID',
    'LOCUST_MIN_WAIT',
    'LOCUST_MAX_WAIT',
])


_dummy_chars = string.lowercase + ' '


def _dummy_text(minlen, maxlen):
    """
    Generate dummy text for forum submissions.
    """
    return "".join(random.choice(_dummy_chars) for _ in xrange(minlen, random.randrange(minlen + 1, maxlen)))


class TeamsDiscussionTasks(AutoAuthTasks):
Exemple #10
0
# 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__))))

from locust import HttpLocust

from courseware_views import CoursewareViewsTasks
from forums import ForumsTasks, SeedForumsTasks
from base import LmsTasks
from proctoring import ProctoredExamTasks
from module_render import ModuleRenderTasks
from wiki_views import WikiViewTask
from tracking import TrackingTasks

from helpers import settings

settings.init(__name__, required=["COURSE_ID", "COURSE_DATA", "LOCUST_TASK_SET", "LOCUST_MIN_WAIT", "LOCUST_MAX_WAIT"])


class LmsTest(LmsTasks):
    """
    TaskSet that pulls together all the LMS-related TaskSets into a single unified test.

    See util/lms_tx_distribution.sh for instructions on generating the data
    below.

    Traffic Distribution on courses.edx.org (last 7d as of 2016-11-16):

    /openedx.core.djangoapps.heartbeat.views Total, 26.34%
    /track.views Total, 24.24%  (TrackingTasks)
    /courseware.module_render Total, 21.74%  (ModuleRenderTasks)
    /enrollment.views Total, 3.60%
import sys

# 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__))))

from collections import deque
import random

from locust import task, HttpLocust
from locust.clients import HttpSession

from helpers import settings, markers
from helpers.api import LocustEdxRestApiClient
from helpers.auto_auth_tasks import AutoAuthTasks

settings.init(__name__, required_data=['credentials'], required_secrets=['oauth'])

markers.install_event_markers()

CREDENTIAL_SERVICE_URL = settings.data['credentials']['url']['service']
LMS_ROOT_URL = settings.data['credentials']['lms_url_root']
PROGRAM_UUID = settings.data['credentials']['program_uuid']
USERNAME = settings.data['credentials']['username']


class CredentialTaskSet(AutoAuthTasks):
    """Tasks exercising Credential functionality."""

    # Keep track of credential UUIDs created during a test, so they can be used to formulate read requests, and patch
    # requests. A deque is used instead of a list in order to enforce maximum length.
    _user_credentials = deque(maxlen=1000)
Exemple #12
0
import os
import sys

# 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__))))

import random

from locust import HttpLocust, task, TaskSet
from locust.clients import HttpSession

from helpers import settings
from helpers.api import LocustEdxRestApiClient


settings.init(__name__, required=['oauth', 'programs'])


class SelfInterruptingTaskSet(TaskSet):
    @task(1)
    def stop(self):
        self.interrupt()


class CatalogTaskSet(SelfInterruptingTaskSet):
    catalog_id = 1

    @task(20)
    def get_catalog_courses(self):
        """Retrieve all courses associated with a catalog."""
        self.client.catalogs(self.catalog_id).courses.get()
Exemple #13
0
import os
import sys

# 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__))))

from locust import HttpLocust, TaskSet

from baskets import BasketsTasks
from payment import CybersourcePaymentTasks
from helpers import settings


settings.init(__name__, required=['ecommerce', 'jwt'])


class EcommerceTest(TaskSet):
    """Load test exercising ecommerce-related operations on the LMS and Otto.

    Execution probabilities are derived from a conservative estimate from
    marketing placing the percentage of paid enrollments at 2% of all
    enrollments.
    """
    tasks = {
        BasketsTasks: 50,
        CybersourcePaymentTasks: 1,
    }


class EcommerceUser(HttpLocust):
    """Representation of an HTTP "user".
# 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__))))

import requests.exceptions
import json
import random
import string
from locust import HttpLocust, task
from helpers.auto_auth_tasks import AutoAuthTasks
from helpers import settings, markers

settings.init(__name__, required_data=[
    'html',
    'video',
    'COURSE_ID',
    'LOCUST_MIN_WAIT',
    'LOCUST_MAX_WAIT',
])

markers.install_event_markers()

_dummy_chars = string.lowercase + ' '


class CompletionAPITaskSet(AutoAuthTasks):
    """
    Tests the course completion API.
    """

    def __init__(self, *args, **kwargs):
Exemple #15
0
import random

from locust import HttpLocust, task, TaskSet
from locust.clients import HttpSession

from helpers import settings
from helpers.api import LocustEdxRestApiClient


settings.init(__name__, required=['oauth'])


class SelfInterruptingTaskSet(TaskSet):
    @task(1)
    def stop(self):
        self.interrupt()


class CatalogTaskSet(SelfInterruptingTaskSet):
    catalog_id = 1

    @task(20)
    def get_catalog_courses(self):
        """Retrieve all courses associated with a catalog."""
        self.client.catalogs(self.catalog_id).courses.get()

    @task(10)
    def list_courses_with_query(self):
        """Query the courses."""
        self.client.courses.get(q='organizations:(MITx OR HarvardX)')
Exemple #16
0
import os
import sys

# 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__))))

import random

from locust import HttpLocust, task, TaskSet
from locust.clients import HttpSession

from helpers import settings
from helpers.api import LocustEdxRestApiClient

settings.init(__name__, required=['oauth', 'programs'])


class SelfInterruptingTaskSet(TaskSet):
    @task(1)
    def stop(self):
        self.interrupt()


class CatalogTaskSet(SelfInterruptingTaskSet):
    catalog_id = 1

    @task(20)
    def get_catalog_courses(self):
        """Retrieve all courses associated with a catalog."""
        self.client.catalogs(self.catalog_id).courses.get()
Exemple #17
0
from contextlib import contextmanager
from copy import copy
import json
from locust import HttpLocust, task, TaskSet
import logging
import random

from helpers import settings
# NOTE: the host URL passed in via command-line '--host' flag is the host of
# the LMS!  Make sure to set the notes service URL via the NOTES_HOST setting.
settings.init(__name__,
              required_data=[
                  'courses',
                  'NOTES_HOST',
                  'NUM_NOTES',
                  'NUM_WORDS',
                  'NUM_TAGS',
                  'NUM_SEARCH_TERMS',
                  'LOCUST_TASK_SET',
                  'LOCUST_MIN_WAIT',
                  'LOCUST_MAX_WAIT',
              ])

from helpers.mixins import EnrollmentTaskSetMixin
from helpers.edx_app import EdxAppTasks

# Constants used by the LMS when searching student notes.
HIGHLIGHT_TAG = 'span'
HIGHLIGHT_CLASS = 'note-highlight'

# Internal constants
DATA_DIRECTORY = os.path.join(os.path.dirname(__file__), 'notes_data/')
Exemple #18
0
Performance tests for enrollment API.
"""
import os
import sys

# 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__))))

import json
import uuid
import random
from locust import HttpLocust, TaskSet, task

from helpers import settings
settings.init(__name__, required=[
    'COURSE_ID_LIST',
])

EMAIL_USERNAME = "******"
EMAIL_URL = "simulator.amazonses.com"
# Set a password for the users
USER_PASSWORD = "******"
ENROLLMENT_API_BASE_URL = "/api/enrollment/v1"


class NotAuthorizedException(Exception):
    """The API returned an HTTP status 403 """
    pass


class EnrollmentApi(object):
Exemple #19
0
import os
import sys

# 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__))))

from locust import HttpLocust, TaskSet

from baskets import BasketsTasks
from payment import CybersourcePaymentTasks
from helpers import settings

settings.init(__name__, required=['ecommerce', 'jwt'])


class EcommerceTest(TaskSet):
    """Load test exercising ecommerce-related operations on the LMS and Otto.

    Execution probabilities are derived from a conservative estimate from
    marketing placing the percentage of paid enrollments at 2% of all
    enrollments.
    """
    tasks = {
        BasketsTasks: 50,
        CybersourcePaymentTasks: 1,
    }


class EcommerceUser(HttpLocust):
    """Representation of an HTTP "user".
Exemple #20
0
"""
import os
import sys

# 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__))))

import random
import time

from locust import HttpLocust, TaskSet, task, events

from helpers import settings
settings.init(__name__, required=[
    'CMS_USER_EMAIL',
    'CMS_USER_PASSWORD',
    'TEST_FILE',
    'NUM_PARALLEL_COURSES',
])


class CourseImport(TaskSet):
    "Course import task set -- creates course and imports tarballs."

    def on_start(self):
        "Setup method; log in to studio and create courses."

        self.login()

        for i in xrange(settings.data['NUM_PARALLEL_COURSES']):
            self.create_course(i)
Exemple #21
0
from locust.exception import LocustError

from warnings import filterwarnings
import MySQLdb as Database

from helpers.raw_logs import RawLogger
from helpers import datadog_reporting

# load the test settings BEFORE django settings where they are used for
# database configuration
from helpers import settings
settings.init(__name__,
              required=[
                  'DB_ENGINE',
                  'DB_HOST',
                  'DB_NAME',
                  'DB_PORT',
                  'DB_USER',
                  'DB_PASSWORD',
              ])

os.environ["DJANGO_SETTINGS_MODULE"] = "csm.locustsettings"
# Load django settings here to trigger edx-platform sys.path manipulations
from django.conf import settings as django_settings  # noqa
django_settings.INSTALLED_APPS

import courseware.user_state_client as user_state_client  # noqa
from student.tests.factories import UserFactory  # noqa
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator  # noqa

LOG = logging.getLogger(__file__)
import os
import sys

# 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__))))

from locust import HttpLocust, TaskSet

from baskets import BasketsTasks
from payment import CybersourcePaymentTasks
from helpers import settings

settings.init(
    __name__,
    required_data=['ecommerce'],
    required_secrets=['ecommerce', 'jwt'],
)


class EcommerceTest(TaskSet):
    """Load test exercising ecommerce-related operations on the LMS and Otto.

    Execution probabilities are derived from a conservative estimate from
    marketing placing the percentage of paid enrollments at 2% of all
    enrollments.
    """
    tasks = {
        BasketsTasks: 50,
        CybersourcePaymentTasks: 1,
    }
Exemple #23
0
Load tests for course import from studio.

By default, this tests loading a relatively small course. I recommend
exporting a large course from edX and using it here.
"""

import random
import time

from locust import HttpLocust, TaskSet, task, events

from helpers import settings
settings.init(__name__,
              required=[
                  'CMS_USER_EMAIL',
                  'CMS_USER_PASSWORD',
                  'TEST_FILE',
                  'NUM_PARALLEL_COURSES',
              ])


class CourseImport(TaskSet):
    "Course import task set -- creates course and imports tarballs."

    def on_start(self):
        "Setup method; log in to studio and create courses."

        self.login()

        for i in xrange(settings.data['NUM_PARALLEL_COURSES']):
            self.create_course(i)
Exemple #24
0
    GetCommentsTask,
    GetThreadListTask,
    GetThreadWithCommentsTask,
    PatchCommentsTask,
    PatchThreadsTask,
    PostCommentsTask,
    PostThreadsTask,
)

requests.packages.urllib3.disable_warnings()

from helpers import settings
settings.init(__name__, required=[
    'COURSE_ID',
    'VERBOSE',
    'LOCUST_TASK_SET',
    'LOCUST_MIN_WAIT',
    'LOCUST_MAX_WAIT',
])


class DiscussionsApiTest(DiscussionsApiTasks):
    """
    This is a repeatable baseline test which utilizes the most used requests.

    PATCH, POST, and DELETE can affect the response times of the discussions
    API over time. Since the GET Thread and GET Thread List are used
    significantly more,  we use GET Thread and GET Thread List as the baseline
    since the requests are read only and the test is repeatable.
    """
Exemple #25
0
from locust import Locust, TaskSet, task, events, web
from locust.exception import LocustError

from warnings import filterwarnings
import MySQLdb as Database

from helpers.raw_logs import RawLogger
from helpers import datadog_reporting

# load the test settings BEFORE django settings where they are used for
# database configuration
from helpers import settings
settings.init(__name__, required=[
    'DB_ENGINE',
    'DB_HOST',
    'DB_NAME',
    'DB_PORT',
    'DB_USER',
    'DB_PASSWORD',
])

os.environ["DJANGO_SETTINGS_MODULE"] = "csm.locustsettings"
# Load django settings here to trigger edx-platform sys.path manipulations
from django.conf import settings as django_settings  # noqa
django_settings.INSTALLED_APPS

import courseware.user_state_client as user_state_client  # noqa
from student.tests.factories import UserFactory  # noqa
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator  # noqa

LOG = logging.getLogger(__file__)
RANDOM_CHARACTERS = [random.choice(string.ascii_letters + string.digits) for __ in xrange(1000)]