def test_create_with_key(self): """ Verify the endpoint supports creating a course_run when specifying a key (if allowed). """ course = self.course_run.course date_key = 'course-v1:{}+1T2000'.format(course.key.replace('/', '+')) desired_key = 'course-v1:{}+HowdyDoing'.format( course.key.replace('/', '+')) url = reverse('api:v1:course_run-list') data = { 'course': course.key, 'start': '2000-01-01T00:00:00Z', 'end': '2001-01-01T00:00:00Z', 'key': desired_key, } # If org doesn't specifically allow it, incoming key is ignored self.mock_post_to_studio(date_key) response = self.client.post(url, data, format='json') self.assertEqual(response.status_code, 201) new_course_run = CourseRun.objects.get(key=date_key) self.assertDictEqual(response.data, self.serialize_course_run(new_course_run)) # Turn on this feature for this org, notice that we can now specify the course key we want org_ext = OrganizationExtensionFactory( organization=course.authoring_organizations.first()) org_ext.auto_create_in_studio = False # badly named, but this controls whether we let org name their keys org_ext.save() self.mock_post_to_studio(desired_key, access_token=False) response = self.client.post(url, data, format='json') self.assertEqual(response.status_code, 201) new_course_run = CourseRun.objects.get(key=desired_key) self.assertDictEqual(response.data, self.serialize_course_run(new_course_run))
def setUpClass(cls): super().setUpClass() cls.user = UserFactory(is_staff=True) first_instructor = PersonFactory(given_name="First", family_name="Instructor") second_instructor = PersonFactory(given_name="Second", family_name="Instructor") cls.instructors = [first_instructor, second_instructor] cls.organizations = OrganizationFactory.create_batch(3) cls.organization_extensions = [] for instructor in cls.instructors: PositionFactory(organization=cls.organizations[0], title="professor", person=instructor) for organization in cls.organizations: cls.organization_extensions.append( OrganizationExtensionFactory(organization=organization)) disco_course = CourseFactory( authoring_organizations=[cls.organizations[0]]) disco_course2 = CourseFactory( authoring_organizations=[cls.organizations[1]]) CourseRunFactory(course=disco_course, staff=[first_instructor]) CourseRunFactory(course=disco_course2, staff=[second_instructor]) cls.user.groups.add(cls.organization_extensions[0].group)
def test_course_without_editors(self): """ Verify we can modify a course with no editors if we're in its authoring org. """ url = reverse('api:v1:course-detail', kwargs={'key': self.course.uuid}) self.user.is_staff = False self.user.save() self.course.draft = True self.course.save() # Try without being in the organization nor an editor self.assertEqual(self.client.patch(url).status_code, 404) # Add to authoring org, and we should be let in org_ext = OrganizationExtensionFactory(organization=self.org) self.user.groups.add(org_ext.group) self.assertNotEqual(self.client.patch(url).status_code, 404) # Now add a random other user as an editor to the course, so that we will no longer be granted access. editor = UserFactory() CourseEditorFactory(user=editor, course=self.course) editor.groups.add(org_ext.group) self.assertEqual(self.client.patch(url).status_code, 404) # But if the editor is no longer valid (even though they exist), we're back to having access. editor.groups.remove(org_ext.group) self.assertNotEqual(self.client.patch(url).status_code, 404) # And finally, for a sanity check, confirm we have access when we become an editor also CourseEditorFactory(user=self.user, course=self.course) self.assertNotEqual(self.client.patch(url).status_code, 404)
def setUp(self): super().setUp() self.org = OrganizationFactory(name='MyOrg', key='myorg') self.course_run = CourseRunFactory(draft=True, title_override='MyCourse') self.course = self.course_run.course self.course.authoring_organizations.add(self.org) self.partner = self.course.partner self.group = GroupFactory() self.pc = self.make_user(email='*****@*****.**') self.editor = self.make_user(groups=[self.group]) self.editor2 = self.make_user(groups=[self.group]) self.non_editor = self.make_user(groups=[self.group]) self.legal = self.make_user( groups=[Group.objects.get(name=LEGAL_TEAM_GROUP_NAME)]) CourseEditorFactory(user=self.editor, course=self.course) CourseEditorFactory(user=self.editor2, course=self.course) OrganizationExtensionFactory(group=self.group, organization=self.org) OrganizationUserRoleFactory(user=self.pc, organization=self.org, role=InternalUserRole.ProjectCoordinator) self.publisher_url = '{}courses/{}'.format(self.partner.publisher_url, self.course_run.course.uuid) self.studio_url = '{}course/{}'.format(self.partner.studio_url, self.course_run.key) self.admin_url = 'https://{}/admin/course_metadata/courserun/{}/change/'.format( self.partner.site.domain, self.course_run.id) self.run_num = CourseKey.from_string(self.course_run.key).run
def test_create_course_run_in_studio_with_image_failure(self, __): organization = OrganizationFactory() OrganizationExtensionFactory(organization=organization) partner = organization.partner start = datetime.datetime.utcnow() course_run_key = 'course-v1:TestX+Testing101x+1T2017' body = {'id': course_run_key} studio_url_root = partner.studio_url.strip('/') url = '{}/api/v1/course_runs/'.format(studio_url_root) responses.add(responses.POST, url, json=body, status=200) course = CourseFactory(organizations=[organization]) with mock.patch('course_discovery.apps.api.utils.logger.exception' ) as mock_logger: publisher_course_run = CourseRunFactory( course=course, start=start, lms_course_id=None, ) assert mock_logger.call_count == 1 assert mock_logger.call_args_list[0] == mock.call( 'An error occurred while setting the course run image for [{key}] in studio. All other fields ' 'were successfully saved in Studio.'.format(key=course_run_key)) publisher_course_run.refresh_from_db() assert len(responses.calls) == 2 assert publisher_course_run.lms_course_id == course_run_key
def test_editor_access_detail_endpoint(self, method, is_staff, in_org, is_editor, allowed): """ Verify we check editor access correctly when hitting the course object endpoint. """ self.user.is_staff = is_staff self.user.save() # Add another editor, because we have some logic that allows access anyway if a course has no valid editors. # That code path is checked in test_course_without_editors below. org_ext = OrganizationExtensionFactory(organization=self.org) user2 = UserFactory() user2.groups.add(org_ext.group) CourseEditorFactory(user=user2, course=self.course) if in_org: # Editors must be in the org to get editor access self.user.groups.add(org_ext.group) if is_editor: CourseEditorFactory(user=self.user, course=self.course) response = getattr(self.client, method)(reverse('api:v1:course-detail', kwargs={'key': self.course.uuid})) if not allowed: self.assertEqual(response.status_code, 404) else: # We'll probably fail because we didn't include the right data - but at least we'll have gotten in self.assertNotEqual(response.status_code, 404)
def test_create_organizations_added_permissions(self): # Make sure created organization automatically have people permissions organization = OrganizationExtensionFactory() target_permissions = Permission.objects.filter( codename__in=['add_person', 'change_person', 'delete_person']) for permission in target_permissions: assert organization.group.permissions.filter( codename=permission.codename).exists()
def setUp(self): super(CourseEditorsViewSetTests, self).setUp() self.staff_user = UserFactory(is_staff=True, is_superuser=True) self.client.login(username=self.staff_user.username, password=USER_PASSWORD) self.user = UserFactory() partner = Partner.objects.first() self.course = CourseFactory(draft=True, partner=partner) self.org_ext = OrganizationExtensionFactory() self.course.authoring_organizations.add(self.org_ext.organization) # pylint: disable=no-member
def setUp(self): super().setUp() self.organization = OrganizationFactory() self.organization_extension = OrganizationExtensionFactory() self.user = UserFactory() self.user.groups.add(self.organization_extension.group) self.course = CourseFactory(title='Test course') assign_perm(OrganizationExtension.VIEW_COURSE, self.organization_extension.group, self.organization_extension)
def setup_course(self, **course_kwargs): """ Creates the course and add organization and admin to this course. Returns: course: a course object course_admin: a user object """ course = CourseFactory(**course_kwargs) course_admin = UserFactory(username='******') organization_extension = OrganizationExtensionFactory() organization = organization_extension.organization course_admin.groups.add(organization_extension.group) course.organizations.add(organization) return course, course_admin
def test_create_course_run_in_studio_with_organization_opt_out(self): with mock.patch( 'course_discovery.apps.publisher.signals.logger.warning' ) as mock_logger: course_organization = OrganizationFactory() OrganizationExtensionFactory(organization=course_organization, auto_create_in_studio=False) publisher_course_run = CourseRunFactory( course__organizations=[course_organization]) mock_logger.assert_called_with( ('Course run [%d] will not be automatically created in studio.' 'Organization [%s] has opted out of this feature.'), publisher_course_run.course.id, course_organization.key, )
def test_update(self): """ Verify that we can update course workflow state with serializer. """ course = self.course_state.course course.image = make_image_file('test_banner.jpg') course.save() course.organizations.add(OrganizationExtensionFactory().organization) self.assertNotEqual(self.course_state, CourseStateChoices.Review) serializer = self.serializer_class(self.course_state, context={'request': self.request}) data = {'name': CourseStateChoices.Review} serializer.update(self.course_state, data) self.course_state = CourseState.objects.get(course=self.course_state.course) self.assertEqual(self.course_state.name, CourseStateChoices.Review) self.assertEqual(self.course_state.owner_role, PublisherUserRole.MarketingReviewer)
def setup_course(self, **course_kwargs): """ Creates the course and add organization and admin to this course. Returns: course: a course object course_admin: a user object """ organization_extension = OrganizationExtensionFactory() defaults = { 'organizations': [organization_extension.organization], } defaults.update(course_kwargs) course = CourseFactory(**defaults) course_admin = UserFactory() course_admin.groups.add(organization_extension.group) return course, course_admin
def test_editor_access_list_endpoint(self, method, is_staff, in_org, allowed): """ Verify we check editor access correctly when hitting the courses endpoint. """ self.user.is_staff = is_staff self.user.save() if in_org: org_ext = OrganizationExtensionFactory(organization=self.org) self.user.groups.add(org_ext.group) response = getattr(self.client, method)(reverse('api:v1:course-list'), { 'org': self.org.key }, format='json') if not allowed: self.assertEqual(response.status_code, 403) else: self.assertNotEqual(response.status_code, 403)
def test_create_if_in_org(self): """ Verify the endpoint supports creating a course_run with organization permissions. """ url = reverse('api:v1:course_run-list') course = self.course_run.course data = {'course': course.key} self.user.is_staff = False self.user.save() # Not in org, not allowed to POST response = self.client.post(url, data, format='json') self.assertEqual(response.status_code, 403) # Add to org org_ext = OrganizationExtensionFactory( organization=course.authoring_organizations.first()) self.user.groups.add(org_ext.group) # now allowed to POST response = self.client.post(url, data, format='json') self.assertEqual(response.status_code, 400) # missing start, but at least we got that far
def test_update_if_editor(self): """ Verify the endpoint supports updating a course_run with editor permissions. """ self.mock_patch_to_studio(self.course_run.key) url = reverse('api:v1:course_run-detail', kwargs={'key': self.course_run.key}) self.user.is_staff = False self.user.save() # Not an editor, not allowed to patch response = self.client.patch(url, {}, format='json') self.assertEqual(response.status_code, 403) # Add as editor org_ext = OrganizationExtensionFactory( organization=self.course_run.course.authoring_organizations.first( )) self.user.groups.add(org_ext.group) CourseEditorFactory(user=self.user, course=self.course_run.course) # now allowed to patch response = self.client.patch(url, {}, format='json') self.assertEqual(response.status_code, 200)