From cec4b162ba2e40bb591df61cf094343895bcd84d Mon Sep 17 00:00:00 2001 From: Alex Sheehan Date: Thu, 26 Jan 2023 21:49:55 +0000 Subject: [PATCH] feat: modifying license enrollment endpoint to support other subsidies --- enterprise/api/v1/serializers.py | 38 +++-- enterprise/api/v1/views.py | 40 +++-- ...nt_subsidizedenterprisecourseenrollment.py | 55 +++++++ enterprise/models.py | 34 +++++ enterprise/utils.py | 55 +++++-- tests/test_enterprise/api/test_views.py | 138 +++++++++++++++++- tests/test_enterprise/test_utils.py | 24 +-- 7 files changed, 325 insertions(+), 59 deletions(-) create mode 100644 enterprise/migrations/0167_historicalsubsidizedenterprisecourseenrollment_subsidizedenterprisecourseenrollment.py diff --git a/enterprise/api/v1/serializers.py b/enterprise/api/v1/serializers.py index 0d9f1497e8..b474373da1 100644 --- a/enterprise/api/v1/serializers.py +++ b/enterprise/api/v1/serializers.py @@ -1229,26 +1229,29 @@ def validate(self, data): # pylint: disable=arguments-renamed return data -class LicensesInfoSerializer(serializers.Serializer): +class EnrollmentsInfoSerializer(serializers.Serializer): """ Nested serializer class to allow for many license info dictionaries. """ - email = serializers.CharField(required=False) - course_run_key = serializers.CharField(required=False) + email = serializers.CharField(required=True) + course_run_key = serializers.CharField(required=True) license_uuid = serializers.CharField(required=False) + transaction_id = serializers.CharField(required=False) def create(self, validated_data): return validated_data def validate(self, data): # pylint: disable=arguments-renamed - missing_fields = [] - for key in self.fields.keys(): - if not data.get(key): - missing_fields.append(key) - - if missing_fields: - raise serializers.ValidationError('Found missing licenses_info field(s): {}.'.format(missing_fields)) - + license_uuid = data.get('license_uuid') + transaction_id = data.get('transaction_id') + if not license_uuid and not transaction_id: + raise serializers.ValidationError( + "At least one subsidy info field [license_uuid or transaction_id] required." + ) + if license_uuid and transaction_id: + raise serializers.ValidationError( + "Enrollment info contains conflicting subsidy information: `license_uuid` and `transaction_id` found" + ) return data @@ -1257,7 +1260,8 @@ class EnterpriseCustomerBulkSubscriptionEnrollmentsSerializer(serializers.Serial """ Serializes a licenses info field for bulk enrollment requests. """ - licenses_info = LicensesInfoSerializer(many=True, required=False) + licenses_info = EnrollmentsInfoSerializer(many=True, required=False) + enrollments_info = EnrollmentsInfoSerializer(many=True, required=False) reason = serializers.CharField(required=False) salesforce_id = serializers.CharField(required=False) discount = serializers.DecimalField(None, 5, required=False) @@ -1267,9 +1271,15 @@ def create(self, validated_data): return validated_data def validate(self, data): # pylint: disable=arguments-renamed - if data.get('licenses_info') is None: + licenses_info = data.get('licenses_info') + enrollments_info = data.get('enrollments_info') + if bool(licenses_info) == bool(enrollments_info): + if licenses_info: + raise serializers.ValidationError( + '`licenses_info` must be ommitted if `enrollments_info` is present.' + ) raise serializers.ValidationError( - 'Must include the "licenses_info" parameter in request.' + 'Must include the `enrollment_info` parameter in request.' ) return data diff --git a/enterprise/api/v1/views.py b/enterprise/api/v1/views.py index f78a3e56a2..b1b89fe9e5 100644 --- a/enterprise/api/v1/views.py +++ b/enterprise/api/v1/views.py @@ -69,7 +69,7 @@ ) from enterprise.utils import ( NotConnectedToOpenEdX, - enroll_licensed_users_in_courses, + enroll_subsidy_users_in_courses, get_best_mode_from_course_key, get_enterprise_customer, get_request_value, @@ -245,23 +245,30 @@ def course_enrollments(self, request, pk): # pylint: disable=unused-argument def enroll_learners_in_courses(self, request, pk): """ - Creates a set of licensed enterprise_learners by bulk enrolling them in all specified courses. This endpoint is - not transactional, in that any one or more failures will not affect other successful enrollments made within - the same request. + Creates a set of enterprise enrollments for specified learners by bulk enrolling them in provided courses. + This endpoint is not transactional, in that any one or more failures will not affect other successful + enrollments smade within the same request. Parameters: - licenses_info (list of dicts): an array of dictionaries, each containing the necessary information to - create a licenced enrollment for a user in a specified course. Each dictionary must contain a user - email, a course run key, and a UUID of the license that the learner is using to enroll with. + enrollment_info (list of dicts): an array of dictionaries, each containing the necessary information to + create an enrollment based on a subsidy for a user in a specified course. Each dictionary must contain + a user email, a course run key, and either a UUID of the license that the learner is using to enroll + with or a transaction ID related to Executive Education the enrollment. `licenses_info` is also + accepted as a body param name. Example:: - licenses_info: [ + enrollment_info: [ { 'email': 'newuser@test.com', 'course_run_key': 'course-v1:edX+DemoX+Demo_Course', 'license_uuid': '5b77bdbade7b4fcb838f8111b68e18ae', }, + { + 'email': 'newuser2@test.com', + 'course_run_key': 'course-v2:edX+FunX+Fun_Course', + 'transaction_id': '84kdbdbade7b4fcb838f8asjke8e18ae', + }, ... ] @@ -298,21 +305,22 @@ def enroll_learners_in_courses(self, request, pk): return Response(serializer.errors, status=HTTP_400_BAD_REQUEST) email_errors = [] - licenses_info = serializer.validated_data.get('licenses_info') + serialized_data = serializer.validated_data + enrollments_info = serialized_data.get('licenses_info', serialized_data.get('enrollments_info')) # Default subscription discount is 100% - discount = serializer.validated_data.get('discount', 100.00) + discount = serialized_data.get('discount', 100.00) emails = set() # Retrieve and store course modes for each unique course provided - course_runs_modes = {license_info['course_run_key']: None for license_info in licenses_info} + course_runs_modes = {enrollment_info['course_run_key']: None for enrollment_info in enrollments_info} for course_run in course_runs_modes: course_runs_modes[course_run] = get_best_mode_from_course_key(course_run) - for index, info in enumerate(licenses_info): + for index, info in enumerate(enrollments_info): emails.add(info['email']) - licenses_info[index]['course_mode'] = course_runs_modes[info['course_run_key']] + enrollments_info[index]['course_mode'] = course_runs_modes[info['course_run_key']] for email in emails: try: @@ -326,12 +334,12 @@ def enroll_learners_in_courses(self, request, pk): except LinkUserToEnterpriseError: email_errors.append(email) - # Remove the bad emails from licenses_info and emails, don't attempt to enroll or link bad emails. + # Remove the bad emails from enrollments_info and the emails set, don't attempt to enroll or link bad emails. for errored_user in email_errors: - licenses_info[:] = [info for info in licenses_info if info['email'] != errored_user] + enrollments_info[:] = [info for info in enrollments_info if info['email'] != errored_user] emails.remove(errored_user) - results = enroll_licensed_users_in_courses(enterprise_customer, licenses_info, discount) + results = enroll_subsidy_users_in_courses(enterprise_customer, enrollments_info, discount) # collect the returned activation links for licenses which need activation activation_links = {} diff --git a/enterprise/migrations/0167_historicalsubsidizedenterprisecourseenrollment_subsidizedenterprisecourseenrollment.py b/enterprise/migrations/0167_historicalsubsidizedenterprisecourseenrollment_subsidizedenterprisecourseenrollment.py new file mode 100644 index 0000000000..447ec580d2 --- /dev/null +++ b/enterprise/migrations/0167_historicalsubsidizedenterprisecourseenrollment_subsidizedenterprisecourseenrollment.py @@ -0,0 +1,55 @@ +# Generated by Django 3.2.16 on 2023-01-27 14:54 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import model_utils.fields +import simple_history.models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('enterprise', '0166_auto_20221209_0819'), + ] + + operations = [ + migrations.CreateModel( + name='SubsidizedEnterpriseCourseEnrollment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), + ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), + ('transaction_id', models.UUIDField(editable=False)), + ('is_revoked', models.BooleanField(default=False, help_text="Whether the subsidized enterprise course enrollment is revoked, e.g., when a user's subsidy is revoked.")), + ('enterprise_course_enrollment', models.OneToOneField(help_text='The course enrollment the associated license is for.', on_delete=django.db.models.deletion.CASCADE, related_name='subsidized_with', to='enterprise.enterprisecourseenrollment')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='HistoricalSubsidizedEnterpriseCourseEnrollment', + fields=[ + ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), + ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), + ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), + ('transaction_id', models.UUIDField(editable=False)), + ('is_revoked', models.BooleanField(default=False, help_text="Whether the subsidized enterprise course enrollment is revoked, e.g., when a user's subsidy is revoked.")), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField()), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('enterprise_course_enrollment', models.ForeignKey(blank=True, db_constraint=False, help_text='The course enrollment the associated license is for.', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='enterprise.enterprisecourseenrollment')), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'historical subsidized enterprise course enrollment', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': 'history_date', + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + ] diff --git a/enterprise/models.py b/enterprise/models.py index 2eb47cdf0c..a0849fd92e 100644 --- a/enterprise/models.py +++ b/enterprise/models.py @@ -1906,6 +1906,40 @@ def __repr__(self): return self.__str__() +class SubsidizedEnterpriseCourseEnrollment(TimeStampedModel): + """ + An Enterprise Course Enrollment that is enrolled via a transaction ID. + + .. no_pii: + """ + + transaction_id = models.UUIDField( + primary_key=False, + editable=False, + null=False + ) + + enterprise_course_enrollment = models.OneToOneField( + EnterpriseCourseEnrollment, + blank=False, + null=False, + related_name='subsidized_with', + on_delete=models.deletion.CASCADE, + help_text=_( + "The course enrollment the associated license is for." + ) + ) + + is_revoked = models.BooleanField( + default=False, + help_text=_( + "Whether the subsidized enterprise course enrollment is revoked, e.g., when a user's subsidy is revoked." + ) + ) + + history = HistoricalRecords() + + class LicensedEnterpriseCourseEnrollment(TimeStampedModel): """ An Enterprise Course Enrollment that is enrolled via a license. diff --git a/enterprise/utils.py b/enterprise/utils.py index 4b6f21e557..c9c84a1706 100644 --- a/enterprise/utils.py +++ b/enterprise/utils.py @@ -711,6 +711,13 @@ def licensed_enterprise_course_enrollment_model(): return apps.get_model('enterprise', 'LicensedEnterpriseCourseEnrollment') +def subsidized_enterprise_course_enrollment_model(): + """ + returns the ``SubsidizedEnterpriseCourseEnrollment`` class. + """ + return apps.get_model('enterprise', 'SubsidizedEnterpriseCourseEnrollment') + + def enterprise_customer_invite_key_model(): """ Returns the ``EnterpriseCustomerInviteKey`` class. @@ -1751,7 +1758,8 @@ def customer_admin_enroll_user_with_status( course_mode, course_id, enrollment_source=None, - license_uuid=None + license_uuid=None, + transaction_id=None, ): """ For use with bulk enrollment, or any use case of admin enrolling a user @@ -1812,6 +1820,11 @@ def customer_admin_enroll_user_with_status( 'source': source } ) + if transaction_id: + subsidized_enterprise_course_enrollment_model().objects.get_or_create( + transaction_id=transaction_id, + enterprise_course_enrollment=obj, + ) if license_uuid: licensed_enterprise_course_enrollment_model().objects.get_or_create( license_uuid=license_uuid, @@ -1897,14 +1910,15 @@ def get_create_ent_enrollment( return enterprise_course_enrollment, created -def enroll_licensed_users_in_courses(enterprise_customer, licensed_users_info, discount=100.00): +def enroll_subsidy_users_in_courses(enterprise_customer, subsidy_users_info, discount=100.00): """ Takes a list of licensed learner data and enrolls each learner in the requested courses. Args: enterprise_customer: The EnterpriseCustomer (object) which is sponsoring the enrollment - licensed_users_info: (list) An array of dictionaries, each containing information necessary to create a - licensed enterprise enrollment for a specific learner in a specified course run. + subsidy_users_info: (list) An array of dictionaries, each containing information necessary to create a + enterprise enrollment from a subsidy for a specific learner in a specified course run. Accepted forms of + subsidies are: [`license_uuid` and `transaction_id`] Example:: licensed_users_info: [ @@ -1913,7 +1927,13 @@ def enroll_licensed_users_in_courses(enterprise_customer, licensed_users_info, d 'course_run_key': 'course-v1:edX+DemoX+Demo_Course', 'course_mode': 'verified', 'license_uuid': '5b77bdbade7b4fcb838f8111b68e18ae' - } + }, + { + 'email': 'newuser2@test.com', + 'course_run_key': 'course-v2:edX+FunX+Fun_Course', + 'course_mode': 'unpaid-executive-education', + 'transaction_id': '84kdbdbade7b4fcb838f8asjke8e18ae', + }, ] discount: (int) the discount offered to the learner for their enrollment. Subscription based enrollments default to 100 @@ -1931,21 +1951,28 @@ def enroll_licensed_users_in_courses(enterprise_customer, licensed_users_info, d 'pending': [], 'failures': [], } - for licensed_user_info in licensed_users_info: - user_email = licensed_user_info.get('email') - course_mode = licensed_user_info.get('course_mode') - course_run_key = licensed_user_info.get('course_run_key') - license_uuid = licensed_user_info.get('license_uuid') - activation_link = licensed_user_info.get('activation_link') - - user = User.objects.filter(email=licensed_user_info['email']).first() + for subsidy_user_info in subsidy_users_info: + user_email = subsidy_user_info.get('email') + course_mode = subsidy_user_info.get('course_mode') + course_run_key = subsidy_user_info.get('course_run_key') + license_uuid = subsidy_user_info.get('license_uuid') + transaction_id = subsidy_user_info.get('transaction_id') + activation_link = subsidy_user_info.get('activation_link') + + user = User.objects.filter(email=subsidy_user_info['email']).first() try: if user: enrollment_source = enterprise_enrollment_source_model().get_source( enterprise_enrollment_source_model().CUSTOMER_ADMIN ) succeeded, created = customer_admin_enroll_user_with_status( - enterprise_customer, user, course_mode, course_run_key, enrollment_source, license_uuid + enterprise_customer, + user, + course_mode, + course_run_key, + enrollment_source, + license_uuid, + transaction_id ) if succeeded: results['successes'].append({ diff --git a/tests/test_enterprise/api/test_views.py b/tests/test_enterprise/api/test_views.py index 930b24190c..05f3010e13 100644 --- a/tests/test_enterprise/api/test_views.py +++ b/tests/test_enterprise/api/test_views.py @@ -3408,11 +3408,34 @@ class TestBulkEnrollment(BaseTestEnterpriseAPIViews): """ @ddt.data( + # enrollment_info usage + { + 'body': { + 'enrollments_info': [{ + 'email': 'abc@test.com', + 'course_run_key': 'course-v1:edX+DemoX+Demo_Course', + 'license_uuid': '5a88bdcade7c4ecb838f8111b68e18ac' + }] + }, + 'expected_code': 202, + 'expected_response': { + 'successes': [], + 'pending': [{ + 'email': 'abc@test.com', + 'course_run_key': 'course-v1:edX+DemoX+Demo_Course', + 'created': True, + 'activation_link': None, + }], + 'failures': [] + }, + 'expected_num_pending_licenses': 1, + 'expected_events': [mock.call(PATHWAY_CUSTOMER_ADMIN_ENROLLMENT, 1, 'course-v1:edX+DemoX+Demo_Course')], + }, # Validation failure cases { 'body': {}, 'expected_code': 400, - 'expected_response': {'non_field_errors': ['Must include the "licenses_info" parameter in request.']}, + 'expected_response': {'non_field_errors': ['Must include the `enrollment_info` parameter in request.']}, 'expected_num_pending_licenses': 0, 'expected_events': None, }, @@ -3433,7 +3456,54 @@ class TestBulkEnrollment(BaseTestEnterpriseAPIViews): }, 'expected_code': 400, 'expected_response': { - 'licenses_info': [{'non_field_errors': ["Found missing licenses_info field(s): ['license_uuid']."]}] + 'licenses_info': [ + { + 'non_field_errors': [ + "At least one subsidy info field [license_uuid or transaction_id] required." + ], + } + ] + }, + 'expected_num_pending_licenses': 0, + 'expected_events': None, + }, + { + 'body': { + 'licenses_info': [ + { + 'email': 'abc@test.com', + 'course_run_key': 'course-v1:edX+DemoX+Demo_Course', + 'license_uuid': 'foobar', + 'transaction_id': 'ayylmao' + }, + ] + }, + 'expected_code': 400, + 'expected_response': { + 'licenses_info': [ + { + 'non_field_errors': [ + "Enrollment info contains conflicting subsidy information: " + "`license_uuid` and `transaction_id` found", + ] + } + ] + }, + 'expected_num_pending_licenses': 0, + 'expected_events': None, + }, + { + 'body': { + 'licenses_info': [ + { + 'course_run_key': 'course-v1:edX+DemoX+Demo_Course', + 'license_uuid': '5a88bdcade7c4ecb838f8111b68e18ac' + } + ] + }, + 'expected_code': 400, + 'expected_response': { + 'licenses_info': [{'email': ['This field is required.']}] }, 'expected_num_pending_licenses': 0, 'expected_events': None, @@ -3579,6 +3649,68 @@ class TestBulkEnrollment(BaseTestEnterpriseAPIViews): mock.call(PATHWAY_CUSTOMER_ADMIN_ENROLLMENT, 1, 'course-v2:edX+DemoX+Second_Demo_Course') ], }, + { + 'body': { + 'enrollments_info': [ + { + 'email': 'abc@test.com', + 'course_run_key': 'course-v1:edX+DemoX+Demo_Course', + 'license_uuid': '5a88bdcade7c4ecb838f8111b68e18ac' + }, + { + 'email': 'xyz@test.com', + 'course_run_key': 'course-v1:edX+DemoX+Demo_Course', + 'license_uuid': '2c58acdade7c4ede838f7111b42e18ac' + }, + { + 'email': 'abc@test.com', + 'course_run_key': 'course-v2:edX+DemoX+Second_Demo_Course', + 'license_uuid': '5a88bdcade7c4ecb838f8111b68e18ac' + }, + { + 'email': 'xyz@test.com', + 'course_run_key': 'course-v2:edX+DemoX+Second_Demo_Course', + 'license_uuid': '2c58acdade7c4ede838f7111b42e18ac' + }, + ] + }, + 'expected_code': 202, + 'expected_response': { + 'successes': [], + 'pending': [ + { + 'email': 'abc@test.com', + 'course_run_key': 'course-v1:edX+DemoX+Demo_Course', + 'created': True, + 'activation_link': None, + }, + { + 'email': 'xyz@test.com', + 'course_run_key': 'course-v1:edX+DemoX+Demo_Course', + 'created': True, + 'activation_link': None, + }, + { + 'email': 'abc@test.com', + 'course_run_key': 'course-v2:edX+DemoX+Second_Demo_Course', + 'created': True, + 'activation_link': None, + }, + { + 'email': 'xyz@test.com', + 'course_run_key': 'course-v2:edX+DemoX+Second_Demo_Course', + 'created': True, + 'activation_link': None, + } + ], + 'failures': [] + }, + 'expected_num_pending_licenses': 4, + 'expected_events': [ + mock.call(PATHWAY_CUSTOMER_ADMIN_ENROLLMENT, 1, 'course-v1:edX+DemoX+Demo_Course'), + mock.call(PATHWAY_CUSTOMER_ADMIN_ENROLLMENT, 1, 'course-v2:edX+DemoX+Second_Demo_Course') + ], + }, ) @ddt.unpack @mock.patch('enterprise.api.v1.views.get_best_mode_from_course_key') @@ -3771,7 +3903,7 @@ def _make_call(course_run, enrolled_learners): mock_notify_task.assert_has_calls(mock_calls, any_order=True) - @mock.patch('enterprise.api.v1.views.enroll_licensed_users_in_courses') + @mock.patch('enterprise.api.v1.views.enroll_subsidy_users_in_courses') @mock.patch('enterprise.api.v1.views.get_best_mode_from_course_key') def test_enroll_learners_in_courses_partial_failure(self, mock_get_course_mode, mock_enroll_user): """ diff --git a/tests/test_enterprise/test_utils.py b/tests/test_enterprise/test_utils.py index ba3b5bb0b6..720c53f9c4 100644 --- a/tests/test_enterprise/test_utils.py +++ b/tests/test_enterprise/test_utils.py @@ -13,7 +13,7 @@ from enterprise.models import EnterpriseCourseEnrollment from enterprise.utils import ( - enroll_licensed_users_in_courses, + enroll_subsidy_users_in_courses, get_idiff_list, get_platform_logo_url, is_pending_user, @@ -97,14 +97,14 @@ def test_get_platform_logo_url(self, logo_url, expected_logo_url, mock_get_logo_ @mock.patch('enterprise.utils.lms_enroll_user_in_course') @mock.patch('enterprise.utils.CourseEnrollmentError', new_callable=lambda: StubException) @mock.patch('enterprise.utils.CourseUserGroup', new_callable=lambda: StubModel) - def test_enroll_licensed_users_in_courses_fails( + def test_enroll_subsidy_users_in_courses_fails( self, mock_model, mock_error, mock_customer_admin_enroll_user_with_status ): """ - Test that `enroll_licensed_users_in_courses` properly handles failure cases where something goes wrong with the + Test that `enroll_subsidy_users_in_courses` properly handles failure cases where something goes wrong with the user enrollment. """ self.create_user() @@ -121,7 +121,7 @@ def test_enroll_licensed_users_in_courses_fails( 'license_uuid': '5b77bdbade7b4fcb838f8111b68e18ae' }] - result = enroll_licensed_users_in_courses(ent_customer, licensed_users_info) + result = enroll_subsidy_users_in_courses(ent_customer, licensed_users_info) self.assertEqual( { 'successes': [], @@ -134,14 +134,14 @@ def test_enroll_licensed_users_in_courses_fails( @mock.patch('enterprise.utils.lms_enroll_user_in_course') @mock.patch('enterprise.utils.CourseEnrollmentError', new_callable=lambda: StubException) @mock.patch('enterprise.utils.CourseUserGroup', new_callable=lambda: StubModel) - def test_enroll_licensed_users_in_courses_partially_fails( + def test_enroll_subsidy_users_in_courses_partially_fails( self, mock_model, mock_error, mock_customer_admin_enroll_user_with_status ): """ - Test that `enroll_licensed_users_in_courses` properly handles partial failure states and still creates + Test that `enroll_subsidy_users_in_courses` properly handles partial failure states and still creates enrollments for the users that succeed. """ self.create_user() @@ -173,7 +173,7 @@ def test_enroll_licensed_users_in_courses_partially_fails( mock_model.DoesNotExist = Exception mock_customer_admin_enroll_user_with_status.side_effect = [True, mock_error('mocked error')] - result = enroll_licensed_users_in_courses(ent_customer, licensed_users_info) + result = enroll_subsidy_users_in_courses(ent_customer, licensed_users_info) self.assertEqual( { 'pending': [], @@ -191,9 +191,9 @@ def test_enroll_licensed_users_in_courses_partially_fails( self.assertEqual(len(EnterpriseCourseEnrollment.objects.all()), 1) @mock.patch('enterprise.utils.lms_enroll_user_in_course') - def test_enroll_licensed_users_in_courses_succeeds(self, mock_customer_admin_enroll_user): + def test_enroll_subsidy_users_in_courses_succeeds(self, mock_customer_admin_enroll_user): """ - Test that users that already exist are enrolled by enroll_licensed_users_in_courses and returned under the + Test that users that already exist are enrolled by enroll_subsidy_users_in_courses and returned under the `succeeded` field. """ self.create_user() @@ -215,7 +215,7 @@ def test_enroll_licensed_users_in_courses_succeeds(self, mock_customer_admin_enr mock_customer_admin_enroll_user.return_value = True - result = enroll_licensed_users_in_courses(ent_customer, licensed_users_info) + result = enroll_subsidy_users_in_courses(ent_customer, licensed_users_info) self.assertEqual( { 'pending': [], @@ -234,7 +234,7 @@ def test_enroll_licensed_users_in_courses_succeeds(self, mock_customer_admin_enr def test_enroll_pending_licensed_users_in_courses_succeeds(self): """ - Test that users that do not exist are pre-enrolled by enroll_licensed_users_in_courses and returned under the + Test that users that do not exist are pre-enrolled by enroll_subsidy_users_in_courses and returned under the `pending` field. """ ent_customer = factories.EnterpriseCustomerFactory( @@ -247,7 +247,7 @@ def test_enroll_pending_licensed_users_in_courses_succeeds(self): 'course_mode': 'verified', 'license_uuid': '5b77bdbade7b4fcb838f8111b68e18ae' }] - result = enroll_licensed_users_in_courses(ent_customer, licensed_users_info) + result = enroll_subsidy_users_in_courses(ent_customer, licensed_users_info) self.assertEqual(result['pending'][0]['email'], 'pending-user-email@example.com') self.assertFalse(result['successes'])