From 6fc4b38ea0b25f42f7600026677d4eb990bf3bcc Mon Sep 17 00:00:00 2001 From: Omar Khan Date: Fri, 29 Jan 2016 11:47:51 +0700 Subject: [PATCH] Disallow free verified course modes The verification workflow assumes that all verified courses will have a price. Free verified course modes cause a 404 when the user attempts to enroll or upgrade. --- .../tests/test_courseware_index.py | 3 +- common/djangoapps/course_modes/models.py | 2 + .../course_modes/tests/factories.py | 18 +++++- .../course_modes/tests/test_admin.py | 5 +- .../course_modes/tests/test_models.py | 56 ++++++++++++------- .../course_modes/tests/test_signals.py | 4 +- .../course_modes/tests/test_views.py | 32 +++++------ common/djangoapps/student/tests/factories.py | 10 +++- .../student/tests/test_recent_enrollments.py | 4 +- .../student/tests/test_verification_status.py | 4 +- common/djangoapps/student/tests/tests.py | 4 +- lms/djangoapps/certificates/tests/test_api.py | 4 +- lms/djangoapps/courseware/tests/test_views.py | 8 ++- lms/djangoapps/instructor/tests/test_api.py | 4 +- .../tests/test_context_processor.py | 2 +- lms/djangoapps/support/tests/test_refund.py | 6 +- .../verify_student/tests/test_integration.py | 2 +- .../verify_student/tests/test_services.py | 2 +- .../verify_student/tests/test_views.py | 22 ++++---- 19 files changed, 122 insertions(+), 70 deletions(-) diff --git a/cms/djangoapps/contentstore/tests/test_courseware_index.py b/cms/djangoapps/contentstore/tests/test_courseware_index.py index d3d2205cea..6e3191c27e 100644 --- a/cms/djangoapps/contentstore/tests/test_courseware_index.py +++ b/cms/djangoapps/contentstore/tests/test_courseware_index.py @@ -488,7 +488,8 @@ class TestCoursewareSearchIndexer(MixedWithOptionsTestCase): verified_mode = CourseMode( course_id=unicode(self.course.id), mode_slug=CourseMode.VERIFIED, - mode_display_name=CourseMode.VERIFIED + mode_display_name=CourseMode.VERIFIED, + min_price=1 ) verified_mode.save() self.reindex_course(store) diff --git a/common/djangoapps/course_modes/models.py b/common/djangoapps/course_modes/models.py index a7409bad23..3bfe45cffb 100644 --- a/common/djangoapps/course_modes/models.py +++ b/common/djangoapps/course_modes/models.py @@ -137,6 +137,8 @@ class CourseMode(models.Model): raise ValidationError( _(u"Professional education modes are not allowed to have expiration_datetime set.") ) + if self.is_verified_slug(self.mode_slug) and self.min_price <= 0: + raise ValidationError(_(u"Verified modes cannot be free.")) def save(self, force_insert=False, force_update=False, using=None): # Ensure currency is always lowercase. diff --git a/common/djangoapps/course_modes/tests/factories.py b/common/djangoapps/course_modes/tests/factories.py index 853922bb32..7470b0530f 100644 --- a/common/djangoapps/course_modes/tests/factories.py +++ b/common/djangoapps/course_modes/tests/factories.py @@ -1,5 +1,11 @@ +""" +Factories for course mode models. +""" +import random + from course_modes.models import CourseMode from factory.django import DjangoModelFactory +from factory import lazy_attribute from opaque_keys.edx.locations import SlashSeparatedCourseKey @@ -11,8 +17,16 @@ class CourseModeFactory(DjangoModelFactory): course_id = SlashSeparatedCourseKey('MITx', '999', 'Robot_Super_Course') mode_slug = 'audit' - mode_display_name = 'audit course' - min_price = 0 currency = 'usd' expiration_datetime = None suggested_prices = '' + + @lazy_attribute + def min_price(self): + if CourseMode.is_verified_slug(self.mode_slug): + return random.randint(1, 100) + return 0 + + @lazy_attribute + def mode_display_name(self): + return '{0} course'.format(self.mode_slug) diff --git a/common/djangoapps/course_modes/tests/test_admin.py b/common/djangoapps/course_modes/tests/test_admin.py index 939c62d3d8..23844811bb 100644 --- a/common/djangoapps/course_modes/tests/test_admin.py +++ b/common/djangoapps/course_modes/tests/test_admin.py @@ -16,6 +16,7 @@ from xmodule.modulestore.tests.factories import CourseFactory from student.tests.factories import UserFactory from course_modes.models import CourseMode from course_modes.admin import CourseModeForm +from course_modes.tests.factories import CourseModeFactory # Technically, we shouldn't be importing verify_student, since it's # defined in the LMS and course_modes is in common. However, the benefits @@ -178,7 +179,7 @@ class AdminCourseModeFormTest(ModuleStoreTestCase): def _configure(self, mode, upgrade_deadline=None, verification_deadline=None): """Configure course modes and deadlines. """ - course_mode = CourseMode.objects.create( + course_mode = CourseModeFactory.create( mode_slug=mode, mode_display_name=mode, ) @@ -193,7 +194,7 @@ class AdminCourseModeFormTest(ModuleStoreTestCase): def _admin_form(self, mode, upgrade_deadline=None): """Load the course mode admin form. """ - course_mode = CourseMode.objects.create( + course_mode = CourseModeFactory.create( course_id=self.course.id, mode_slug=mode, ) diff --git a/common/djangoapps/course_modes/tests/test_models.py b/common/djangoapps/course_modes/tests/test_models.py index 0d525128dc..c0bc61522e 100644 --- a/common/djangoapps/course_modes/tests/test_models.py +++ b/common/djangoapps/course_modes/tests/test_models.py @@ -17,6 +17,7 @@ import pytz from course_modes.helpers import enrollment_mode_display from course_modes.models import CourseMode, Mode +from course_modes.tests.factories import CourseModeFactory @ddt.ddt @@ -74,9 +75,9 @@ class CourseModeModelTest(TestCase): Find the modes for a course with only one mode """ - self.create_mode('verified', 'Verified Certificate') + self.create_mode('verified', 'Verified Certificate', 10) modes = CourseMode.modes_for_course(self.course_key) - mode = Mode(u'verified', u'Verified Certificate', 0, '', 'usd', None, None, None) + mode = Mode(u'verified', u'Verified Certificate', 10, '', 'usd', None, None, None) self.assertEqual([mode], modes) modes_dict = CourseMode.modes_for_course_dict(self.course_key) @@ -89,7 +90,7 @@ class CourseModeModelTest(TestCase): Finding the modes when there's multiple modes """ mode1 = Mode(u'honor', u'Honor Code Certificate', 0, '', 'usd', None, None, None) - mode2 = Mode(u'verified', u'Verified Certificate', 0, '', 'usd', None, None, None) + mode2 = Mode(u'verified', u'Verified Certificate', 10, '', 'usd', None, None, None) set_modes = [mode1, mode2] for mode in set_modes: self.create_mode(mode.slug, mode.name, mode.min_price, mode.suggested_prices) @@ -119,7 +120,7 @@ class CourseModeModelTest(TestCase): self.assertEqual(80, CourseMode.min_course_price_for_currency(self.course_key, 'cny')) def test_modes_for_course_expired(self): - expired_mode, _status = self.create_mode('verified', 'Verified Certificate') + expired_mode, _status = self.create_mode('verified', 'Verified Certificate', 10) expired_mode.expiration_datetime = datetime.now(pytz.UTC) + timedelta(days=-1) expired_mode.save() modes = CourseMode.modes_for_course(self.course_key) @@ -133,7 +134,7 @@ class CourseModeModelTest(TestCase): expiration_datetime = datetime.now(pytz.UTC) + timedelta(days=1) expired_mode.expiration_datetime = expiration_datetime expired_mode.save() - expired_mode_value = Mode(u'verified', u'Verified Certificate', 0, '', 'usd', expiration_datetime, None, None) + expired_mode_value = Mode(u'verified', u'Verified Certificate', 10, '', 'usd', expiration_datetime, None, None) modes = CourseMode.modes_for_course(self.course_key) self.assertEqual([expired_mode_value, mode1], modes) @@ -141,14 +142,14 @@ class CourseModeModelTest(TestCase): self.assertEqual([CourseMode.DEFAULT_MODE], modes) def test_verified_mode_for_course(self): - self.create_mode('verified', 'Verified Certificate') + self.create_mode('verified', 'Verified Certificate', 10) mode = CourseMode.verified_mode_for_course(self.course_key) self.assertEqual(mode.slug, 'verified') # verify that the professional mode is preferred - self.create_mode('professional', 'Professional Education Verified Certificate') + self.create_mode('professional', 'Professional Education Verified Certificate', 10) mode = CourseMode.verified_mode_for_course(self.course_key) @@ -163,9 +164,8 @@ class CourseModeModelTest(TestCase): verified, _ = self.create_mode('verified', 'Verified', min_price=5) self.assertTrue(CourseMode.has_payment_options(self.course_key)) - # Unset verified's minimum price. - verified.min_price = 0 - verified.save() + # Remove the verified option. + verified.delete() self.assertFalse(CourseMode.has_payment_options(self.course_key)) # Finally, give the honor mode payment options @@ -215,7 +215,7 @@ class CourseModeModelTest(TestCase): past = now - timedelta(days=1) # Unexpired, no expiration date - CourseMode.objects.create( + CourseModeFactory.create( course_id=self.course_key, mode_display_name="Honor No Expiration", mode_slug="honor_no_expiration", @@ -223,7 +223,7 @@ class CourseModeModelTest(TestCase): ) # Unexpired, expiration date in future - CourseMode.objects.create( + CourseModeFactory.create( course_id=self.course_key, mode_display_name="Honor Not Expired", mode_slug="honor_not_expired", @@ -231,7 +231,7 @@ class CourseModeModelTest(TestCase): ) # Expired - CourseMode.objects.create( + CourseModeFactory.create( course_id=self.course_key, mode_display_name="Verified Expired", mode_slug="verified_expired", @@ -306,9 +306,9 @@ class CourseModeModelTest(TestCase): def test_invalid_mode_expiration(self, mode_slug, exp_dt): is_error_expected = CourseMode.is_professional_slug(mode_slug) and exp_dt is not None try: - self.create_mode(mode_slug=mode_slug, mode_name=mode_slug.title(), expiration_datetime=exp_dt) + self.create_mode(mode_slug=mode_slug, mode_name=mode_slug.title(), expiration_datetime=exp_dt, min_price=10) self.assertFalse(is_error_expected, "Expected a ValidationError to be thrown.") - except ValidationError, exc: + except ValidationError as exc: self.assertTrue(is_error_expected, "Did not expect a ValidationError to be thrown.") self.assertEqual( exc.messages, @@ -367,7 +367,7 @@ class CourseModeModelTest(TestCase): def test_hide_credit_modes(self, available_modes, expected_selectable_modes): # Create the course modes for mode in available_modes: - CourseMode.objects.create( + CourseModeFactory.create( course_id=self.course_key, mode_display_name=mode, mode_slug=mode, @@ -406,7 +406,7 @@ class CourseModeModelTest(TestCase): def test_expiration_datetime_explicitly_set(self): """ Verify that setting the expiration_date property sets the explicit flag. """ - verified_mode, __ = self.create_mode('verified', 'Verified Certificate') + verified_mode, __ = self.create_mode('verified', 'Verified Certificate', 10) now = datetime.now() verified_mode.expiration_datetime = now @@ -415,7 +415,7 @@ class CourseModeModelTest(TestCase): def test_expiration_datetime_not_explicitly_set(self): """ Verify that setting the _expiration_date property does not set the explicit flag. """ - verified_mode, __ = self.create_mode('verified', 'Verified Certificate') + verified_mode, __ = self.create_mode('verified', 'Verified Certificate', 10) now = datetime.now() verified_mode._expiration_datetime = now # pylint: disable=protected-access @@ -424,7 +424,7 @@ class CourseModeModelTest(TestCase): def test_expiration_datetime_explicitly_set_to_none(self): """ Verify that setting the _expiration_date property does not set the explicit flag. """ - verified_mode, __ = self.create_mode('verified', 'Verified Certificate') + verified_mode, __ = self.create_mode('verified', 'Verified Certificate', 10) self.assertFalse(verified_mode.expiration_datetime_is_explicit) verified_mode.expiration_datetime = None @@ -443,3 +443,21 @@ class CourseModeModelTest(TestCase): def test_eligible_for_cert(self, mode_slug, expected_eligibility): """Verify that non-audit modes are eligible for a cert.""" self.assertEqual(CourseMode.is_eligible_for_certificate(mode_slug), expected_eligibility) + + @ddt.data( + (CourseMode.AUDIT, False), + (CourseMode.HONOR, False), + (CourseMode.VERIFIED, True), + (CourseMode.CREDIT_MODE, False), + (CourseMode.PROFESSIONAL, True), + (CourseMode.NO_ID_PROFESSIONAL_MODE, False), + ) + @ddt.unpack + def test_verified_min_price(self, mode_slug, is_error_expected): + """Verify that verified modes have a price.""" + try: + self.create_mode(mode_slug=mode_slug, mode_name=mode_slug.title(), min_price=0) + except ValidationError: + self.assertTrue(is_error_expected, "Did not expect a ValidationError to be thrown.") + else: + self.assertFalse(is_error_expected, "Expected a ValidationError to be thrown.") diff --git a/common/djangoapps/course_modes/tests/test_signals.py b/common/djangoapps/course_modes/tests/test_signals.py index 36d0dde1d2..984f231f3c 100644 --- a/common/djangoapps/course_modes/tests/test_signals.py +++ b/common/djangoapps/course_modes/tests/test_signals.py @@ -60,7 +60,7 @@ class CourseModeSignalTest(ModuleStoreTestCase): @ddt.data(1, 14, 30) def test_verified_mode(self, verification_window): """ Verify signal updates expiration to configured time period before course end for verified mode. """ - course_mode, __ = self.create_mode('verified', 'verified') + course_mode, __ = self.create_mode('verified', 'verified', 10) self.assertIsNone(course_mode.expiration_datetime) with patch('course_modes.models.CourseModeExpirationConfig.current') as config: @@ -75,7 +75,7 @@ class CourseModeSignalTest(ModuleStoreTestCase): @ddt.data(1, 14, 30) def test_verified_mode_explicitly_set(self, verification_window): """ Verify signal does not update expiration for verified mode with explicitly set expiration. """ - course_mode, __ = self.create_mode('verified', 'verified') + course_mode, __ = self.create_mode('verified', 'verified', 10) course_mode.expiration_datetime_is_explicit = True self.assertIsNone(course_mode.expiration_datetime) diff --git a/common/djangoapps/course_modes/tests/test_views.py b/common/djangoapps/course_modes/tests/test_views.py index 844c82c8f2..a4e6af1ab4 100644 --- a/common/djangoapps/course_modes/tests/test_views.py +++ b/common/djangoapps/course_modes/tests/test_views.py @@ -50,7 +50,7 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase): def test_redirect_to_dashboard(self, is_active, enrollment_mode, redirect): # Create the course modes for mode in ('audit', 'honor', 'verified'): - CourseModeFactory(mode_slug=mode, course_id=self.course.id) + CourseModeFactory.create(mode_slug=mode, course_id=self.course.id) # Enroll the user in the test course if enrollment_mode is not None: @@ -73,7 +73,7 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase): def test_no_id_redirect(self): # Create the course modes - CourseModeFactory(mode_slug=CourseMode.NO_ID_PROFESSIONAL_MODE, course_id=self.course.id, min_price=100) + CourseModeFactory.create(mode_slug=CourseMode.NO_ID_PROFESSIONAL_MODE, course_id=self.course.id, min_price=100) # Enroll the user in the test course CourseEnrollmentFactory( @@ -112,7 +112,7 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase): def test_no_enrollment(self): # Create the course modes for mode in ('audit', 'honor', 'verified'): - CourseModeFactory(mode_slug=mode, course_id=self.course.id) + CourseModeFactory.create(mode_slug=mode, course_id=self.course.id) # User visits the track selection page directly without ever enrolling url = reverse('course_modes_choose', args=[unicode(self.course.id)]) @@ -130,9 +130,9 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase): # Create the course modes for mode in ('audit', 'honor'): - CourseModeFactory(mode_slug=mode, course_id=self.course.id) + CourseModeFactory.create(mode_slug=mode, course_id=self.course.id) - CourseModeFactory( + CourseModeFactory.create( mode_slug='verified', course_id=self.course.id, suggested_prices=price_list @@ -164,7 +164,7 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase): def test_credit_upsell_message(self, available_modes, show_upsell): # Create the course modes for mode in available_modes: - CourseModeFactory(mode_slug=mode, course_id=self.course.id) + CourseModeFactory.create(mode_slug=mode, course_id=self.course.id) # Check whether credit upsell is shown on the page # This should *only* be shown when a credit mode is available @@ -179,7 +179,7 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase): @ddt.data('professional', 'no-id-professional') def test_professional_enrollment(self, mode): # The only course mode is professional ed - CourseModeFactory(mode_slug=mode, course_id=self.course.id, min_price=1) + CourseModeFactory.create(mode_slug=mode, course_id=self.course.id, min_price=1) # Go to the "choose your track" page choose_track_url = reverse('course_modes_choose', args=[unicode(self.course.id)]) @@ -219,8 +219,8 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase): def test_choose_mode_redirect(self, course_mode, expected_redirect): # Create the course modes for mode in ('audit', 'honor', 'verified'): - min_price = 0 if course_mode in ["honor", "audit"] else 1 - CourseModeFactory(mode_slug=mode, course_id=self.course.id, min_price=min_price) + min_price = 0 if mode in ["honor", "audit"] else 1 + CourseModeFactory.create(mode_slug=mode, course_id=self.course.id, min_price=min_price) # Choose the mode (POST request) choose_track_url = reverse('course_modes_choose', args=[unicode(self.course.id)]) @@ -241,8 +241,8 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase): def test_remember_donation_for_course(self): # Create the course modes - for mode in ('honor', 'verified'): - CourseModeFactory(mode_slug=mode, course_id=self.course.id) + CourseModeFactory.create(mode_slug='honor', course_id=self.course.id) + CourseModeFactory.create(mode_slug='verified', course_id=self.course.id, min_price=1) # Choose the mode (POST request) choose_track_url = reverse('course_modes_choose', args=[unicode(self.course.id)]) @@ -259,7 +259,7 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase): def test_successful_default_enrollment(self): # Create the course modes for mode in (CourseMode.DEFAULT_MODE_SLUG, 'verified'): - CourseModeFactory(mode_slug=mode, course_id=self.course.id) + CourseModeFactory.create(mode_slug=mode, course_id=self.course.id) # Enroll the user in the default mode (honor) to emulate # automatic enrollment @@ -281,7 +281,7 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase): def test_unsupported_enrollment_mode_failure(self): # Create the supported course modes for mode in ('honor', 'verified'): - CourseModeFactory(mode_slug=mode, course_id=self.course.id) + CourseModeFactory.create(mode_slug=mode, course_id=self.course.id) # Choose an unsupported mode (POST request) choose_track_url = reverse('course_modes_choose', args=[unicode(self.course.id)]) @@ -356,7 +356,7 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase): def test_hide_nav(self): # Create the course modes for mode in ["honor", "verified"]: - CourseModeFactory(mode_slug=mode, course_id=self.course.id) + CourseModeFactory.create(mode_slug=mode, course_id=self.course.id) # Load the track selection page url = reverse('course_modes_choose', args=[unicode(self.course.id)]) @@ -393,8 +393,8 @@ class TrackSelectionEmbargoTest(UrlResetMixin, ModuleStoreTestCase): # Create a course and course modes self.course = CourseFactory.create() - CourseModeFactory(mode_slug='honor', course_id=self.course.id) - CourseModeFactory(mode_slug='verified', course_id=self.course.id, min_price=10) + CourseModeFactory.create(mode_slug='honor', course_id=self.course.id) + CourseModeFactory.create(mode_slug='verified', course_id=self.course.id, min_price=10) # Create a user and log in self.user = UserFactory.create(username="Bob", email="bob@example.com", password="edx") diff --git a/common/djangoapps/student/tests/factories.py b/common/djangoapps/student/tests/factories.py index e9ed3395d5..6f246dc413 100644 --- a/common/djangoapps/student/tests/factories.py +++ b/common/djangoapps/student/tests/factories.py @@ -1,4 +1,6 @@ """Provides factories for student models.""" +import random + from student.models import (User, UserProfile, Registration, CourseEnrollmentAllowed, CourseEnrollment, PendingEmailChange, UserStanding, @@ -7,6 +9,7 @@ from course_modes.models import CourseMode from django.contrib.auth.models import Group, AnonymousUser from datetime import datetime import factory +from factory import lazy_attribute from factory.django import DjangoModelFactory from uuid import uuid4 from pytz import UTC @@ -54,11 +57,16 @@ class CourseModeFactory(DjangoModelFactory): course_id = None mode_display_name = CourseMode.DEFAULT_MODE.name mode_slug = CourseMode.DEFAULT_MODE_SLUG - min_price = 0 suggested_prices = '' currency = 'usd' expiration_datetime = None + @lazy_attribute + def min_price(self): + if CourseMode.is_verified_slug(self.mode_slug): + return random.randint(1, 100) + return 0 + class RegistrationFactory(DjangoModelFactory): class Meta(object): diff --git a/common/djangoapps/student/tests/test_recent_enrollments.py b/common/djangoapps/student/tests/test_recent_enrollments.py index d518f3f2af..943e348eed 100644 --- a/common/djangoapps/student/tests/test_recent_enrollments.py +++ b/common/djangoapps/student/tests/test_recent_enrollments.py @@ -182,7 +182,7 @@ class TestRecentEnrollments(ModuleStoreTestCase, XssTestMixin): # Create the course mode(s) for mode, min_price in course_modes: - CourseModeFactory(mode_slug=mode, course_id=self.course.id, min_price=min_price) + CourseModeFactory.create(mode_slug=mode, course_id=self.course.id, min_price=min_price) self.enrollment.mode = enrollment_mode self.enrollment.save() @@ -203,7 +203,7 @@ class TestRecentEnrollments(ModuleStoreTestCase, XssTestMixin): # Create a white-label course mode # (honor mode with a price set) - CourseModeFactory(mode_slug="honor", course_id=self.course.id, min_price=100) + CourseModeFactory.create(mode_slug="honor", course_id=self.course.id, min_price=100) # Check that the donate button is NOT displayed self.client.login(username=self.student.username, password=self.PASSWORD) diff --git a/common/djangoapps/student/tests/test_verification_status.py b/common/djangoapps/student/tests/test_verification_status.py index 498a147fa2..013bf53b1d 100644 --- a/common/djangoapps/student/tests/test_verification_status.py +++ b/common/djangoapps/student/tests/test_verification_status.py @@ -245,7 +245,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase): # Adding another verification with different course. # Its created_at is greater than course deadline. course2 = CourseFactory.create() - CourseModeFactory( + CourseModeFactory.create( course_id=course2.id, mode_slug="verified", expiration_datetime=self.PAST @@ -277,7 +277,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase): enrollment_mode (str): The mode of the enrollment. """ - CourseModeFactory( + CourseModeFactory.create( course_id=self.course.id, mode_slug="verified", expiration_datetime=deadline diff --git a/common/djangoapps/student/tests/tests.py b/common/djangoapps/student/tests/tests.py index fc5ee82c0e..d93bb56a4e 100644 --- a/common/djangoapps/student/tests/tests.py +++ b/common/djangoapps/student/tests/tests.py @@ -226,7 +226,7 @@ class DashboardTest(ModuleStoreTestCase): """ Check that the css class and the status message are in the dashboard html. """ - CourseModeFactory(mode_slug=mode, course_id=self.course.id) + CourseModeFactory.create(mode_slug=mode, course_id=self.course.id) CourseEnrollment.enroll(self.user, self.course.location.course_key, mode=mode) if mode == 'verified': @@ -263,7 +263,7 @@ class DashboardTest(ModuleStoreTestCase): """ Check that the css class and the status message are not in the dashboard html. """ - CourseModeFactory(mode_slug=mode, course_id=self.course.id) + CourseModeFactory.create(mode_slug=mode, course_id=self.course.id) CourseEnrollment.enroll(self.user, self.course.location.course_key, mode=mode) if mode == 'verified': diff --git a/lms/djangoapps/certificates/tests/test_api.py b/lms/djangoapps/certificates/tests/test_api.py index 357dae3659..fda6acb811 100644 --- a/lms/djangoapps/certificates/tests/test_api.py +++ b/lms/djangoapps/certificates/tests/test_api.py @@ -369,8 +369,8 @@ class GenerateExampleCertificatesTest(TestCase): def test_generate_example_certs_with_verified_mode(self): # Create verified and honor modes for the course - CourseModeFactory(course_id=self.COURSE_KEY, mode_slug='honor') - CourseModeFactory(course_id=self.COURSE_KEY, mode_slug='verified') + CourseModeFactory.create(course_id=self.COURSE_KEY, mode_slug='honor') + CourseModeFactory.create(course_id=self.COURSE_KEY, mode_slug='verified') # Generate certificates for the course with self._mock_xqueue() as mock_queue: diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index 5a21d32c4c..4fb2db296b 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -649,9 +649,13 @@ class ViewsTestCase(ModuleStoreTestCase): (verified_course_deadline_passed, CourseMode.AUDIT, datetime.now(UTC) - timedelta(days=1)) ) for course, mode, expiration in enrollments: - CourseModeFactory(mode_slug=CourseMode.AUDIT, course_id=course) + CourseModeFactory.create(mode_slug=CourseMode.AUDIT, course_id=course) if course != non_verified_course: - CourseModeFactory(mode_slug=CourseMode.VERIFIED, course_id=course, expiration_datetime=expiration) + CourseModeFactory.create( + mode_slug=CourseMode.VERIFIED, + course_id=course, + expiration_datetime=expiration + ) CourseEnrollmentFactory(course_id=course, user=self.user, mode=mode) self.client.login(username=self.user.username, password=self.password) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index 96d6e35d23..b5abbbb299 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -403,7 +403,7 @@ class TestInstructorAPIBulkAccountCreationAndEnrollment(SharedModuleStoreTestCas # Create a course with mode 'audit' cls.audit_course = CourseFactory.create() - CourseModeFactory(course_id=cls.audit_course.id, mode_slug=CourseMode.AUDIT) + CourseModeFactory.create(course_id=cls.audit_course.id, mode_slug=CourseMode.AUDIT) cls.url = reverse( 'register_and_enroll_students', kwargs={'course_id': unicode(cls.course.id)} @@ -417,7 +417,7 @@ class TestInstructorAPIBulkAccountCreationAndEnrollment(SharedModuleStoreTestCas # Create a course with mode 'honor' and with price self.white_label_course = CourseFactory.create() - self.white_label_course_mode = CourseModeFactory( + self.white_label_course_mode = CourseModeFactory.create( course_id=self.white_label_course.id, mode_slug=CourseMode.HONOR, min_price=10, diff --git a/lms/djangoapps/shoppingcart/tests/test_context_processor.py b/lms/djangoapps/shoppingcart/tests/test_context_processor.py index 010504c6ed..4a62667e3e 100644 --- a/lms/djangoapps/shoppingcart/tests/test_context_processor.py +++ b/lms/djangoapps/shoppingcart/tests/test_context_processor.py @@ -29,7 +29,7 @@ class UserCartContextProcessorUnitTest(ModuleStoreTestCase): Adds content to self.user's cart """ course = CourseFactory.create(org='MITx', number='999', display_name='Robot Super Course') - CourseModeFactory(course_id=course.id) + CourseModeFactory.create(course_id=course.id) cart = Order.get_cart_for_user(self.user) PaidCourseRegistration.add_to_order(cart, course.id) diff --git a/lms/djangoapps/support/tests/test_refund.py b/lms/djangoapps/support/tests/test_refund.py index 62df658eeb..602251707a 100644 --- a/lms/djangoapps/support/tests/test_refund.py +++ b/lms/djangoapps/support/tests/test_refund.py @@ -44,7 +44,11 @@ class RefundTests(ModuleStoreTestCase): username='student', email='student+refund@edx.org' ) - self.course_mode = CourseMode.objects.get_or_create(course_id=self.course_id, mode_slug='verified')[0] + self.course_mode = CourseMode.objects.get_or_create( + course_id=self.course_id, + mode_slug='verified', + min_price=1 + )[0] self.order = None self.form_pars = {'course_id': str(self.course_id), 'user': self.student.email} diff --git a/lms/djangoapps/verify_student/tests/test_integration.py b/lms/djangoapps/verify_student/tests/test_integration.py index e792425080..c2401b6231 100644 --- a/lms/djangoapps/verify_student/tests/test_integration.py +++ b/lms/djangoapps/verify_student/tests/test_integration.py @@ -26,7 +26,7 @@ class TestProfEdVerification(ModuleStoreTestCase): self.client.login(username="rusty", password="test") course = CourseFactory.create(org='Robot', number='999', display_name='Test Course') self.course_key = course.id - CourseModeFactory( + CourseModeFactory.create( mode_slug="professional", course_id=self.course_key, min_price=self.MIN_PRICE, diff --git a/lms/djangoapps/verify_student/tests/test_services.py b/lms/djangoapps/verify_student/tests/test_services.py index c2f8f9e42f..7000fd462b 100644 --- a/lms/djangoapps/verify_student/tests/test_services.py +++ b/lms/djangoapps/verify_student/tests/test_services.py @@ -31,7 +31,7 @@ class TestReverificationService(ModuleStoreTestCase): self.user = UserFactory.create(username="rusty", password="test") self.course = CourseFactory.create(org='Robot', number='999', display_name='Test Course') self.course_id = self.course.id - CourseModeFactory( + CourseModeFactory.create( mode_slug="verified", course_id=self.course_id, min_price=100, diff --git a/lms/djangoapps/verify_student/tests/test_views.py b/lms/djangoapps/verify_student/tests/test_views.py index 28430f7d5f..6d98db120d 100644 --- a/lms/djangoapps/verify_student/tests/test_views.py +++ b/lms/djangoapps/verify_student/tests/test_views.py @@ -822,7 +822,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin): for course_mode in course_modes: min_price = (0 if course_mode in ["honor", "audit"] else self.MIN_PRICE) - CourseModeFactory( + CourseModeFactory.create( course_id=course.id, mode_slug=course_mode, mode_display_name=course_mode, @@ -1012,7 +1012,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin): course = CourseFactory.create(display_name=mode_display_name) for course_mode in [CourseMode.DEFAULT_MODE_SLUG, "verified"]: min_price = (self.MIN_PRICE if course_mode != CourseMode.DEFAULT_MODE_SLUG else 0) - CourseModeFactory( + CourseModeFactory.create( course_id=course.id, mode_slug=course_mode, mode_display_name=mode_display_name, @@ -1074,7 +1074,7 @@ class CheckoutTestMixin(object): self.user = UserFactory.create(username="test", password="test") self.course = CourseFactory.create() for mode, min_price in (('audit', 0), ('honor', 0), ('verified', 100)): - CourseModeFactory(mode_slug=mode, course_id=self.course.id, min_price=min_price, sku=self.make_sku()) + CourseModeFactory.create(mode_slug=mode, course_id=self.course.id, min_price=min_price, sku=self.make_sku()) self.client.login(username="test", password="test") def _assert_checked_out( @@ -1119,7 +1119,7 @@ class CheckoutTestMixin(object): def test_create_order_prof_ed(self, patched_create_order): # Create a prof ed course course = CourseFactory.create() - CourseModeFactory(mode_slug="professional", course_id=course.id, min_price=10, sku=self.make_sku()) + CourseModeFactory.create(mode_slug="professional", course_id=course.id, min_price=10, sku=self.make_sku()) # Create an order for a prof ed course params = {'course_id': unicode(course.id)} self._assert_checked_out(params, patched_create_order, course.id, 'professional') @@ -1127,7 +1127,7 @@ class CheckoutTestMixin(object): def test_create_order_no_id_professional(self, patched_create_order): # Create a no-id-professional ed course course = CourseFactory.create() - CourseModeFactory(mode_slug="no-id-professional", course_id=course.id, min_price=10, sku=self.make_sku()) + CourseModeFactory.create(mode_slug="no-id-professional", course_id=course.id, min_price=10, sku=self.make_sku()) # Create an order for a prof ed course params = {'course_id': unicode(course.id)} self._assert_checked_out(params, patched_create_order, course.id, 'no-id-professional') @@ -1135,8 +1135,8 @@ class CheckoutTestMixin(object): def test_create_order_for_multiple_paid_modes(self, patched_create_order): # Create a no-id-professional ed course course = CourseFactory.create() - CourseModeFactory(mode_slug="no-id-professional", course_id=course.id, min_price=10, sku=self.make_sku()) - CourseModeFactory(mode_slug="professional", course_id=course.id, min_price=10, sku=self.make_sku()) + CourseModeFactory.create(mode_slug="no-id-professional", course_id=course.id, min_price=10, sku=self.make_sku()) + CourseModeFactory.create(mode_slug="professional", course_id=course.id, min_price=10, sku=self.make_sku()) # Create an order for a prof ed course params = {'course_id': unicode(course.id)} # TODO jsa - is this the intended behavior? @@ -1227,7 +1227,7 @@ class TestCheckoutWithEcommerceService(ModuleStoreTestCase): ecommerce api, we correctly call to that api to create a basket. """ user = UserFactory.create(username="test-username") - course_mode = CourseModeFactory(sku="test-sku").to_tuple() # pylint: disable=no-member + course_mode = CourseModeFactory.create(sku="test-sku").to_tuple() # pylint: disable=no-member expected_payment_data = {'foo': 'bar'} # mock out the payment processors endpoint httpretty.register_uri( @@ -2079,7 +2079,7 @@ class TestInCourseReverifyView(ModuleStoreTestCase): # Create the course modes for mode in ('audit', 'honor', 'verified'): min_price = 0 if mode in ["honor", "audit"] else 1 - CourseModeFactory(mode_slug=mode, course_id=self.course_key, min_price=min_price) + CourseModeFactory.create(mode_slug=mode, course_id=self.course_key, min_price=min_price) # Create the 'edx-reverification-block' in course tree section = ItemFactory.create(parent=self.course, category='chapter', display_name='Test Section') @@ -2280,7 +2280,7 @@ class TestEmailMessageWithCustomICRVBlock(ModuleStoreTestCase): # Create the course modes for mode in ('audit', 'honor', 'verified'): min_price = 0 if mode in ["honor", "audit"] else 1 - CourseModeFactory(mode_slug=mode, course_id=self.course_key, min_price=min_price) + CourseModeFactory.create(mode_slug=mode, course_id=self.course_key, min_price=min_price) # Create the 'edx-reverification-block' in course tree section = ItemFactory.create(parent=self.course, category='chapter', display_name='Test Section') @@ -2483,7 +2483,7 @@ class TestEmailMessageWithDefaultICRVBlock(ModuleStoreTestCase): # Create the course modes for mode in ('audit', 'honor', 'verified'): min_price = 0 if mode in ["honor", "audit"] else 1 - CourseModeFactory(mode_slug=mode, course_id=self.course_key, min_price=min_price) + CourseModeFactory.create(mode_slug=mode, course_id=self.course_key, min_price=min_price) # Create the 'edx-reverification-block' in course tree section = ItemFactory.create(parent=self.course, category='chapter', display_name='Test Section')