Files
edx-platform/lms/djangoapps/courseware/tests/test_microsites.py
David Ormsbee 2051c90924 Test Speedup: Isolate Modulestore Signals
There are a number of Django Signals that are on the modulestore's
SignalHandler class, such as SignalHandler.course_published. These
signals can trigger very expensive processes to occur, such as course
overview or block structures generation. Most of the time, the test
author doesn't care about these side-effects.

This commit does a few things:

* Converts the signals on SignalHandler to be instances of a new
  SwitchedSignal class, that allows signal sending to be disabled.

* Creates a SignalIsolationMixin helper similar in spirit to the
  CacheIsolationMixin, and adds it to the ModuleStoreIsolationMixin
  (and thus to ModuleStoreTestCase and SharedModuleStoreTestCase).

* Converts our various tests to use this new mechanism. In some cases,
  this means adjusting query counts downwards because they no longer
  have to account for publishing listener actions.

Modulestore generated signals are now muted by default during test runs.
Calls to send() them will result in no-ops. You can choose to enable
specific signals for a given subclass of ModuleStoreTestCase or
SharedModuleStoreTestCase by specifying an ENABLED_SIGNALS class
attribute, like the following example:

    from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase

    class MyPublishTestCase(ModuleStoreTestCase):
        ENABLED_SIGNALS = ['course_published', 'pre_publish']

You should take great care when disabling signals outside of a
ModuleStoreTestCase or SharedModuleStoreTestCase, since they can leak
out into other tests. Be sure to always clean up, and never disable
signals outside of testing. Because signals are essentially process
globals, it can have a lot of unpleasant side-effects if we start
mucking around with them during live requests.

Overall, this change has cut the total test execution time for
edx-platform by a bit over a third, though we still spend a lot in
pre-test setup during our test builds.

[PERF-413]
2017-02-23 10:31:16 -05:00

263 lines
11 KiB
Python

