def test_filter_by_roles_course_staff(self): """ Verify that course_ids are filtered by the provided roles. """ # Make this user a course staff user for the course. course_staff_user = self.create_user(username='******', is_staff=False) add_users(self.global_admin, CourseStaffRole(self.course.id), course_staff_user) # Create a second course, along with an instructor user for it. alternate_course1 = self.create_course(org='test1') course_instructor_user = self.create_user(username='******', is_staff=False) add_users(self.global_admin, CourseInstructorRole(alternate_course1.id), course_instructor_user) # Create a third course, along with an user that has both staff and instructor for it. alternate_course2 = self.create_course(org='test2') course_instructor_staff_user = self.create_user(username='******', is_staff=False) add_users(self.global_admin, CourseInstructorRole(alternate_course2.id), course_instructor_staff_user) add_users(self.global_admin, CourseStaffRole(alternate_course2.id), course_instructor_staff_user) # Requesting the courses for which the course staff user is staff should return *only* the single course. self.setup_user(self.staff_user) filtered_response = self.verify_response(params={ 'username': course_staff_user.username, 'role': 'staff' }) assert len(filtered_response.data['results']) == 1 assert filtered_response.data['results'][0].startswith(self.course.org) # The course staff user does *not* have the course instructor role on any courses. filtered_response = self.verify_response(params={ 'username': course_staff_user.username, 'role': 'instructor' }) assert len(filtered_response.data['results']) == 0 # The course instructor user only has the course instructor role on one course. filtered_response = self.verify_response(params={ 'username': course_instructor_user.username, 'role': 'instructor' }) assert len(filtered_response.data['results']) == 1 assert filtered_response.data['results'][0].startswith(alternate_course1.org) # The course instructor user has the inferred course staff role on one course. self.setup_user(course_instructor_user) filtered_response = self.verify_response(params={ 'username': course_instructor_user.username, 'role': 'staff' }) assert len(filtered_response.data['results']) == 1 assert filtered_response.data['results'][0].startswith(alternate_course1.org) # The user with both instructor AND staff on a course has the inferred course staff role on that one course. self.setup_user(course_instructor_staff_user) filtered_response = self.verify_response(params={ 'username': course_instructor_staff_user.username, 'role': 'staff' }) assert len(filtered_response.data['results']) == 1 assert filtered_response.data['results'][0].startswith(alternate_course2.org)
def test_org_and_course_roles(self): """ Test that Org and course roles don't interfere with course roles or vice versa """ OrgInstructorRole(self.course_key.org).add_users(self.student) CourseInstructorRole(self.course_key).add_users(self.student) assert OrgInstructorRole(self.course_key.org).has_user(self.student), \ f"Student doesn't have access to {six.text_type(self.course_key.org)}" assert CourseInstructorRole(self.course_key).has_user(self.student), \ f"Student doesn't have access to {six.text_type(self.course_key)}" # remove access and confirm OrgInstructorRole(self.course_key.org).remove_users(self.student) assert not OrgInstructorRole(self.course_key.org).has_user(self.student), \ f'Student still has access to {self.course_key.org}' assert CourseInstructorRole(self.course_key).has_user(self.student), \ f"Student doesn't have access to {six.text_type(self.course_key)}" # ok now keep org role and get rid of course one OrgInstructorRole(self.course_key.org).add_users(self.student) CourseInstructorRole(self.course_key).remove_users(self.student) assert OrgInstructorRole(self.course_key.org).has_user(self.student), \ f'Student lost has access to {self.course_key.org}' assert not CourseInstructorRole(self.course_key).has_user(self.student), \ f"Student doesn't have access to {six.text_type(self.course_key)}"
def test_org_and_course_roles(self): """ Test that Org and course roles don't interfere with course roles or vice versa """ OrgInstructorRole(self.course_key.org).add_users(self.student) CourseInstructorRole(self.course_key).add_users(self.student) self.assertTrue( OrgInstructorRole(self.course_key.org).has_user(self.student), "Student doesn't have access to {}".format( six.text_type(self.course_key.org))) self.assertTrue( CourseInstructorRole(self.course_key).has_user(self.student), "Student doesn't have access to {}".format( six.text_type(self.course_key))) # remove access and confirm OrgInstructorRole(self.course_key.org).remove_users(self.student) self.assertFalse( OrgInstructorRole(self.course_key.org).has_user(self.student), "Student still has access to {}".format(self.course_key.org)) self.assertTrue( CourseInstructorRole(self.course_key).has_user(self.student), "Student doesn't have access to {}".format( six.text_type(self.course_key))) # ok now keep org role and get rid of course one OrgInstructorRole(self.course_key.org).add_users(self.student) CourseInstructorRole(self.course_key).remove_users(self.student) self.assertTrue( OrgInstructorRole(self.course_key.org).has_user(self.student), "Student lost has access to {}".format(self.course_key.org)) self.assertFalse( CourseInstructorRole(self.course_key).has_user(self.student), "Student doesn't have access to {}".format( six.text_type(self.course_key)))
def remove_all_instructors(course_key): """ Removes all instructor and staff users from the given course. """ staff_role = CourseStaffRole(course_key) staff_role.remove_users(*staff_role.users_with_role()) instructor_role = CourseInstructorRole(course_key) instructor_role.remove_users(*instructor_role.users_with_role())
def make_instructor(self): """ create instructor user. """ instructor = UserFactory.create(password="******") role = CourseInstructorRole(self.course.id) role.add_users(instructor) return instructor
def test_keep_instructors(self): course_run = CourseFactory() instructor = UserFactory() CourseInstructorRole(course_run.id).add_users(instructor) with mock.patch(self.YESNO_PATCH_LOCATION, return_value=True): call_command('delete_course', str(course_run.id), '--keep-instructors') assert CourseInstructorRole(course_run.id).has_user(instructor)
def test_detail_delete_instructor(self): auth.add_users(self.user, CourseInstructorRole(self.course.id), self.ext_user, self.user) resp = self.client.delete( self.detail_url, HTTP_ACCEPT="application/json", ) self.assertEqual(resp.status_code, 204) # reload user from DB ext_user = User.objects.get(email=self.ext_user.email) self.assertFalse(auth.user_has_role(ext_user, CourseInstructorRole(self.course.id)))
def test_delete_last_instructor(self): auth.add_users(self.user, CourseInstructorRole(self.course.id), self.ext_user) resp = self.client.delete( self.detail_url, HTTP_ACCEPT="application/json", ) self.assertEqual(resp.status_code, 400) result = json.loads(resp.content.decode('utf-8')) self.assertIn("error", result) # reload user from DB ext_user = User.objects.get(email=self.ext_user.email) self.assertTrue(auth.user_has_role(ext_user, CourseInstructorRole(self.course.id)))
def test_add_user_to_course_group(self): """ Tests adding user to course group (happy path). """ # Create groups for a new course (and assign instructor role to the creator). self.assertFalse(user_has_role(self.creator, CourseInstructorRole(self.course_key))) add_users(self.global_admin, CourseInstructorRole(self.course_key), self.creator) add_users(self.global_admin, CourseStaffRole(self.course_key), self.creator) self.assertTrue(user_has_role(self.creator, CourseInstructorRole(self.course_key))) # Add another user to the staff role. self.assertFalse(user_has_role(self.staff, CourseStaffRole(self.course_key))) add_users(self.creator, CourseStaffRole(self.course_key), self.staff) self.assertTrue(user_has_role(self.staff, CourseStaffRole(self.course_key)))
def test_remove_user_from_course_group(self): """ Tests removing user from course group (happy path). """ add_users(self.global_admin, CourseInstructorRole(self.course_key), self.creator) add_users(self.global_admin, CourseStaffRole(self.course_key), self.creator) add_users(self.creator, CourseStaffRole(self.course_key), self.staff) self.assertTrue(user_has_role(self.staff, CourseStaffRole(self.course_key))) remove_users(self.creator, CourseStaffRole(self.course_key), self.staff) self.assertFalse(user_has_role(self.staff, CourseStaffRole(self.course_key))) remove_users(self.creator, CourseInstructorRole(self.course_key), self.creator) self.assertFalse(user_has_role(self.creator, CourseInstructorRole(self.course_key)))
def delete(self, request, course_key_string): course_key = CourseKey.from_string(course_key_string) email = request.data.get("email", None) validate_param_exist(email, "email") try: user = User.objects.get(email=email) except Exception: # pylint: disable=broad-except msg = { "error": "Could not find user by email address '{email}'".format( email=email) } return Response(msg, 404) auth.get_user_permissions(request.user, course_key) auth.remove_users(request.user, CourseStaffRole(course_key), user) auth.remove_users(request.user, CourseInstructorRole(course_key), user) CourseEnrollment.unenroll(user, course_key) msg = "'{email}''s permissions are revoked from '{course_key}'".format( email=email, course_key=course_key) log.info(msg) return Response( {'message': "User is removed from {}.".format(course_key)})
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 _manage_users(request, course_key): """ This view will return all CMS users who are editors for the specified course """ # check that logged in user has permissions to this item user_perms = get_user_permissions(request.user, course_key) if not user_perms & STUDIO_VIEW_USERS: raise PermissionDenied() course_module = modulestore().get_course(course_key) instructors = set(CourseInstructorRole(course_key).users_with_role()) # the page only lists staff and assumes they're a superset of instructors. Do a union to ensure. staff = set( CourseStaffRole(course_key).users_with_role()).union(instructors) formatted_users = [] for user in instructors: formatted_users.append(user_with_role(user, 'instructor')) for user in staff - instructors: formatted_users.append(user_with_role(user, 'staff')) return render_to_response( 'manage_users.html', { 'context_course': course_module, 'show_transfer_ownership_hint': request.user in instructors and len(instructors) == 1, 'users': formatted_users, 'allow_actions': bool(user_perms & STUDIO_EDIT_ROLES), })
def get(self, request): """Displays course Enrollment and staffing course statistics""" if not request.user.is_staff: raise Http404 data = [] for course in self.get_courses(): datum = [course.display_name, course.id] datum += [CourseEnrollment.objects.filter( course_id=course.id).count()] datum += [CourseStaffRole(course.id).users_with_role().count()] datum += [','.join([x.username for x in CourseInstructorRole( course.id).users_with_role()])] data.append(datum) datatable = dict(header=[_('Course Name'), _('course_id'), _('# enrolled'), _('# staff'), _('instructors')], title=_('Enrollment information for all courses'), data=data) context = { 'datatable': datatable, 'msg': self.msg, 'djangopid': os.getpid(), 'modeflag': {'staffing': 'active-section'}, } return render_to_response(self.template_name, context)
def setUpClass(cls): super().setUpClass() cls.store = modulestore() cls.course = ToyCourseFactory.create( end=datetime(2028, 1, 1, 1, 1, 1), enrollment_start=datetime(2020, 1, 1, 1, 1, 1), enrollment_end=datetime(2028, 1, 1, 1, 1, 1), emit_signals=True, modulestore=cls.store, ) cls.chapter = ItemFactory(parent=cls.course, category='chapter') cls.sequence = ItemFactory(parent=cls.chapter, category='sequential', display_name='sequence') cls.unit = ItemFactory.create(parent=cls.sequence, category='vertical', display_name="Vertical") cls.user = UserFactory(username='******', email=u'*****@*****.**', password='******', is_staff=False) cls.instructor = UserFactory(username='******', email=u'*****@*****.**', password='******', is_staff=False) CourseInstructorRole(cls.course.id).add_users(cls.instructor) cls.url = '/api/courseware/course/{}'.format(cls.course.id)
def get_users(self, course_id, user_id=None): """ Gets the users for a given target. Result is returned in the form of a queryset, and may contain duplicates. """ staff_qset = CourseStaffRole(course_id).users_with_role() instructor_qset = CourseInstructorRole(course_id).users_with_role() staff_instructor_qset = (staff_qset | instructor_qset) enrollment_query = models.Q(is_active=True, courseenrollment__course_id=course_id, courseenrollment__is_active=True) enrollment_qset = User.objects.filter(enrollment_query) if self.target_type == SEND_TO_MYSELF: if user_id is None: raise ValueError( "Must define self user to send email to self.") user = User.objects.filter(id=user_id) return use_read_replica_if_available(user) elif self.target_type == SEND_TO_STAFF: return use_read_replica_if_available(staff_instructor_qset) elif self.target_type == SEND_TO_LEARNERS: return use_read_replica_if_available( enrollment_qset.exclude(id__in=staff_instructor_qset)) elif self.target_type == SEND_TO_COHORT: return self.cohorttarget.cohort.users.filter( id__in=enrollment_qset) # lint-amnesty, pylint: disable=no-member elif self.target_type == SEND_TO_TRACK: return use_read_replica_if_available( User.objects.filter( models.Q(courseenrollment__mode=self.coursemodetarget. track.mode_slug) & enrollment_query)) else: raise ValueError(f"Unrecognized target type {self.target_type}")
def test_get_course_list_with_invalid_course_location(self, store): """ Test getting courses with invalid course location (course deleted from modulestore). """ with self.store.default_store(store): course_key = self.store.make_course_key('Org', 'Course', 'Run') self._create_course_with_access_groups(course_key, self.user, store) # get courses through iterating all courses courses_iter, __ = _accessible_courses_iter_for_tests(self.request) courses_list = list(courses_iter) self.assertEqual(len(courses_list), 1) courses_summary_iter, __ = _accessible_courses_summary_iter( self.request) courses_summary_list = list(courses_summary_iter) # Verify fetched accessible courses list is a list of CourseSummery instances and only one course # is returned self.assertTrue( all( isinstance(course, CourseSummary) for course in courses_summary_list)) self.assertEqual(len(courses_summary_list), 1) # get courses by reversing group name formats courses_list_by_groups, __ = _accessible_courses_list_from_groups( self.request) self.assertEqual(len(courses_list_by_groups), 1) course_keys_in_course_list = [course.id for course in courses_list] course_keys_in_courses_list_by_groups = [ course.id for course in courses_list_by_groups ] # check course lists have same courses self.assertEqual(course_keys_in_course_list, course_keys_in_courses_list_by_groups) # now delete this course and re-add user to instructor group of this course delete_course(course_key, self.user.id) CourseInstructorRole(course_key).add_users(self.user) # Get courses through iterating all courses courses_iter, __ = _accessible_courses_iter_for_tests(self.request) # Get course summaries by iterating all courses courses_summary_iter, __ = _accessible_courses_summary_iter( self.request) # Get courses by reversing group name formats courses_list_by_groups, __ = _accessible_courses_list_from_groups( self.request) # Test that course list returns no course self.assertEqual([ len(list(courses_iter)), len(courses_list_by_groups), len(list(courses_summary_iter)) ], [0, 0, 0])
class RoleCacheTestCase(TestCase): # lint-amnesty, pylint: disable=missing-class-docstring IN_KEY = CourseKey.from_string('edX/toy/2012_Fall') NOT_IN_KEY = CourseKey.from_string('edX/toy/2013_Fall') ROLES = ( (CourseStaffRole(IN_KEY), ('staff', IN_KEY, 'edX')), (CourseInstructorRole(IN_KEY), ('instructor', IN_KEY, 'edX')), (OrgStaffRole(IN_KEY.org), ('staff', None, 'edX')), (OrgInstructorRole(IN_KEY.org), ('instructor', None, 'edX')), (CourseBetaTesterRole(IN_KEY), ('beta_testers', IN_KEY, 'edX')), ) def setUp(self): super(RoleCacheTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments self.user = UserFactory() @ddt.data(*ROLES) @ddt.unpack def test_only_in_role(self, role, target): role.add_users(self.user) cache = RoleCache(self.user) assert cache.has_role(*target) for other_role, other_target in self.ROLES: if other_role == role: continue assert not cache.has_role(*other_target) @ddt.data(*ROLES) @ddt.unpack def test_empty_cache(self, role, target): # lint-amnesty, pylint: disable=unused-argument cache = RoleCache(self.user) assert not cache.has_role(*target)
def test_creation(self): """ The user that creates a library should have instructor (admin) and staff permissions """ # self.library has been auto-created by the staff user. self.assertTrue(has_studio_write_access(self.user, self.lib_key)) self.assertTrue(has_studio_read_access(self.user, self.lib_key)) # Make sure the user was actually assigned the instructor role and not just using is_staff superpowers: self.assertTrue(CourseInstructorRole(self.lib_key).has_user(self.user)) # Now log out and ensure we are forbidden from creating a library: self.client.logout() self._assert_cannot_create_library( expected_code=302) # 302 redirect to login expected # Now check that logged-in users without CourseCreator role cannot create libraries self._login_as_non_staff_user(logout_first=False) with patch.dict('django.conf.settings.FEATURES', {'ENABLE_CREATOR_GROUP': True}): self._assert_cannot_create_library( expected_code=403) # 403 user is not CourseCreator # Now check that logged-in users with CourseCreator role can create libraries add_user_with_status_granted(self.user, self.non_staff_user) with patch.dict('django.conf.settings.FEATURES', {'ENABLE_CREATOR_GROUP': True}): lib_key2 = self._create_library(library="lib2", display_name="Test Library 2") library2 = modulestore().get_library(lib_key2) self.assertIsNotNone(library2)
def setUpClass(cls): super().setUpClass() cls.store = modulestore() cls.course = ToyCourseFactory.create( end=datetime(2028, 1, 1, 1, 1, 1), enrollment_start=datetime(2020, 1, 1, 1, 1, 1), enrollment_end=datetime(2028, 1, 1, 1, 1, 1), emit_signals=True, modulestore=cls.store, certificate_available_date=_NEXT_WEEK, certificates_display_behavior=CertificatesDisplayBehaviors. END_WITH_DATE) cls.chapter = ItemFactory(parent=cls.course, category='chapter') cls.sequence = ItemFactory(parent=cls.chapter, category='sequential', display_name='sequence') cls.unit = ItemFactory.create(parent=cls.sequence, category='vertical', display_name="Vertical") cls.user = UserFactory(username='******', email='*****@*****.**', password='******', is_staff=False) cls.instructor = UserFactory(username='******', email='*****@*****.**', password='******', is_staff=False) CourseInstructorRole(cls.course.id).add_users(cls.instructor) cls.url = f'/api/courseware/course/{cls.course.id}'
class RoleCacheTestCase(TestCase): IN_KEY = CourseKey.from_string('edX/toy/2012_Fall') NOT_IN_KEY = CourseKey.from_string('edX/toy/2013_Fall') ROLES = ( (CourseStaffRole(IN_KEY), ('staff', IN_KEY, 'edX')), (CourseInstructorRole(IN_KEY), ('instructor', IN_KEY, 'edX')), (OrgStaffRole(IN_KEY.org), ('staff', None, 'edX')), (OrgInstructorRole(IN_KEY.org), ('instructor', None, 'edX')), (CourseBetaTesterRole(IN_KEY), ('beta_testers', IN_KEY, 'edX')), ) def setUp(self): super(RoleCacheTestCase, self).setUp() self.user = UserFactory() @ddt.data(*ROLES) @ddt.unpack def test_only_in_role(self, role, target): role.add_users(self.user) cache = RoleCache(self.user) self.assertTrue(cache.has_role(*target)) for other_role, other_target in self.ROLES: if other_role == role: continue self.assertFalse(cache.has_role(*other_target)) @ddt.data(*ROLES) @ddt.unpack def test_empty_cache(self, role, target): cache = RoleCache(self.user) self.assertFalse(cache.has_role(*target))
def test_masquerade_in_holdback(self, mock_get_course_run_details): mock_get_course_run_details.return_value = {'weeks_to_complete': 12} audit_student = UserFactory(username='******') enrollment = CourseEnrollmentFactory.create(user=audit_student, course_id=self.course.id, mode='audit') FBEEnrollmentExclusion.objects.create(enrollment=enrollment) CourseDurationLimitConfig.objects.create( enabled=True, course=CourseOverview.get_from_id(self.course.id), enabled_as_of=self.course.start, ) instructor = UserFactory.create(username='******') enrollment = CourseEnrollmentFactory.create(user=instructor, course_id=self.course.id, mode='audit') CourseInstructorRole(self.course.id).add_users(instructor) self.client.login(username=instructor.username, password='******') self.update_masquerade(username='******') response = self.get_courseware() assert response.status_code == 200 self.assertCountEqual(response.redirect_chain, []) banner_text = 'You lose all access to this course, including your progress,' self.assertNotContains(response, banner_text)
def test_masquerade_expired(self, mock_get_course_run_details): mock_get_course_run_details.return_value = {'weeks_to_complete': 1} audit_student = UserFactory(username='******') enrollment = CourseEnrollmentFactory.create( user=audit_student, course_id=self.course.id, mode='audit', ) enrollment.created = self.course.start enrollment.save() CourseDurationLimitConfig.objects.create( enabled=True, course=CourseOverview.get_from_id(self.course.id), enabled_as_of=self.course.start, ) instructor = UserFactory.create(username='******') CourseEnrollmentFactory.create(user=instructor, course_id=self.course.id, mode='audit') CourseInstructorRole(self.course.id).add_users(instructor) self.client.login(username=instructor.username, password='******') self.update_masquerade(username='******') response = self.get_courseware() assert response.status_code == 200 self.assertCountEqual(response.redirect_chain, []) banner_text = 'This learner does not have access to this course. Their access expired on' self.assertContains(response, banner_text)
def user_has_role(user, role): """ Check whether this user has access to this role (either direct or implied) :param user: :param role: an AccessRole """ if not user.is_active: return False # Do cheapest check first even though it's not the direct one if GlobalStaff().has_user(user): return True # CourseCreator is odd b/c it can be disabled via config if isinstance(role, CourseCreatorRole): # completely shut down course creation setting if settings.FEATURES.get('DISABLE_COURSE_CREATION', False): return False # wide open course creation setting if not settings.FEATURES.get('ENABLE_CREATOR_GROUP', False): return True if role.has_user(user): return True # If not, then check inferred permissions if (isinstance(role, (CourseStaffRole, CourseBetaTesterRole)) and CourseInstructorRole(role.course_key).has_user(user)): return True return False
def get_user_permissions(user, course_key, org=None): """ Get the bitmask of permissions that this user has in the given course context. Can also set course_key=None and pass in an org to get the user's permissions for that organization as a whole. """ if org is None: org = course_key.org course_key = course_key.for_branch(None) else: assert course_key is None # No one has studio permissions for CCX courses if is_ccx_course(course_key): return STUDIO_NO_PERMISSIONS all_perms = STUDIO_EDIT_ROLES | STUDIO_VIEW_USERS | STUDIO_EDIT_CONTENT | STUDIO_VIEW_CONTENT # global staff, org instructors, and course instructors have all permissions: if GlobalStaff().has_user(user) or OrgInstructorRole(org=org).has_user(user): return all_perms if course_key and user_has_role(user, CourseInstructorRole(course_key)): return all_perms # Staff have all permissions except EDIT_ROLES: if OrgStaffRole(org=org).has_user(user) or (course_key and user_has_role(user, CourseStaffRole(course_key))): return STUDIO_VIEW_USERS | STUDIO_EDIT_CONTENT | STUDIO_VIEW_CONTENT # Otherwise, for libraries, users can view only: if course_key and isinstance(course_key, LibraryLocator): if OrgLibraryUserRole(org=org).has_user(user) or user_has_role(user, LibraryUserRole(course_key)): return STUDIO_VIEW_USERS | STUDIO_VIEW_CONTENT return STUDIO_NO_PERMISSIONS
def test_enrollment_limit(self): """ Assert that in a course with max student limit set to 1, we can enroll staff and instructor along with student. To make sure course full check excludes staff and instructors. """ assert self.course_limited.max_student_enrollments_allowed == 1 user1 = UserFactory.create(username="******", email="*****@*****.**", password="******") user2 = UserFactory.create(username="******", email="*****@*****.**", password="******") # create staff on course. staff = UserFactory.create(username="******", email="*****@*****.**", password="******") role = CourseStaffRole(self.course_limited.id) role.add_users(staff) # create instructor on course. instructor = UserFactory.create(username="******", email="*****@*****.**", password="******") role = CourseInstructorRole(self.course_limited.id) role.add_users(instructor) CourseEnrollment.enroll(staff, self.course_limited.id, check_access=True) CourseEnrollment.enroll(instructor, self.course_limited.id, check_access=True) assert CourseEnrollment.objects.filter(course_id=self.course_limited.id, user=staff).exists() assert CourseEnrollment.objects.filter(course_id=self.course_limited.id, user=instructor).exists() CourseEnrollment.enroll(user1, self.course_limited.id, check_access=True) assert CourseEnrollment.objects.filter(course_id=self.course_limited.id, user=user1).exists() with pytest.raises(CourseFullError): CourseEnrollment.enroll(user2, self.course_limited.id, check_access=True) assert not CourseEnrollment.objects.filter(course_id=self.course_limited.id, user=user2).exists()
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() assert CourseStaffRole(self.course.id).has_user(staff) # adding instructor to master course. instructor = self.make_instructor() assert 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.id, 'staff') list_instructor_master_course = list_with_level( self.course.id, 'instructor') with ccx_course(self.ccx_locator) as course_ccx: list_staff_ccx_course = list_with_level(course_ccx.id, 'staff') assert len(list_staff_master_course) == len(list_staff_ccx_course) assert list_staff_master_course[0].email == list_staff_ccx_course[ 0].email list_instructor_ccx_course = list_with_level( course_ccx.id, 'instructor') assert len(list_instructor_ccx_course) == len( list_instructor_master_course) assert list_instructor_ccx_course[ 0].email == list_instructor_master_course[0].email
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() assert CourseStaffRole(self.course.id).has_user(staff) # adding instructor to master course. instructor = self.make_instructor() assert 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 = f'custom_display_{uuid.uuid4()}' list_staff_master_course = list_with_level(self.course.id, 'staff') list_instructor_master_course = list_with_level( self.course.id, 'instructor') assert len(outbox) == 0 # give access to the course staff/instructor remove_master_course_staff_from_ccx(self.course, self.ccx_locator, display_name) assert len(outbox) == (len(list_staff_master_course) + len(list_instructor_master_course)) for email in outbox: assert display_name in email.subject
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() assert CourseStaffRole(self.course.id).has_user(staff) # adding instructor to master course. instructor = self.make_instructor() assert 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) assert not CourseEnrollment.objects.filter( course_id=self.ccx_locator, user=staff).exists() assert not 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) assert not CourseEnrollment.objects.filter( course_id=self.ccx_locator, user=staff).exists() assert not CourseEnrollment.objects.filter( course_id=self.ccx_locator, user=instructor).exists()
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)