def test_create_case_for_course_salesforce_case_id_not_set_salesforce_id_set( self): course = CourseFactoryNoSignals( partner=self.salesforce_config.partner, salesforce_id='TestSalesforceId', ) official_course = CourseFactoryNoSignals( partner=self.salesforce_config.partner, draft_version=course, ) return_value = {'id': 'SomeSalesforceId'} with mock.patch(self.salesforce_path) as mock_salesforce: mock_salesforce().Case.create.return_value = return_value util = SalesforceUtil(self.salesforce_config.partner) util.create_case_for_course(course) mock_salesforce().Case.create.assert_called_with({ 'Course__c': course.salesforce_id, 'Status': 'Open', 'Origin': 'Publisher', 'Subject': '{} Comments'.format(course.title), 'Description': 'This case is required to be Open for the Publisher comment service.', 'RecordTypeId': self.salesforce_config.case_record_type_id, }) assert course.salesforce_case_id == return_value.get('id') assert official_course.salesforce_case_id == return_value.get('id')
def setUp(self): super().setUp() self.salesforce_config = SalesforceConfigurationFactory(partner=self.partner) self.user = UserFactory(is_staff=True) self.request.user = self.user self.request.site.partner = self.partner self.client.login(username=self.user.username, password=USER_PASSWORD) self.course = CourseFactoryNoSignals(partner=self.partner, title='Fake Test', key='edX+Fake101', draft=True) self.org = OrganizationFactoryNoSignals(key='edX', partner=self.partner) self.course.authoring_organizations.add(self.org)
def test_wrapper_salesforce_os_error_calls_login(self): """ Tests the wrapper when an OSError exception is thrown. The first exception thrown will trigger a re-login, the second will throw an exception as we don't want infinite retries. """ course = CourseFactoryNoSignals( partner=self.salesforce_config.partner, salesforce_id='TestSalesforceId', salesforce_case_id='TestSalesforceCaseId', ) with mock.patch(self.salesforce_util_path + '._query', side_effect=OSError('Test Error')): with mock.patch(self.salesforce_path) as mock_salesforce: with mock.patch( 'course_discovery.apps.course_metadata.salesforce.logger' ) as mock_logger: util = SalesforceUtil(self.salesforce_config.partner) # Any method that has the decorator with pytest.raises(OSError): util.get_comments_for_course(course) # 2 calls, one for initialization, one for login before exception assert len(mock_salesforce.call_args_list) == 2 mock_logger.warning.assert_called_with( 'An OSError occurred while attempting to call get_comments_for_course for {}' .format(str(course)))
def test_create_comment_for_course_case_salesforce_case_id_not_set(self): create_case_path = self.salesforce_util_path + '.create_case_for_course' course = CourseFactoryNoSignals(partner=self.salesforce_config.partner, salesforce_id='TestSalesforceId') user = UserFactory() body = 'Test body' # Need to modify state of the instance passed in def new_create_course_case(self, instance): # pylint: disable=unused-argument instance.salesforce_case_id = 'SomeSalesforceId' instance.save() with mock.patch(self.salesforce_path) as mock_salesforce: with mock.patch(create_case_path, new=new_create_course_case): util = SalesforceUtil(self.salesforce_config.partner) assert course.salesforce_case_id is None util.create_comment_for_course_case(course, user, body) mock_salesforce().FeedItem.create.assert_called_with({ 'ParentId': course.salesforce_case_id, 'Body': util.format_user_comment_body(user, body, None) }) assert course.salesforce_case_id is not None
def test_create_course_run_salesforce_id_not_set(self): course = CourseFactoryNoSignals(partner=self.salesforce_config.partner, salesforce_id='TestSalesforceId') course_run = CourseRunFactoryNoSignals(course=course) partner = self.salesforce_config.partner return_value = {'id': 'SomeSalesforceId'} with mock.patch(self.salesforce_path) as mock_salesforce: mock_salesforce().Course_Run__c.create.return_value = return_value util = SalesforceUtil(self.salesforce_config.partner) util.create_course_run(course_run) mock_salesforce().Course_Run__c.create.assert_called_with({ 'Course__c': course_run.course.salesforce_id, 'Link_to_Admin_Portal__c': '{url}/admin/course_metadata/courserun/{id}/change/'.format( url=partner.site.domain.strip('/'), id=course_run.id), 'Course_Start_Date__c': course_run.start.isoformat(), 'Course_End_Date__c': course_run.end.isoformat(), 'Publisher_Status__c': 'Live', # Expected return value from _get_equivalent_status 'Course_Run_Name__c': course_run.title, 'Expected_Go_Live_Date__c': None, 'Course_Number__c': course_run.key, # Expected return value from _get_equivalent_ofac_review_decision 'OFAC_Review_Decision__c': 'OFAC Enabled', }) assert course_run.salesforce_id == return_value.get('id')
def test_create_course_salesforce_id_not_set(self): course = CourseFactoryNoSignals(partner=self.salesforce_config.partner) organization = OrganizationFactoryNoSignals( key='edX', partner=self.salesforce_config.partner, salesforce_id='Test') course.authoring_organizations.add(organization) partner = self.salesforce_config.partner return_value = {'id': 'SomeSalesforceId'} with mock.patch(self.salesforce_path) as mock_salesforce: mock_salesforce().Course__c.create.return_value = return_value util = SalesforceUtil(self.salesforce_config.partner) util.create_course(course) mock_salesforce().Course__c.create.assert_called_with({ 'Course_Name__c': course.title, 'Link_to_Publisher__c': '{url}/courses/{uuid}'.format( url=partner.publisher_url.strip('/'), uuid=course.uuid) if partner.publisher_url else None, 'Link_to_Admin_Portal__c': '{url}/admin/course_metadata/course/{id}/change/'.format( url=partner.site.domain.strip('/'), id=course.id) if partner.site.domain else None, 'Course_Key__c': course.key, 'Publisher_Organization__c': organization.salesforce_id, }) assert course.salesforce_id == return_value.get('id')
def test_wrapper_salesforce_expired_session_calls_login(self): """ Tests the wrapper when a SalesforceExpiredSession is thrown. The first exception thrown will trigger a re-login, the second will throw an exception as we don't want infinite retries. """ course = CourseFactoryNoSignals( partner=self.salesforce_config.partner, salesforce_id='TestSalesforceId', salesforce_case_id='TestSalesforceCaseId', ) with mock.patch(self.salesforce_util_path + '._query', side_effect=SalesforceExpiredSession( url='Test', status=401, resource_name='Test', content='Test')): with mock.patch(self.salesforce_path) as mock_salesforce: util = SalesforceUtil(self.salesforce_config.partner) # Any method that has the decorator with pytest.raises(SalesforceExpiredSession): util.get_comments_for_course(course) # 2 calls, one for initialization, one for login before exception assert len(mock_salesforce.call_args_list) == 2
def test_get_comments_for_course_case_id_not_set(self): course = CourseFactoryNoSignals(partner=self.salesforce_config.partner, salesforce_id='TestSalesforceId') with mock.patch(self.salesforce_path): util = SalesforceUtil(self.salesforce_config.partner) comments = util.get_comments_for_course(course) assert comments == []
def test_create_case_for_course_salesforce_case_id_set(self): course = CourseFactoryNoSignals(partner=self.salesforce_config.partner, salesforce_case_id='Test') with mock.patch(self.salesforce_path) as mock_salesforce: util = SalesforceUtil(self.salesforce_config.partner) util.create_case_for_course(course) mock_salesforce().Case.create.assert_not_called()
def test_create_course_run_salesforce_id_set(self): course = CourseFactoryNoSignals(partner=self.salesforce_config.partner, salesforce_id='Test') course_run = CourseRunFactoryNoSignals(course=course, salesforce_id='Test') with mock.patch(self.salesforce_path) as mock_salesforce: util = SalesforceUtil(self.salesforce_config.partner) util.create_course_run(course_run) mock_salesforce().Course_Run__c.create.assert_not_called()
def test_wrapper_salesforce_without_client_raises_not_configured_exception( self): """ Tests the wrapper when no config is found but a query is run """ course = CourseFactoryNoSignals( partner=self.salesforce_config.partner, salesforce_id='TestSalesforceId', salesforce_case_id='TestSalesforceCaseId', ) with mock.patch(self.salesforce_path): util = SalesforceUtil(self.salesforce_config.partner) SalesforceUtil.instances[ self.salesforce_config.partner].client = None # Any method that has the decorator with pytest.raises(SalesforceNotConfiguredException): util.get_comments_for_course(course)
def test_create_course_run_course_salesforce_id_not_set(self): create_course_path = self.salesforce_util_path + '.create_course' course = CourseFactoryNoSignals(partner=self.salesforce_config.partner) course_run = CourseRunFactoryNoSignals(course=course) partner = self.salesforce_config.partner return_value = {'id': 'SomeSalesforceId'} # Need to modify state of the instance passed in def new_create_course(self, instance): # pylint: disable=unused-argument instance.salesforce_id = 'SomeSalesforceId' instance.save() with mock.patch(self.salesforce_path) as mock_salesforce: with mock.patch(create_course_path, new=new_create_course): mock_salesforce( ).Course_Run__c.create.return_value = return_value util = SalesforceUtil(self.salesforce_config.partner) assert course.salesforce_id is None util.create_course_run(course_run) mock_salesforce().Course_Run__c.create.assert_called_with({ 'Course__c': course_run.course.salesforce_id, 'Link_to_Admin_Portal__c': '{url}/admin/course_metadata/courserun/{id}/change/'. format(url=partner.site, id=course_run.id), 'Course_Start_Date__c': course_run.start.isoformat(), 'Course_End_Date__c': course_run.end.isoformat(), 'Publisher_Status__c': 'Live', # Expected return value from _get_equivalent_status 'Course_Run_Name__c': course_run.title, 'Expected_Go_Live_Date__c': None, 'Course_Number__c': course_run.key, # Expected return value from _get_equivalent_ofac_review_decision 'OFAC_Review_Decision__c': 'OFAC Enabled', }) assert course.salesforce_id is not None assert course_run.salesforce_id == return_value.get('id')
def test_create_course_organization_salesforce_id_not_set(self): create_pub_org_path = (self.salesforce_util_path + '.create_publisher_organization') course = CourseFactoryNoSignals(partner=self.salesforce_config.partner) organization = OrganizationFactoryNoSignals( key='edX', partner=self.salesforce_config.partner) course.authoring_organizations.add(organization) partner = self.salesforce_config.partner return_value = {'id': 'SomeSalesforceId'} # Need to modify state of the instance passed in def new_create_organization(self, instance): # pylint: disable=unused-argument instance.salesforce_id = 'SomeSalesforceId' instance.save() with mock.patch(self.salesforce_path) as mock_salesforce: with mock.patch(create_pub_org_path, new=new_create_organization): mock_salesforce().Course__c.create.return_value = return_value util = SalesforceUtil(self.salesforce_config.partner) assert organization.salesforce_id is None util.create_course(course) organization.refresh_from_db() mock_salesforce().Course__c.create.assert_called_with({ 'Course_Name__c': course.title, 'Link_to_Publisher__c': '{url}/courses/{uuid}'.format( url=partner.publisher_url.strip('/'), uuid=course.uuid) if partner.publisher_url else None, 'Link_to_Admin_Portal__c': '{url}/admin/course_metadata/course/{id}/change/'.format( url=partner.site.domain.strip('/'), id=course.id) if partner.site.domain else None, 'Course_Key__c': course.key, 'Publisher_Organization__c': organization.salesforce_id, }) assert organization.salesforce_id is not None assert course.salesforce_id == return_value.get('id')
def test_create_comment_for_course_case_raises_exceptions(self): create_case_path = self.salesforce_util_path + '.create_case_for_course' course = CourseFactoryNoSignals(partner=self.salesforce_config.partner, salesforce_id='TestSalesforceId') user = UserFactory() body = 'Test body' # Need to modify state of the instance passed in and make it None to simulate an error def new_create_course_case(self, instance): # pylint: disable=unused-argument instance.salesforce_case_id = None instance.save() with mock.patch(self.salesforce_path): with mock.patch(create_case_path, new=new_create_course_case): util = SalesforceUtil(self.salesforce_config.partner) assert course.salesforce_case_id is None with pytest.raises(SalesforceMissingCaseException): util.create_comment_for_course_case(course, user, body)
def test_create_comment_for_course_case_salesforce_case_id_set(self): create_case_path = self.salesforce_util_path + '.create_case_for_course' course = CourseFactoryNoSignals(partner=self.salesforce_config.partner, salesforce_case_id='TestSalesforceId') user = UserFactory() body = 'Test body' with mock.patch(self.salesforce_path) as mock_salesforce: with mock.patch(create_case_path) as mock_create_case_for_course: util = SalesforceUtil(self.salesforce_config.partner) util.create_comment_for_course_case(course, user, body) mock_salesforce().FeedItem.create.assert_called_with({ 'ParentId': course.salesforce_case_id, 'Body': util.format_user_comment_body(user, body, None) }) mock_create_case_for_course.assert_not_called()
def test_create_case_for_course_salesforce_case_id_not_set_salesforce_id_not_set( self): create_course_path = self.salesforce_util_path + '.create_course' self.salesforce_config.case_record_type_id = 'TestId' course = CourseFactoryNoSignals(partner=self.salesforce_config.partner) return_value = {'id': 'SomeSalesforceId'} # Need to modify state of the instance passed in def new_create_course(self, instance): # pylint: disable=unused-argument instance.salesforce_id = 'SomeSalesforceId' instance.save() with mock.patch(self.salesforce_path) as mock_salesforce: with mock.patch(create_course_path, new=new_create_course): mock_salesforce().Case.create.return_value = return_value util = SalesforceUtil(self.salesforce_config.partner) self.assertIsNone(course.salesforce_id) util.create_case_for_course(course) mock_salesforce().Case.create.assert_called_with({ 'Course__c': course.salesforce_id, 'Status': 'Open', 'Origin': 'Publisher', 'Subject': '{} Comments'.format(course.title), 'Description': 'This case is required to be Open for the Publisher comment service.', 'RecordTypeId': self.salesforce_config.case_record_type_id, }) self.assertIsNotNone(course.salesforce_id) self.assertEqual(course.salesforce_case_id, return_value.get('id'))
class CommentViewSetTests(OAuth2Mixin, APITestCase): @factory.django.mute_signals(m2m_changed) def setUp(self): super().setUp() self.salesforce_config = SalesforceConfigurationFactory(partner=self.partner) self.user = UserFactory(is_staff=True) self.request.user = self.user self.request.site.partner = self.partner self.client.login(username=self.user.username, password=USER_PASSWORD) self.course = CourseFactoryNoSignals(partner=self.partner, title='Fake Test', key='edX+Fake101', draft=True) self.org = OrganizationFactoryNoSignals(key='edX', partner=self.partner) self.course.authoring_organizations.add(self.org) def tearDown(self): super().tearDown() # Zero out the instances that are created during testing SalesforceUtil.instances = {} def test_list_no_salesforce_case_id_set(self): user_orgs_path = 'course_discovery.apps.course_metadata.models.Organization.user_organizations' with mock.patch('course_discovery.apps.course_metadata.salesforce.Salesforce'): with mock.patch(user_orgs_path, return_value=[self.org]): url = '{}?course_uuid={}'.format(reverse('api:v1:comment-list'), self.course.uuid) response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertEqual(response.data, []) def test_list_salesforce_case_id_set(self): self.course.salesforce_id = 'TestSalesforceId' with factory.django.mute_signals(post_save): self.course.save() salesforce_path = 'course_discovery.apps.course_metadata.salesforce.Salesforce' get_comments_path = 'course_discovery.apps.api.v1.views.comments.SalesforceUtil.get_comments_for_course' user_orgs_path = 'course_discovery.apps.course_metadata.models.Organization.user_organizations' return_value = [ { 'user': { 'first_name': 'TestFirst', 'last_name': 'TestLast', 'email': '*****@*****.**', 'username': '******', }, 'course_run_key': None, 'created': '2000-01-01T00:00:00.000+0000', 'comment': 'This is a test comment', } ] with mock.patch(salesforce_path): with mock.patch(user_orgs_path, return_value=[self.org]): with mock.patch(get_comments_path, return_value=return_value) as mock_get_comments: url = '{}?course_uuid={}'.format(reverse('api:v1:comment-list'), self.course.uuid) response = self.client.get(url) mock_get_comments.assert_called_with(self.course) self.assertEqual(response.status_code, 200) self.assertEqual(response.data, return_value) def test_list_400s_without_course_uuid(self): with mock.patch('course_discovery.apps.course_metadata.salesforce.Salesforce'): url = reverse('api:v1:comment-list') response = self.client.get(url) self.assertEqual(response.status_code, 400) def test_list_404s_without_finding_course(self): fake_uuid = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' # Needs to resemble a uuid to pass validation with mock.patch('course_discovery.apps.course_metadata.salesforce.Salesforce'): url = '{}?course_uuid={}'.format(reverse('api:v1:comment-list'), fake_uuid) response = self.client.get(url) self.assertEqual(response.status_code, 404) def test_list_403s_without_permissions(self): salesforce_path = 'course_discovery.apps.course_metadata.salesforce.Salesforce' user_orgs_path = 'course_discovery.apps.course_metadata.models.Organization.user_organizations' self.user.is_staff = False self.user.save() with mock.patch(salesforce_path): with mock.patch(user_orgs_path, return_value=[]): url = '{}?course_uuid={}'.format(reverse('api:v1:comment-list'), self.course.uuid) response = self.client.get(url) self.assertEqual(response.status_code, 403) def test_list_200s_as_staff(self): salesforce_path = 'course_discovery.apps.course_metadata.salesforce.Salesforce' user_orgs_path = 'course_discovery.apps.course_metadata.models.Organization.user_organizations' with mock.patch(salesforce_path): with mock.patch(user_orgs_path, return_value=[]): url = '{}?course_uuid={}'.format(reverse('api:v1:comment-list'), self.course.uuid) response = self.client.get(url) self.assertEqual(response.status_code, 200) def test_create(self): body = { 'course_uuid': self.course.uuid, 'comment': 'Test comment', 'course_run_key': 'test-key', } salesforce_path = 'course_discovery.apps.course_metadata.salesforce.Salesforce' create_comment_path = ('course_discovery.apps.api.v1.views.comments.' 'SalesforceUtil.create_comment_for_course_case') with mock.patch(salesforce_path): with mock.patch(create_comment_path, return_value={ 'user': { 'username': self.user.username, 'email': self.user.email, 'first_name': self.user.first_name, 'last_name': self.user.last_name, }, 'comment': 'Comment body', 'created': datetime.datetime.now(datetime.timezone.utc).isoformat(), }) as mock_create_comment: url = reverse('api:v1:comment-list') response = self.client.post(url, body, format='json') mock_create_comment.assert_called_with( self.course, self.request.user, body.get('comment'), course_run_key=body.get('course_run_key'), ) self.assertEqual(response.status_code, 201) def test_create_400s_without_data(self): body = {} salesforce_path = 'course_discovery.apps.course_metadata.salesforce.Salesforce' with mock.patch(salesforce_path): url = reverse('api:v1:comment-list') response = self.client.post(url, body, format='json') self.assertEqual(response.status_code, 400) def test_create_403s_without_permissions(self): body = { 'course_uuid': self.course.uuid, 'comment': 'Test comment', 'course_run_key': 'test-key', } salesforce_path = 'course_discovery.apps.course_metadata.salesforce.Salesforce' is_editable_path = 'course_discovery.apps.api.v1.views.comments.CourseEditor.is_course_editable' with mock.patch(salesforce_path): with mock.patch(is_editable_path, return_value=False): url = reverse('api:v1:comment-list') response = self.client.post(url, body, format='json') self.assertEqual(response.status_code, 403) def test_create_404s_without_finding_course(self): body = { 'course_uuid': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', # Needs to resemble a uuid to pass validation 'comment': 'Test comment', 'course_run_key': 'test-key', } salesforce_path = 'course_discovery.apps.course_metadata.salesforce.Salesforce' with mock.patch(salesforce_path): url = reverse('api:v1:comment-list') response = self.client.post(url, body, format='json') self.assertEqual(response.status_code, 404) def test_create_404s_without_a_config(self): body = { 'course_uuid': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', # Needs to resemble a uuid to pass validation 'comment': 'Test comment', 'course_run_key': 'test-key', } salesforce_path = 'course_discovery.apps.course_metadata.salesforce.Salesforce' with mock.patch(salesforce_path): url = reverse('api:v1:comment-list') response = self.client.post(url, body, format='json') self.assertEqual(response.status_code, 404) def test_create_500s_without_a_successful_case_create(self): body = { 'course_uuid': self.course.uuid, 'comment': 'Test comment', 'course_run_key': 'test-key', } salesforce_path = 'course_discovery.apps.course_metadata.salesforce.Salesforce' create_comment_path = ('course_discovery.apps.api.v1.views.comments.' 'SalesforceUtil.create_comment_for_course_case') with mock.patch(salesforce_path): with mock.patch(create_comment_path, side_effect=SalesforceMissingCaseException('Error')): url = reverse('api:v1:comment-list') response = self.client.post(url, body, format='json') self.assertEqual(response.status_code, 500) def test_list_404s_without_a_config(self): self.salesforce_config.delete() body = { 'course_uuid': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', # Needs to resemble a uuid to pass validation 'comment': 'Test comment', 'course_run_key': 'test-key', } salesforce_path = 'course_discovery.apps.course_metadata.salesforce.Salesforce' with mock.patch(salesforce_path): url = reverse('api:v1:comment-list') response = self.client.post(url, body, format='json') self.assertEqual(response.status_code, 404)
def test_get_comments_for_course_case_id_set(self): user = UserFactory() query_path = self.salesforce_util_path + '._query' course = CourseFactoryNoSignals(partner=self.salesforce_config.partner, salesforce_case_id='TestSalesforceId') return_value = { 'records': [ { 'CreatedBy': { 'Username': user.username }, 'CreatedDate': '2000-01-01', 'Body': ('[User]\n{}\n\n' '[Course Run]\ncourse-v1:testX+TestX+Test\n\n' '[Body]\nThis is a formatted test message.').format( user.username), }, { 'CreatedBy': { 'Username': '******' }, 'CreatedDate': '2000-01-01', 'Body': 'This is an internal user comment without formatting.' }, ] } with mock.patch(self.salesforce_path): with mock.patch(query_path, return_value=return_value) as mock_query: util = SalesforceUtil(self.salesforce_config.partner) comments = util.get_comments_for_course(course) mock_query.assert_called_with( "SELECT CreatedDate,Body,CreatedBy.Username,CreatedBy.Email,CreatedBy.FirstName,CreatedBy.LastName " "FROM FeedItem WHERE ParentId='{}' AND IsDeleted=FALSE ORDER BY CreatedDate ASC" .format(course.salesforce_case_id)) assert comments == [{ 'user': { 'username': user.username, 'email': user.email, 'first_name': user.first_name, 'last_name': user.last_name }, 'course_run_key': 'course-v1:testX+TestX+Test', 'comment': 'This is a formatted test message.', 'created': '2000-01-01' }, { 'user': { 'username': '******', 'email': None, 'first_name': None, 'last_name': None }, 'course_run_key': None, 'comment': 'This is an internal user comment without formatting.', 'created': '2000-01-01' }]