def test_with_in_valid_staff(self, field):
        """ Verify that property returns False staff has bio or image is missing."""
        staff = PersonFactory(profile_image_url=None)
        self.course_run.staff.add(staff)

        setattr(staff, field, None)
        staff.save()
        self.assertFalse(self.course_run.has_valid_staff)
    def test_course_staff(self):
        """Verify that the wrapper return staff list."""
        staff = PersonFactory()
        staff.profile_image_url = None
        staff.save()

        # another staff with position by default staff has no position associated.
        staff_2 = PersonFactory()
        position = PositionFactory(person=staff_2)

        self.course_run.staff = [staff, staff_2]
        self.course_run.save()

        facebook = PersonSocialNetworkFactory(person=staff_2, type='facebook')
        twitter = PersonSocialNetworkFactory(person=staff_2, type='twitter')

        expected = [{
            'uuid': str(staff.uuid),
            'full_name': staff.full_name,
            'image_url': staff.get_profile_image_url,
            'profile_url': staff.profile_url,
            'social_networks': {},
            'bio': staff.bio,
            'is_new': True,
            'email': staff.email
        }, {
            'uuid': str(staff_2.uuid),
            'full_name': staff_2.full_name,
            'image_url': staff_2.get_profile_image_url,
            'position': position.title,
            'organization': position.organization_name,
            'profile_url': staff.profile_url,
            'is_new': False,
            'social_networks': {
                'facebook': facebook.value,
                'twitter': twitter.value
            },
            'bio': staff_2.bio,
            'email': staff_2.email
        }]

        self.assertEqual(self.wrapped_course_run.course_staff, expected)
Beispiel #3
0
class PersonViewSetTests(SerializationMixin, APITestCase):
    """ Tests for the person resource. """
    people_list_url = reverse('api:v1:person-list')

    def setUp(self):
        super(PersonViewSetTests, self).setUp()
        self.user = UserFactory()
        self.request.user = self.user
        self.target_permissions = Permission.objects.filter(
            codename__in=['add_person', 'change_person', 'delete_person'])
        self.permisson_class = ReadOnlyByPublisherUser()
        self.internal_test_group = Group.objects.create(name='internal-test')
        self.internal_test_group.permissions.add(*self.target_permissions)
        self.user.groups.add(self.internal_test_group)
        self.client.login(username=self.user.username, password=USER_PASSWORD)
        self.person = PersonFactory(partner=self.partner)
        self.organization = OrganizationFactory(partner=self.partner)
        PositionFactory(person=self.person, organization=self.organization)
        toggle_switch('publish_person_to_marketing_site', True)
        self.expected_node = {
            'resource': 'node',
            ''
            'id': '28691',
            'uuid': '18d5542f-fa80-418e-b416-455cfdeb4d4e',
            'uri': 'https://stage.edx.org/node/28691'
        }

    def person_exists(self, data):
        return Person.objects.filter(given_name=data['given_name'],
                                     family_name=data['family_name'],
                                     bio=data['bio']).exists()

    def test_create_with_authentication(self):
        """ Verify endpoint successfully creates a person. """
        with mock.patch.object(MarketingSitePeople,
                               'publish_person',
                               return_value=self.expected_node):
            response = self.client.post(self.people_list_url,
                                        self._person_data(),
                                        format='json')
            self.assertEqual(response.status_code, 201)

        data = self._person_data()
        person = Person.objects.last()
        self.assertDictEqual(response.data, self.serialize_person(person))
        self.assertEqual(person.given_name, data['given_name'])
        self.assertEqual(person.family_name, data['family_name'])
        self.assertEqual(person.bio, data['bio'])
        self.assertEqual(person.position.title, data['position']['title'])
        self.assertEqual(person.position.organization, self.organization)
        self.assertEqual(person.major_works, data['major_works'])
        self.assertListEqual(
            sorted([
                social_network.url
                for social_network in person.person_networks.all()
            ]),
            sorted([
                url_detailed['url'] for url_detailed in data['urls_detailed']
            ]))
        self.assertListEqual(
            sorted([
                social_network.title
                for social_network in person.person_networks.all()
            ]),
            sorted([
                url_detailed['title'] for url_detailed in data['urls_detailed']
            ]))

        # Test display_title
        # Test that empty string titles get changed to type when looking at display title for not OTHERS
        self.assertEqual(
            'Facebook',
            person.person_networks.get(type='facebook',
                                       title='').display_title)
        # Test that defined titles are shown
        self.assertEqual(
            'Hopkins Twitter',
            person.person_networks.get(type='twitter',
                                       title='Hopkins Twitter').display_title)
        self.assertEqual(
            'blog',
            person.person_networks.get(type='blog',
                                       title='blog').display_title)
        # Test that empty string titles get changed to url when looking at display title for OTHERS
        self.assertEqual(
            'http://www.others.com/hopkins',
            person.person_networks.get(type='others', title='').display_title)

        self.assertListEqual(
            sorted([
                area_of_expertise.value
                for area_of_expertise in person.areas_of_expertise.all()
            ]),
            sorted([
                area_of_expertise['value']
                for area_of_expertise in data['areas_of_expertise']
            ]))

    def test_create_without_drupal_client_settings(self):
        """ Verify that if credentials are missing api will return the error. """
        self.partner.marketing_site_api_username = None
        self.partner.save()
        data = self._person_data()

        with LogCapture(people_logger.name) as log_capture:
            response = self.client.post(self.people_list_url,
                                        self._person_data(),
                                        format='json')
            self.assertEqual(response.status_code, 400)
            log_capture.check((
                people_logger.name, 'ERROR',
                'An error occurred while adding the person [{}]-[{}] to the marketing site.'
                .format(data['given_name'], data['family_name'])))

        self.assertFalse(self.person_exists(data))

    def test_create_with_api_exception(self):
        """ Verify that after creating drupal page if serializer fail due to any error, message
        will be logged and drupal page will be deleted. """

        data = self._person_data()
        with mock.patch.object(MarketingSitePeople,
                               'publish_person',
                               return_value=self.expected_node):
            with mock.patch(
                    'course_discovery.apps.api.v1.views.people.PersonViewSet.perform_create',
                    side_effect=IntegrityError):
                with mock.patch.object(MarketingSitePeople,
                                       'delete_person',
                                       return_value=None):
                    with LogCapture(people_logger.name) as log_capture:
                        response = self.client.post(self.people_list_url,
                                                    self._person_data(),
                                                    format='json')
                        self.assertEqual(response.status_code, 400)
                        log_capture.check((
                            people_logger.name, 'ERROR',
                            'An error occurred while adding the person [{}]-[{}] in discovery.'
                            .format(
                                data['given_name'],
                                data['family_name'],
                            )))

        self.assertFalse(self.person_exists(data))

    def test_create_without_authentication(self):
        """ Verify authentication is required when creating a person. """
        self.client.logout()
        Person.objects.all().delete()

        response = self.client.post(self.people_list_url)
        assert response.status_code == 403
        assert Person.objects.count() == 0

    def test_create_without_permission(self):
        """ Verify group is required when creating a person. """
        self.client.logout()
        new_user = UserFactory()
        new_user.groups.clear()
        self.client.login(username=new_user.username, password=USER_PASSWORD)
        current_people_count = Person.objects.count()
        response = self.client.post(self.people_list_url)
        assert response.status_code == 403
        assert Person.objects.count() == current_people_count

    def test_get_single_person_without_publisher_user(self):
        """ Verify the endpoint shows permission error for the details for a single person. """
        self.user.groups.remove(self.internal_test_group)
        url = reverse('api:v1:person-detail',
                      kwargs={'uuid': self.person.uuid})
        response = self.client.get(url)
        self.assertEqual(response.status_code, 403)

    def test_get_single_person_with_publisher_user(self):
        """ Verify the endpoint returns the details for a single person. """
        url = reverse('api:v1:person-detail',
                      kwargs={'uuid': self.person.uuid})
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        self.assertDictEqual(response.data, self.serialize_person(self.person))

    def test_get_without_authentication(self):
        """ Verify the endpoint shows auth error when the details for a single person unauthenticated """
        self.client.logout()
        url = reverse('api:v1:person-detail',
                      kwargs={'uuid': self.person.uuid})
        response = self.client.get(url)
        self.assertEqual(response.status_code, 403)

    def test_list_with_publisher_user(self):
        """ Verify the endpoint returns a list of all people with the publisher user """
        response = self.client.get(self.people_list_url)
        self.assertEqual(response.status_code, 200)
        self.assertListEqual(
            response.data['results'],
            self.serialize_person(Person.objects.all(), many=True))

    def test_list_without_publisher_user(self):
        """ Verify the endpoint shows permission error when non-publisher user acccessed """
        self.user.groups.remove(self.internal_test_group)
        response = self.client.get(self.people_list_url)
        self.assertEqual(response.status_code, 403)

    def test_list_different_partner(self):
        """ Verify the endpoint only shows people for the current partner. """
        PersonFactory(
        )  # create person for a partner that isn't self.partner; we expect this to not show up later
        response = self.client.get(self.people_list_url)
        self.assertEqual(response.status_code, 200)
        # Make sure the list does not include the new person above
        self.assertListEqual(response.data['results'],
                             self.serialize_person([self.person], many=True))

    def test_list_filter_by_slug(self):
        """ Verify the endpoint allows people to be filtered by slug. """
        person = PersonFactory(partner=self.partner)
        url = '{root}?slug={slug}'.format(root=self.people_list_url,
                                          slug=person.slug)
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        self.assertListEqual(response.data['results'],
                             self.serialize_person([person], many=True))

    def test_create_without_waffle_switch(self):
        """ Verify endpoint proceeds if waffle switch is disabled. """
        toggle_switch('publish_person_to_marketing_site', False)
        data = self._person_data()
        with mock.patch.object(MarketingSitePeople, 'publish_person') as cm:
            response = self.client.post(self.people_list_url,
                                        data,
                                        format='json')
            self.assertEqual(cm.call_count, 0)
        self.assertEqual(response.status_code, 201)
        self.assertTrue(self.person_exists(data))

    def test_include_course_runs_staffed(self):
        """ Verify the endpoint shows linked course runs when asked. """
        url = reverse('api:v1:person-detail',
                      kwargs={'uuid': self.person.uuid})
        course_run = CourseRunFactory(course__partner=self.partner,
                                      staff=[self.person])

        # Not present normally
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        self.assertListEqual(response.data['course_runs_staffed'], [])

        # But is present when asked
        response = self.client.get(url + '?include_course_runs_staffed=1')
        self.assertEqual(response.status_code, 200)
        self.assertListEqual(
            response.data['course_runs_staffed'],
            self.serialize_minimal_course_run([course_run], many=True))

    def test_include_publisher_course_runs_staffed(self):
        """ Verify the endpoint shows linked publisher course runs when asked. """
        url = reverse('api:v1:person-detail',
                      kwargs={'uuid': self.person.uuid})
        course_run = PublisherCourseRunFactory(
            course__organizations=[self.organization], staff=[self.person])

        # Not present normally
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        self.assertListEqual(response.data['publisher_course_runs_staffed'],
                             [])

        # But is present when asked
        response = self.client.get(url +
                                   '?include_publisher_course_runs_staffed=1')
        self.assertEqual(response.status_code, 200)
        self.assertListEqual(
            response.data['publisher_course_runs_staffed'],
            self.serialize_minimal_publisher_course_run([course_run],
                                                        many=True))

    def _person_data(self):
        return {
            'given_name':
            "Robert",
            'family_name':
            "Ford",
            'bio':
            "The maze is not for him.",
            'position': {
                'title': "Park Director",
                'organization': self.organization.id
            },
            'major_works':
            'Delores\nTeddy\nMaive',
            'urls_detailed': [
                {
                    'id': '1',
                    'type': 'facebook',
                    'title': '',
                    'display_title': 'Facebook',
                    'url': 'http://www.facebook.com/hopkins',
                },
                {
                    'id': '2',
                    'type': 'twitter',
                    'title': 'Hopkins Twitter',
                    'display_title': 'Hopkins Twitter',
                    'url': 'http://www.twitter.com/hopkins',
                },
                {
                    'id': '3',
                    'type': 'blog',
                    'title': 'blog',
                    'display_title': 'blog',
                    'url': 'http://www.blog.com/hopkins',
                },
                {
                    'id': '4',
                    'type': 'others',
                    'title': '',
                    'display_title': 'http://www.others.com/hopkins',
                    'url': 'http://www.others.com/hopkins',
                },
            ],
            'areas_of_expertise': [
                {
                    'id': '1',
                    'value': 'area 1'
                },
                {
                    'id': '2',
                    'value': 'area 2'
                },
            ],
        }

    def _update_person_data(self):
        return {
            'given_name':
            "updated",
            'family_name':
            "name",
            'bio':
            "updated bio",
            'position': {
                'title': "new title",
                'organization': self.organization.id
            },
            'major_works':
            'new works',
            'urls_detailed': [
                {
                    'id': '1',
                    'type': 'facebook',
                    'title': '',
                    'display_title': 'Facebook',
                    'url': 'http://www.facebook.com/new',
                },
                {
                    'id': '2',
                    'type': 'twitter',
                    'title': 'Hopkins new Twitter',
                    'display_title': 'Hopkins new Twitter',
                    'url': 'http://www.twitter.com/new',
                },
                {
                    'id': '4',
                    'type': 'others',
                    'title': 'new others',
                    'display_title': 'new others',
                    'url': 'http://www.others.com/new',
                },
                {
                    'id': '',
                    'type': 'others',
                    'title': 'Create new',
                    'display_title': 'Create new',
                    'url': 'http://www.others.com/new',
                },
            ],
            'areas_of_expertise': [
                {
                    'id': '1',
                    'value': 'new area 1'
                },
                {
                    'id': '',
                    'value': 'area 3'
                },
            ],
        }

    def test_update_without_drupal_client_settings(self):
        """ Verify that if credentials are missing api will return the error. """
        url = reverse('api:v1:person-detail',
                      kwargs={'uuid': self.person.uuid})
        self.partner.marketing_site_api_username = None
        self.partner.save()
        data = self._update_person_data()

        with LogCapture(people_logger.name) as log_capture:
            response = self.client.patch(url, data, format='json')
            self.assertEqual(response.status_code, 400)
            log_capture.check((
                people_logger.name, 'ERROR',
                'An error occurred while updating the person [{}]-[{}] on the marketing site.'
                .format(data['given_name'], data['family_name'])))

    def test_update_with_api_exception(self):
        """ Verify that if the serializer fails, error message is logged and update fails"""
        url = reverse('api:v1:person-detail',
                      kwargs={'uuid': self.person.uuid})
        data = self._update_person_data()
        with mock.patch.object(MarketingSitePeople,
                               'update_person',
                               return_value={}):
            with mock.patch(
                    'course_discovery.apps.api.v1.views.people.PersonViewSet.perform_update',
                    side_effect=IntegrityError):
                with LogCapture(people_logger.name) as log_capture:
                    response = self.client.patch(url,
                                                 self._update_person_data(),
                                                 format='json')
                    self.assertEqual(response.status_code, 400)
                    log_capture.check((
                        people_logger.name, 'ERROR',
                        'An error occurred while updating the person [{}]-[{}] in discovery.'
                        .format(data['given_name'], data['family_name'])))

    def test_update_without_waffle_switch(self):
        """ Verify update endpoint proceeds if waffle switch is disabled. """
        url = reverse('api:v1:person-detail',
                      kwargs={'uuid': self.person.uuid})
        toggle_switch('publish_person_to_marketing_site', False)
        data = self._update_person_data()
        with mock.patch.object(MarketingSitePeople, 'update_person') as cm:
            response = self.client.patch(url, data, format='json')
            self.assertEqual(cm.call_count, 0)
        self.assertEqual(response.status_code, 200)
        self.assertTrue(self.person_exists(data))

    def test_update(self):
        """Verify that people data can be updated using endpoint."""
        url = reverse('api:v1:person-detail',
                      kwargs={'uuid': self.person.uuid})

        data = self._update_person_data()

        # These are being created so we can verify they are deleted since they are not part of updated_data
        PersonSocialNetworkFactory(person=self.person, type='blog')
        removed_value = PersonAreaOfExpertiseFactory(person=self.person).value

        # After updating, profile_image.url should overwrite profile_image_url
        self.assertNotEqual(self.person.profile_image_url,
                            self.person.profile_image.url)
        with mock.patch.object(MarketingSitePeople,
                               'update_person',
                               return_value={}):
            response = self.client.patch(url, data, format='json')
            self.assertEqual(response.status_code, 200)

        updated_person = Person.objects.get(id=self.person.id)

        self.assertEqual(updated_person.given_name, data['given_name'])
        self.assertEqual(updated_person.family_name, data['family_name'])
        self.assertEqual(updated_person.bio, data['bio'])
        self.assertEqual(updated_person.position.title,
                         data['position']['title'])
        self.assertEqual(updated_person.major_works, data['major_works'])
        self.assertEqual(updated_person.profile_image_url,
                         updated_person.profile_image.url)
        self.assertListEqual(
            sorted([
                social_network.url
                for social_network in updated_person.person_networks.all()
            ]),
            sorted([
                url_detailed['url'] for url_detailed in data['urls_detailed']
            ]))
        self.assertListEqual(
            sorted([
                social_network.title
                for social_network in updated_person.person_networks.all()
            ]),
            sorted([
                url_detailed['title'] for url_detailed in data['urls_detailed']
            ]))
        self.assertFalse(
            updated_person.person_networks.filter(type='blog').exists())

        # Test display_title
        # Test that empty string titles get changed to type when looking at display title for not OTHERS
        self.assertEqual(
            'Facebook',
            updated_person.person_networks.get(type='facebook',
                                               title='').display_title)
        # Test that defined titles are shown
        self.assertEqual(
            'Hopkins new Twitter',
            updated_person.person_networks.get(
                type='twitter', title='Hopkins new Twitter').display_title)
        self.assertEqual(
            'new others',
            updated_person.person_networks.get(
                type='others', title='new others').display_title)
        self.assertEqual(
            'Create new',
            updated_person.person_networks.get(
                type='others', title='Create new').display_title)

        self.assertListEqual(
            sorted([
                area_of_expertise.value for area_of_expertise in
                updated_person.areas_of_expertise.all()
            ]),
            sorted([
                area_of_expertise['value']
                for area_of_expertise in data['areas_of_expertise']
            ]))
        self.assertFalse(
            updated_person.areas_of_expertise.filter(
                value=removed_value).exists())

    def test_update_without_position(self):
        """
        Verify that if the people has no position a new position is created while updating people
        """
        url = reverse('api:v1:person-detail',
                      kwargs={'uuid': self.person.uuid})

        data = self._update_person_data()
        Position.objects.all().delete()

        with mock.patch.object(MarketingSitePeople,
                               'update_person',
                               return_value={}):
            response = self.client.patch(url, data, format='json')
            self.assertEqual(response.status_code, 200)

        updated_person = Person.objects.get(id=self.person.id)

        self.assertEqual(updated_person.position.title,
                         data['position']['title'])

    def test_profile_image_url_not_set(self):
        ''' Test that if profile_image_url is not set, it is left as None '''
        url = reverse('api:v1:person-detail',
                      kwargs={'uuid': self.person.uuid})

        data = self._update_person_data()
        self.person.profile_image_url = None
        self.person.save()

        with mock.patch.object(MarketingSitePeople,
                               'update_person',
                               return_value={}):
            response = self.client.patch(url, data, format='json')
            self.assertEqual(response.status_code, 200)

        updated_person = Person.objects.get(id=self.person.id)

        self.assertEqual(updated_person.profile_image_url, None)
    def test_course_staff(self):
        """Verify that the wrapper return staff list."""
        staff = PersonFactory()
        staff.profile_image_url = None
        staff.save()

        # another staff with position by default staff has no position associated.
        staff_2 = PersonFactory()
        position = PositionFactory(person=staff_2)

        self.course_run.staff = [staff, staff_2]
        self.course_run.save()

        facebook = PersonSocialNetworkFactory(person=staff_2, type='facebook')
        twitter = PersonSocialNetworkFactory(person=staff_2,
                                             type='twitter',
                                             title='@MrTerry')

        area_1 = PersonAreaOfExpertiseFactory(person=staff)
        area_2 = PersonAreaOfExpertiseFactory(person=staff)
        area_3 = PersonAreaOfExpertiseFactory(person=staff_2)

        expected = [{
            'uuid':
            str(staff.uuid),
            'full_name':
            staff.full_name,
            'image_url':
            staff.get_profile_image_url,
            'profile_url':
            staff.profile_url,
            'social_networks': [],
            'major_works':
            staff.major_works,
            'bio':
            staff.bio,
            'areas_of_expertise': [
                {
                    'id': area_1.id,
                    'value': area_1.value
                },
                {
                    'id': area_2.id,
                    'value': area_2.value
                },
            ],
        }, {
            'uuid':
            str(staff_2.uuid),
            'full_name':
            staff_2.full_name,
            'image_url':
            staff_2.get_profile_image_url,
            'position':
            position.title,
            'organization':
            position.organization_name,
            'profile_url':
            staff_2.profile_url,
            'social_networks': [
                {
                    'id': facebook.id,
                    'type': facebook.type,
                    'url': facebook.url,
                    'title': facebook.title,
                },
                {
                    'id': twitter.id,
                    'type': twitter.type,
                    'url': twitter.url,
                    'title': twitter.title,
                },
            ],
            'bio':
            staff_2.bio,
            'major_works':
            staff_2.major_works,
            'areas_of_expertise': [
                {
                    'id': area_3.id,
                    'value': area_3.value
                },
            ],
        }]
        self.assertEqual(self.wrapped_course_run.course_staff, expected)