Files
edx-platform/lms/djangoapps/ccx/tests/test_utils.py
2017-06-11 21:48:06 -04:00

404 lines
18 KiB
Python

"""
test utils
"""
import uuid
from smtplib import SMTPException
import mock
from ccx_keys.locator import CCXLocator
from nose.plugins.attrib import attr
from opaque_keys.edx.keys import CourseKey
from lms.djangoapps.ccx import utils
from lms.djangoapps.ccx.tests.factories import CcxFactory
from lms.djangoapps.ccx.tests.utils import CcxTestCase
from lms.djangoapps.ccx.utils import add_master_course_staff_to_ccx, ccx_course, remove_master_course_staff_from_ccx
from lms.djangoapps.instructor.access import list_with_level
from student.models import CourseEnrollment, CourseEnrollmentException
from student.roles import CourseCcxCoachRole, CourseInstructorRole, CourseStaffRole
from student.tests.factories import AdminFactory
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@attr(shard=1)
class TestGetCCXFromCCXLocator(ModuleStoreTestCase):
"""Verify that get_ccx_from_ccx_locator functions properly"""
MODULESTORE = TEST_DATA_SPLIT_MODULESTORE
def setUp(self):
"""Set up a course, coach, ccx and user"""
super(TestGetCCXFromCCXLocator, self).setUp()
self.course = CourseFactory.create()
coach = self.coach = AdminFactory.create()
role = CourseCcxCoachRole(self.course.id)
role.add_users(coach)
def call_fut(self, course_id):
"""call the function under test in this test case"""
from lms.djangoapps.ccx.utils import get_ccx_from_ccx_locator
return get_ccx_from_ccx_locator(course_id)
def test_non_ccx_locator(self):
"""verify that nothing is returned if locator is not a ccx locator
"""
result = self.call_fut(self.course.id)
self.assertEqual(result, None)
def test_ccx_locator(self):
"""verify that the ccx is retuned if using a ccx locator
"""
ccx = CcxFactory(course_id=self.course.id, coach=self.coach)
course_key = CCXLocator.from_course_locator(self.course.id, ccx.id)
result = self.call_fut(course_key)
self.assertEqual(result, ccx)
@attr(shard=1)
class TestGetCourseChapters(CcxTestCase):
"""
Tests for the `get_course_chapters` util function
"""
ENABLED_SIGNALS = ['course_published']
def setUp(self):
"""
Set up tests
"""
super(TestGetCourseChapters, self).setUp()
self.course_key = self.course.location.course_key
def test_get_structure_non_existing_key(self):
"""
Test to get the course structure
"""
self.assertEqual(utils.get_course_chapters(None), None)
# build a fake key
fake_course_key = CourseKey.from_string('course-v1:FakeOrg+CN1+CR-FALLNEVER1')
self.assertEqual(utils.get_course_chapters(fake_course_key), None)
@mock.patch('openedx.core.djangoapps.content.course_structures.models.CourseStructure.structure',
new_callable=mock.PropertyMock)
def test_wrong_course_structure(self, mocked_attr):
"""
Test the case where the course has an unexpected structure.
"""
mocked_attr.return_value = {'foo': 'bar'}
self.assertEqual(utils.get_course_chapters(self.course_key), [])
def test_get_chapters(self):
"""
Happy path
"""
course_chapters = utils.get_course_chapters(self.course_key)
self.assertEqual(len(course_chapters), 2)
self.assertEqual(
sorted(course_chapters),
sorted([unicode(child) for child in self.course.children])
)
class TestStaffOnCCX(CcxTestCase):
"""
Tests for staff on ccx courses.
"""
MODULESTORE = TEST_DATA_SPLIT_MODULESTORE
def setUp(self):
super(TestStaffOnCCX, self).setUp()
# Create instructor account
self.client.login(username=self.coach.username, password="test")
# create an instance of modulestore
self.mstore = modulestore()
self.make_coach()
self.ccx = self.make_ccx()
self.ccx_locator = CCXLocator.from_course_locator(self.course.id, self.ccx.id)
def test_add_master_course_staff_to_ccx(self):
"""
Test add staff of master course to ccx course
"""
# adding staff to master course.
staff = self.make_staff()
self.assertTrue(CourseStaffRole(self.course.id).has_user(staff))
# adding instructor to master course.
instructor = self.make_instructor()
self.assertTrue(CourseInstructorRole(self.course.id).has_user(instructor))
add_master_course_staff_to_ccx(self.course, self.ccx_locator, self.ccx.display_name)
# assert that staff and instructors of master course has staff and instructor roles on ccx
list_staff_master_course = list_with_level(self.course, 'staff')
list_instructor_master_course = list_with_level(self.course, 'instructor')
with ccx_course(self.ccx_locator) as course_ccx:
list_staff_ccx_course = list_with_level(course_ccx, 'staff')
self.assertEqual(len(list_staff_master_course), len(list_staff_ccx_course))
self.assertEqual(list_staff_master_course[0].email, list_staff_ccx_course[0].email)
list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
self.assertEqual(len(list_instructor_ccx_course), len(list_instructor_master_course))
self.assertEqual(list_instructor_ccx_course[0].email, list_instructor_master_course[0].email)
def test_add_master_course_staff_to_ccx_with_exception(self):
"""
When exception raise from ``enroll_email`` assert that enrollment skipped for that staff or
instructor.
"""
staff = self.make_staff()
self.assertTrue(CourseStaffRole(self.course.id).has_user(staff))
# adding instructor to master course.
instructor = self.make_instructor()
self.assertTrue(CourseInstructorRole(self.course.id).has_user(instructor))
with mock.patch.object(CourseEnrollment, 'enroll_by_email', side_effect=CourseEnrollmentException()):
add_master_course_staff_to_ccx(self.course, self.ccx_locator, self.ccx.display_name)
self.assertFalse(
CourseEnrollment.objects.filter(course_id=self.ccx_locator, user=staff).exists()
)
self.assertFalse(
CourseEnrollment.objects.filter(course_id=self.ccx_locator, user=instructor).exists()
)
with mock.patch.object(CourseEnrollment, 'enroll_by_email', side_effect=SMTPException()):
add_master_course_staff_to_ccx(self.course, self.ccx_locator, self.ccx.display_name)
self.assertFalse(
CourseEnrollment.objects.filter(course_id=self.ccx_locator, user=staff).exists()
)
self.assertFalse(
CourseEnrollment.objects.filter(course_id=self.ccx_locator, user=instructor).exists()
)
def test_remove_master_course_staff_from_ccx(self):
"""
Test remove staff of master course to ccx course
"""
staff = self.make_staff()
self.assertTrue(CourseStaffRole(self.course.id).has_user(staff))
# adding instructor to master course.
instructor = self.make_instructor()
self.assertTrue(CourseInstructorRole(self.course.id).has_user(instructor))
add_master_course_staff_to_ccx(self.course, self.ccx_locator, self.ccx.display_name, send_email=False)
list_staff_master_course = list_with_level(self.course, 'staff')
list_instructor_master_course = list_with_level(self.course, 'instructor')
with ccx_course(self.ccx_locator) as course_ccx:
list_staff_ccx_course = list_with_level(course_ccx, 'staff')
self.assertEqual(len(list_staff_master_course), len(list_staff_ccx_course))
self.assertEqual(list_staff_master_course[0].email, list_staff_ccx_course[0].email)
list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
self.assertEqual(len(list_instructor_ccx_course), len(list_instructor_master_course))
self.assertEqual(list_instructor_ccx_course[0].email, list_instructor_master_course[0].email)
# assert that role of staff and instructors of master course removed from ccx.
remove_master_course_staff_from_ccx(
self.course, self.ccx_locator, self.ccx.display_name, send_email=False
)
list_staff_ccx_course = list_with_level(course_ccx, 'staff')
self.assertNotEqual(len(list_staff_master_course), len(list_staff_ccx_course))
list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
self.assertNotEqual(len(list_instructor_ccx_course), len(list_instructor_master_course))
for user in list_staff_master_course:
self.assertNotIn(user, list_staff_ccx_course)
for user in list_instructor_master_course:
self.assertNotIn(user, list_instructor_ccx_course)
def test_remove_master_course_staff_from_ccx_idempotent(self):
"""
Test remove staff of master course from ccx course
"""
staff = self.make_staff()
self.assertTrue(CourseStaffRole(self.course.id).has_user(staff))
# adding instructor to master course.
instructor = self.make_instructor()
self.assertTrue(CourseInstructorRole(self.course.id).has_user(instructor))
outbox = self.get_outbox()
self.assertEqual(len(outbox), 0)
add_master_course_staff_to_ccx(self.course, self.ccx_locator, self.ccx.display_name, send_email=False)
list_staff_master_course = list_with_level(self.course, 'staff')
list_instructor_master_course = list_with_level(self.course, 'instructor')
with ccx_course(self.ccx_locator) as course_ccx:
list_staff_ccx_course = list_with_level(course_ccx, 'staff')
self.assertEqual(len(list_staff_master_course), len(list_staff_ccx_course))
self.assertEqual(list_staff_master_course[0].email, list_staff_ccx_course[0].email)
list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
self.assertEqual(len(list_instructor_ccx_course), len(list_instructor_master_course))
self.assertEqual(list_instructor_ccx_course[0].email, list_instructor_master_course[0].email)
# assert that role of staff and instructors of master course removed from ccx.
remove_master_course_staff_from_ccx(
self.course, self.ccx_locator, self.ccx.display_name, send_email=True
)
self.assertEqual(len(outbox), len(list_staff_master_course) + len(list_instructor_master_course))
list_staff_ccx_course = list_with_level(course_ccx, 'staff')
self.assertNotEqual(len(list_staff_master_course), len(list_staff_ccx_course))
list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
self.assertNotEqual(len(list_instructor_ccx_course), len(list_instructor_master_course))
for user in list_staff_master_course:
self.assertNotIn(user, list_staff_ccx_course)
for user in list_instructor_master_course:
self.assertNotIn(user, list_instructor_ccx_course)
# Run again
remove_master_course_staff_from_ccx(self.course, self.ccx_locator, self.ccx.display_name)
self.assertEqual(len(outbox), len(list_staff_master_course) + len(list_instructor_master_course))
with ccx_course(self.ccx_locator) as course_ccx:
list_staff_ccx_course = list_with_level(course_ccx, 'staff')
self.assertNotEqual(len(list_staff_master_course), len(list_staff_ccx_course))
list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
self.assertNotEqual(len(list_instructor_ccx_course), len(list_instructor_master_course))
for user in list_staff_master_course:
self.assertNotIn(user, list_staff_ccx_course)
for user in list_instructor_master_course:
self.assertNotIn(user, list_instructor_ccx_course)
def test_add_master_course_staff_to_ccx_display_name(self):
"""
Test add staff of master course to ccx course.
Specific test to check that a passed display name is in the
subject of the email sent to the enrolled users.
"""
staff = self.make_staff()
self.assertTrue(CourseStaffRole(self.course.id).has_user(staff))
# adding instructor to master course.
instructor = self.make_instructor()
self.assertTrue(CourseInstructorRole(self.course.id).has_user(instructor))
outbox = self.get_outbox()
# create a unique display name
display_name = 'custom_display_{}'.format(uuid.uuid4())
list_staff_master_course = list_with_level(self.course, 'staff')
list_instructor_master_course = list_with_level(self.course, 'instructor')
self.assertEqual(len(outbox), 0)
# give access to the course staff/instructor
add_master_course_staff_to_ccx(self.course, self.ccx_locator, display_name)
self.assertEqual(len(outbox), len(list_staff_master_course) + len(list_instructor_master_course))
for email in outbox:
self.assertIn(display_name, email.subject)
def test_remove_master_course_staff_from_ccx_display_name(self):
"""
Test remove role of staff of master course on ccx course.
Specific test to check that a passed display name is in the
subject of the email sent to the unenrolled users.
"""
staff = self.make_staff()
self.assertTrue(CourseStaffRole(self.course.id).has_user(staff))
# adding instructor to master course.
instructor = self.make_instructor()
self.assertTrue(CourseInstructorRole(self.course.id).has_user(instructor))
outbox = self.get_outbox()
add_master_course_staff_to_ccx(self.course, self.ccx_locator, self.ccx.display_name, send_email=False)
# create a unique display name
display_name = 'custom_display_{}'.format(uuid.uuid4())
list_staff_master_course = list_with_level(self.course, 'staff')
list_instructor_master_course = list_with_level(self.course, 'instructor')
self.assertEqual(len(outbox), 0)
# give access to the course staff/instructor
remove_master_course_staff_from_ccx(self.course, self.ccx_locator, display_name)
self.assertEqual(len(outbox), len(list_staff_master_course) + len(list_instructor_master_course))
for email in outbox:
self.assertIn(display_name, email.subject)
def test_add_master_course_staff_to_ccx_idempotent(self):
"""
Test add staff of master course to ccx course multiple time will
not result in multiple enrollments.
"""
staff = self.make_staff()
self.assertTrue(CourseStaffRole(self.course.id).has_user(staff))
# adding instructor to master course.
instructor = self.make_instructor()
self.assertTrue(CourseInstructorRole(self.course.id).has_user(instructor))
outbox = self.get_outbox()
list_staff_master_course = list_with_level(self.course, 'staff')
list_instructor_master_course = list_with_level(self.course, 'instructor')
self.assertEqual(len(outbox), 0)
# run the assignment the first time
add_master_course_staff_to_ccx(self.course, self.ccx_locator, self.ccx.display_name)
self.assertEqual(len(outbox), len(list_staff_master_course) + len(list_instructor_master_course))
with ccx_course(self.ccx_locator) as course_ccx:
list_staff_ccx_course = list_with_level(course_ccx, 'staff')
list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
self.assertEqual(len(list_staff_master_course), len(list_staff_ccx_course))
for user in list_staff_master_course:
self.assertIn(user, list_staff_ccx_course)
self.assertEqual(len(list_instructor_master_course), len(list_instructor_ccx_course))
for user in list_instructor_master_course:
self.assertIn(user, list_instructor_ccx_course)
# run the assignment again
add_master_course_staff_to_ccx(self.course, self.ccx_locator, self.ccx.display_name)
# there are no new duplicated email
self.assertEqual(len(outbox), len(list_staff_master_course) + len(list_instructor_master_course))
# there are no duplicated staffs
with ccx_course(self.ccx_locator) as course_ccx:
list_staff_ccx_course = list_with_level(course_ccx, 'staff')
list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
self.assertEqual(len(list_staff_master_course), len(list_staff_ccx_course))
for user in list_staff_master_course:
self.assertIn(user, list_staff_ccx_course)
self.assertEqual(len(list_instructor_master_course), len(list_instructor_ccx_course))
for user in list_instructor_master_course:
self.assertIn(user, list_instructor_ccx_course)
def test_add_master_course_staff_to_ccx_no_email(self):
"""
Test add staff of master course to ccx course without
sending enrollment email.
"""
staff = self.make_staff()
self.assertTrue(CourseStaffRole(self.course.id).has_user(staff))
# adding instructor to master course.
instructor = self.make_instructor()
self.assertTrue(CourseInstructorRole(self.course.id).has_user(instructor))
outbox = self.get_outbox()
self.assertEqual(len(outbox), 0)
add_master_course_staff_to_ccx(self.course, self.ccx_locator, self.ccx.display_name, send_email=False)
self.assertEqual(len(outbox), 0)
def test_remove_master_course_staff_from_ccx_no_email(self):
"""
Test remove role of staff of master course on ccx course without
sending enrollment email.
"""
staff = self.make_staff()
self.assertTrue(CourseStaffRole(self.course.id).has_user(staff))
# adding instructor to master course.
instructor = self.make_instructor()
self.assertTrue(CourseInstructorRole(self.course.id).has_user(instructor))
outbox = self.get_outbox()
self.assertEqual(len(outbox), 0)
remove_master_course_staff_from_ccx(self.course, self.ccx_locator, self.ccx.display_name, send_email=False)
self.assertEqual(len(outbox), 0)