def create_paid_but_no_enrollable_run(self, enrollable, in_future, fuzzy): """Make paid but not enrolled, with offered currently, in future, and fuzzy """ self.make_fa_program_enrollment(FinancialAidStatus.AUTO_APPROVED) course = Course.objects.get(title='Digital Learning 200') course_run = course.courserun_set.order_by('start_date').first() # course_run = CourseRunFactory.create(course=course, edx_course_key='course-paid') add_paid_order_for_course(user=self.user, course_run=course_run) if enrollable: course_run = CourseRunFactory.create(course=course, edx_course_key='course-enrollable') call_command( "alter_data", 'set_to_offered', '--username', 'staff', '--course-run-key', course_run.edx_course_key ) if in_future: course_run = CourseRunFactory.create(course=course, edx_course_key='course-in-future') call_command( "alter_data", 'set_to_offered', '--username', 'staff', '--course-run-key', course_run.edx_course_key, '--in-future' ) if fuzzy: course_run = CourseRunFactory.create(course=course, edx_course_key='course-fuzzy') call_command( "alter_data", 'set_to_offered', '--username', 'staff', '--course-run-key', course_run.edx_course_key, '--fuzzy' )
def two_no_show_exam_attempts(self): """Passed and later failed course, and two exam attempts""" self.make_fa_program_enrollment(FinancialAidStatus.AUTO_APPROVED) course = Course.objects.get(title='Digital Learning 200') course_run = CourseRunFactory(course=course, edx_course_key='course-passed') call_command( "alter_data", 'set_to_passed', '--username', 'staff', '--course-run-key', course_run.edx_course_key ) ExamProfileFactory.create(status='success', profile=self.user.profile) # run 1 exam_run = ExamRunFactory.create(course=course, eligibility_past=True, scheduling_past=True) ExamAuthorizationFactory.create( user=self.user, course=course, exam_run=exam_run, status='success', exam_taken=True, exam_no_show=True ) # run 2 exam_run = ExamRunFactory.create(course=course, eligibility_past=True, scheduling_past=True) ExamAuthorizationFactory.create( user=self.user, course=course, exam_run=exam_run, status='success', exam_taken=True, exam_no_show=True ) # another offered course_run = CourseRunFactory.create(course=course, edx_course_key='course-enrollable') call_command( "alter_data", 'set_to_offered', '--username', 'staff', '--course-run-key', course_run.edx_course_key ) course_run = CourseRunFactory.create(course=course, edx_course_key='course-failed') call_command( "alter_data", 'set_to_failed', '--username', 'staff', '--course-run-key', course_run.edx_course_key, '--audit', )
def base_test_data(): """ Fixture for test data that should be available to any test case in the suite """ # Create a live program with valid prices and financial aid program = ProgramFactory.create( live=True, financial_aid_availability=True, price=1000, ) CourseRunFactory.create(course__program=program) TierProgramFactory.create_properly_configured_batch(2, program=program) # Create users staff_user, student_user = (create_user_for_login(is_staff=True), create_user_for_login(is_staff=False)) ProgramEnrollment.objects.create(program=program, user=staff_user) ProgramEnrollment.objects.create(program=program, user=student_user) Role.objects.create( role=Staff.ROLE_ID, user=staff_user, program=program, ) return SimpleNamespace( staff_user=staff_user, student_user=student_user, program=program )
def missed_payment_can_reenroll(self): """User has missed payment but they can re-enroll""" call_command( "alter_data", 'set_to_needs_upgrade', '--username', 'staff', '--course-title', 'Analog Learning 200', '--missed-deadline', ) course = Course.objects.get(title='Analog Learning 200') CourseRunFactory.create(course=course)
def failed_run_missed_payment_can_reenroll(self): """Failed User has missed payment but they can re-enroll""" call_command( "alter_data", 'set_to_failed', '--username', 'staff', '--course-title', 'Analog Learning 200', '--grade', '0', '--audit' ) course = Course.objects.get(title='Analog Learning 200') CourseRunFactory.create(course=course)
def test_course_fuzzy_start_date(self): """Test course with promised course run""" CourseRunFactory.create( course=self.course, fuzzy_start_date="Fall 2017", start_date=None, end_date=None, enrollment_start=None, enrollment_end=None, ) assert self.course.enrollment_text == 'Coming Fall 2017'
def setUpTestData(cls): """Create a set of course runs for testing""" super().setUpTestData() cls.run1 = CourseRunFactory.create( course__program__live=True, course__program__financial_aid_availability=True, ) cls.program = cls.run1.course.program cls.run2 = CourseRunFactory.create(course=cls.run1.course) cls.runs = [cls.run1, cls.run2] cls.user = UserFactory.create() ProgramEnrollment.objects.create(user=cls.user, program=cls.run1.course.program)
def test_course_run_finder_success(self): """Tests that CourseRunFinder will return a desired course run""" course_run = CourseRunFactory.create(title='courserun1', edx_course_key='coursekey1') CourseRunFactory.create(title='courserun2', edx_course_key='coursekey2') found_course_runs = [ CourseRunFinder.find(course_run_title='courserun1'), CourseRunFinder.find(course_run_title='run1'), CourseRunFinder.find(course_run_key='coursekey1'), CourseRunFinder.find(course_run_key='key1') ] assert all([course_run == found_course_run for found_course_run in found_course_runs])
def setUpTestData(cls): super().setUpTestData() # create an user cls.user = UserFactory.create() cls.cached_edx_user_data = MagicMock( spec=CachedEdxUserData, enrollments=CachedEnrollment.deserialize_edx_data(cls.enrollments_json), certificates=CachedCertificate.deserialize_edx_data(cls.certificates_json), current_grades=CachedCurrentGrade.deserialize_edx_data(cls.current_grades_json), ) # create the programs cls.program = ProgramFactory.create(live=True, financial_aid_availability=False, price=1000) cls.program_financial_aid = ProgramFactory.create(live=True, financial_aid_availability=True, price=1000) # create course runs for the normal program cls.course = CourseFactory.create(program=cls.program) expected_course_keys = [ "course-v1:edX+DemoX+Demo_Course", "course-v1:MITx+8.MechCX+2014_T1", '', None, 'course-v1:odl+FOO102+CR-FALL16' ] cls.cruns = [] for course_key in expected_course_keys: course_run = CourseRunFactory.create( course=cls.course, edx_course_key=course_key ) if course_key: cls.cruns.append(course_run) # and the program with financial aid finaid_course = CourseFactory.create(program=cls.program_financial_aid) cls.now = now_in_utc() cls.end_date = cls.now - timedelta(weeks=45) cls.crun_fa = CourseRunFactory.create( course=finaid_course, start_date=cls.now-timedelta(weeks=52), end_date=cls.end_date, enrollment_start=cls.now-timedelta(weeks=62), enrollment_end=cls.now-timedelta(weeks=53), edx_course_key="course-v1:odl+FOO101+CR-FALL15" ) cls.crun_fa2 = CourseRunFactory.create( course=finaid_course ) CourseRunFactory.create( course=finaid_course, edx_course_key=None )
def setUpTestData(cls): cls.user = SocialUserFactory.create() cls.run_fa = CourseRunFactory.create( freeze_grade_date=now_in_utc()-timedelta(days=1), course__program__financial_aid_availability=True, ) cls.run_fa_with_cert = CourseRunFactory.create( freeze_grade_date=None, course__program=cls.run_fa.course.program, ) cls.run_no_fa = CourseRunFactory.create( freeze_grade_date=now_in_utc()+timedelta(days=1), course__program__financial_aid_availability=False, ) cls.run_no_fa_with_cert = CourseRunFactory.create( course__program=cls.run_no_fa.course.program, ) all_course_runs = (cls.run_fa, cls.run_fa_with_cert, cls.run_no_fa, cls.run_no_fa_with_cert, ) for run in all_course_runs: if run.course.program.financial_aid_availability: FinancialAidFactory.create( user=cls.user, tier_program=TierProgramFactory.create( program=run.course.program, income_threshold=0, current=True ), status=FinancialAidStatus.RESET, ) cls.enrollments = { course_run.edx_course_key: CachedEnrollmentFactory.create( user=cls.user, course_run=course_run) for course_run in all_course_runs } cls.current_grades = { course_run.edx_course_key: CachedCurrentGradeFactory.create( user=cls.user, course_run=course_run) for course_run in all_course_runs } cls.certificates = { course_run.edx_course_key: CachedCertificateFactory.create( user=cls.user, course_run=course_run) for course_run in (cls.run_fa_with_cert, cls.run_no_fa_with_cert) } cls.user_edx_data = CachedEdxUserData(cls.user)
def test_course_keys(self): """ Coupon.course_keys should return a list of all course run keys in a program, course, or course run """ run1 = CourseRunFactory.create(course__program__financial_aid_availability=True) run2 = CourseRunFactory.create(course=run1.course) run3 = CourseRunFactory.create(course__program=run1.course.program) run4 = CourseRunFactory.create(course=run3.course) coupon_program = CouponFactory.create( content_object=run1.course.program, ) assert sorted(coupon_program.course_keys) == sorted([run.edx_course_key for run in [run1, run2, run3, run4]]) coupon_course = CouponFactory.create(content_object=run1.course) assert sorted(coupon_course.course_keys) == sorted([run.edx_course_key for run in [run1, run2]])
def setUpTestData(cls): super().setUpTestData() cls.user = UserFactory() cls.user.social_auth.create( provider='not_edx', ) cls.user.social_auth.create( provider=EdxOrgOAuth2.name, uid="{}_edx".format(cls.user.username), ) cls.order = OrderFactory.create(status=Order.CREATED, user=cls.user) cls.line1 = LineFactory.create(order=cls.order) cls.line2 = LineFactory.create(order=cls.order) cls.course_run1 = CourseRunFactory.create(edx_course_key=cls.line1.course_key) cls.course_run2 = CourseRunFactory.create(edx_course_key=cls.line2.course_key)
def create_missed_payment_for_exam(self, enrollable, future_exam, current): """Passed course but missed deadline to pay to take exam""" self.make_fa_program_enrollment(FinancialAidStatus.AUTO_APPROVED) if current: call_command( "alter_data", 'set_to_enrolled', '--username', 'staff', '--course-title', 'Digital Learning 200', '--missed-deadline' ) else: call_command( "alter_data", 'set_past_run_to_passed', '--username', 'staff', '--course-title', 'Digital Learning 200', '--grade', '87', '--audit', '--missed-deadline' ) course = Course.objects.get(title='Digital Learning 200') ExamProfileFactory.create(status='success', profile=self.user.profile) ExamRunFactory.create(course=course, eligibility_past=True, scheduling_past=True) if enrollable: course_run = CourseRunFactory.create(course=course, edx_course_key='course-enrollable') call_command( "alter_data", 'set_to_offered', '--username', 'staff', '--course-run-key', course_run.edx_course_key ) if future_exam: ExamRunFactory.create( scheduling_past=False, scheduling_future=True, authorized=True, course=course )
def test_user_has_redemptions_left(self, order_status, has_unpurchased_run, another_already_redeemed, expected): """ Coupon.user_has_redemptions_left should be true if user has not yet purchased all course runs """ run1 = CourseRunFactory.create(course__program__financial_aid_availability=True) if has_unpurchased_run: CourseRunFactory.create(course__program=run1.course.program) line = LineFactory.create(course_key=run1.edx_course_key, order__status=order_status) coupon = CouponFactory.create(content_object=run1.course.program) with patch( 'ecommerce.models.Coupon.another_user_already_redeemed', autospec=True, ) as _already_redeemed: _already_redeemed.return_value = another_already_redeemed assert coupon.user_has_redemptions_left(line.order.user) is expected _already_redeemed.assert_called_with(coupon, line.order.user)
def create_passed_and_offered_course_run(self, grades_frozen, with_certificate): """Make passed and currently offered course run, and see the View Certificate and Re-Enroll""" self.make_fa_program_enrollment(FinancialAidStatus.AUTO_APPROVED) call_command( "alter_data", 'set_to_passed', '--username', 'staff', '--course-title', 'Digital Learning 200', '--grade', '89', ) course = Course.objects.get(title='Digital Learning 200') # create another currently offered run CourseRunFactory.create(course=course) if grades_frozen: final_grade = FinalGrade.objects.filter(user=self.user, course_run__course=course, passed=True).first() CourseRunGradingStatus.objects.create(course_run=final_grade.course_run, status='complete') if with_certificate: MicromastersCourseCertificate.objects.create(user=self.user, course=course) CourseCertificateSignatoriesFactory.create(course=course)
def setUpTestData(cls): cls.user = SocialUserFactory.create() cls.run_1 = CourseRunFactory.create( freeze_grade_date=now_in_utc()-timedelta(days=1), course__program__financial_aid_availability=True, ) CourseRunGradingStatus.objects.create(course_run=cls.run_1, status='complete') cls.program = cls.run_1.course.program
def setUpTestData(cls): cls.users = [UserFactory.create() for _ in range(35)] freeze_date = now_in_utc()-timedelta(days=1) future_freeze_date = now_in_utc()+timedelta(days=1) cls.course_run1 = CourseRunFactory.create(freeze_grade_date=freeze_date) cls.course_run2 = CourseRunFactory.create(freeze_grade_date=freeze_date) cls.all_freezable_runs = [cls.course_run1, cls.course_run2] cls.course_run_future = CourseRunFactory.create(freeze_grade_date=future_freeze_date) cls.course_run_frozen = CourseRunFactory.create(freeze_grade_date=freeze_date) CourseRunGradingStatus.objects.create(course_run=cls.course_run_frozen, status=FinalGradeStatus.COMPLETE) for user in cls.users: CachedEnrollmentFactory.create(user=user, course_run=cls.course_run1) CachedCurrentGradeFactory.create(user=user, course_run=cls.course_run1)
def setUpTestData(cls): cls.user = SocialUserFactory.create() cls.run_1 = CourseRunFactory.create( freeze_grade_date=now_in_utc()-timedelta(days=1), course__program__financial_aid_availability=True, ) cls.program = cls.run_1.course.program
def test_create_final_grade_fa(self, generate_letter_mock, update_grade_mock, mock_on_commit): """ Test that final grades created for non-FA courses will try to update combined final grades. """ fa_course_run = CourseRunFactory.create(course__program__financial_aid_availability=True) FinalGradeFactory.create(user=self.user, course_run=fa_course_run, grade=0.9) update_grade_mock.assert_called_once_with(self.user, fa_course_run.course) generate_letter_mock.assert_not_called()
def test_course_with_run(self): """ Make sure the course URL serializes properly """ course_run = CourseRunFactory.create() course = course_run.course data = CourseSerializer(course).data assert data['url'] == course_run.enrollment_url assert data['enrollment_text'] == course.enrollment_text
def setUpTestData(cls): """ Create user, run, and coupons for testing """ super().setUpTestData() cls.user = SocialProfileFactory.create().user UserSocialAuthFactory.create(user=cls.user, provider='not_edx') run = CourseRunFactory.create(course__program__financial_aid_availability=True) cls.coupon = CouponFactory.create(content_object=run.course.program) UserCoupon.objects.create(coupon=cls.coupon, user=cls.user)
def test_coupon_allowed_course(self): """ Assert that the price is not adjusted if the coupon is for a different course in the same program """ course_run, _ = create_purchasable_course_run() other_course = CourseRunFactory.create(course__program=course_run.course.program).course price = Decimal('0.3') coupon = CouponFactory.create(content_object=other_course) assert coupon.content_object != course_run assert calculate_coupon_price(coupon, price, course_run.edx_course_key) == price
def test_another_user_already_redeemed(self, order_status, other_user_redeemed, is_automatic, expected): """ Tests for Coupon.another_user_already_redeemed """ run1 = CourseRunFactory.create(course__program__financial_aid_availability=True) run2 = CourseRunFactory.create(course=run1.course) coupon = CouponFactory.create( content_object=run1.course, coupon_type=Coupon.DISCOUNTED_PREVIOUS_COURSE if is_automatic else Coupon.STANDARD, ) line1 = LineFactory.create(course_key=run1.edx_course_key, order__status=Order.FULFILLED) RedeemedCoupon.objects.create(order=line1.order, coupon=coupon) if other_user_redeemed: line2 = LineFactory.create(course_key=run2.edx_course_key, order__status=order_status) RedeemedCoupon.objects.create(order=line2.order, coupon=coupon) assert coupon.another_user_already_redeemed(line1.order.user) is expected
def test_deserialize_user_data(self): """Test that user data is correctly deserialized""" new_course_run = CourseRunFactory.create(edx_course_key='course-v1:MITx+Analog+Learning+100+Aug_2015') new_program = new_course_run.course.program with mute_signals(post_save): user = deserialize_user_data(self.USER_DATA, [new_program]) assert user.username == '{}mario.medina'.format(FAKE_USER_USERNAME_PREFIX) assert user.profile.first_name == 'Mario' assert user.profile.date_of_birth == '1961-04-29' assert CachedEnrollment.objects.filter(user=user, course_run=new_course_run).count() == 1
def setUpTestData(cls): super().setUpTestData() # create a user cls.user = SocialUserFactory.create() # create the course run cls.course_id = "edx+fake+key" cls.course_run = CourseRunFactory.create(edx_course_key=cls.course_id) # url for the dashboard cls.url = reverse('user_course_enrollments')
def test_course_run(self): """ Make sure course run serializer correctly """ course_run = CourseRunFactory.create() data = CourseRunSerializer(course_run).data expected = { 'edx_course_key': course_run.edx_course_key, 'program_title': course_run.course.program.title, } assert data == expected
def create_frozen_run(self, course): """helper function to create frozen course runs""" now = now_in_utc() run = CourseRunFactory.create( course=course, title="Title Run", freeze_grade_date=now - timedelta(weeks=1), ) CourseRunGradingStatus.objects.create(course_run=run, status='complete') return run
def test_validate_discount_prev_run_coupon_type(self): """Coupon must be for a course if Coupon.coupon_type is DISCOUNTED_PREVIOUS_RUN""" run = CourseRunFactory.create() with self.assertRaises(ValidationError) as ex: CouponFactory.create( coupon_type=Coupon.DISCOUNTED_PREVIOUS_COURSE, content_object=run.course.program, ) assert ex.exception.args[0]['__all__'][0].args[0] == ( 'coupon must be for a course if coupon_type is discounted-previous-course' )
def test_is_automatic(self): """ Coupon.is_automatic_qset should be true if the coupon type is DISCOUNTED_PREVIOUS_COURSE """ coupon_not_automatic = CouponFactory.create(coupon_type=Coupon.STANDARD) assert Coupon.is_automatic_qset().filter(id=coupon_not_automatic.id).exists() is False run = CourseRunFactory.create(course__program__financial_aid_availability=True) coupon_is_automatic = CouponFactory.create( coupon_type=Coupon.DISCOUNTED_PREVIOUS_COURSE, content_object=run.course, ) assert Coupon.is_automatic_qset().filter(id=coupon_is_automatic.id).exists() is True
def test_enrollment_url(self): """If both enrollment_url and edx_course_key are available, use enrollment_url""" course_run = CourseRunFactory.create( course=self.course, start_date=self.from_weeks(-1), end_date=None, enrollment_start=self.from_weeks(-1), enrollment_end=None, enrollment_url="http://enrollment.url/", edx_course_key="course_key" ) assert course_run.course.url == "http://enrollment.url/"
def create_paid_failed_course_run(self, *, current, in_future, fuzzy): """Make paid failed course run, and offer another run""" self.make_fa_program_enrollment(FinancialAidStatus.AUTO_APPROVED) call_command( "alter_data", 'set_to_failed', '--username', 'staff', '--course-title', 'Digital Learning 200', '--grade', '45', ) course = Course.objects.get(title='Digital Learning 200') if current: CourseRunFactory.create(course=course) if in_future: course_run = CourseRunFactory.create(course=course, edx_course_key='course-in-future') call_command( "alter_data", 'set_to_offered', '--username', 'staff', '--course-run-key', course_run.edx_course_key, '--in-future' ) if fuzzy: course_run = CourseRunFactory.create(course=course, edx_course_key='course-fuzzy') call_command( "alter_data", 'set_to_offered', '--username', 'staff', '--course-run-key', course_run.edx_course_key, '--fuzzy' )
def test_create_final_grade_non_fa(self, generate_letter_mock, update_grade_mock, mock_on_commit): """ Test that final grades created for non-FA courses will try to update combined final grades and generate a program commendation letter. """ non_fa_course_run = CourseRunFactory.create( course__program__financial_aid_availability=False) FinalGradeFactory.create(user=self.user, course_run=non_fa_course_run, grade=0.9) update_grade_mock.assert_called_once_with(self.user, non_fa_course_run.course) generate_letter_mock.assert_called_once_with( self.user, non_fa_course_run.course.program)
def test_serialize_course_run_detail(): """Test CourseRunDetailSerializer serialization""" course_run = CourseRunFactory.create() data = CourseRunDetailSerializer(course_run).data assert data == { "course": BaseCourseSerializer(course_run.course).data, "title": course_run.title, "courseware_id": course_run.courseware_id, "courseware_url": course_run.courseware_url, "start_date": drf_datetime(course_run.start_date), "end_date": drf_datetime(course_run.end_date), "enrollment_start": drf_datetime(course_run.enrollment_start), "enrollment_end": drf_datetime(course_run.enrollment_end), "expiration_date": drf_datetime(course_run.expiration_date), "id": course_run.id, }
def test_run_queryset(is_program): """ run_queryset should return all runs related to the product """ program = ProgramFactory.create() runs = [CourseRunFactory.create(course__program=program) for _ in range(4)] run = runs[2] obj = program if is_program else run product = ProductFactory.create(content_object=obj) def key_func(_run): return _run.id assert sorted(product.run_queryset, key=key_func) == sorted(runs if is_program else [run], key=key_func)
def test_update_edx_record(self): """ Test that a cached edX record is reindexed after being updated """ program_enrollment = ProgramEnrollmentFactory.create() for edx_cached_model_factory in [ CachedCertificateFactory, CachedEnrollmentFactory ]: assert es.search()['total'] == 1 course_run = CourseRunFactory.create( program=program_enrollment.program) edx_record = edx_cached_model_factory.create( user=program_enrollment.user, course_run=course_run) edx_record.data.update({'new': 'data'}) edx_record.save() assert_search(es.search(), [program_enrollment])
def setUpTestData(cls): cls.user = SocialUserFactory.create() cls.course_run = CourseRunFactory.create( course__program__financial_aid_availability=True ) cls.exam_run = ExamRunFactory.create( course=cls.course_run.course, date_grades_available=now_in_utc() - timedelta(weeks=1) ) cls.not_passing_final_grade = FinalGradeFactory.create( user=cls.user, course_run=cls.course_run, grade=0.5, passed=False )
def test_not_live_program(self): """ An order is created using create_unfulfilled_order and a payload is generated using generate_cybersource_sa_payload """ user = UserFactory.create() self.client.force_login(user) course_run = CourseRunFactory.create( course__program__live=False, course__program__financial_aid_availability=True, ) resp = self.client.post(reverse('checkout'), {'course_id': course_run.edx_course_key}, format='json') assert resp.status_code == status.HTTP_404_NOT_FOUND
def test_url_with_course_key(self): """Test course url with a course key and no enrollment_url""" course_run = CourseRunFactory.create( course=self.course, start_date=self.from_weeks(-1), end_date=None, enrollment_start=self.from_weeks(-1), enrollment_end=None, enrollment_url=None, edx_course_key="course_key" ) expected = urljoin( BASE_URL, 'courses/{key}/about'.format(key=course_run.edx_course_key) ) assert course_run.course.url == expected
def setUpTestData(cls): """ Set up data """ cls.user = UserFactory.create() cls.user.social_auth.create( provider=EdxOrgOAuth2.name, uid="{}_edx".format(cls.user.username), extra_data={"access_token": "fooooootoken"}) certificates_json = load_json_from_file( 'dashboard/fixtures/certificates.json') cls.certificates = Certificates( [Certificate(cert_json) for cert_json in certificates_json]) enrollments_json = load_json_from_file( 'dashboard/fixtures/user_enrollments.json') cls.enrollments = Enrollments(enrollments_json) # the grades need to have all the same usernames current_grades_json = [] for grade in load_json_from_file( 'dashboard/fixtures/current_grades.json'): grade.update({'username': cls.user.username}) current_grades_json.append(grade) cls.current_grades = CurrentGrades( [CurrentGrade(grade_json) for grade_json in current_grades_json]) cls.certificates_ids = set(cls.certificates.all_courses_certs) cls.verified_certificates_ids = set( cls.certificates.all_courses_verified_certs) cls.enrollment_ids = set(cls.enrollments.get_enrolled_course_ids()) cls.grades_ids = set(cls.current_grades.all_course_ids) cls.all_course_run_ids = list(cls.certificates_ids | cls.enrollment_ids | cls.grades_ids) cls.all_runs = [] for course_id in cls.all_course_run_ids: cls.all_runs.append( CourseRunFactory.create( edx_course_key=course_id, course__program__live=True, )) cls.edx_client = MagicMock() cls.edx_client.enrollments.get_student_enrollments.return_value = cls.enrollments cls.edx_client.certificates.get_student_certificates.return_value = cls.certificates cls.edx_client.current_grades.get_student_current_grades.return_value = cls.current_grades
def test_course_run_not_beyond_enrollment(end_days, enroll_start_days, enroll_end_days, expected): """ Test that CourseRun.is_beyond_enrollment returns the expected boolean value """ now = now_in_utc() end_date = None if end_days is None else now + timedelta(days=end_days) enr_end_date = (None if enroll_end_days is None else now + timedelta(days=enroll_end_days)) enr_start_date = (None if enroll_start_days is None else now + timedelta(days=enroll_start_days)) assert (CourseRunFactory.create( end_date=end_date, enrollment_end=enr_end_date, enrollment_start=enr_start_date, ).is_not_beyond_enrollment is expected)
def test_creates_order(self): """ An order is created using create_unfulfilled_order and a payload is generated using generate_cybersource_sa_payload """ user = UserFactory.create() self.client.force_login(user) course_run = CourseRunFactory.create( course__program__live=True, course__program__financial_aid_availability=True, ) order = LineFactory.create(order__status=Order.CREATED).order fake_ip = "195.0.0.1" payload = { 'a': 'payload', } with patch( 'ecommerce.views.create_unfulfilled_order', autospec=True, return_value=order, ) as create_mock, patch( 'ecommerce.views.generate_cybersource_sa_payload', autospec=True, return_value=payload, ) as generate_mock, patch("ecommerce.views.get_client_ip", return_value=(fake_ip, True)) as mock_ip_call: resp = self.client.post(reverse('checkout'), {'course_id': course_run.edx_course_key}, format='json') assert mock_ip_call.call_count == 1 assert resp.status_code == status.HTTP_200_OK assert resp.json() == { 'payload': payload, 'url': CYBERSOURCE_SECURE_ACCEPTANCE_URL, 'method': 'POST', } assert create_mock.call_count == 1 assert create_mock.call_args[0] == (course_run.edx_course_key, user) assert generate_mock.call_count == 1 assert generate_mock.call_args[0] == (order, 'http://testserver/dashboard/', fake_ip)
def test_add_edx_record(self, index_type, mock_on_commit): """ Test that cached edX records are indexed after being added """ program_enrollment = ProgramEnrollmentFactory.create() for edx_cached_model_factory in [ CachedCertificateFactory, CachedEnrollmentFactory, CachedCurrentGradeFactory ]: assert es.search(index_type)['total'] == DOC_TYPES_PER_ENROLLMENT course = CourseFactory.create(program=program_enrollment.program) course_run = CourseRunFactory.create(course=course) edx_cached_model_factory.create(user=program_enrollment.user, course_run=course_run) index_program_enrolled_users([program_enrollment]) assert_search(es.search(index_type), [program_enrollment], index_type=index_type)
def test_instructors(has_page): """CourseRun.instructors should list instructors from the related CMS page, or provide an empty list""" faculty_names = ["Teacher One", "Teacher Two"] course_run = CourseRunFactory.create(course__page=None) if has_page: course_page = CoursePageFactory.create(course=course_run.course) FacultyMembersPageFactory.create( parent=course_page, **{ f"members__{idx}__member__name": name for idx, name in enumerate(faculty_names) }, ) assert course_run.instructors == ([{ "name": name } for name in faculty_names] if has_page else [])
def test_basket_thumbnail_courserun(basket_and_coupons, mock_context): """Basket thumbnail should be serialized for a courserun""" thumbnail_filename = "abcde.jpg" course_page = CoursePageFactory.create( thumbnail_image__file__filename=thumbnail_filename ) run = CourseRunFactory.create(course=course_page.course) product_version = ProductVersionFactory.create(product__content_object=run) data = FullProductVersionSerializer( instance=product_version, context=mock_context ).data assert ( data["thumbnail_url"] == course_page.thumbnail_image.get_rendition( CATALOG_COURSE_IMG_WAGTAIL_FILL ).url )
def test_course_enrollments_serialized_unique(self): """ Tests that enrollments in multiple runs of the same course won't result in multiple serializations """ # Create an enrollment for a different course run of an already-enrolled course serialized_program_user = UserProgramSearchSerializer.serialize( self.non_fa_program_enrollment) serialized_count_before_addition = len( serialized_program_user['courses']) first_enrollment = self.non_fa_enrollments[0] new_course_run = CourseRunFactory.create( course=first_enrollment.course_run.course) self.verified_enroll(self.user, course_run=new_course_run) serialized_program_user = UserProgramSearchSerializer.serialize( self.non_fa_program_enrollment) # Number of serialized enrollments should be unaffected assert len(serialized_program_user['courses'] ) == serialized_count_before_addition
def test_calculate_run_price_other_run(self): """ If the coupon is for another course in this program it should not be returned here """ course_run, user = create_purchasable_course_run() other_course = CourseRunFactory.create( course__program=course_run.course.program).course coupon = CouponFactory.create(content_object=other_course) UserCoupon.objects.create(coupon=coupon, user=user) discounted_price = 5 program_enrollment = course_run.course.program.programenrollment_set.first( ) fa_price = get_formatted_course_price(program_enrollment)['price'] with patch('ecommerce.api.calculate_coupon_price', autospec=True) as _calculate_coupon_price: _calculate_coupon_price.return_value = discounted_price assert calculate_run_price(course_run, user) == (fa_price, None) assert _calculate_coupon_price.called is False
def test_create_run_enrollments_api_fail(mocker, user, exception_cls): """ create_run_enrollments should log a message and still create local enrollment records when certain exceptions are raised if a flag is set to true """ patched_edx_enroll = mocker.patch("courses.api.enroll_in_edx_course_runs", side_effect=exception_cls) patched_log_exception = mocker.patch("courses.api.log.exception") patched_send_enrollment_email = mocker.patch( "courses.api.mail_api.send_course_run_enrollment_email") run = CourseRunFactory.create() successful_enrollments, edx_request_success = create_run_enrollments( user, [run], order=None, company=None, keep_failed_enrollments=True) patched_edx_enroll.assert_called_once_with(user, [run]) patched_log_exception.assert_called_once() patched_send_enrollment_email.assert_not_called() assert len(successful_enrollments) == 1 assert edx_request_success is False
def setUpTestData(cls): with mute_signals(post_save): cls.profile = ProfileFactory.create() EducationFactory.create(profile=cls.profile) EmploymentFactory.create(profile=cls.profile) EmploymentFactory.create(profile=cls.profile, end_date=None) program = ProgramFactory.create() course = CourseFactory.create(program=program) course_runs = [ CourseRunFactory.create(course=course) for _ in range(2) ] for course_run in course_runs: CachedCertificateFactory.create(user=cls.profile.user, course_run=course_run) CachedEnrollmentFactory.create(user=cls.profile.user, course_run=course_run) cls.program_enrollment = ProgramEnrollment.objects.create( user=cls.profile.user, program=program)
def setUpTestData(cls): super().setUpTestData() with mute_signals(post_save): staff_profile = ProfileFactory.create() cls.staff_user = staff_profile.user cls.course = CourseFactory.create( contact_email='*****@*****.**', program__financial_aid_availability=False) course_run = CourseRunFactory.create(course=cls.course) ProgramEnrollmentFactory.create(user=cls.staff_user, program=cls.course.program) CachedEnrollmentFactory.create(user=cls.staff_user, course_run=course_run) cls.url_name = 'course_team_mail_api' cls.request_data = { 'email_subject': 'email subject', 'email_body': 'email body' }
def create_passed_enrolled_again(self): """Make course passed and user retaking/auditing the course again""" self.make_fa_program_enrollment(FinancialAidStatus.AUTO_APPROVED) course = Course.objects.get(title='Digital Learning 200') CourseCertificateSignatoriesFactory.create(course=course) call_command( "alter_data", 'set_past_run_to_passed', '--username', 'staff', '--course-title', 'Digital Learning 200', '--grade', '87', ) final_grade = FinalGrade.objects.filter( course_run__course__title='Digital Learning 200', user=self.user ).first() CourseRunGradingStatus.objects.create(course_run=final_grade.course_run, status='complete') MicromastersCourseCertificateFactory.create(user=self.user, course=course) course_run = CourseRunFactory.create(course=course) set_course_run_current(course_run, upgradeable=True, save=True) CachedEnrollmentHandler(self.user).set_or_create(course_run, verified=False)
def create_purchasable_course_run(): """ Creates a purchasable course run and an associated user """ course_run = CourseRunFactory.create( course__program__live=True, course__program__financial_aid_availability=True, ) price = course_run.course.program.price user = UserFactory.create() FinancialAidFactory.create( tier_program__current=True, tier_program__program=course_run.course.program, tier_program__discount_amount=price/2, user=user, status=FinancialAidStatus.APPROVED, ) ProgramEnrollment.objects.create(user=user, program=course_run.course.program) return course_run, user
def test_get_best_final_grade_for_course(self): """ Test for get_best_final_grade_for_course to return the highest grade over all course runs """ mmtrack = MMTrack( user=self.user, program=self.program_financial_aid, edx_user_data=self.cached_edx_user_data ) finaid_course = self.crun_fa.course FinalGradeFactory.create(user=self.user, course_run=self.crun_fa, grade=0.3, passed=False) assert mmtrack.get_best_final_grade_for_course(finaid_course) is None for grade in [0.3, 0.5, 0.8]: course_run = CourseRunFactory.create( course=finaid_course, ) FinalGradeFactory.create(user=self.user, course_run=course_run, grade=grade, passed=True) assert mmtrack.get_best_final_grade_for_course(finaid_course).grade == 0.8
def create_run(self, course=None, start=None, end=None, enr_start=None, enr_end=None, upgrade_deadline=None, freeze_grade_date=None): """helper function to create course runs""" # pylint: disable=too-many-arguments return CourseRunFactory.create( course=course or self.course, title="Title Run", start_date=start, end_date=end, enrollment_start=enr_start, enrollment_end=enr_end, upgrade_deadline=upgrade_deadline, freeze_grade_date=freeze_grade_date, )
def test_course_certificate_view(user_client, user, wagtail_basics): """ Test that certificate page show correctly """ home = HomePageFactory.create(parent=wagtail_basics.root, slug="home") home.save_revision().publish() course_page = CoursePageFactory.create(parent=home) course_page.save_revision().publish() course_run = CourseRunFactory.create(course=course_page.course) course_run_certificate = CourseRunCertificateFactory.create( user=user, course_run=course_run ) resp = user_client.get(course_run_certificate.link) assert resp.status_code == status.HTTP_200_OK assert resp.context_data["page"] == course_page.certificate_page assert resp.context_data["page"].certificate == course_run_certificate
def test_delete_edx_record(self, index_type, mock_on_commit): """ Test that a cached edX record is removed from index after being deleted """ program_enrollment = ProgramEnrollmentFactory.create() for edx_cached_model_factory in [ CachedCertificateFactory, CachedEnrollmentFactory, CachedCurrentGradeFactory ]: course = CourseFactory.create(program=program_enrollment.program) course_run = CourseRunFactory.create(course=course) edx_record = edx_cached_model_factory.create( user=program_enrollment.user, course_run=course_run) index_program_enrolled_users([program_enrollment]) assert_search(es.search(index_type), [program_enrollment], index_type=index_type) edx_record.delete() index_program_enrolled_users([program_enrollment]) assert_search(es.search(index_type), [program_enrollment], index_type=index_type)
def test_update_exam_authorization_cached_enrollment_when_no_exam_run( self): """ Test no exam profile created when course has no ExamRun """ self.exam_run.delete() course = CourseFactory.create(program=self.program) course_run = CourseRunFactory.create( end_date=now_in_utc() - timedelta(days=100), enrollment_end=now_in_utc() + timedelta(hours=1), course=course) create_order(self.profile.user, course_run) # exam profile before enrollment. assert ExamProfile.objects.filter( profile=self.profile).exists() is False CachedEnrollmentFactory.create(user=self.profile.user, course_run=course_run) assert ExamProfile.objects.filter( profile=self.profile).exists() is False
def test_build_digital_credential_course_run(settings, mocker): "Verify build_digital_credential works correctly for a course run" mock_build_course_run_credential = mocker.patch( "courses.credentials.build_course_run_credential", autospec=True) course_run = CourseRunFactory.create() learner_did = LearnerDIDFactory.create() certificate = CourseRunCertificateFactory.create(user=learner_did.learner, course_run=course_run) assert build_digital_credential(certificate, learner_did) == { "credential": { "@context": [ "https://www.w3.org/2018/credentials/v1", "https://w3id.org/security/suites/ed25519-2020/v1", "https://w3id.org/dcc/v1" ], "id": urljoin(settings.SITE_BASE_URL, certificate.link), "type": ["VerifiableCredential", "LearningCredential"], "issuer": { "type": "Issuer", "id": settings.DIGITAL_CREDENTIALS_ISSUER_ID, "name": settings.SITE_NAME, "url": settings.SITE_BASE_URL, }, "issuanceDate": any_instance_of(str), "credentialSubject": { "type": "schema:Person", "id": learner_did.did, "name": learner_did.learner.name, "hasCredential": mock_build_course_run_credential.return_value, }, }, "options": { "verificationMethod": settings.DIGITAL_CREDENTIALS_VERIFICATION_METHOD }, } mock_build_course_run_credential.assert_called_once_with(certificate)
def test_create_combined_final_grade(mocker): """ Test create_combined_final_grade creates the grade when it is missing """ update_mock = mocker.patch( 'grades.api.update_or_create_combined_final_grade', autospec=True) course_run = CourseRunFactory.create( freeze_grade_date=now_in_utc() - timedelta(days=1), course__program__financial_aid_availability=True, course__program__live=True) course = course_run.course CourseRunGradingStatus.objects.create(course_run=course_run, status='complete') # Create exam run for course with date_grades_available True exam_run_grades_available = ExamRunFactory.create( course=course, date_grades_available=now_in_utc() - timedelta(weeks=1)) exam_grades = ProctoredExamGradeFactory.create_batch( 5, course=course, exam_run=exam_run_grades_available, passed=True, ) for exam_grade in exam_grades[:3]: CombinedFinalGrade.objects.create(user=exam_grade.user, course=course, grade=0.7) # Only 3 users will have combined grades for exam_grade in exam_grades[3:]: FinalGradeFactory.create(user=exam_grade.user, course_run=course_run, passed=True) tasks.create_combined_final_grades.delay() assert update_mock.call_count == 2 update_mock.assert_has_calls( [call(exam_grades[3].user, course), call(exam_grades[4].user, course)], any_order=True)
def test_zero_price_checkout(self): """ If the order total is $0, we should just fulfill the order and direct the user to our order receipt page """ user = UserFactory.create() self.client.force_login(user) course_run = CourseRunFactory.create( course__program__live=True, course__program__financial_aid_availability=True, ) order = LineFactory.create( order__status=Order.CREATED, order__total_price_paid=0, price=0, ).order with patch( 'ecommerce.views.create_unfulfilled_order', autospec=True, return_value=order, ) as create_mock, patch('ecommerce.views.enroll_user_on_success', autospec=True) as enroll_user_mock: resp = self.client.post(reverse('checkout'), {'course_id': course_run.edx_course_key}, format='json') assert resp.status_code == status.HTTP_200_OK assert resp.json() == { 'payload': {}, 'url': 'http://testserver/dashboard/?status=receipt&course_key={}'.format( quote_plus(course_run.edx_course_key)), 'method': 'GET', } assert create_mock.call_count == 1 assert create_mock.call_args[0] == (course_run.edx_course_key, user) assert enroll_user_mock.call_count == 1 assert enroll_user_mock.call_args[0] == (order, )
def test_decimal(self): """ Test that a model with a decimal field is handled correctly """ course_run = CourseRunFactory.create() assert serialize_model_object(course_run) == { 'course': course_run.course.id, 'edx_course_key': course_run.edx_course_key, 'end_date': format_as_iso8601(course_run.end_date), 'enrollment_end': format_as_iso8601(course_run.enrollment_end), 'enrollment_start': format_as_iso8601(course_run.enrollment_start), 'enrollment_url': course_run.enrollment_url, 'freeze_grade_date': format_as_iso8601(course_run.freeze_grade_date), 'fuzzy_enrollment_start_date': course_run.fuzzy_enrollment_start_date, 'fuzzy_start_date': course_run.fuzzy_start_date, 'id': course_run.id, 'prerequisites': course_run.prerequisites, 'start_date': format_as_iso8601(course_run.start_date), 'title': course_run.title, 'upgrade_deadline': format_as_iso8601(course_run.upgrade_deadline), }