280 lines
12 KiB
Python
280 lines
12 KiB
Python
"""
|
|
Tests for student enrollment.
|
|
"""
|
|
import unittest
|
|
|
|
import ddt
|
|
from django.conf import settings
|
|
from django.test.utils import override_settings
|
|
from mock import Mock, patch
|
|
from nose.tools import raises
|
|
|
|
from course_modes.models import CourseMode
|
|
from enrollment import api
|
|
from enrollment.errors import CourseModeNotFoundError, EnrollmentApiLoadError, EnrollmentNotFoundError
|
|
from enrollment.tests import fake_data_api
|
|
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
|
|
|
|
|
|
@ddt.ddt
|
|
@override_settings(ENROLLMENT_DATA_API="enrollment.tests.fake_data_api")
|
|
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
|
class EnrollmentTest(CacheIsolationTestCase):
|
|
"""
|
|
Test student enrollment, especially with different course modes.
|
|
"""
|
|
USERNAME = "Bob"
|
|
COURSE_ID = "some/great/course"
|
|
|
|
ENABLED_CACHES = ['default']
|
|
|
|
def setUp(self):
|
|
super(EnrollmentTest, self).setUp()
|
|
fake_data_api.reset()
|
|
|
|
@ddt.data(
|
|
# Default (no course modes in the database)
|
|
# Expect automatically being 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'),
|
|
|
|
# Check for professional ed happy path.
|
|
(['professional'], 'professional'),
|
|
(['no-id-professional'], 'no-id-professional')
|
|
)
|
|
@ddt.unpack
|
|
def test_enroll(self, course_modes, mode):
|
|
# Add a fake course enrollment information to the fake data API
|
|
fake_data_api.add_course(self.COURSE_ID, course_modes=course_modes)
|
|
# Enroll in the course and verify the URL we get sent to
|
|
result = api.add_enrollment(self.USERNAME, self.COURSE_ID, mode=mode)
|
|
self.assertIsNotNone(result)
|
|
self.assertEquals(result['student'], self.USERNAME)
|
|
self.assertEquals(result['course']['course_id'], self.COURSE_ID)
|
|
self.assertEquals(result['mode'], mode)
|
|
|
|
get_result = api.get_enrollment(self.USERNAME, self.COURSE_ID)
|
|
self.assertEquals(result, get_result)
|
|
|
|
@ddt.data(
|
|
([CourseMode.DEFAULT_MODE_SLUG, 'verified', 'credit'], CourseMode.DEFAULT_MODE_SLUG),
|
|
(['audit', 'verified', 'credit'], 'audit'),
|
|
(['honor', 'verified', 'credit'], 'honor'),
|
|
)
|
|
@ddt.unpack
|
|
def test_enroll_no_mode_success(self, course_modes, expected_mode):
|
|
# Add a fake course enrollment information to the fake data API
|
|
fake_data_api.add_course(self.COURSE_ID, course_modes=course_modes)
|
|
with patch('enrollment.api.CourseMode.modes_for_course') as mock_modes_for_course:
|
|
mock_course_modes = [Mock(slug=mode) for mode in course_modes]
|
|
mock_modes_for_course.return_value = mock_course_modes
|
|
# Enroll in the course and verify the URL we get sent to
|
|
result = api.add_enrollment(self.USERNAME, self.COURSE_ID)
|
|
self.assertIsNotNone(result)
|
|
self.assertEquals(result['student'], self.USERNAME)
|
|
self.assertEquals(result['course']['course_id'], self.COURSE_ID)
|
|
self.assertEquals(result['mode'], expected_mode)
|
|
|
|
@ddt.data(
|
|
['professional'],
|
|
['verified'],
|
|
['verified', 'professional'],
|
|
)
|
|
@raises(CourseModeNotFoundError)
|
|
def test_enroll_no_mode_error(self, course_modes):
|
|
# Add a fake course enrollment information to the fake data API
|
|
fake_data_api.add_course(self.COURSE_ID, course_modes=course_modes)
|
|
# Enroll in the course and verify that we raise CourseModeNotFoundError
|
|
api.add_enrollment(self.USERNAME, self.COURSE_ID)
|
|
|
|
@raises(CourseModeNotFoundError)
|
|
def test_prof_ed_enroll(self):
|
|
# Add a fake course enrollment information to the fake data API
|
|
fake_data_api.add_course(self.COURSE_ID, course_modes=['professional'])
|
|
# Enroll in the course and verify the URL we get sent to
|
|
api.add_enrollment(self.USERNAME, self.COURSE_ID, mode='verified')
|
|
|
|
@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'),
|
|
|
|
# Check for professional ed happy path.
|
|
(['professional'], 'professional'),
|
|
(['no-id-professional'], 'no-id-professional')
|
|
)
|
|
@ddt.unpack
|
|
def test_unenroll(self, course_modes, mode):
|
|
# Add a fake course enrollment information to the fake data API
|
|
fake_data_api.add_course(self.COURSE_ID, course_modes=course_modes)
|
|
# Enroll in the course and verify the URL we get sent to
|
|
result = api.add_enrollment(self.USERNAME, self.COURSE_ID, mode=mode)
|
|
self.assertIsNotNone(result)
|
|
self.assertEquals(result['student'], self.USERNAME)
|
|
self.assertEquals(result['course']['course_id'], self.COURSE_ID)
|
|
self.assertEquals(result['mode'], mode)
|
|
self.assertTrue(result['is_active'])
|
|
|
|
result = api.update_enrollment(self.USERNAME, self.COURSE_ID, mode=mode, is_active=False)
|
|
self.assertIsNotNone(result)
|
|
self.assertEquals(result['student'], self.USERNAME)
|
|
self.assertEquals(result['course']['course_id'], self.COURSE_ID)
|
|
self.assertEquals(result['mode'], mode)
|
|
self.assertFalse(result['is_active'])
|
|
|
|
@raises(EnrollmentNotFoundError)
|
|
def test_unenroll_not_enrolled_in_course(self):
|
|
# Add a fake course enrollment information to the fake data API
|
|
fake_data_api.add_course(self.COURSE_ID, course_modes=['honor'])
|
|
api.update_enrollment(self.USERNAME, self.COURSE_ID, mode='honor', is_active=False)
|
|
|
|
@ddt.data(
|
|
# Simple test of honor and verified.
|
|
([
|
|
{'course_id': 'the/first/course', 'course_modes': [], 'mode': 'honor'},
|
|
{'course_id': 'the/second/course', 'course_modes': ['honor', 'verified'], 'mode': 'verified'}
|
|
]),
|
|
|
|
# No enrollments
|
|
([]),
|
|
|
|
# One Enrollment
|
|
([
|
|
{'course_id': 'the/third/course', 'course_modes': ['honor', 'verified', 'audit'], 'mode': 'audit'}
|
|
]),
|
|
)
|
|
def test_get_all_enrollments(self, enrollments):
|
|
for enrollment in enrollments:
|
|
fake_data_api.add_course(enrollment['course_id'], course_modes=enrollment['course_modes'])
|
|
api.add_enrollment(self.USERNAME, enrollment['course_id'], enrollment['mode'])
|
|
result = api.get_enrollments(self.USERNAME)
|
|
self.assertEqual(len(enrollments), len(result))
|
|
for result_enrollment in result:
|
|
self.assertIn(
|
|
result_enrollment['course']['course_id'],
|
|
[enrollment['course_id'] for enrollment in enrollments]
|
|
)
|
|
|
|
def test_update_enrollment(self):
|
|
# Add fake course enrollment information to the fake data API
|
|
fake_data_api.add_course(self.COURSE_ID, course_modes=['honor', 'verified', 'audit'])
|
|
# Enroll in the course and verify the URL we get sent to
|
|
result = api.add_enrollment(self.USERNAME, self.COURSE_ID, mode='audit')
|
|
get_result = api.get_enrollment(self.USERNAME, self.COURSE_ID)
|
|
self.assertEquals(result, get_result)
|
|
|
|
result = api.update_enrollment(self.USERNAME, self.COURSE_ID, mode='honor')
|
|
self.assertEquals('honor', result['mode'])
|
|
|
|
result = api.update_enrollment(self.USERNAME, self.COURSE_ID, mode='verified')
|
|
self.assertEquals('verified', result['mode'])
|
|
|
|
def test_update_enrollment_attributes(self):
|
|
# Add fake course enrollment information to the fake data API
|
|
fake_data_api.add_course(self.COURSE_ID, course_modes=['honor', 'verified', 'audit', 'credit'])
|
|
# Enroll in the course and verify the URL we get sent to
|
|
result = api.add_enrollment(self.USERNAME, self.COURSE_ID, mode='audit')
|
|
get_result = api.get_enrollment(self.USERNAME, self.COURSE_ID)
|
|
self.assertEquals(result, get_result)
|
|
|
|
enrollment_attributes = [
|
|
{
|
|
"namespace": "credit",
|
|
"name": "provider_id",
|
|
"value": "hogwarts",
|
|
}
|
|
]
|
|
|
|
result = api.update_enrollment(
|
|
self.USERNAME, self.COURSE_ID, mode='credit', enrollment_attributes=enrollment_attributes
|
|
)
|
|
self.assertEquals('credit', result['mode'])
|
|
attributes = api.get_enrollment_attributes(self.USERNAME, self.COURSE_ID)
|
|
self.assertEquals(enrollment_attributes[0], attributes[0])
|
|
|
|
def test_get_course_details(self):
|
|
# Add a fake course enrollment information to the fake data API
|
|
fake_data_api.add_course(self.COURSE_ID, course_modes=['honor', 'verified', 'audit'])
|
|
result = api.get_course_enrollment_details(self.COURSE_ID)
|
|
self.assertEquals(result['course_id'], self.COURSE_ID)
|
|
self.assertEquals(3, len(result['course_modes']))
|
|
|
|
@override_settings(ENROLLMENT_DATA_API='foo.bar.biz.baz')
|
|
@raises(EnrollmentApiLoadError)
|
|
def test_data_api_config_error(self):
|
|
# Enroll in the course and verify the URL we get sent to
|
|
api.add_enrollment(self.USERNAME, self.COURSE_ID, mode='audit')
|
|
|
|
def test_caching(self):
|
|
# Add fake course enrollment information to the fake data API
|
|
fake_data_api.add_course(self.COURSE_ID, course_modes=['honor', 'verified', 'audit'])
|
|
|
|
# Hit the fake data API.
|
|
details = api.get_course_enrollment_details(self.COURSE_ID)
|
|
|
|
# Reset the fake data API, should rely on the cache.
|
|
fake_data_api.reset()
|
|
cached_details = api.get_course_enrollment_details(self.COURSE_ID)
|
|
|
|
# The data matches
|
|
self.assertEqual(len(details['course_modes']), 3)
|
|
self.assertEqual(details, cached_details)
|
|
|
|
def test_update_enrollment_expired_mode_with_error(self):
|
|
""" Verify that if verified mode is expired and include expire flag is
|
|
false then enrollment cannot be updated. """
|
|
self.assert_add_modes_with_enrollment('audit')
|
|
# On updating enrollment mode to verified it should the raise the error.
|
|
with self.assertRaises(CourseModeNotFoundError):
|
|
self.assert_update_enrollment(mode='verified', include_expired=False)
|
|
|
|
def test_update_enrollment_with_expired_mode(self):
|
|
""" Verify that if verified mode is expired then enrollment can be
|
|
updated if include_expired flag is true."""
|
|
self.assert_add_modes_with_enrollment('audit')
|
|
# enrollment in verified mode will work fine with include_expired=True
|
|
self.assert_update_enrollment(mode='verified', include_expired=True)
|
|
|
|
@ddt.data(True, False)
|
|
def test_unenroll_with_expired_mode(self, include_expired):
|
|
""" Verify that un-enroll will work fine for expired courses whether include_expired
|
|
is true or false."""
|
|
self.assert_add_modes_with_enrollment('verified')
|
|
self.assert_update_enrollment(mode='verified', is_active=False, include_expired=include_expired)
|
|
|
|
def assert_add_modes_with_enrollment(self, enrollment_mode):
|
|
""" Dry method for adding fake course enrollment information to fake
|
|
data API and enroll the student in the course. """
|
|
fake_data_api.add_course(self.COURSE_ID, course_modes=['honor', 'verified', 'audit'])
|
|
result = api.add_enrollment(self.USERNAME, self.COURSE_ID, mode=enrollment_mode)
|
|
get_result = api.get_enrollment(self.USERNAME, self.COURSE_ID)
|
|
self.assertEquals(result, get_result)
|
|
# set the course verify mode as expire.
|
|
fake_data_api.set_expired_mode(self.COURSE_ID)
|
|
|
|
def assert_update_enrollment(self, mode, is_active=True, include_expired=False):
|
|
""" Dry method for updating enrollment."""
|
|
|
|
result = api.update_enrollment(
|
|
self.USERNAME, self.COURSE_ID, mode=mode, is_active=is_active, include_expired=include_expired
|
|
)
|
|
self.assertEquals(mode, result['mode'])
|
|
self.assertIsNotNone(result)
|
|
self.assertEquals(result['student'], self.USERNAME)
|
|
self.assertEquals(result['course']['course_id'], self.COURSE_ID)
|
|
self.assertEquals(result['mode'], mode)
|
|
|
|
if is_active:
|
|
self.assertTrue(result['is_active'])
|
|
else:
|
|
self.assertFalse(result['is_active'])
|