def test_enrollment_includes_expired_verified(self): """With the right API key, request that expired course verifications are still returned. """ # Create a honor mode for a course. CourseModeFactory.create( course_id=self.course.id, mode_slug=CourseMode.HONOR, mode_display_name=CourseMode.HONOR, ) # Create a verified mode for a course. CourseModeFactory.create(course_id=self.course.id, mode_slug=CourseMode.VERIFIED, mode_display_name=CourseMode.VERIFIED, expiration_datetime='1970-01-01 05:00:00') # Passes the include_expired parameter to the API call v_response = self.client.get( reverse('courseenrollmentdetails', kwargs={"course_id": unicode(self.course.id)}), {'include_expired': True}) v_data = json.loads(v_response.content) # Ensure that both course modes are returned self.assertEqual(len(v_data['course_modes']), 2) # Omits the include_expired parameter from the API call h_response = self.client.get( reverse('courseenrollmentdetails', kwargs={"course_id": unicode(self.course.id)})) h_data = json.loads(h_response.content) # Ensure that only one course mode is returned and that it is honor self.assertEqual(len(h_data['course_modes']), 1) self.assertEqual(h_data['course_modes'][0]['slug'], CourseMode.HONOR)
def test_linked_in_add_to_profile_btn_not_appearing_without_config(self): # Without linked-in config don't show Add Certificate to LinkedIn button self.client.login(username="******", password="******") CourseModeFactory.create(course_id=self.course.id, mode_slug='verified', mode_display_name='verified', expiration_datetime=datetime.now(pytz.UTC) - timedelta(days=1)) CourseEnrollment.enroll(self.user, self.course.id, mode='honor') self.course.start = datetime.now(pytz.UTC) - timedelta(days=2) self.course.end = datetime.now(pytz.UTC) - timedelta(days=1) self.course.display_name = u"Omega" self.course = self.update_course(self.course, self.user.id) download_url = 'www.edx.org' GeneratedCertificateFactory.create( user=self.user, course_id=self.course.id, status=CertificateStatuses.downloadable, mode='honor', grade='67', download_url=download_url) response = self.client.get(reverse('dashboard')) self.assertEquals(response.status_code, 200) self.assertNotIn('Add Certificate to LinkedIn', response.content) response_url = 'http://www.linkedin.com/profile/add?_ed=' self.assertNotContains(response, response_url)
def test_user_does_not_match_param(self): """ The view should return status 404 if the enrollment username does not match the username of the user making the request, unless the request is made by a superuser or with a server API key. """ CourseModeFactory.create( course_id=self.course.id, mode_slug=CourseMode.HONOR, mode_display_name=CourseMode.HONOR, ) url = reverse('courseenrollment', kwargs={ 'username': self.other_user.username, "course_id": unicode(self.course.id) }) response = self.client.get(url) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) # Verify that the server still has access to this endpoint. self.client.logout() response = self.client.get(url, **{'HTTP_X_EDX_API_KEY': self.API_KEY}) self.assertEqual(response.status_code, status.HTTP_200_OK) # Verify superusers have access to this endpoint superuser = UserFactory.create(password=self.PASSWORD, is_superuser=True) self.client.login(username=superuser.username, password=self.PASSWORD) response = self.client.get(url) self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_enrollment_includes_expired_verified(self): """With the right API key, request that expired course verifications are still returned. """ # Create a honor mode for a course. CourseModeFactory.create( course_id=self.course.id, mode_slug=CourseMode.HONOR, mode_display_name=CourseMode.HONOR, ) # Create a verified mode for a course. CourseModeFactory.create( course_id=self.course.id, mode_slug=CourseMode.VERIFIED, mode_display_name=CourseMode.VERIFIED, expiration_datetime='1970-01-01 05:00:00' ) # Passes the include_expired parameter to the API call v_response = self.client.get( reverse('courseenrollmentdetails', kwargs={"course_id": unicode(self.course.id)}), {'include_expired': True} ) v_data = json.loads(v_response.content) # Ensure that both course modes are returned self.assertEqual(len(v_data['course_modes']), 2) # Omits the include_expired parameter from the API call h_response = self.client.get(reverse('courseenrollmentdetails', kwargs={"course_id": unicode(self.course.id)})) h_data = json.loads(h_response.content) # Ensure that only one course mode is returned and that it is honor self.assertEqual(len(h_data['course_modes']), 1) self.assertEqual(h_data['course_modes'][0]['slug'], CourseMode.HONOR)
def test_update_enrollment_with_expired_mode_throws_error(self): """Verify that if verified mode is expired than it's enrollment cannot be updated. """ for mode in [CourseMode.DEFAULT_MODE_SLUG, CourseMode.VERIFIED]: CourseModeFactory.create( course_id=self.course.id, mode_slug=mode, mode_display_name=mode, ) # Create an enrollment self.assert_enrollment_status(as_server=True) # Check that the enrollment is the default. self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id)) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG) # Change verified mode expiration. mode = CourseMode.objects.get(course_id=self.course.id, mode_slug=CourseMode.VERIFIED) mode.expiration_datetime = datetime.datetime(year=1970, month=1, day=1, tzinfo=pytz.utc) mode.save() self.assert_enrollment_status( as_server=True, mode=CourseMode.VERIFIED, expected_status=status.HTTP_400_BAD_REQUEST ) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)
def test_auto_enroll_step(self, course_modes, enrollment_mode): # Create the course modes for the test case for mode_slug in course_modes: CourseModeFactory.create( course_id=self.course.id, mode_slug=mode_slug, mode_display_name=mode_slug.capitalize() ) # Simulate the pipeline step, passing in a course ID # to indicate that the user was trying to enroll # when they started the auth process. strategy = self._fake_strategy() strategy.session_set('enroll_course_id', unicode(self.course.id)) result = pipeline.change_enrollment(strategy, 1, user=self.user) # pylint: disable=E1111,E1124 self.assertEqual(result, {}) # Check that the user was or was not enrolled # (this will vary based on the course mode) if enrollment_mode is not None: actual_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(actual_mode, enrollment_mode) else: self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course.id))
def test_auto_enroll_step(self, course_modes, enrollment_mode): # Create the course modes for the test case for mode_slug in course_modes: CourseModeFactory.create(course_id=self.course.id, mode_slug=mode_slug, mode_display_name=mode_slug.capitalize()) # Simulate the pipeline step, passing in a course ID # to indicate that the user was trying to enroll # when they started the auth process. strategy = self._fake_strategy() strategy.session_set('enroll_course_id', unicode(self.course.id)) result = pipeline.change_enrollment(strategy, 1, user=self.user) # pylint: disable=E1111,E1124 self.assertEqual(result, {}) # Check that the user was or was not enrolled # (this will vary based on the course mode) if enrollment_mode is not None: actual_mode, is_active = CourseEnrollment.enrollment_mode_for_user( self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(actual_mode, enrollment_mode) else: self.assertFalse( CourseEnrollment.is_enrolled(self.user, self.course.id))
def setUp(self): super(ChangeEnrollmentTests, self).setUp() self.course = CourseFactory.create() self.audit_mode = CourseModeFactory.create( course_id=self.course.id, mode_slug='audit', mode_display_name='Audit', ) self.honor_mode = CourseModeFactory.create( course_id=self.course.id, mode_slug='honor', mode_display_name='Honor', ) self.user_info = [('amy', '*****@*****.**', 'password'), ('rory', '*****@*****.**', 'password'), ('river', '*****@*****.**', 'password')] self.enrollments = [] self.users = [] for username, email, password in self.user_info: user = UserFactory.create(username=username, email=email, password=password) self.users.append(user) self.enrollments.append( CourseEnrollment.enroll(user, self.course.id, mode='audit'))
def test_user_does_not_match_param(self): """ The view should return status 404 if the enrollment username does not match the username of the user making the request, unless the request is made by a superuser or with a server API key. """ CourseModeFactory.create( course_id=self.course.id, mode_slug=CourseMode.HONOR, mode_display_name=CourseMode.HONOR, ) url = reverse('courseenrollment', kwargs={'username': self.other_user.username, "course_id": unicode(self.course.id)}) response = self.client.get(url) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) # Verify that the server still has access to this endpoint. self.client.logout() response = self.client.get(url, **{'HTTP_X_EDX_API_KEY': self.API_KEY}) self.assertEqual(response.status_code, status.HTTP_200_OK) # Verify superusers have access to this endpoint superuser = UserFactory.create(password=self.PASSWORD, is_superuser=True) self.client.login(username=superuser.username, password=self.PASSWORD) response = self.client.get(url) self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_get_course_details(self): CourseModeFactory.create(course_id=self.course.id, mode_slug="honor", mode_display_name="Honor") resp = self.client.get(reverse("courseenrollmentdetails", kwargs={"course_id": unicode(self.course.id)})) self.assertEqual(resp.status_code, status.HTTP_200_OK) data = json.loads(resp.content) self.assertEqual(unicode(self.course.id), data["course_id"])
def test_enroll(self, course_modes, next_url, enrollment_mode): # Create the course modes (if any) required for this test case for mode_slug in course_modes: CourseModeFactory.create( course_id=self.course.id, mode_slug=mode_slug, mode_display_name=mode_slug, ) # Reverse the expected next URL, if one is provided # (otherwise, use an empty string, which the JavaScript client # interprets as a redirect to the dashboard) full_url = ( reverse(next_url, kwargs={'course_id': unicode(self.course.id)}) if next_url else next_url ) # Enroll in the course and verify the URL we get sent to resp = self._change_enrollment('enroll') self.assertEqual(resp.status_code, 200) self.assertEqual(resp.content, full_url) # If we're not expecting to be enrolled, verify that this is the case if enrollment_mode is None: self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course.id)) # Otherwise, verify that we're enrolled with the expected course mode else: self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id)) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(course_mode, enrollment_mode)
def test_update_enrollment_with_mode(self): """With the right API key, update an existing enrollment with a new mode. """ # Create an honor and verified mode for a course. This allows an update. for mode in [CourseMode.HONOR, CourseMode.VERIFIED]: CourseModeFactory.create( course_id=self.course.id, mode_slug=mode, mode_display_name=mode, ) # Create an enrollment self._create_enrollment(as_server=True) # Check that the enrollment is honor. self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id)) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user( self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(course_mode, CourseMode.HONOR) # Check that the enrollment upgraded to verified. self._create_enrollment(as_server=True, mode=CourseMode.VERIFIED, expected_status=status.HTTP_200_OK) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user( self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(course_mode, CourseMode.VERIFIED)
def test_linked_in_add_to_profile_btn_not_appearing_without_config(self): # Without linked-in config don't show Add Certificate to LinkedIn button self.client.login(username="******", password="******") CourseModeFactory.create( course_id=self.course.id, mode_slug='verified', mode_display_name='verified', expiration_datetime=datetime.now(pytz.UTC) - timedelta(days=1) ) CourseEnrollment.enroll(self.user, self.course.id, mode='honor') self.course.start = datetime.now(pytz.UTC) - timedelta(days=2) self.course.end = datetime.now(pytz.UTC) - timedelta(days=1) self.course.display_name = u"Omega" self.course = self.update_course(self.course, self.user.id) download_url = 'www.edx.org' GeneratedCertificateFactory.create( user=self.user, course_id=self.course.id, status=CertificateStatuses.downloadable, mode='honor', grade='67', download_url=download_url ) response = self.client.get(reverse('dashboard')) self.assertEquals(response.status_code, 200) self.assertNotIn('Add Certificate to LinkedIn', response.content) response_url = 'http://www.linkedin.com/profile/add?_ed=' self.assertNotContains(response, escape(response_url))
def test_auto_enroll_step(self, course_modes, enrollment_mode, email_opt_in, email_opt_in_result): # Create the course modes for the test case for mode_slug in course_modes: CourseModeFactory.create(course_id=self.course.id, mode_slug=mode_slug, mode_display_name=mode_slug.capitalize()) # Simulate the pipeline step, passing in a course ID # to indicate that the user was trying to enroll # when they started the auth process. strategy = self._fake_strategy() strategy.session_set('enroll_course_id', unicode(self.course.id)) strategy.session_set('email_opt_in', email_opt_in) result = pipeline.change_enrollment(strategy, 1, user=self.user) # pylint: disable=assignment-from-no-return,redundant-keyword-arg self.assertEqual(result, {}) # Check that the user was or was not enrolled # (this will vary based on the course mode) if enrollment_mode is not None: actual_mode, is_active = CourseEnrollment.enrollment_mode_for_user( self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(actual_mode, enrollment_mode) else: self.assertFalse( CourseEnrollment.is_enrolled(self.user, self.course.id)) # Check that the Email Opt In option was set tag = UserOrgTag.objects.get(user=self.user) self.assertIsNotNone(tag) self.assertEquals(tag.value, email_opt_in_result)
def test_downgrade_enrollment_with_mode(self): """With the right API key, downgrade an existing enrollment with a new mode. """ # Create an honor and verified mode for a course. This allows an update. for mode in [CourseMode.DEFAULT_MODE_SLUG, CourseMode.VERIFIED]: CourseModeFactory.create( course_id=self.course.id, mode_slug=mode, mode_display_name=mode, ) # Create a 'verified' enrollment self.assert_enrollment_status(as_server=True, mode=CourseMode.VERIFIED) # Check that the enrollment is verified. self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id)) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user( self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(course_mode, CourseMode.VERIFIED) # Check that the enrollment was downgraded to the default mode. self.assert_enrollment_status(as_server=True, mode=CourseMode.DEFAULT_MODE_SLUG, expected_status=status.HTTP_200_OK) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user( self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)
def test_change_mode_from_user(self): """Users should not be able to alter the enrollment mode on an enrollment. """ # Create a default and a verified mode for a course. This allows an update. for mode in [CourseMode.DEFAULT_MODE_SLUG, CourseMode.VERIFIED]: CourseModeFactory.create( course_id=self.course.id, mode_slug=mode, mode_display_name=mode, ) # Create an enrollment self.assert_enrollment_status() # Check that the enrollment is honor. self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id)) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user( self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG) # Get a 403 response when trying to upgrade yourself. self.assert_enrollment_status( mode=CourseMode.VERIFIED, expected_status=status.HTTP_403_FORBIDDEN) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user( self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)
def test_enrollment_list_permissions(self): """ Test that the correct list of enrollments is returned, depending on the permissions of the requesting user. """ # Create another course, and enroll self.user in both courses. other_course = CourseFactory.create(emit_signals=True) for course in self.course, other_course: CourseModeFactory.create( course_id=unicode(course.id), mode_slug=CourseMode.DEFAULT_MODE_SLUG, mode_display_name=CourseMode.DEFAULT_MODE_SLUG, ) self.assert_enrollment_status( course_id=unicode(course.id), max_mongo_calls=0, ) # Verify the user himself can see both of his enrollments. self._assert_enrollments_visible_in_list([self.course, other_course]) # Verify that self.other_user can't see any of the enrollments. self.client.login(username=self.OTHER_USERNAME, password=self.PASSWORD) self._assert_enrollments_visible_in_list([]) # Create a staff user for self.course (but nor for other_course) and log her in. staff_user = UserFactory.create(username='******', email='*****@*****.**', password=self.PASSWORD) CourseStaffRole(self.course.id).add_users(staff_user) self.client.login(username='******', password=self.PASSWORD) # Verify that she can see only the enrollment in the course she has staff privileges for. self._assert_enrollments_visible_in_list([self.course]) # Create a global staff user, and verify she can see all enrollments. AdminFactory(username='******', email='*****@*****.**', password=self.PASSWORD) self.client.login(username='******', password=self.PASSWORD) self._assert_enrollments_visible_in_list([self.course, other_course]) # Verify the server can see all enrollments. self.client.logout() self._assert_enrollments_visible_in_list([self.course, other_course], use_server_key=True)
def test_deactivate_enrollment(self, configured_modes, selected_mode): """With the right API key, deactivate (i.e., unenroll from) an existing enrollment.""" # Configure a set of modes for the course. for mode in configured_modes: CourseModeFactory.create(course_id=self.course.id, mode_slug=mode, mode_display_name=mode) # Create an enrollment with the selected mode. self.assert_enrollment_status(as_server=True, mode=selected_mode) # Check that the enrollment has the correct mode and is active. self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id)) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(course_mode, selected_mode) # Verify that a non-Boolean enrollment status is treated as invalid. self.assert_enrollment_status( as_server=True, mode=None, is_active="foo", expected_status=status.HTTP_400_BAD_REQUEST ) # Verify that the enrollment has been deactivated, and that the mode is unchanged. self.assert_enrollment_activation(False, selected_mode) # Verify that enrollment deactivation is idempotent. self.assert_enrollment_activation(False, selected_mode) # Verify that omitting the mode returns 400 for course configurations # in which the default mode doesn't exist. expected_status = ( status.HTTP_200_OK if CourseMode.DEFAULT_MODE_SLUG in configured_modes else status.HTTP_400_BAD_REQUEST ) self.assert_enrollment_status(as_server=True, is_active=False, expected_status=expected_status)
def _check_verification_status_off(self, mode, value): """ Check that the css class and the status message are not in the dashboard html. """ CourseModeFactory.create(mode_slug=mode, course_id=self.course.id) CourseEnrollment.enroll(self.user, self.course.location.course_key, mode=mode) if mode == 'verified': # Simulate a successful verification attempt attempt = SoftwareSecurePhotoVerification.objects.create( user=self.user) attempt.mark_ready() attempt.submit() attempt.approve() response = self.client.get(reverse('dashboard')) if mode == 'audit': # Audit mode does not have a banner. Assert no banner element. self.assertEqual(pq(response.content)(".sts-enrollment").length, 0) else: self.assertNotContains(response, "class=\"course {0}\"".format(mode)) self.assertNotContains(response, value)
def test_third_party_auth_course_id_verified(self, modes): # Create a course with the specified course modes course = CourseFactory.create() for slug in modes: CourseModeFactory.create(course_id=course.id, mode_slug=slug, mode_display_name=slug) # Verify that the entry URL for third party auth # contains the course ID and redirects to the track selection page. course_modes_choose_url = reverse("course_modes_choose", kwargs={"course_id": unicode(course.id)}) expected_providers = [ { "name": "Facebook", "iconClass": "fa-facebook", "loginUrl": self._third_party_login_url( "facebook", "login", course_id=unicode(course.id), redirect_url=course_modes_choose_url ), "registerUrl": self._third_party_login_url( "facebook", "register", course_id=unicode(course.id), redirect_url=course_modes_choose_url ), }, { "name": "Google", "iconClass": "fa-google-plus", "loginUrl": self._third_party_login_url( "google-oauth2", "login", course_id=unicode(course.id), redirect_url=course_modes_choose_url ), "registerUrl": self._third_party_login_url( "google-oauth2", "register", course_id=unicode(course.id), redirect_url=course_modes_choose_url ), }, ] # Verify that the login page contains the correct provider URLs response = self.client.get(reverse("account_login"), {"course_id": unicode(course.id)}) self._assert_third_party_auth_data(response, None, expected_providers)
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_enrollment_with_credit_mode(self): """With the right API key, update an existing enrollment with credit mode and set enrollment attributes. """ for mode in [CourseMode.DEFAULT_MODE_SLUG, CourseMode.CREDIT_MODE]: CourseModeFactory.create( course_id=self.course.id, mode_slug=mode, mode_display_name=mode, ) # Create an enrollment self.assert_enrollment_status(as_server=True) # Check that the enrollment is the default. self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id)) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG) # Check that the enrollment upgraded to credit. enrollment_attributes = [{ "namespace": "credit", "name": "provider_id", "value": "hogwarts", }] self.assert_enrollment_status( as_server=True, mode=CourseMode.CREDIT_MODE, expected_status=status.HTTP_200_OK, enrollment_attributes=enrollment_attributes ) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(course_mode, CourseMode.CREDIT_MODE)
def test_auto_enroll_step(self, course_modes, enrollment_mode, email_opt_in, email_opt_in_result): # Create the course modes for the test case for mode_slug in course_modes: CourseModeFactory.create( course_id=self.course.id, mode_slug=mode_slug, mode_display_name=mode_slug.capitalize() ) # Simulate the pipeline step, passing in a course ID # to indicate that the user was trying to enroll # when they started the auth process. strategy = self._fake_strategy() strategy.session_set('enroll_course_id', unicode(self.course.id)) strategy.session_set('email_opt_in', email_opt_in) result = pipeline.change_enrollment(strategy, 1, user=self.user) # pylint: disable=assignment-from-no-return,redundant-keyword-arg self.assertEqual(result, {}) # Check that the user was or was not enrolled # (this will vary based on the course mode) if enrollment_mode is not None: actual_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(actual_mode, enrollment_mode) else: self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course.id)) # Check that the Email Opt In option was set tag = UserOrgTag.objects.get(user=self.user) self.assertIsNotNone(tag) self.assertEquals(tag.value, email_opt_in_result)
def test_enroll(self, course_modes, next_url, enrollment_mode, auto_reg): # Create the course modes (if any) required for this test case for mode_slug in course_modes: CourseModeFactory.create(course_id=self.course.id, mode_slug=mode_slug, mode_display_name=mode_slug) # Reverse the expected next URL, if one is provided # (otherwise, use an empty string, which the JavaScript client # interprets as a redirect to the dashboard) full_url = reverse(next_url, kwargs={"course_id": unicode(self.course.id)}) if next_url else next_url # Enroll in the course and verify the URL we get sent to resp = self._change_enrollment("enroll", auto_reg=auto_reg) self.assertEqual(resp.status_code, 200) self.assertEqual(resp.content, full_url) # TODO (ECOM-16): If auto-registration is enabled, check that we're # storing the auto-reg flag in the user's session if auto_reg: self.assertIn("auto_register", self.client.session) self.assertTrue(self.client.session["auto_register"]) # If we're not expecting to be enrolled, verify that this is the case if enrollment_mode is None: self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course.id)) # Otherwise, verify that we're enrolled with the expected course mode else: self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id)) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(course_mode, enrollment_mode)
def test_enrollment_with_invalid_attr(self): """Check response status is bad request when invalid enrollment attributes are passed """ for mode in [CourseMode.DEFAULT_MODE_SLUG, CourseMode.CREDIT_MODE]: CourseModeFactory.create( course_id=self.course.id, mode_slug=mode, mode_display_name=mode, ) # Create an enrollment self.assert_enrollment_status(as_server=True) # Check that the enrollment is the default. self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id)) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG) # Check that the enrollment upgraded to credit. enrollment_attributes = [{ "namespace": "credit", "name": "invalid", "value": "hogwarts", }] self.assert_enrollment_status( as_server=True, mode=CourseMode.CREDIT_MODE, expected_status=status.HTTP_400_BAD_REQUEST, enrollment_attributes=enrollment_attributes ) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)
def test_deactivate_enrollment(self): """With the right API key, deactivate (i.e., unenroll from) an existing enrollment.""" # Create an honor and verified mode for a course. This allows an update. for mode in [CourseMode.HONOR, CourseMode.VERIFIED]: CourseModeFactory.create( course_id=self.course.id, mode_slug=mode, mode_display_name=mode, ) # Create a 'verified' enrollment self.assert_enrollment_status(as_server=True, mode=CourseMode.VERIFIED) # Check that the enrollment is 'verified' and active. self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id)) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(course_mode, CourseMode.VERIFIED) # Verify that a non-Boolean enrollment status is treated as invalid. self.assert_enrollment_status( as_server=True, mode=None, is_active='foo', expected_status=status.HTTP_400_BAD_REQUEST ) # Verify that the enrollment has been deactivated, and that the mode is unchanged. self.assert_enrollment_activation(False) # Verify that enrollment deactivation is idempotent. self.assert_enrollment_activation(False)
def test_third_party_auth_course_id_shopping_cart(self): # Create a course with a white-label course mode course = CourseFactory.create() CourseModeFactory.create(course_id=course.id, mode_slug="honor", mode_display_name="Honor", min_price=100) # Verify that the entry URL for third party auth # contains the course ID and redirects to the shopping cart shoppingcart_url = reverse("shoppingcart.views.show_cart") expected_providers = [ { "name": "Facebook", "iconClass": "fa-facebook", "loginUrl": self._third_party_login_url( "facebook", "login", course_id=unicode(course.id), redirect_url=shoppingcart_url ), "registerUrl": self._third_party_login_url( "facebook", "register", course_id=unicode(course.id), redirect_url=shoppingcart_url ), }, { "name": "Google", "iconClass": "fa-google-plus", "loginUrl": self._third_party_login_url( "google-oauth2", "login", course_id=unicode(course.id), redirect_url=shoppingcart_url ), "registerUrl": self._third_party_login_url( "google-oauth2", "register", course_id=unicode(course.id), redirect_url=shoppingcart_url ), }, ] # Verify that the login page contains the correct provider URLs response = self.client.get(reverse("account_login"), {"course_id": unicode(course.id)}) self._assert_third_party_auth_data(response, None, expected_providers)
def test_downgrade_enrollment_with_mode(self): """With the right API key, downgrade an existing enrollment with a new mode. """ # Create an honor and verified mode for a course. This allows an update. for mode in [CourseMode.DEFAULT_MODE_SLUG, CourseMode.VERIFIED]: CourseModeFactory.create( course_id=self.course.id, mode_slug=mode, mode_display_name=mode, ) # Create a 'verified' enrollment self.assert_enrollment_status(as_server=True, mode=CourseMode.VERIFIED) # Check that the enrollment is verified. self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id)) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(course_mode, CourseMode.VERIFIED) # Check that the enrollment was downgraded to the default mode. self.assert_enrollment_status( as_server=True, mode=CourseMode.DEFAULT_MODE_SLUG, expected_status=status.HTTP_200_OK ) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id) self.assertTrue(is_active) self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)
def setUp(self): super(ChangeEnrollmentTests, self).setUp() self.course = CourseFactory.create() self.audit_mode = CourseModeFactory.create( course_id=self.course.id, mode_slug='audit', mode_display_name='Audit', ) self.honor_mode = CourseModeFactory.create( course_id=self.course.id, mode_slug='honor', mode_display_name='Honor', ) self.user_info = [ ('amy', '*****@*****.**', 'password'), ('rory', '*****@*****.**', 'password'), ('river', '*****@*****.**', 'password') ] self.enrollments = [] self.users = [] for username, email, password in self.user_info: user = UserFactory.create(username=username, email=email, password=password) self.users.append(user) self.enrollments.append(CourseEnrollment.enroll(user, self.course.id, mode='audit'))
def setUp(self): super(AnonymousLookupTable, self).setUp() self.course = CourseFactory.create() self.user = UserFactory() CourseModeFactory.create(course_id=self.course.id, mode_slug="honor", mode_display_name="Honor Code") patcher = patch("student.models.tracker") patcher.start() self.addCleanup(patcher.stop)
def test_user_does_not_match_param_for_list(self): CourseModeFactory.create( course_id=self.course.id, mode_slug='honor', mode_display_name='Honor', ) resp = self.client.get(reverse('courseenrollments'), {"user": "******"}) self.assertEqual(resp.status_code, status.HTTP_404_NOT_FOUND)
def test_user_does_not_match_url(self): # Try to enroll a user that is not the authenticated user. CourseModeFactory.create( course_id=self.course.id, mode_slug='honor', mode_display_name='Honor', ) self._create_enrollment(username='******', expected_status=status.HTTP_404_NOT_FOUND)
def setUp(self): self.course = CourseFactory.create(org=self.COURSE_ORG, display_name=self.COURSE_NAME, number=self.COURSE_SLUG) self.assertIsNotNone(self.course) self.user = UserFactory() CourseModeFactory.create(course_id=self.course.id, mode_slug="honor", mode_display_name="Honor Code") patcher = patch("student.models.server_track") self.mock_server_track = patcher.start() self.addCleanup(patcher.stop)
def setUp(self): super(DashboardTestsWithSiteOverrides, self).setUp() self.org = 'fakeX' self.course = CourseFactory.create(org=self.org) self.user = UserFactory.create(username='******', email='*****@*****.**', password='******') CourseModeFactory.create(mode_slug='no-id-professional', course_id=self.course.id) CourseEnrollment.enroll(self.user, self.course.location.course_key, mode='no-id-professional') cache.clear()
def setUp(self): super(DashboardTestsWithSiteOverrides, self).setUp() self.org = "fakeX" self.course = CourseFactory.create(org=self.org) self.user = UserFactory.create(username="******", email="*****@*****.**", password="******") CourseModeFactory.create(mode_slug="no-id-professional", course_id=self.course.id) CourseEnrollment.enroll(self.user, self.course.location.course_key, mode="no-id-professional") cache.clear()
def setUp(self): super(TestCourseRegistrationCodeStatus, self).setUp() CourseModeFactory.create(course_id=self.course.id, min_price=50) self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password='******') CourseSalesAdminRole(self.course.id).add_users(self.instructor) # create testing invoice self.sale_invoice = Invoice.objects.create( total_amount=1234.32, company_name='Test1', company_contact_name='TestName', company_contact_email='*****@*****.**', recipient_name='Testw', recipient_email='*****@*****.**', customer_reference_number='2Fwe23S', internal_reference="A", course_id=self.course.id, is_valid=True) self.invoice_item = CourseRegistrationCodeInvoiceItem.objects.create( invoice=self.sale_invoice, qty=1, unit_price=1234.32, course_id=self.course.id) self.lookup_code_url = reverse( 'look_up_registration_code', kwargs={'course_id': unicode(self.course.id)}) self.registration_code_detail_url = reverse( 'registration_code_details', kwargs={'course_id': unicode(self.course.id)}) url = reverse( 'generate_registration_codes', kwargs={'course_id': self.course.id.to_deprecated_string()}) data = { 'total_registration_codes': 12, 'company_name': 'Test Group', 'company_contact_name': '*****@*****.**', 'company_contact_email': '*****@*****.**', 'unit_price': 122.45, 'recipient_name': 'Test123', 'recipient_email': '*****@*****.**', 'address_line_1': 'Portland Street', 'address_line_2': '', 'address_line_3': '', 'city': '', 'state': '', 'zip': '', 'country': '', 'customer_reference_number': '123A23F', 'internal_reference': '', 'invoice': '' } response = self.client.post(url, data) self.assertEqual(response.status_code, 200, response.content)
def _create_course_modes(self, course_modes, course=None): """Create the course modes required for a test. """ course_id = course.id if course else self.course.id for mode_slug in course_modes: CourseModeFactory.create( course_id=course_id, mode_slug=mode_slug, mode_display_name=mode_slug, )
def setUp(self): self.course = CourseFactory.create(org=self.COURSE_ORG, display_name=self.COURSE_NAME, number=self.COURSE_SLUG) self.assertIsNotNone(self.course) self.user = UserFactory.create(username="******", email="*****@*****.**") CourseModeFactory.create( course_id=self.course.id, mode_slug='honor', mode_display_name='Honor Code', )
def test_user_does_not_match_url(self): # Try to enroll a user that is not the authenticated user. CourseModeFactory.create( course_id=self.course.id, mode_slug=CourseMode.HONOR, mode_display_name=CourseMode.HONOR ) self.assert_enrollment_status(username=self.other_user.username, expected_status=status.HTTP_404_NOT_FOUND) # Verify that the server still has access to this endpoint. self.client.logout() self.assert_enrollment_status(username=self.other_user.username, as_server=True)
def test_enrollment_throttle_for_service(self): """Make sure a service can call the enrollment API as many times as needed. """ self.rate_limit_config.enabled = True self.rate_limit_config.save() CourseModeFactory.create( course_id=self.course.id, mode_slug=CourseMode.HONOR, mode_display_name=CourseMode.HONOR ) for attempt in xrange(self.rate_limit + 10): self.assert_enrollment_status(as_server=True)
def test_user_not_specified(self): CourseModeFactory.create(course_id=self.course.id, mode_slug="honor", mode_display_name="Honor") # Create an enrollment self._create_enrollment() resp = self.client.get(reverse("courseenrollment", kwargs={"course_id": unicode(self.course.id)})) self.assertEqual(resp.status_code, status.HTTP_200_OK) data = json.loads(resp.content) self.assertEqual(unicode(self.course.id), data["course_details"]["course_id"]) self.assertEqual("honor", data["mode"]) self.assertTrue(data["is_active"])
def _enrollment_with_complete_course(self, enrollment_mode): """"Dry method for course enrollment.""" CourseModeFactory.create( course_id=self.course.id, mode_slug='verified', mode_display_name='Verified', expiration_datetime=datetime.now(pytz.UTC) + timedelta(days=1) ) enrollment = CourseEnrollment.enroll(self.user, self.course.id, mode=enrollment_mode) return complete_course_mode_info(self.course.id, enrollment)
def test_user_does_not_match_url(self): # Try to enroll a user that is not the authenticated user. CourseModeFactory.create( course_id=self.course.id, mode_slug='honor', mode_display_name='Honor', ) self._create_enrollment(username=self.other_user.username, expected_status=status.HTTP_404_NOT_FOUND) # Verify that the server still has access to this endpoint. self.client.logout() self._create_enrollment(username=self.other_user.username, as_server=True)
def setUp(self): self.course = CourseFactory.create(org=self.COURSE_ORG, display_name=self.COURSE_NAME, number=self.COURSE_SLUG) self.assertIsNotNone(self.course) self.user = UserFactory() CourseModeFactory.create( course_id=self.course.id, mode_slug='honor', mode_display_name='Honor Code', ) patcher = patch('student.models.server_track') self.mock_server_track = patcher.start() self.addCleanup(patcher.stop)
def test_enrollment_throttle_for_service(self): """Make sure a service can call the enrollment API as many times as needed. """ self.rate_limit_config.enabled = True self.rate_limit_config.save() CourseModeFactory.create( course_id=self.course.id, mode_slug=CourseMode.DEFAULT_MODE_SLUG, mode_display_name=CourseMode.DEFAULT_MODE_SLUG, ) for __ in xrange(self.rate_limit + 10): self.assert_enrollment_status(as_server=True)
def test_enterprise_course_enrollment_invalid_consent(self): """Verify that the enterprise_course_consent must be a boolean. """ CourseModeFactory.create( course_id=self.course.id, mode_slug=CourseMode.DEFAULT_MODE_SLUG, mode_display_name=CourseMode.DEFAULT_MODE_SLUG, ) self.assert_enrollment_status( expected_status=status.HTTP_400_BAD_REQUEST, enterprise_course_consent='invalid', as_server=True, )
def setUp(self): super(AnonymousLookupTable, self).setUp() self.course = CourseFactory.create() self.user = UserFactory() CourseModeFactory.create( course_id=self.course.id, mode_slug='honor', mode_display_name='Honor Code', ) patcher = patch('student.models.tracker') patcher.start() self.addCleanup(patcher.stop)
def test_enrollment_throttle_for_service(self): """Make sure a service can call the enrollment API as many times as needed. """ self.rate_limit_config.enabled = True self.rate_limit_config.save() CourseModeFactory.create( course_id=self.course.id, mode_slug=CourseMode.HONOR, mode_display_name=CourseMode.HONOR, ) for attempt in xrange(self.rate_limit + 10): self._create_enrollment(as_server=True)
def test_enrollment_throttle_for_user(self): """Make sure a user requests do not exceed the maximum number of requests""" self.rate_limit_config.enabled = True self.rate_limit_config.save() CourseModeFactory.create( course_id=self.course.id, mode_slug=CourseMode.DEFAULT_MODE_SLUG, mode_display_name=CourseMode.DEFAULT_MODE_SLUG, ) for attempt in xrange(self.rate_limit + 10): expected_status = status.HTTP_429_TOO_MANY_REQUESTS if attempt >= self.rate_limit else status.HTTP_200_OK self.assert_enrollment_status(expected_status=expected_status)
def test_get_course_details(self): CourseModeFactory.create( course_id=self.course.id, mode_slug='honor', mode_display_name='Honor', ) resp = self.client.get( reverse('courseenrollmentdetails', kwargs={"course_id": unicode(self.course.id)})) self.assertEqual(resp.status_code, status.HTTP_200_OK) data = json.loads(resp.content) self.assertEqual(unicode(self.course.id), data['course_id'])
def test_user_does_not_match_param_for_list(self): CourseModeFactory.create( course_id=self.course.id, mode_slug='honor', mode_display_name='Honor', ) resp = self.client.get(reverse('courseenrollments'), {"user": self.other_user.username}) self.assertEqual(resp.status_code, status.HTTP_404_NOT_FOUND) # Verify that the server still has access to this endpoint. self.client.logout() resp = self.client.get( reverse('courseenrollments'), {"user": self.other_user.username}, **{'HTTP_X_EDX_API_KEY': self.API_KEY} ) self.assertEqual(resp.status_code, status.HTTP_200_OK)
def test_third_party_auth_course_id_verified(self, modes): # Create a course with the specified course modes course = CourseFactory.create() for slug in modes: CourseModeFactory.create( course_id=course.id, mode_slug=slug, mode_display_name=slug ) # Verify that the entry URL for third party auth # contains the course ID and redirects to the track selection page. course_modes_choose_url = reverse( "course_modes_choose", kwargs={"course_id": unicode(course.id)} ) expected_providers = [ { "name": "Facebook", "iconClass": "fa-facebook", "loginUrl": self._third_party_login_url( "facebook", "login", course_id=unicode(course.id), redirect_url=course_modes_choose_url ), "registerUrl": self._third_party_login_url( "facebook", "register", course_id=unicode(course.id), redirect_url=course_modes_choose_url ) }, { "name": "Google", "iconClass": "fa-google-plus", "loginUrl": self._third_party_login_url( "google-oauth2", "login", course_id=unicode(course.id), redirect_url=course_modes_choose_url ), "registerUrl": self._third_party_login_url( "google-oauth2", "register", course_id=unicode(course.id), redirect_url=course_modes_choose_url ) } ] # Verify that the login page contains the correct provider URLs response = self.client.get(reverse("account_login"), {"course_id": unicode(course.id)}) self._assert_third_party_auth_data(response, None, expected_providers)
def test_change_mode_from_server(self, old_mode, new_mode, old_is_active, new_is_active): """ Server-to-server calls should be allowed to change the mode of any enrollment, as long as the enrollment is not being deactivated during the same call (this is assumed to be an error on the client's side). """ for mode in [CourseMode.HONOR, CourseMode.VERIFIED]: CourseModeFactory.create( course_id=self.course.id, mode_slug=mode, mode_display_name=mode, ) # Set up the initial enrollment self.assert_enrollment_status(as_server=True, mode=old_mode, is_active=old_is_active) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user( self.user, self.course.id) self.assertEqual(is_active, old_is_active) self.assertEqual(course_mode, old_mode) expected_status = status.HTTP_400_BAD_REQUEST if ( old_mode != new_mode and old_is_active != new_is_active and not new_is_active) else status.HTTP_200_OK # simulate the server-server api call under test response = self.assert_enrollment_status( as_server=True, mode=new_mode, is_active=new_is_active, expected_status=expected_status, ) course_mode, is_active = CourseEnrollment.enrollment_mode_for_user( self.user, self.course.id) if expected_status == status.HTTP_400_BAD_REQUEST: # nothing should have changed self.assertEqual(is_active, old_is_active) self.assertEqual(course_mode, old_mode) # error message should contain specific text. Otto checks for this text in the message. self.assertRegexpMatches( json.loads(response.content)['message'], 'Enrollment mode mismatch') else: # call should have succeeded self.assertEqual(is_active, new_is_active) self.assertEqual(course_mode, new_mode)