""" Tests the transfer student management command """ import unittest from unittest.mock import call, patch import ddt from django.conf import settings from django.core.management import call_command from opaque_keys.edx import locator from common.djangoapps.course_modes.models import CourseMode from common.djangoapps.student.models import ( EVENT_NAME_ENROLLMENT_ACTIVATED, EVENT_NAME_ENROLLMENT_DEACTIVATED, EVENT_NAME_ENROLLMENT_MODE_CHANGED, CourseEnrollment ) from common.djangoapps.student.signals import UNENROLL_DONE from common.djangoapps.student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') @ddt.ddt class TestTransferStudents(ModuleStoreTestCase): """ Tests for transferring students between courses. """ PASSWORD = 'test' signal_fired = False def setUp(self, **kwargs): # lint-amnesty, pylint: disable=unused-argument """ Connect a stub receiver, and analytics event tracking. """ super().setUp() UNENROLL_DONE.connect(self.assert_unenroll_signal) patcher = patch('common.djangoapps.student.models.tracker') self.mock_tracker = patcher.start() self.addCleanup(patcher.stop) self.addCleanup(UNENROLL_DONE.disconnect, self.assert_unenroll_signal) def assert_unenroll_signal(self, skip_refund=False, **kwargs): # pylint: disable=unused-argument """ Signal Receiver stub for testing that the unenroll signal was fired. """ assert not self.signal_fired assert skip_refund self.signal_fired = True def test_transfer_students(self): """ Verify the transfer student command works as intended. """ student = UserFactory.create() student.set_password(self.PASSWORD) student.save() mode = 'verified' # Original Course original_course_location = locator.CourseLocator('Org0', 'Course0', 'Run0') course = self._create_course(original_course_location) # Enroll the student in 'verified' CourseEnrollment.enroll(student, course.id, mode='verified') # Create and purchase a verified cert for the original course. self._create_and_purchase_verified(student, course.id) # New Course 1 course_location_one = locator.CourseLocator('Org1', 'Course1', 'Run1') new_course_one = self._create_course(course_location_one) # New Course 2 course_location_two = locator.CourseLocator('Org2', 'Course2', 'Run2') new_course_two = self._create_course(course_location_two) original_key = str(course.id) new_key_one = str(new_course_one.id) new_key_two = str(new_course_two.id) # Run the actual management command call_command( 'transfer_students', '--from', original_key, '--to', new_key_one, new_key_two, ) assert self.signal_fired # Confirm the analytics event was emitted. self.mock_tracker.emit.assert_has_calls( [ call( EVENT_NAME_ENROLLMENT_ACTIVATED, {'course_id': original_key, 'user_id': student.id, 'mode': mode} ), call( EVENT_NAME_ENROLLMENT_MODE_CHANGED, {'course_id': original_key, 'user_id': student.id, 'mode': mode} ), call( EVENT_NAME_ENROLLMENT_DEACTIVATED, {'course_id': original_key, 'user_id': student.id, 'mode': mode} ), call( EVENT_NAME_ENROLLMENT_ACTIVATED, {'course_id': new_key_one, 'user_id': student.id, 'mode': mode} ), call( EVENT_NAME_ENROLLMENT_MODE_CHANGED, {'course_id': new_key_one, 'user_id': student.id, 'mode': mode} ), call( EVENT_NAME_ENROLLMENT_ACTIVATED, {'course_id': new_key_two, 'user_id': student.id, 'mode': mode} ), call( EVENT_NAME_ENROLLMENT_MODE_CHANGED, {'course_id': new_key_two, 'user_id': student.id, 'mode': mode} ) ] ) self.mock_tracker.reset_mock() # Confirm the enrollment mode is verified on the new courses, and enrollment is enabled as appropriate. assert (mode, False) == CourseEnrollment.enrollment_mode_for_user(student, course.id) assert (mode, True) == CourseEnrollment.enrollment_mode_for_user(student, new_course_one.id) assert (mode, True) == CourseEnrollment.enrollment_mode_for_user(student, new_course_two.id) def _create_course(self, course_location): """ Creates a course """ return CourseFactory.create( org=course_location.org, number=course_location.course, run=course_location.run ) def _create_and_purchase_verified(self, student, course_id): # lint-amnesty, pylint: disable=unused-argument """ Creates a verified mode for the course and purchases it for the student. """ course_mode = CourseMode(course_id=course_id, mode_slug='verified', mode_display_name='verified cert', min_price=50) course_mode.save()