Commit 5cc6455f by Will Daly

Merge pull request #5006 from edx/will/ecom-199

ECOM-199: Auto-registration bugfix
parents e33d9b73 9d4bf890
...@@ -29,12 +29,16 @@ class EnrollmentTest(ModuleStoreTestCase): ...@@ -29,12 +29,16 @@ class EnrollmentTest(ModuleStoreTestCase):
""" """
Test student enrollment, especially with different course modes. Test student enrollment, especially with different course modes.
""" """
USERNAME = "Bob"
EMAIL = "bob@example.com"
PASSWORD = "edx"
def setUp(self): def setUp(self):
""" Create a course and user, then log in. """ """ Create a course and user, then log in. """
super(EnrollmentTest, self).setUp() super(EnrollmentTest, self).setUp()
self.course = CourseFactory.create() self.course = CourseFactory.create()
self.user = UserFactory.create(username="Bob", email="bob@example.com", password="edx") self.user = UserFactory.create(username=self.USERNAME, email=self.EMAIL, password=self.PASSWORD)
self.client.login(username=self.user.username, password="edx") self.client.login(username=self.USERNAME, password=self.PASSWORD)
self.urls = [ self.urls = [
reverse('course_modes_choose', kwargs={'course_id': unicode(self.course.id)}) reverse('course_modes_choose', kwargs={'course_id': unicode(self.course.id)})
...@@ -74,7 +78,6 @@ class EnrollmentTest(ModuleStoreTestCase): ...@@ -74,7 +78,6 @@ class EnrollmentTest(ModuleStoreTestCase):
course_id=self.course.id, course_id=self.course.id,
mode_slug=mode_slug, mode_slug=mode_slug,
mode_display_name=mode_slug, mode_display_name=mode_slug,
expiration_datetime=datetime.now(pytz.UTC) + timedelta(days=1)
) )
# Reverse the expected next URL, if one is provided # Reverse the expected next URL, if one is provided
...@@ -107,6 +110,74 @@ class EnrollmentTest(ModuleStoreTestCase): ...@@ -107,6 +110,74 @@ class EnrollmentTest(ModuleStoreTestCase):
self.assertTrue(is_active) self.assertTrue(is_active)
self.assertEqual(course_mode, enrollment_mode) self.assertEqual(course_mode, enrollment_mode)
# TODO (ECOM-16): Remove once the auto-registration A/B test completes.
def test_enroll_from_redirect_autoreg(self):
# Bugfix (ECOM-199): Visitors removed from auto-enroll pool
# if they need to authenticate while attempting to enroll.
# If a user is (a) in the experimental condition of the A/B test (autoreg enabled)
# and (b) is not logged in when registering for a course, then
# the user will be redirected to the "control" URL (change_enrollment)
# instead of the experimental URL (change_enrollment_autoreg)
# We work around this by setting a flag in the session, such that
# if a user is *ever* in the experimental condition, they remain
# in the experimental condition.
for mode_slug in ['honor', 'audit', 'verified']:
CourseModeFactory.create(
course_id=self.course.id,
mode_slug=mode_slug,
mode_display_name=mode_slug
)
# Log out, so we're no longer authenticated
self.client.logout()
# Visit the experimental condition URL
resp = self._change_enrollment('enroll', auto_reg=True)
# Expect that we're denied (since we're not authenticated)
# and instead are redirected to the login page
self.assertEqual(resp.status_code, 403)
# Log the user in
self.client.login(username=self.USERNAME, password=self.PASSWORD)
# Go to the control URL
# This simulates what the JavaScript client does after getting a 403 response
# from the register button.
resp = self._change_enrollment('enroll')
# Expect that we're auto-enrolled (even though we used the control URL the second time)
self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id))
# Expect that the auto register flag is still set in the user's session
self.assertIn('auto_register', self.client.session)
self.assertTrue(self.client.session['auto_register'])
# TODO (ECOM-16): Remove once the auto-registration A/B test completes
def test_enroll_auto_registration_excluded_course(self):
# Create the course modes
for mode_slug in ['honor', 'audit', 'verified']:
CourseModeFactory.create(
course_id=self.course.id,
mode_slug=mode_slug,
mode_display_name=mode_slug,
)
# Visit the experimental condition URL (when the course is NOT excluded)
# This should place us into the experimental condition flow
self._change_enrollment('enroll', auto_reg=True)
# Unenroll from the course (we were registered because auto enroll was enabled)
self._change_enrollment('unenroll')
# Register for the course again, with the course excluded
# At this point, we should NOT be in the experimental condition flow
excluded_course_ids = [self.course.id.to_deprecated_string()]
with self.settings(AUTO_REGISTRATION_AB_TEST_EXCLUDE_COURSES=excluded_course_ids):
self._change_enrollment('enroll')
self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course.id))
self.assertNotIn('auto_register', self.client.session)
def test_unenroll(self): def test_unenroll(self):
# Enroll the student in the course # Enroll the student in the course
CourseEnrollment.enroll(self.user, self.course.id, mode="honor") CourseEnrollment.enroll(self.user, self.course.id, mode="honor")
......
...@@ -15,6 +15,7 @@ from django.test.utils import override_settings ...@@ -15,6 +15,7 @@ from django.test.utils import override_settings
from django.test.client import RequestFactory, Client from django.test.client import RequestFactory, Client
from django.contrib.auth.models import User, AnonymousUser from django.contrib.auth.models import User, AnonymousUser
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.contrib.sessions.middleware import SessionMiddleware
from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.tests.factories import CourseFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
...@@ -634,8 +635,17 @@ class PaidRegistrationTest(ModuleStoreTestCase): ...@@ -634,8 +635,17 @@ class PaidRegistrationTest(ModuleStoreTestCase):
@unittest.skipUnless(settings.FEATURES.get('ENABLE_SHOPPING_CART'), "Shopping Cart not enabled in settings") @unittest.skipUnless(settings.FEATURES.get('ENABLE_SHOPPING_CART'), "Shopping Cart not enabled in settings")
def test_change_enrollment_add_to_cart(self): def test_change_enrollment_add_to_cart(self):
request = self.req_factory.post(reverse('change_enrollment'), {'course_id': self.course.id.to_deprecated_string(), request = self.req_factory.post(
'enrollment_action': 'add_to_cart'}) reverse('change_enrollment'), {
'course_id': self.course.id.to_deprecated_string(),
'enrollment_action': 'add_to_cart'
}
)
# Add a session to the request
SessionMiddleware().process_request(request)
request.session.save()
request.user = self.user request.user = self.user
response = change_enrollment(request) response = change_enrollment(request)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
......
...@@ -634,6 +634,25 @@ def change_enrollment(request, auto_register=False): ...@@ -634,6 +634,25 @@ def change_enrollment(request, auto_register=False):
)) ))
return HttpResponseBadRequest(_("Invalid course id")) return HttpResponseBadRequest(_("Invalid course id"))
# TODO (ECOM-16): Remove this once the auto-registration A/B test completes
# If a user is in the experimental condition (auto-registration enabled),
# immediately set a session flag so they stay in the experimental condition.
# We keep them in the experimental condition even if later on the user
# tries to register using the control URL (e.g. because of a redirect from the login page,
# which is hard-coded to use the control URL).
if auto_register:
request.session['auto_register'] = True
if request.session.get('auto_register') and not auto_register:
auto_register = True
# TODO (ECOM-16): Remove this once the auto-registration A/B test completes
# We've agreed to exclude certain courses from the A/B test. If we find ourselves
# registering for one of these courses, immediately switch to the control.
if unicode(course_id) in getattr(settings, 'AUTO_REGISTRATION_AB_TEST_EXCLUDE_COURSES', []):
auto_register = False
if 'auto_register' in request.session:
del request.session['auto_register']
if not user.is_authenticated(): if not user.is_authenticated():
return HttpResponseForbidden() return HttpResponseForbidden()
...@@ -671,11 +690,6 @@ def change_enrollment(request, auto_register=False): ...@@ -671,11 +690,6 @@ def change_enrollment(request, auto_register=False):
# TODO (ECOM-16): Once the auto-registration AB-test is complete, delete # TODO (ECOM-16): Once the auto-registration AB-test is complete, delete
# one of these two conditions and remove the `auto_register` flag. # one of these two conditions and remove the `auto_register` flag.
if auto_register: if auto_register:
# TODO (ECOM-16): This stores a flag in the session so downstream
# views will recognize that the user is in the "auto-registration"
# experimental condition. We can remove this once the AB test completes.
request.session['auto_register'] = True
available_modes = CourseMode.modes_for_course_dict(course_id) available_modes = CourseMode.modes_for_course_dict(course_id)
# Handle professional ed as a special case. # Handle professional ed as a special case.
...@@ -710,12 +724,6 @@ def change_enrollment(request, auto_register=False): ...@@ -710,12 +724,6 @@ def change_enrollment(request, auto_register=False):
# before sending them to the "choose your track" page. # before sending them to the "choose your track" page.
# This is the control for the auto-registration AB-test. # This is the control for the auto-registration AB-test.
else: else:
# TODO (ECOM-16): If the user is NOT in the experimental condition,
# make sure their session reflects this. We can remove this
# once the AB test completes.
if 'auto_register' in request.session:
del request.session['auto_register']
# If this course is available in multiple modes, redirect them to a page # If this course is available in multiple modes, redirect them to a page
# where they can choose which mode they want. # where they can choose which mode they want.
available_modes = CourseMode.modes_for_course(course_id) available_modes = CourseMode.modes_for_course(course_id)
......
...@@ -445,3 +445,6 @@ OPTIMIZELY_PROJECT_ID = AUTH_TOKENS.get('OPTIMIZELY_PROJECT_ID', OPTIMIZELY_PROJ ...@@ -445,3 +445,6 @@ OPTIMIZELY_PROJECT_ID = AUTH_TOKENS.get('OPTIMIZELY_PROJECT_ID', OPTIMIZELY_PROJ
#### Course Registration Code length #### #### Course Registration Code length ####
REGISTRATION_CODE_LENGTH = ENV_TOKENS.get('REGISTRATION_CODE_LENGTH', 8) REGISTRATION_CODE_LENGTH = ENV_TOKENS.get('REGISTRATION_CODE_LENGTH', 8)
# TODO (ECOM-16): Remove once the A/B test of auto-registration completes
AUTO_REGISTRATION_AB_TEST_EXCLUDE_COURSES = set(ENV_TOKENS.get('AUTO_REGISTRATION_AB_TEST_EXCLUDE_COURSES', AUTO_REGISTRATION_AB_TEST_EXCLUDE_COURSES))
...@@ -1735,3 +1735,21 @@ OPENID_DOMAIN_PREFIX = 'openid:' ...@@ -1735,3 +1735,21 @@ OPENID_DOMAIN_PREFIX = 'openid:'
ANALYTICS_DATA_URL = "" ANALYTICS_DATA_URL = ""
ANALYTICS_DATA_TOKEN = "" ANALYTICS_DATA_TOKEN = ""
ANALYTICS_DASHBOARD_URL = "" ANALYTICS_DASHBOARD_URL = ""
# TODO (ECOM-16): Remove once the A/B test of auto-registration completes
AUTO_REGISTRATION_AB_TEST_EXCLUDE_COURSES = set([
"HarvardX/SW12.2x/1T2014",
"HarvardX/SW12.3x/1T2014",
"HarvardX/SW12.4x/1T2014",
"HarvardX/SW12.5x/2T2014",
"HarvardX/SW12.6x/2T2014",
"HarvardX/HUM2.1x/3T2014",
"HarvardX/SW12x/2013_SOND",
"LinuxFoundationX/LFS101x/2T2014",
"HarvardX/CS50x/2014_T1",
"HarvardX/AmPoX.1/2014_T3",
"HarvardX/SW12.7x/3T2014",
"HarvardX/SW12.10x/1T2015",
"HarvardX/SW12.9x/3T2014",
"HarvardX/SW12.8x/3T2014",
])
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment