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)
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)