"""
Tests related to the Site COnfiguration feature
"""
from django.conf import settings
from django.core.urlresolvers import reverse
from django.test.utils import override_settings
from nose.plugins.attrib import attr
from courseware.tests.helpers import LoginEnrollmentTestCase
from course_modes.models import CourseMode
from xmodule.course_module import (
CATALOG_VISIBILITY_CATALOG_AND_ABOUT, CATALOG_VISIBILITY_NONE)
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
@attr(shard=1)
class TestSites(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
"""
This is testing of the Site Configuration feature
"""
STUDENT_INFO = [('view@test.com', 'foo'), ('view2@test.com', 'foo')]
ENABLED_SIGNALS = ['course_published']
@classmethod
def setUpClass(cls):
super(TestSites, cls).setUpClass()
cls.course = CourseFactory.create(
display_name='Robot_Super_Course',
org='TestSiteX',
emit_signals=True,
)
cls.chapter0 = ItemFactory.create(parent_location=cls.course.location, display_name='Overview')
cls.chapter9 = ItemFactory.create(parent_location=cls.course.location, display_name='factory_chapter')
cls.section0 = ItemFactory.create(parent_location=cls.chapter0.location, display_name='Welcome')
cls.section9 = ItemFactory.create(parent_location=cls.chapter9.location, display_name='factory_section')
cls.course_outside_site = CourseFactory.create(
display_name='Robot_Course_Outside_Site',
org='FooX',
emit_signals=True,
)
# have a course which explicitly sets visibility in catalog to False
cls.course_hidden_visibility = CourseFactory.create(
display_name='Hidden_course',
org='TestSiteX',
catalog_visibility=CATALOG_VISIBILITY_NONE,
emit_signals=True,
)
# have a course which explicitly sets visibility in catalog and about to true
cls.course_with_visibility = CourseFactory.create(
display_name='visible_course',
org='TestSiteX',
course="foo",
catalog_visibility=CATALOG_VISIBILITY_CATALOG_AND_ABOUT,
emit_signals=True,
)
def setUp(self):
super(TestSites, self).setUp()
def setup_users(self):
# Create student accounts and activate them.
for i in range(len(self.STUDENT_INFO)):
email, password = self.STUDENT_INFO[i]
username = 'u{0}'.format(i)
self.create_account(username, email, password)
self.activate_user(email)
@override_settings(SITE_NAME=settings.MICROSITE_TEST_HOSTNAME)
def test_site_anonymous_homepage_content(self):
"""
Verify that the homepage, when accessed via a Site domain, returns
HTML that reflects the Site branding elements
"""
resp = self.client.get('/', HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME)
self.assertEqual(resp.status_code, 200)
# assert various branding definitions on this Site
# as per the configuration and Site overrides
self.assertContains(resp, 'This is a Test Site Overlay') # Overlay test message
self.assertContains(resp, 'test_site/images/header-logo.png') # logo swap
self.assertContains(resp, 'test_site/css/test_site') # css override
self.assertContains(resp, 'Test Site') # page title
# assert that test course display name is visible
self.assertContains(resp, 'Robot_Super_Course')
# assert that test course with 'visible_in_catalog' to True is showing up
self.assertContains(resp, 'visible_course')
# assert that test course that is outside current configured site is not visible
self.assertNotContains(resp, 'Robot_Course_Outside_Site')
# assert that a course that has visible_in_catalog=False is not visible
self.assertNotContains(resp, 'Hidden_course')
# assert that footer template has been properly overriden on homepage
self.assertContains(resp, 'This is a Test Site footer')
# assert that the edX partners section is not in the HTML
self.assertNotContains(resp, '<section class="university-partners university-partners2x6">')
# assert that the edX partners tag line is not in the HTML
self.assertNotContains(resp, 'Explore free courses from')
def test_no_configuration_anonymous_homepage_content(self):
"""
Make sure we see the right content on the homepage if there is no site configuration defined.
"""
resp = self.client.get('/')
self.assertEqual(resp.status_code, 200)
# assert various branding definitions on this Site ARE NOT VISIBLE
self.assertNotContains(resp, 'This is a Test Site Overlay') # Overlay test message
self.assertNotContains(resp, 'test_site/images/header-logo.png') # logo swap
self.assertNotContains(resp, 'test_site/css/test_site') # css override
self.assertNotContains(resp, '<title>Test Site</title>') # page title
# assert that test course display name IS NOT VISIBLE
self.assertNotContains(resp, 'Robot_Super_Course')
# assert that test course that is outside site IS VISIBLE
self.assertContains(resp, 'Robot_Course_Outside_Site')
# assert that footer template has been properly overriden on homepage
self.assertNotContains(resp, 'This is a Test Site footer')
@override_settings(SITE_NAME=settings.MICROSITE_TEST_HOSTNAME)
def test_site_anonymous_copyright_content(self):
"""
Verify that the copyright, when accessed via a Site domain, returns
the expected 200 response
"""
resp = self.client.get('/copyright', HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME)
self.assertEqual(resp.status_code, 200)
self.assertContains(resp, 'This is a copyright page for an Open edX site.')
def test_not_site_anonymous_copyright_content(self):
"""
Verify that the copyright page does not exist if we are not in a configured site.
"""
resp = self.client.get('/copyright')
self.assertEqual(resp.status_code, 404)
def test_no_redirect_on_homepage_when_no_enrollments(self):
"""
Verify that a user going to homepage will not redirect if he/she has no course enrollments
"""
self.setup_users()
email, password = self.STUDENT_INFO[0]
self.login(email, password)
resp = self.client.get(reverse('root'), HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME)
self.assertEquals(resp.status_code, 200)
def test_no_redirect_on_homepage_when_has_enrollments(self):
"""
Verify that a user going to homepage will not redirect to dashboard if he/she has
a course enrollment
"""
self.setup_users()
email, password = self.STUDENT_INFO[0]
self.login(email, password)
self.enroll(self.course, True)
resp = self.client.get(reverse('root'), HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME)
self.assertEquals(resp.status_code, 200)
def test_site_course_enrollment(self):
"""
Enroll user in a course scoped in a Site and one course outside of a Site
and make sure that they are only visible in the right Dashboards
"""
self.setup_users()
email, password = self.STUDENT_INFO[1]
self.login(email, password)
self.enroll(self.course, True)
self.enroll(self.course_outside_site, True)
# Access the site dashboard and make sure the right courses appear
resp = self.client.get(reverse('dashboard'), HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME)
self.assertContains(resp, 'Robot_Super_Course')
self.assertNotContains(resp, 'Robot_Course_Outside_Site')
# Now access the non-site dashboard and make sure the right courses appear
resp = self.client.get(reverse('dashboard'))
self.assertNotContains(resp, 'Robot_Super_Course')
self.assertContains(resp, 'Robot_Course_Outside_Site')
def test_site_course_custom_tabs(self):
"""
Enroll user in a course scoped in a Site and make sure that
template with tabs is overridden
"""
self.setup_users()
email, password = self.STUDENT_INFO[1]
self.login(email, password)
self.enroll(self.course, True)
resp = self.client.get(reverse('courseware', args=[unicode(self.course.id)]),
HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME)
self.assertContains(resp, 'Test Site Tab:')
@override_settings(SITE_NAME=settings.MICROSITE_TEST_HOSTNAME)
def test_visible_about_page_settings(self):
"""
Make sure the Site is honoring the visible_about_page permissions that is
set in configuration
"""
url = reverse('about_course', args=[self.course_with_visibility.id.to_deprecated_string()])
resp = self.client.get(url, HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME)
self.assertEqual(resp.status_code, 200)
url = reverse('about_course', args=[self.course_hidden_visibility.id.to_deprecated_string()])
resp = self.client.get(url, HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME)
self.assertEqual(resp.status_code, 404)
@override_settings(SITE_NAME=settings.MICROSITE_TEST_HOSTNAME)
def test_paid_course_registration(self):
"""
Make sure that Site overrides on the ENABLE_SHOPPING_CART and
ENABLE_PAID_COURSE_ENROLLMENTS are honored
"""
course_mode = CourseMode(
course_id=self.course_with_visibility.id,
mode_slug=CourseMode.DEFAULT_MODE_SLUG,
mode_display_name=CourseMode.DEFAULT_MODE_SLUG,
min_price=10,
)
course_mode.save()
# first try on the non site, which
# should pick up the global configuration (where ENABLE_PAID_COURSE_REGISTRATIONS = False)
url = reverse('about_course', args=[self.course_with_visibility.id.to_deprecated_string()])
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
self.assertIn("Enroll in {}".format(self.course_with_visibility.id.course), resp.content)
self.assertNotIn("Add {} to Cart ($10)".format(self.course_with_visibility.id.course), resp.content)
# now try on the site
url = reverse('about_course', args=[self.course_with_visibility.id.to_deprecated_string()])
resp = self.client.get(url, HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME)
self.assertEqual(resp.status_code, 200)
self.assertNotIn("Enroll in {}".format(self.course_with_visibility.id.course), resp.content)
self.assertIn("Add {} to Cart <span>($10 USD)</span>".format(
self.course_with_visibility.id.course
), resp.content)
self.assertIn('$("#add_to_cart_post").click', resp.content)