Example #1
0
    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')
Example #2
0
 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)
Example #3
0
    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)))
Example #4
0
    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
Example #5
0
    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')
Example #6
0
    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')
Example #7
0
    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
Example #8
0
    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 == []
Example #9
0
    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()
Example #10
0
    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()
Example #11
0
 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)
Example #12
0
    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')
Example #13
0
    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')
Example #14
0
    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)
Example #15
0
    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()
Example #16
0
    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'))
Example #17
0
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)
Example #18
0
    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'
                }]