""" Test the Data Aggregation Layer for Course Enrollments. """ import ddt from mock import patch from nose.tools import raises import unittest from django.conf import settings from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory from enrollment.errors import ( UserNotFoundError, CourseEnrollmentClosedError, CourseEnrollmentFullError, CourseEnrollmentExistsError, ) from openedx.core.lib.exceptions import CourseNotFoundError from student.tests.factories import UserFactory, CourseModeFactory from student.models import CourseEnrollment, EnrollmentClosedError, CourseFullError, AlreadyEnrolledError from enrollment import data @ddt.ddt @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') class EnrollmentDataTest(ModuleStoreTestCase): """ Test course enrollment data aggregation. """ USERNAME = "Bob" EMAIL = "bob@example.com" PASSWORD = "edx" def setUp(self): """Create a course and user, then log in. """ super(EnrollmentDataTest, self).setUp() self.course = CourseFactory.create() self.user = UserFactory.create(username=self.USERNAME, email=self.EMAIL, password=self.PASSWORD) self.client.login(username=self.USERNAME, password=self.PASSWORD) @ddt.data( # Default (no course modes in the database) # Expect that users are automatically enrolled as "honor". ([], 'honor'), # Audit / Verified / Honor # We should always go to the "choose your course" page. # We should also be enrolled as "honor" by default. (['honor', 'verified', 'audit'], 'honor'), ) @ddt.unpack def test_enroll(self, course_modes, enrollment_mode): # Create the course modes (if any) required for this test case self._create_course_modes(course_modes) enrollment = data.create_course_enrollment( self.user.username, unicode(self.course.id), enrollment_mode, True ) 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) # Confirm the returned enrollment and the data match up. self.assertEqual(course_mode, enrollment['mode']) self.assertEqual(is_active, enrollment['is_active']) def test_unenroll(self): # Enroll the user in the course CourseEnrollment.enroll(self.user, self.course.id, mode="honor") enrollment = data.update_course_enrollment( self.user.username, unicode(self.course.id), is_active=False ) # Determine that the returned enrollment is inactive. self.assertFalse(enrollment['is_active']) # Expect that we're no longer enrolled self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course.id)) @ddt.data( # No course modes, no course enrollments. ([]), # Audit / Verified / Honor course modes, with three course enrollments. (['honor', 'verified', 'audit']), ) def test_get_course_info(self, course_modes): self._create_course_modes(course_modes, course=self.course) result_course = data.get_course_enrollment_info(unicode(self.course.id)) result_slugs = [mode['slug'] for mode in result_course['course_modes']] for course_mode in course_modes: self.assertIn(course_mode, result_slugs) @ddt.data( # No course modes, no course enrollments. ([], []), # Audit / Verified / Honor course modes, with three course enrollments. (['honor', 'verified', 'audit'], ['1', '2', '3']), ) @ddt.unpack def test_get_course_enrollments(self, course_modes, course_numbers): # Create all the courses created_courses = [] for course_number in course_numbers: created_courses.append(CourseFactory.create(number=course_number)) created_enrollments = [] for course in created_courses: self._create_course_modes(course_modes, course=course) # Create the original enrollment. created_enrollments.append(data.create_course_enrollment( self.user.username, unicode(course.id), 'honor', True )) # Compare the created enrollments with the results # from the get enrollments request. results = data.get_course_enrollments(self.user.username) self.assertEqual(results, created_enrollments) # Now create a course enrollment with some invalid course (does # not exist in database) for the user and check that the method # 'get_course_enrollments' ignores course enrollments for invalid # or deleted courses CourseEnrollment.objects.create( user=self.user, course_id='InvalidOrg/InvalidCourse/InvalidRun', mode='honor', is_active=True ) updated_results = data.get_course_enrollments(self.user.username) self.assertEqual(results, updated_results) @ddt.data( # Default (no course modes in the database) # Expect that users are automatically enrolled as "honor". ([], 'honor'), # Audit / Verified / Honor # We should always go to the "choose your course" page. # We should also be enrolled as "honor" by default. (['honor', 'verified', 'audit'], 'verified'), ) @ddt.unpack def test_get_course_enrollment(self, course_modes, enrollment_mode): self._create_course_modes(course_modes) # Try to get an enrollment before it exists. result = data.get_course_enrollment(self.user.username, unicode(self.course.id)) self.assertIsNone(result) # Create the original enrollment. enrollment = data.create_course_enrollment( self.user.username, unicode(self.course.id), enrollment_mode, True ) # Get the enrollment and compare it to the original. result = data.get_course_enrollment(self.user.username, unicode(self.course.id)) self.assertEqual(self.user.username, result['user']) self.assertEqual(enrollment, result) @ddt.data( # Default (no course modes in the database) # Expect that users are automatically enrolled as "honor". ([], 'credit'), # Audit / Verified / Honor # We should always go to the "choose your course" page. # We should also be enrolled as "honor" by default. (['honor', 'verified', 'audit', 'credit'], 'credit'), ) @ddt.unpack def test_add_or_update_enrollment_attr(self, course_modes, enrollment_mode): # Create the course modes (if any) required for this test case self._create_course_modes(course_modes) data.create_course_enrollment(self.user.username, unicode(self.course.id), enrollment_mode, True) enrollment_attributes = [ { "namespace": "credit", "name": "provider_id", "value": "hogwarts", } ] data.add_or_update_enrollment_attr(self.user.username, unicode(self.course.id), enrollment_attributes) enrollment_attr = data.get_enrollment_attributes(self.user.username, unicode(self.course.id)) self.assertEqual(enrollment_attr[0], enrollment_attributes[0]) enrollment_attributes = [ { "namespace": "credit", "name": "provider_id", "value": "ASU", } ] data.add_or_update_enrollment_attr(self.user.username, unicode(self.course.id), enrollment_attributes) enrollment_attr = data.get_enrollment_attributes(self.user.username, unicode(self.course.id)) self.assertEqual(enrollment_attr[0], enrollment_attributes[0]) @raises(CourseNotFoundError) def test_non_existent_course(self): data.get_course_enrollment_info("this/is/bananas") 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, ) @raises(UserNotFoundError) def test_enrollment_for_non_existent_user(self): data.create_course_enrollment("some_fake_user", unicode(self.course.id), 'honor', True) @raises(CourseNotFoundError) def test_enrollment_for_non_existent_course(self): data.create_course_enrollment(self.user.username, "some/fake/course", 'honor', True) @raises(CourseEnrollmentClosedError) @patch.object(CourseEnrollment, "enroll") def test_enrollment_for_closed_course(self, mock_enroll): mock_enroll.side_effect = EnrollmentClosedError("Bad things happened") data.create_course_enrollment(self.user.username, unicode(self.course.id), 'honor', True) @raises(CourseEnrollmentFullError) @patch.object(CourseEnrollment, "enroll") def test_enrollment_for_closed_course(self, mock_enroll): mock_enroll.side_effect = CourseFullError("Bad things happened") data.create_course_enrollment(self.user.username, unicode(self.course.id), 'honor', True) @raises(CourseEnrollmentExistsError) @patch.object(CourseEnrollment, "enroll") def test_enrollment_for_closed_course(self, mock_enroll): mock_enroll.side_effect = AlreadyEnrolledError("Bad things happened") data.create_course_enrollment(self.user.username, unicode(self.course.id), 'honor', True) @raises(UserNotFoundError) def test_update_for_non_existent_user(self): data.update_course_enrollment("some_fake_user", unicode(self.course.id), is_active=False) def test_update_for_non_existent_course(self): enrollment = data.update_course_enrollment(self.user.username, "some/fake/course", is_active=False) self.assertIsNone(enrollment)