class UpdateCourseKeySerializerTests(TestCase): serializer_class = UpdateCourseKeySerializer def setUp(self): super(UpdateCourseKeySerializerTests, self).setUp() self.course_run = CourseRunFactory() self.request = RequestFactory() self.user = UserFactory() self.request.user = self.user def get_expected_data(self): return { 'lms_course_id': self.course_run.lms_course_id, 'changed_by': self.user } def test_validation(self): self.course_run.lms_course_id = 'course-v1:edxTest+TC101+2016_Q1' self.course_run.save() # pylint: disable=no-member serializer = self.serializer_class(self.course_run) serializer.context['request'] = self.request expected = serializer.validate(serializer.data) self.assertEqual(self.get_expected_data(), expected) def test_validation_error(self): self.course_run.lms_course_id = 'wrong-course-id' self.course_run.save() # pylint: disable=no-member serializer = self.serializer_class(self.course_run) with self.assertRaises(ValidationError): serializer.validate(serializer.data)
def test_create_course_run_in_studio(self, mock_access_token): # pylint: disable=unused-argument organization = OrganizationFactory() partner = organization.partner start = datetime.datetime.utcnow() course_run_key = 'course-v1:TestX+Testing101x+1T2017' body = {'id': course_run_key} studio_url_root = partner.studio_url.strip('/') url = '{}/api/v1/course_runs/'.format(studio_url_root) responses.add(responses.POST, url, json=body, status=200) body = {'card_image': 'https://example.com/image.jpg'} url = '{root}/api/v1/course_runs/{course_run_key}/images/'.format( root=studio_url_root, course_run_key=course_run_key) responses.add(responses.POST, url, json=body, status=200) with mock.patch( 'course_discovery.apps.publisher.signals.logger.exception' ) as mock_logger: publisher_course_run = CourseRunFactory( start=start, lms_course_id=None, course__organizations=[organization]) # We refresh because the signal should update the instance with the course run key from Studio publisher_course_run.refresh_from_db() assert len(responses.calls) == 2 assert publisher_course_run.lms_course_id == course_run_key mock_logger.assert_called_with( 'Organization [%s] does not have an associated OrganizationExtension', organization.key, )
def test_create_course_run_in_studio_as_rerun(self, mock_access_token): # pylint: disable=unused-argument number = 'TestX' organization = OrganizationFactory() partner = organization.partner course_key = '{org}+{number}'.format(org=organization.key, number=number) discovery_course_run = DiscoveryCourseRunFactory( course__partner=partner, course__key=course_key) start = datetime.datetime.utcnow() course_run_key = 'course-v1:TestX+Testing101x+1T2017' body = {'id': course_run_key} studio_url_root = partner.studio_url.strip('/') url = '{root}/api/v1/course_runs/{course_run_key}/rerun/'.format( root=studio_url_root, course_run_key=discovery_course_run.key) responses.add(responses.POST, url, json=body, status=200) body = {'card_image': 'https://example.com/image.jpg'} url = '{root}/api/v1/course_runs/{course_run_key}/images/'.format( root=studio_url_root, course_run_key=course_run_key) responses.add(responses.POST, url, json=body, status=200) publisher_course_run = CourseRunFactory( start=start, lms_course_id=None, course__organizations=[organization], course__number=number) # We refresh because the signal should update the instance with the course run key from Studio publisher_course_run.refresh_from_db() assert len(responses.calls) == 2 assert publisher_course_run.lms_course_id == course_run_key
def test_create_course_run_in_studio_with_image_failure(self, __): organization = OrganizationFactory() OrganizationExtensionFactory(organization=organization) partner = organization.partner start = datetime.datetime.utcnow() course_run_key = 'course-v1:TestX+Testing101x+1T2017' body = {'id': course_run_key} studio_url_root = partner.studio_url.strip('/') url = '{}/api/v1/course_runs/'.format(studio_url_root) responses.add(responses.POST, url, json=body, status=200) course = CourseFactory(organizations=[organization]) with mock.patch('course_discovery.apps.api.utils.logger.exception' ) as mock_logger: publisher_course_run = CourseRunFactory( course=course, start=start, lms_course_id=None, ) assert mock_logger.call_count == 1 assert mock_logger.call_args_list[0] == mock.call( 'An error occurred while setting the course run image for [{key}] in studio. All other fields ' 'were successfully saved in Studio.'.format(key=course_run_key)) publisher_course_run.refresh_from_db() assert len(responses.calls) == 2 assert publisher_course_run.lms_course_id == course_run_key
class UpdateCourseKeySerializerTests(TestCase): serializer_class = UpdateCourseKeySerializer def setUp(self): super(UpdateCourseKeySerializerTests, self).setUp() self.course_run = CourseRunFactory() self.request = RequestFactory() self.user = UserFactory() self.request.user = self.user def get_expected_data(self): return { 'lms_course_id': self.course_run.lms_course_id, 'changed_by': self.user } def test_validation(self): self.course_run.lms_course_id = 'course-v1:edxTest+TC101+2016_Q1' self.course_run.save() # pylint: disable=no-member serializer = self.serializer_class(self.course_run, context={'request': self.request}) expected = serializer.validate(serializer.data) self.assertEqual(self.get_expected_data(), expected) def test_validation_error(self): self.course_run.lms_course_id = 'wrong-course-id' self.course_run.save() # pylint: disable=no-member serializer = self.serializer_class(self.course_run) with self.assertRaises(ValidationError): serializer.validate(serializer.data)
def setUp(self): super(CourseRunSerializerTests, self).setUp() self.course_run = CourseRunFactory() self.course_run.lms_course_id = 'course-v1:edX+DemoX+Demo_Course' self.request = RequestFactory() self.user = UserFactory() self.request.user = self.user self.course_state = CourseRunStateFactory(course_run=self.course_run, owner_role=PublisherUserRole.Publisher)
def setUp(self): super(CourseRunSerializerTests, self).setUp() self.course_run = CourseRunFactory() self.course_run.lms_course_id = 'course-v1:edX+DemoX+Demo_Course' self.course_run.external_key = 'testOrg-course-1' self.person = PersonFactory() self.discovery_course_run = DiscoveryCourseRunFactory( key=self.course_run.lms_course_id, external_key=self.course_run.external_key, staff=[self.person]) self.request = RequestFactory() self.user = UserFactory() self.request.user = self.user self.course_state = CourseRunStateFactory( course_run=self.course_run, owner_role=PublisherUserRole.Publisher)
def setUp(self): super(TestMigrateCommentsToSalesforce, self).setUp() self.partner = PartnerFactory() self.user_1 = UserFactory() self.org_1 = OrganizationFactory(partner=self.partner) self.course_1 = CourseFactory( partner=self.partner, authoring_organizations=[self.org_1], key=self.org_1.key + '+101x', title='Old Title', ) self.course_run_1 = CourseRunFactory( key='course-v1:{key}+1T2019'.format( key=self.course_1.key, ), course=self.course_1, ) self.publisher_course_1 = PublisherCourseFactory(number='101x', title='New Title') self.publisher_course_1.organizations.add(self.org_1) # pylint: disable=no-member self.publisher_course_run_1 = PublisherCourseRunFactory( course=self.publisher_course_1, lms_course_id='course-v1:{org}+{number}+1T2019'.format( org=self.org_1.key, number=self.publisher_course_1.number ), )
def test_create_course_run_in_studio_with_image_failure(self, __, ___): # pylint: disable=unused-argument organization = OrganizationFactory() partner = organization.partner start = datetime.datetime.utcnow() course_run_key = 'course-v1:TestX+Testing101x+1T2017' body = {'id': course_run_key} studio_url_root = partner.studio_url.strip('/') url = '{}/api/v1/course_runs/'.format(studio_url_root) responses.add(responses.POST, url, json=body, status=200) with mock.patch( 'course_discovery.apps.publisher.signals.logger.exception' ) as mock_logger: publisher_course_run = CourseRunFactory( start=start, lms_course_id=None, course__organizations=[organization]) assert len(responses.calls) == 1 assert publisher_course_run.lms_course_id == course_run_key mock_logger.assert_called_with( 'Failed to update Studio image for course run [%s]', course_run_key)
def test_create_course_run_in_studio_with_image_api_failure( self, mock_access_token): organization = OrganizationFactory() partner = organization.partner start = datetime.datetime.utcnow() course_run_key = 'course-v1:TestX+Testing101x+1T2017' body = {'id': course_run_key} studio_url_root = partner.studio_url.strip('/') url = '{}/api/v1/course_runs/'.format(studio_url_root) responses.add(responses.POST, url, json=body, status=200) body = {'error': 'Server error'} url = '{root}/api/v1/course_runs/{course_run_key}/images/'.format( root=studio_url_root, course_run_key=course_run_key) responses.add(responses.POST, url, json=body, status=500) with mock.patch( 'course_discovery.apps.publisher.signals.logger.exception' ) as mock_logger: publisher_course_run = CourseRunFactory( start=start, lms_course_id=None, course__organizations=[organization]) assert len(responses.calls) == 2 assert publisher_course_run.lms_course_id == course_run_key mock_logger.assert_called_with( 'Failed to update Studio image for course run [%s]: %s', course_run_key, json.dumps(body).encode('utf8'))
def test_create_course_run_error_with_discovery_run( self, mock_access_token): # pylint: disable=unused-argument """ Tests that course run creations raises exception and logs expected exception message """ number = 'TestX' organization = OrganizationFactory() partner = organization.partner course_key = '{org}+{number}'.format(org=organization.key, number=number) discovery_course_run = DiscoveryCourseRunFactory( course__partner=partner, course__key=course_key) body = {'error': 'Server error'} studio_url_root = partner.studio_url.strip('/') url = '{root}/api/v1/course_runs/{course_run_key}/rerun/'.format( root=studio_url_root, course_run_key=discovery_course_run.key) responses.add(responses.POST, url, json=body, status=500) with mock.patch( 'course_discovery.apps.publisher.signals.logger.exception' ) as mock_logger: with pytest.raises(HttpServerError): CourseRunFactory(lms_course_id=None, course__organizations=[organization], course__number=number) assert len(responses.calls) == 1 mock_logger.assert_called_with( 'Failed to create course run [%s] on Studio: %s', course_key, json.dumps(body).encode('utf8'))
def test_create_seat_masters_track(self, form_data): course_run = CourseRunFactory() form = SeatForm(data=form_data) seat = form.save(course_run=course_run) expected_masters = 'masters_track' in form_data and form_data[ 'masters_track'] self.assertEqual(expected_masters, seat.masters_track)
def test_update_course_run_image_in_studio_without_course_image(): publisher_course_run = CourseRunFactory(course__image=None) api = StudioAPI(None) with mock.patch('course_discovery.apps.api.utils.logger') as mock_logger: api.update_course_run_image_in_studio(publisher_course_run) mock_logger.warning.assert_called_with( 'Card image for course run [%d] cannot be updated. The related course [%d] has no image defined.', publisher_course_run.id, publisher_course_run.course.id)
def test_create_course_run_in_studio_without_partner(self): with mock.patch('course_discovery.apps.publisher.signals.logger.error' ) as mock_logger: publisher_course_run = CourseRunFactory(course__organizations=[]) assert publisher_course_run.course.partner is None mock_logger.assert_called_with( 'Failed to publish course run [%d] to Studio. Related course [%d] has no associated Partner.', publisher_course_run.id, publisher_course_run.course.id)
def test_generate_data_for_studio_api_without_team(): course_run = CourseRunFactory( course__organizations=[OrganizationFactory()]) with mock.patch( 'course_discovery.apps.api.utils.logger.warning') as mock_logger: assert_data_generated_correctly(course_run, []) mock_logger.assert_called_with( 'No course team admin specified for course [%s]. This may result in a Studio course run ' 'being created without a course team.', course_run.course.number)
def _create_course_run_for_publication(self): organization = OrganizationFactory() transcript_languages = [LanguageTag.objects.first()] mock_image_file = make_image_file('test_image.jpg') return CourseRunFactory(course__organizations=[organization], course__tertiary_subject=None, course__image__from_file=mock_image_file, lms_course_id='a/b/c', transcript_languages=transcript_languages, staff=PersonFactory.create_batch(2))
def test_generate_data_for_studio_api(): course_run = CourseRunFactory(course__organizations=[OrganizationFactory()]) course = course_run.course role = CourseUserRoleFactory(course=course, role=PublisherUserRole.CourseTeam) team = [ { 'user': role.user.username, 'role': 'instructor', }, ] assert_data_generated_correctly(course_run, team)
def test_successful_post(self): """Posting data to the comment post endpoint creates a comment.""" path = reverse('comments-post-comment') self.assertEqual(Comments.objects.count(), 0) course_run = CourseRunFactory() generated_data = self.generate_data(course_run) self.client.post(path, data=generated_data) self.assertEqual(Comments.objects.count(), 1) comment = Comments.objects.first() self.assertEqual(comment.user_name, generated_data['name']) self.assertEqual(comment.comment, generated_data['comment']) self.assertEqual(comment.user_email, generated_data['email'])
class CourseRunSerializerTests(TestCase): serializer_class = CourseRunSerializer def setUp(self): super(CourseRunSerializerTests, self).setUp() self.course_run = CourseRunFactory() self.course_run.lms_course_id = 'course-v1:edX+DemoX+Demo_Course' self.request = RequestFactory() self.user = UserFactory() self.request.user = self.user self.course_state = CourseRunStateFactory(course_run=self.course_run, owner_role=PublisherUserRole.Publisher) def get_expected_data(self): return { 'lms_course_id': self.course_run.lms_course_id, 'changed_by': self.user, 'preview_url': self.course_run.preview_url } def test_validate_lms_course_id(self): """ Verify that serializer raises error if 'lms_course_id' has invalid format. """ self.course_run.lms_course_id = 'invalid-course-id' self.course_run.save() # pylint: disable=no-member serializer = self.serializer_class(self.course_run) with self.assertRaises(ValidationError): serializer.validate_lms_course_id(self.course_run.lms_course_id) def test_validate_preview_url(self): """ Verify that serializer raises error if 'preview_url' has invalid format. """ self.course_run.preview_url = 'invalid-preview-url' self.course_run.save() # pylint: disable=no-member serializer = self.serializer_class(self.course_run) with self.assertRaises(ValidationError): serializer.validate_preview_url(self.course_run.preview_url) def test_serializer_with_valid_data(self): """ Verify that serializer validate course_run object. """ serializer = self.serializer_class(self.course_run, context={'request': self.request}) self.assertEqual(self.get_expected_data(), serializer.validate(serializer.data)) def test_update_preview_url(self): """ Verify that course 'owner_role' will be changed to course_team after updating course run with preview url. """ serializer = self.serializer_class(self.course_run) serializer.update(self.course_run, serializer.data) self.assertEqual(self.course_state.owner_role, PublisherUserRole.CourseTeam) def test_update_lms_course_id(self): """ Verify that 'changed_by' also updated after updating course_run's lms_course_id.""" self.course_run.preview_url = None self.course_run.save() serializer = self.serializer_class(self.course_run, context={'request': self.request}) serializer.update(self.course_run, serializer.validate(serializer.data)) self.assertEqual(self.course_run.lms_course_id, serializer.data['lms_course_id']) self.assertEqual(self.course_run.changed_by, self.user)
def test_create_course_run_in_studio_with_organization_opt_out(self): with mock.patch( 'course_discovery.apps.publisher.signals.logger.warning' ) as mock_logger: course_organization = OrganizationFactory() OrganizationExtensionFactory(organization=course_organization, auto_create_in_studio=False) publisher_course_run = CourseRunFactory( course__organizations=[course_organization]) mock_logger.assert_called_with( ('Course run [%d] will not be automatically created in studio.' 'Organization [%s] has opted out of this feature.'), publisher_course_run.course.id, course_organization.key, )
def test_calculate_course_run_key_run_value_with_multiple_runs_per_trimester(): number = 'TestX' organization = OrganizationFactory() partner = organization.partner course_key = '{org}+{number}'.format(org=organization.key, number=number) discovery_course = DiscoveryCourseFactory(partner=partner, key=course_key) DiscoveryCourseRunFactory(key='course-v1:TestX+Testing101x+1T2017', course=discovery_course) course_run = CourseRunFactory( start=datetime.datetime(2017, 2, 1), lms_course_id=None, course__organizations=[organization], course__number=number ) assert StudioAPI.calculate_course_run_key_run_value(course_run) == '1T2017a' DiscoveryCourseRunFactory(key='course-v1:TestX+Testing101x+1T2017a', course=discovery_course) assert StudioAPI.calculate_course_run_key_run_value(course_run) == '1T2017b'
def test_create_course_run_in_studio(self, mock_access_token): # pylint: disable=unused-argument organization = OrganizationFactory() partner = organization.partner start = datetime.datetime.utcnow() course_run_key = 'course-v1:TestX+Testing101x+1T2017' body = {'id': course_run_key} studio_url_root = partner.studio_url.strip('/') url = '{}/api/v1/course_runs/'.format(studio_url_root) responses.add(responses.POST, url, json=body, status=200) body = {'card_image': 'https://example.com/image.jpg'} url = '{root}/api/v1/course_runs/{course_run_key}/images/'.format( root=studio_url_root, course_run_key=course_run_key) responses.add(responses.POST, url, json=body, status=200) publisher_course_run = CourseRunFactory( start=start, lms_course_id=None, course__organizations=[organization]) assert len(responses.calls) == 2 assert publisher_course_run.lms_course_id == course_run_key
def test_create_course_run_in_studio_with_api_failure( self, mock_access_token): # pylint: disable=unused-argument organization = OrganizationFactory() partner = organization.partner body = {'error': 'Server error'} studio_url_root = partner.studio_url.strip('/') url = '{}/api/v1/course_runs/'.format(studio_url_root) responses.add(responses.POST, url, json=body, status=500) with mock.patch( 'course_discovery.apps.publisher.signals.logger.exception' ) as mock_logger: with pytest.raises(HttpServerError): publisher_course_run = CourseRunFactory( lms_course_id=None, course__organizations=[organization]) assert len(responses.calls) == 1 assert publisher_course_run.lms_course_id is None mock_logger.assert_called_with( 'Failed to create course run [%d] on Studio: %s', publisher_course_run.id, json.dumps(body).encode('utf8'))
def setUp(self): super(UpdateCourseKeySerializerTests, self).setUp() self.course_run = CourseRunFactory() self.request = RequestFactory() self.user = UserFactory() self.request.user = self.user
def test_calculate_course_run_key_run_value(month, expected): course_run = CourseRunFactory(start=datetime.datetime(2017, month, 1)) assert StudioAPI.calculate_course_run_key_run_value(course_run) == expected
class TestMigrateCommentsToSalesforce(TestCase): LOGGER_PATH = 'course_discovery.apps.course_metadata.management.commands.migrate_comments_to_salesforce.logger' def setUp(self): super(TestMigrateCommentsToSalesforce, self).setUp() self.partner = PartnerFactory() self.user_1 = UserFactory() self.org_1 = OrganizationFactory(partner=self.partner) self.course_1 = CourseFactory( partner=self.partner, authoring_organizations=[self.org_1], key=self.org_1.key + '+101x', title='Old Title', ) self.course_run_1 = CourseRunFactory( key='course-v1:{key}+1T2019'.format( key=self.course_1.key, ), course=self.course_1, ) self.publisher_course_1 = PublisherCourseFactory(number='101x', title='New Title') self.publisher_course_1.organizations.add(self.org_1) # pylint: disable=no-member self.publisher_course_run_1 = PublisherCourseRunFactory( course=self.publisher_course_1, lms_course_id='course-v1:{org}+{number}+1T2019'.format( org=self.org_1.key, number=self.publisher_course_1.number ), ) def tearDown(self): super(TestMigrateCommentsToSalesforce, self).tearDown() # Zero out the instances that are created during testing SalesforceUtil.instances = {} @mock.patch(LOGGER_PATH) def test_handle_no_orgs(self, mock_logger): config = MigrateCommentsToSalesforceFactory() config.orgs.all().delete() with self.assertRaises(CommandError): Command().handle() mock_logger.error.assert_called_with( 'No organizations were defined. Please add organizations to the MigrateCommentsToSalesforce model.' ) @mock.patch(LOGGER_PATH) def test_handle_no_partner(self, mock_logger): config = MigrateCommentsToSalesforceFactory() config.orgs.add(self.org_1) with self.assertRaises(CommandError): Command().handle() mock_logger.error.assert_called_with( 'No partner was defined. Please add a partner to the MigrateCommentsToSalesforce model.' ) @mock.patch(LOGGER_PATH) def test_handle_no_salesforce_configuration(self, mock_logger): config = MigrateCommentsToSalesforceFactory(partner=self.partner) config.orgs.add(self.org_1) with self.assertRaises(CommandError): Command().handle() mock_logger.error.assert_called_with( 'Salesforce configuration for {} does not exist'.format(self.partner.name) ) @mock.patch('course_discovery.apps.course_metadata.salesforce.Salesforce') def test_handle_without_publisher_course_run(self, mock_salesforce): config = MigrateCommentsToSalesforceFactory(partner=self.partner) config.orgs.add(self.org_1) SalesforceConfigurationFactory(partner=self.partner) self.publisher_course_run_1.delete() # Set return values for all of the Salesforce methods that get called mock_salesforce().Publisher_Organization__c.create.return_value = {'id': 'SomePubOrgId'} mock_salesforce().Course__c.create.return_value = {'id': 'SomeCourseId'} mock_salesforce().Case.create.return_value = {'id': 'SomeCaseId'} mock_salesforce().Course_Run__c.create.return_value = {'id': 'SomeCourseRunId'} with mock.patch(self.LOGGER_PATH) as mock_logger: Command().handle() calls = [ mock.call('No PublisherCourseRun found for {}.'.format(self.course_run_1.key)), mock.call('No PublisherCourses found for {}'.format(self.course_1.key)) ] mock_logger.warning.assert_has_calls(calls, any_order=True) @mock.patch('course_discovery.apps.course_metadata.salesforce.Salesforce') def test_handle_with_comments(self, mock_salesforce): config = MigrateCommentsToSalesforceFactory(partner=self.partner) config.orgs.add(self.org_1) SalesforceConfigurationFactory(partner=self.partner) course_comment = CommentFactory( user=self.user_1, content_type_id=ContentType.objects.get_for_model(PublisherCourse), object_pk=self.publisher_course_1.id, ) course_comment.content_type_id = ContentType.objects.get_for_model(PublisherCourse) course_comment.object_pk = self.publisher_course_1.id course_comment.save() course_run_comment = CommentFactory( user=self.user_1, content_type_id=ContentType.objects.get_for_model(PublisherCourseRun), object_pk=self.publisher_course_run_1.id, ) course_run_comment.content_type_id = ContentType.objects.get_for_model(PublisherCourseRun) course_run_comment.object_pk = self.publisher_course_run_1.id course_run_comment.save() # Set return values for all of the Salesforce methods that get called mock_salesforce().Publisher_Organization__c.create.return_value = {'id': 'SomePubOrgId'} mock_salesforce().Course__c.create.return_value = {'id': 'SomeCourseId'} mock_salesforce().Case.create.return_value = {'id': 'SomeCaseId'} mock_salesforce().Course_Run__c.create.return_value = {'id': 'SomeCourseRunId'} with mock.patch(self.LOGGER_PATH) as mock_logger: Command().handle() self.org_1.refresh_from_db() self.course_1.refresh_from_db() self.course_run_1.refresh_from_db() self.assertEqual(self.org_1.salesforce_id, 'SomePubOrgId') self.assertEqual(self.course_1.salesforce_id, 'SomeCourseId') self.assertEqual(self.course_1.salesforce_case_id, 'SomeCaseId') self.assertEqual(self.course_run_1.salesforce_id, 'SomeCourseRunId') mock_logger.info.assert_called_with('Inserted 2 comments for {}'.format(self.course_1.title))
class CourseRunSerializerTests(TestCase): serializer_class = CourseRunSerializer def setUp(self): super(CourseRunSerializerTests, self).setUp() self.course_run = CourseRunFactory() self.course_run.lms_course_id = 'course-v1:edX+DemoX+Demo_Course' self.person = PersonFactory() self.discovery_course_run = DiscoveryCourseRunFactory( key=self.course_run.lms_course_id, staff=[self.person]) self.request = RequestFactory() self.user = UserFactory() self.request.user = self.user self.course_state = CourseRunStateFactory( course_run=self.course_run, owner_role=PublisherUserRole.Publisher) def get_expected_data(self): """ Helper method which will return expected serialize data. """ return { 'lms_course_id': self.course_run.lms_course_id, 'changed_by': self.user, 'preview_url': self.course_run.preview_url } def test_validate_lms_course_id(self): """ Verify that serializer raises error if 'lms_course_id' has invalid format. """ self.course_run.lms_course_id = 'invalid-course-id' self.course_run.save() serializer = self.serializer_class(self.course_run) with self.assertRaises(ValidationError): serializer.validate_lms_course_id(self.course_run.lms_course_id) def test_validate_preview_url(self): """ Verify that serializer raises error if 'preview_url' has invalid format. """ serializer = self.serializer_class(self.course_run, context={'request': self.request}) with self.assertRaises(ValidationError): serializer.validate({'preview_url': 'invalid-url'}) def test_serializer_with_valid_data(self): """ Verify that serializer validate course_run object. """ serializer = self.serializer_class(self.course_run, context={'request': self.request}) self.assertEqual(self.get_expected_data(), serializer.validate(serializer.data)) def test_update_preview_url(self): """ Verify that course 'owner_role' will be changed to course_team after updating course run with preview url. """ self.discovery_course_run.slug = '' self.discovery_course_run.save(suppress_publication=True) serializer = self.serializer_class(self.course_run, context={'request': self.request}) serializer.update(self.course_run, {'preview_url': 'https://example.com/abc/course'}) self.assertEqual(self.course_state.owner_role, PublisherUserRole.CourseTeam) self.assertEqual( self.course_run.preview_url.rsplit('/', 1)[-1], 'course') def test_update_preview_url_no_op(self): """ Verify we don't push to marketing if no change required """ self.discovery_course_run.slug = '' self.discovery_course_run.save(suppress_publication=True) toggle_switch('publish_course_runs_to_marketing_site') serializer = self.serializer_class(self.course_run, context={'request': self.request}) mock_path = 'course_discovery.apps.course_metadata.publishers.CourseRunMarketingSitePublisher.publish_obj' with mock.patch(mock_path) as mock_save: serializer.update( self.course_run, {'preview_url': 'https://example.com/abc/course'}) self.assertEqual(mock_save.call_count, 1) # Now when we update a second time, there should be nothing to do, call count should remain at 1 serializer.update( self.course_run, {'preview_url': 'https://example.com/abc/course'}) self.assertEqual(mock_save.call_count, 1) def test_update_preview_url_slug_exists(self): """ Verify we don't push to marketing if no change required """ DiscoveryCourseRunFactory( title='course') # will create the slug 'course' serializer = self.serializer_class(self.course_run, context={'request': self.request}) with self.assertRaises(Exception) as cm: serializer.update( self.course_run, {'preview_url': 'https://example.com/abc/course'}) self.assertEqual(cm.exception.args[0], 'Preview URL already in use for another course') def test_update_lms_course_id(self): """ Verify that 'changed_by' also updated after updating course_run's lms_course_id.""" serializer = self.serializer_class(self.course_run, context={'request': self.request}) serializer.update(self.course_run, serializer.validate(serializer.data)) self.assertEqual(self.course_run.lms_course_id, serializer.data['lms_course_id']) self.assertEqual(self.course_run.changed_by, self.user) def test_update_with_transaction_rollback(self): """ Verify that transaction roll backed if an error occurred. """ serializer = self.serializer_class(self.course_run) with self.assertRaises(Exception): serializer.update(self.course_run, {'preview_url': 'invalid_url'}) self.assertFalse(self.course_run.preview_url) def test_transaction_roll_back_with_error_on_email(self): """ Verify that transaction is roll backed if error occurred during email sending. """ toggle_switch('enable_publisher_email_notifications', True) serializer = self.serializer_class(self.course_run) self.assertEqual(self.course_run.course_run_state.owner_role, PublisherUserRole.Publisher) with self.assertRaises(Exception): serializer.update( self.course_run, {'preview_url': 'https://example.com/abc/course'}) self.course_run = CourseRun.objects.get(id=self.course_run.id) self.assertFalse(self.course_run.preview_url) # Verify that owner role not changed. self.assertEqual(self.course_run.course_run_state.owner_role, PublisherUserRole.Publisher)
class CourseRunSerializerTests(TestCase): serializer_class = CourseRunSerializer def setUp(self): super(CourseRunSerializerTests, self).setUp() self.course_run = CourseRunFactory() self.course_run.lms_course_id = 'course-v1:edX+DemoX+Demo_Course' self.request = RequestFactory() self.user = UserFactory() self.request.user = self.user self.course_state = CourseRunStateFactory( course_run=self.course_run, owner_role=PublisherUserRole.Publisher) def get_expected_data(self): """ Helper method which will return expected serialize data. """ return { 'lms_course_id': self.course_run.lms_course_id, 'changed_by': self.user, 'preview_url': self.course_run.preview_url } def test_validate_lms_course_id(self): """ Verify that serializer raises error if 'lms_course_id' has invalid format. """ self.course_run.lms_course_id = 'invalid-course-id' self.course_run.save() # pylint: disable=no-member serializer = self.serializer_class(self.course_run) with self.assertRaises(ValidationError): serializer.validate_lms_course_id(self.course_run.lms_course_id) def test_validate_preview_url(self): """ Verify that serializer raises error if 'preview_url' has invalid format. """ self.course_run.preview_url = 'invalid-preview-url' self.course_run.save() # pylint: disable=no-member serializer = self.serializer_class(self.course_run) with self.assertRaises(ValidationError): serializer.validate_preview_url(self.course_run.preview_url) def test_serializer_with_valid_data(self): """ Verify that serializer validate course_run object. """ serializer = self.serializer_class(self.course_run, context={'request': self.request}) self.assertEqual(self.get_expected_data(), serializer.validate(serializer.data)) def test_update_preview_url(self): """ Verify that course 'owner_role' will be changed to course_team after updating course run with preview url. """ self.course_run.preview_url = '' self.course_run.save() serializer = self.serializer_class(self.course_run) serializer.update(self.course_run, {'preview_url': 'https://example.com/abc/course'}) self.assertEqual(self.course_state.owner_role, PublisherUserRole.CourseTeam) self.assertEqual(self.course_run.preview_url, serializer.data['preview_url']) def test_update_lms_course_id(self): """ Verify that 'changed_by' also updated after updating course_run's lms_course_id.""" self.course_run.preview_url = None self.course_run.save() serializer = self.serializer_class(self.course_run, context={'request': self.request}) serializer.update(self.course_run, serializer.validate(serializer.data)) self.assertEqual(self.course_run.lms_course_id, serializer.data['lms_course_id']) self.assertEqual(self.course_run.changed_by, self.user) def test_update_with_transaction_rollback(self): """ Verify that transaction roll backed if an error occurred. """ self.course_run.preview_url = '' self.course_run.save() serializer = self.serializer_class(self.course_run) with self.assertRaises(Exception): serializer.update(self.course_run, {'preview_url': 'invalid_url'}) self.assertFalse(self.course_run.preview_url) def test_transaction_roll_back_with_error_on_email(self): """ Verify that transaction is roll backed if error occurred during email sending. """ toggle_switch('enable_publisher_email_notifications', True) self.course_run.preview_url = '' self.course_run.save() serializer = self.serializer_class(self.course_run) self.assertEqual(self.course_run.course_run_state.owner_role, PublisherUserRole.Publisher) with self.assertRaises(Exception): serializer.update( self.course_run, {'preview_url': 'https://example.com/abc/course'}) self.course_run = CourseRun.objects.get(id=self.course_run.id) self.assertFalse(self.course_run.preview_url) # Verify that owner role not changed. self.assertEqual(self.course_run.course_run_state.owner_role, PublisherUserRole.Publisher)