def test_get_viewers(self):
        """ Verify the method returns a QuerySet of individuals with explicit permission to view a Catalog. """
        catalog = self.catalog
        self.assertFalse(catalog.viewers.exists())  # pylint:disable=no-member

        user = UserFactory()
        user.add_obj_perm(Catalog.VIEW_PERMISSION, catalog)
        self.assertListEqual(list(catalog.viewers), [user])
class RateLimitingTest(APITestCase):
    """
    Testing rate limiting of API calls.
    """

    def setUp(self):
        super(RateLimitingTest, self).setUp()
        self.url = reverse('django.swagger.resources.view')
        self.user = UserFactory()
        self.client.login(username=self.user.username, password=USER_PASSWORD)

    def tearDown(self):
        """
        Clear the cache, since DRF uses it for recording requests against a
        URL. Django does not clear the cache between test runs.
        """
        super(RateLimitingTest, self).tearDown()
        cache.clear()

    def _make_requests(self):
        """ Make multiple requests until the throttle's limit is exceeded.

        Returns
            Response: Response of the last request.
        """
        num_requests = OverridableUserRateThrottle().num_requests
        for __ in range(num_requests + 1):
            response = self.client.get(self.url)
        return response

    def test_rate_limiting(self):
        """ Verify the API responds with HTTP 429 if a normal user exceeds the rate limit. """
        response = self._make_requests()
        self.assertEqual(response.status_code, 429)

    def test_user_throttle_rate(self):
        """ Verify the UserThrottleRate can be used to override the default rate limit. """
        UserThrottleRate.objects.create(user=self.user, rate='1000/day')
        self.assert_rate_limit_successfully_exceeded()

    def assert_rate_limit_successfully_exceeded(self):
        """ Asserts that the throttle's rate limit can be exceeded without encountering an error. """
        response = self._make_requests()
        self.assertEqual(response.status_code, 200)

    def test_superuser_throttling(self):
        """ Verify superusers are not throttled. """
        self.user.is_superuser = True
        self.user.save()
        self.assert_rate_limit_successfully_exceeded()

    def test_staff_throttling(self):
        """ Verify staff users are not throttled. """
        self.user.is_staff = True
        self.user.save()
        self.assert_rate_limit_successfully_exceeded()
Esempio n. 3
0
    def setUp(self):
        super(UpdateCommentTests, self).setUp()

        self.user = UserFactory.create()
        self.comment = CommentFactory.create(user=self.user)
        self.path = reverse('publisher_comments:api:comments', kwargs={'pk': self.comment.id})
        self.data = {'comment': 'updated comment'}
Esempio n. 4
0
    def test_update_without_editing_permission(self):
        """ Verify that non owner user of the comment can not edit. """
        dummy_user = UserFactory.create()
        self.client.login(username=dummy_user.username, password=USER_PASSWORD)

        response = self.client.patch(self.path, json.dumps(self.data), JSON_CONTENT_TYPE)
        self.assertEqual(response.status_code, 403)
Esempio n. 5
0
    def test_retrieve_permissions(self):
        """ Verify only users with the correct permissions can create, read, or modify a Catalog. """
        # Use an unprivileged user
        user = UserFactory(is_staff=False, is_superuser=False)
        self.client.force_authenticate(user)
        url = reverse('api:v1:catalog-detail', kwargs={'id': self.catalog.id})

        # A user with no permissions should NOT be able to view a Catalog.
        self.assertFalse(user.has_perm('catalogs.view_catalog', self.catalog))
        response = self.client.get(url)
        self.assertEqual(response.status_code, 403)

        # The permitted user should be able to view the Catalog.
        self.grant_catalog_permission_to_user(user, 'view')
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
Esempio n. 6
0
 def setUp(self):
     super(AutocompleteTests, self).setUp()
     self.user = UserFactory(is_staff=True)
     self.client.login(username=self.user.username, password=USER_PASSWORD)
     self.courses = factories.CourseFactory.create_batch(3, title='Some random course title')
     for course in self.courses:
         factories.CourseRunFactory(course=course)
     self.organizations = factories.OrganizationFactory.create_batch(3)
Esempio n. 7
0
    def setUp(self):
        super(AdminTests, self).setUp()
        self.user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=self.user.username, password=USER_PASSWORD)
        self.course_runs = factories.CourseRunFactory.create_batch(3)
        self.courses = [course_run.course for course_run in self.course_runs]

        self.excluded_course_run = factories.CourseRunFactory(course=self.courses[0])
        self.program = factories.ProgramFactory(
            courses=self.courses, excluded_course_runs=[self.excluded_course_run]
        )
Esempio n. 8
0
    def test_is_owner_permission(self):
        """ If object.user matches request.user, return True. """

        # users has access to their own objects
        request = self._make_request(user=self.user, data={'comment': 'update_comment'})
        self.assertTrue(self.permissions_class.has_object_permission(request, None, self.comment))

        # users CANNOT have access to object of other users
        user = UserFactory.create()
        request = self._make_request(user=user, data={'username': '******'})
        self.assertFalse(self.permissions_class.has_object_permission(request, None, self.comment))
    def test_get_full_name(self):
        """ Test that the user model concatenates first and last name if the full name is not set. """
        full_name = "George Costanza"
        user = UserFactory(full_name=full_name)
        self.assertEqual(user.get_full_name(), full_name)

        first_name = "Jerry"
        last_name = "Seinfeld"
        user = UserFactory(full_name=None, first_name=first_name, last_name=last_name)
        expected = "{first_name} {last_name}".format(first_name=first_name, last_name=last_name)
        self.assertEqual(user.get_full_name(), expected)

        user = UserFactory(full_name=full_name, first_name=first_name, last_name=last_name)
        self.assertEqual(user.get_full_name(), full_name)
Esempio n. 10
0
    def test_set_viewers(self):
        """ Verify the method updates the set of users with permission to view a Catalog. """
        users = UserFactory.create_batch(2)
        permission = 'catalogs.' + Catalog.VIEW_PERMISSION

        for user in users:
            self.assertFalse(user.has_perm(permission, self.catalog))

        # Verify a list of users can be added as viewers
        self.catalog.viewers = users
        for user in users:
            self.assertTrue(user.has_perm(permission, self.catalog))

        # Verify existing users, not in the list, have their access revoked.
        permitted = users[0]
        revoked = users[1]
        self.catalog.viewers = [permitted]
        self.assertTrue(permitted.has_perm(permission, self.catalog))
        self.assertFalse(revoked.has_perm(permission, self.catalog))

        # Verify all users have their access revoked when passing in an empty list
        self.catalog.viewers = []
        for user in users:
            self.assertFalse(user.has_perm(permission, self.catalog))
 def setUp(self):
     super(IsOwnerTests, self).setUp()
     self.permissions_class = IsOwner()
     self.user = UserFactory.create()
     self.comment = CommentFactory.create(user=self.user, comment='test comment')
Esempio n. 12
0
 def _make_user_non_staff(self):
     self.client.logout()
     self.user = UserFactory(is_staff=False)
     self.user.save()
     self.client.login(username=self.user.username, password=USER_PASSWORD)
Esempio n. 13
0
 def setUp(self):
     super(UserTests, self).setUp()
     self.user = UserFactory()
Esempio n. 14
0
 def setUp(self):
     super(PublisherUtilsTests, self).setUp()
     self.user = UserFactory()
Esempio n. 15
0
 def setUp(self):
     super().setUp()
     self.service_user = UserFactory(username=self.SERVICE_USERNAME)
     self.url = reverse("api:v1:replace_usernames")
 def setUp(self):
     super(JournalViewSetTests, self).setUp()
     self.user = UserFactory(is_staff=True)
     self.journal = JournalFactory(uuid=uuid.uuid4())
     self.client.login(username=self.user.username, password=USER_PASSWORD)
     self.journal_url = reverse('journal:api:v1:journal-list')
Esempio n. 17
0
 def setUp(self):
     super().setUp()
     self.user = UserFactory(is_staff=True, is_superuser=True)
     self.client.login(username=self.user.username, password=USER_PASSWORD)
Esempio n. 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))
                self.assertEqual(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',
                    },
                ])
Esempio n. 19
0
class AutoCompletePersonTests(mixins.APITestCase):
    """
    Tests for person autocomplete lookups
    """
    def setUp(self):
        super(AutoCompletePersonTests, self).setUp()
        self.user = UserFactory(is_staff=True)
        self.client.login(username=self.user.username, password=USER_PASSWORD)
        self.courses = publisher_factories.CourseFactory.create_batch(
            3, title='Some random course title')

        first_instructor = PersonFactory(given_name="First",
                                         family_name="Instructor")
        second_instructor = PersonFactory(given_name="Second",
                                          family_name="Instructor")
        self.instructors = [first_instructor, second_instructor]

        self.organizations = OrganizationFactory.create_batch(3)
        self.organization_extensions = []

        for instructor in self.instructors:
            PositionFactory(organization=self.organizations[0],
                            title="professor",
                            person=instructor)

        self.course_runs = [
            publisher_factories.CourseRunFactory(course=course)
            for course in self.courses
        ]

        for organization in self.organizations:
            org_ex = publisher_factories.OrganizationExtensionFactory(
                organization=organization)
            self.organization_extensions.append(org_ex)

        disco_course = CourseFactory(
            authoring_organizations=[self.organizations[0]])
        disco_course2 = CourseFactory(
            authoring_organizations=[self.organizations[1]])
        CourseRunFactory(course=disco_course, staff=[first_instructor])
        CourseRunFactory(course=disco_course2, staff=[second_instructor])

        self.user.groups.add(self.organization_extensions[0].group)

    def query(self, q):
        query_params = '?q={q}'.format(q=q)
        path = reverse('api:v1:person-search-typeahead')
        return self.client.get(path + query_params)

    def test_instructor_autocomplete(self):
        """ Verify instructor autocomplete returns the data. """
        response = self.query('ins')
        self._assert_response(response, 2)

        # update first instructor's name
        self.instructors[0].given_name = 'dummy_name'
        self.instructors[0].save()

        response = self.query('dummy')
        self._assert_response(response, 1)

    def test_instructor_autocomplete_non_staff_user(self):
        """ Verify instructor autocomplete works for non-staff users. """
        self._make_user_non_staff()
        response = self.query('dummy')
        self._assert_response(response, 0)

    def test_instructor_autocomplete_no_query_param(self):
        """ Verify instructor autocomplete returns bad response for request with no query. """
        self._make_user_non_staff()
        response = self.client.get(reverse('api:v1:person-search-typeahead'))
        self._assert_error_response(
            response,
            ["The 'q' querystring parameter is required for searching."], 400)

    def test_instructor_autocomplete_spaces(self):
        """ Verify instructor autocomplete allows spaces. """
        response = self.query('sec ins')
        self._assert_response(response, 1)

    def test_instructor_autocomplete_no_results(self):
        """ Verify instructor autocomplete correctly finds no matches if string doesn't match. """
        response = self.query('second nope')
        self._assert_response(response, 0)

    def test_instructor_autocomplete_last_name_first_name(self):
        """ Verify instructor autocomplete allows last name first. """
        response = self.query('instructor first')
        self._assert_response(response, 1)

    def test_instructor_position_in_label(self):
        """ Verify that instructor label contains position of instructor if it exists."""
        position_title = 'professor'

        response = self.query('ins')

        self.assertContains(response, position_title)

    def test_instructor_image_in_label(self):
        """ Verify that instructor label contains profile image url."""
        response = self.query('ins')
        self.assertContains(response,
                            self.instructors[0].get_profile_image_url)
        self.assertContains(response,
                            self.instructors[1].get_profile_image_url)

    def _assert_response(self, response, expected_length):
        """ Assert autocomplete response. """
        assert response.status_code == 200
        data = json.loads(response.content.decode('utf-8'))
        assert len(data) == expected_length

    def _assert_error_response(self,
                               response,
                               expected_response,
                               expected_response_code=200):
        """ Assert autocomplete response. """
        assert response.status_code == expected_response_code
        data = json.loads(response.content.decode('utf-8'))
        assert data == expected_response

    def test_instructor_autocomplete_with_uuid(self):
        """ Verify instructor autocomplete returns the data with valid uuid. """
        uuid = self.instructors[0].uuid
        response = self.query(uuid)
        self._assert_response(response, 1)

    def test_instructor_autocomplete_with_invalid_uuid(self):
        """ Verify instructor autocomplete returns empty list without giving error. """
        uuid = 'invalid-uuid'
        response = self.query(uuid)
        self._assert_response(response, 0)

    def test_instructor_autocomplete_without_staff_user(self):
        """ Verify instructor autocomplete returns the data if user is not staff. """
        non_staff_user = UserFactory()
        non_staff_user.groups.add(self.organization_extensions[0].group)
        self.client.logout()
        self.client.login(username=non_staff_user.username,
                          password=USER_PASSWORD)

        response = self.query('ins')
        self._assert_response(response, 2)

    def test_instructor_autocomplete_without_login(self):
        """ Verify instructor autocomplete returns a forbidden code if user is not logged in. """
        self.client.logout()
        person_autocomplete_url = reverse(
            'api:v1:person-search-typeahead') + '?q={q}'.format(
                q=self.instructors[0].uuid)

        response = self.client.get(person_autocomplete_url)
        self._assert_error_response(
            response,
            {'detail': 'Authentication credentials were not provided.'}, 401)

    def test_autocomplete_limit_by_org(self):
        org = self.organizations[0]
        person_autocomplete_url = reverse(
            'api:v1:person-search-typeahead') + '?q=ins'
        single_autocomplete_url = person_autocomplete_url + '&org={key}'.format(
            key=org.key)
        response = self.client.get(single_autocomplete_url)
        self._assert_response(response, 1)

        org2 = self.organizations[1]
        multiple_autocomplete_url = single_autocomplete_url + '&org={key}'.format(
            key=org2.key)
        response = self.client.get(multiple_autocomplete_url)
        self._assert_response(response, 2)

    def _make_user_non_staff(self):
        self.client.logout()
        self.user = UserFactory(is_staff=False)
        self.user.save()
        self.client.login(username=self.user.username, password=USER_PASSWORD)
 def setUp(self):
     super(RateLimitingTest, self).setUp()
     self.url = reverse('django.swagger.resources.view')
     self.user = UserFactory()
     self.client.login(username=self.user.username, password=USER_PASSWORD)
Esempio n. 21
0
 def setUp(self):
     super(LoginMixin, self).setUp()
     self.user = UserFactory()
     self.client.login(username=self.user.username, password=USER_PASSWORD)
     if getattr(self, 'request'):
         self.request.user = self.user
Esempio n. 22
0
 def setUp(self):
     super(LoginMixin, self).setUp()
     self.user = UserFactory()
     self.client.login(username=self.user.username, password=USER_PASSWORD)
Esempio n. 23
0
 def setUp(self):
     super().setUp()
     self.user = UserFactory()
Esempio n. 24
0
 def setUp(self):
     super(CourseViewSetTests, self).setUp()
     self.user = UserFactory(is_staff=True, is_superuser=True)
     self.client.login(username=self.user.username, password=USER_PASSWORD)
     self.course = CourseFactory()
Esempio n. 25
0
 def setUp(self):
     super(OrganizationExtensionAdminTests, self).setUp()
     self.user = UserFactory(is_staff=True, is_superuser=True)
     self.client.login(username=self.user.username, password=USER_PASSWORD)
     self.admin_page_url = reverse(
         'admin:publisher_organizationextension_add')
Esempio n. 26
0
 def test_course_form(self):
     """
     Verify that UserModelChoiceField returns `full_name` as choice label.
     """
     user = UserFactory(username='******', full_name='Test Full Name')
     self._assert_choice_label(user.full_name)
 def setUp(self):
     super(RateLimitingTest, self).setUp()
     self.url = reverse('django.swagger.resources.view')
     self.user = UserFactory()
     self.client.login(username=self.user.username, password=USER_PASSWORD)
Esempio n. 28
0
 def _make_user_non_staff(self):
     self.client.logout()
     self.user = UserFactory(is_staff=False)
     self.user.save()
     self.client.login(username=self.user.username, password=USER_PASSWORD)
Esempio n. 29
0
class CommentViewSetTests(OAuth2Mixin, APITestCase):
    @factory.django.mute_signals(m2m_changed)
    def setUp(self):
        super(CommentViewSetTests, self).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)  # pylint: disable=no-member

    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)
Esempio n. 30
0
class AdminTests(TestCase):
    """ Tests Admin page."""

    def setUp(self):
        super(AdminTests, self).setUp()
        self.user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=self.user.username, password=USER_PASSWORD)
        self.course_runs = factories.CourseRunFactory.create_batch(3)
        self.courses = [course_run.course for course_run in self.course_runs]

        self.excluded_course_run = factories.CourseRunFactory(course=self.courses[0])
        self.program = factories.ProgramFactory(
            courses=self.courses, excluded_course_runs=[self.excluded_course_run]
        )

    def _post_data(self, status=ProgramStatus.Unpublished, marketing_slug='/foo'):
        return {
            'title': 'some test title',
            'courses': [self.courses[0].id],
            'type': self.program.type.id,
            'status': status,
            'marketing_slug': marketing_slug,
            'partner': self.program.partner.id
        }

    def assert_form_valid(self, data, files):
        form = ProgramAdminForm(data=data, files=files)
        self.assertTrue(form.is_valid())
        program = form.save()
        response = self.client.get(reverse('admin:course_metadata_program_change', args=(program.id,)))
        self.assertEqual(response.status_code, 200)

    def assert_form_invalid(self, data, files):
        form = ProgramAdminForm(data=data, files=files)
        self.assertFalse(form.is_valid())
        self.assertEqual(
            form.errors['__all__'],
            ['Programs can only be activated if they have a marketing slug and a banner image.']
        )
        with self.assertRaises(ValueError):
            form.save()

    def test_program_detail_form(self):
        """ Verify in admin panel program detail form load successfully. """
        response = self.client.get(reverse('admin:course_metadata_program_change', args=(self.program.id,)))
        self.assertEqual(response.status_code, 200)

    def test_custom_course_selection_page(self):
        """ Verify that course selection page loads successfully. """
        response = self.client.get(reverse('admin_metadata:update_course_runs', args=(self.program.id,)))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, reverse('admin:course_metadata_program_change', args=(self.program.id,)))
        self.assertContains(response, reverse('admin:course_metadata_program_changelist'))

    def test_custom_course_selection_page_with_invalid_id(self):
        """ Verify that course selection page will return 404 for invalid program id. """
        response = self.client.get(reverse('admin_metadata:update_course_runs', args=(10,)))
        self.assertEqual(response.status_code, 404)

    def test_custom_course_selection_page_with_non_staff(self):
        """ Verify that course selection page will return 404 for non authorized user. """
        self.client.logout()
        self.user.is_superuser = False
        self.user.is_staff = False
        self.user.save()
        self.client.login(username=self.user.username, password=USER_PASSWORD)
        response = self.client.get(reverse('admin_metadata:update_course_runs', args=(self.program.id,)))
        self.assertEqual(response.status_code, 404)

    def test_page_loads_only_course_related_runs(self):
        """ Verify that course selection page loads only all course runs. Also marked checkboxes with
        excluded courses runs only.
        """
        # add some new courses and course runs
        factories.CourseRunFactory.create_batch(2)
        response = self.client.get(reverse('admin_metadata:update_course_runs', args=(self.program.id,)))
        html = '<input checked="checked" id="id_excluded_course_runs_0" '
        html += 'name="excluded_course_runs" type="checkbox" value="{id}" />'.format(
            id=self.excluded_course_run.id
        )
        self.assertContains(response, html)
        for run in self.course_runs:
            self.assertContains(response, run.key)

    def test_page_with_post_new_course_run(self):
        """ Verify that course selection page with posting the data. """

        self.assertEqual(1, self.program.excluded_course_runs.all().count())
        self.assertEqual(3, sum(1 for _ in self.program.course_runs))

        params = {
            'excluded_course_runs': [self.excluded_course_run.id, self.course_runs[0].id],
        }
        post_url = reverse('admin_metadata:update_course_runs', args=(self.program.id,))
        response = self.client.post(post_url, params)
        self.assertRedirects(
            response,
            expected_url=reverse('admin:course_metadata_program_change', args=(self.program.id,)),
            status_code=302,
            target_status_code=200
        )
        self.assertEqual(2, self.program.excluded_course_runs.all().count())
        self.assertEqual(2, sum(1 for _ in self.program.course_runs))

    def test_page_with_post_without_course_run(self):
        """ Verify that course selection page without posting any selected excluded check run. """

        self.assertEqual(1, self.program.excluded_course_runs.all().count())
        params = {
            'excluded_course_runs': [],
        }
        post_url = reverse('admin_metadata:update_course_runs', args=(self.program.id,))
        response = self.client.post(post_url, params)
        self.assertRedirects(
            response,
            expected_url=reverse('admin:course_metadata_program_change', args=(self.program.id,)),
            status_code=302,
            target_status_code=200
        )
        self.assertEqual(0, self.program.excluded_course_runs.all().count())
        self.assertEqual(4, sum(1 for _ in self.program.course_runs))
        response = self.client.get(reverse('admin_metadata:update_course_runs', args=(self.program.id,)))
        self.assertNotContains(response, '<input checked="checked")')

    @ddt.data(
        *itertools.product(
            (
                (False, False, False),
                (True, False, False),
                (False, True, False),
                (True, True, True)
            ),
            ProgramStatus.labels
        )
    )
    @ddt.unpack
    def test_program_activation_restrictions(self, booleans, label):
        """Verify that program activation requires both a marketing slug and a banner image."""
        has_marketing_slug, has_banner_image, can_be_activated = booleans
        status = getattr(ProgramStatus, label)

        marketing_slug = '/foo' if has_marketing_slug else ''
        banner_image = make_image_file('test_banner.jpg') if has_banner_image else ''

        data = self._post_data(status=status, marketing_slug=marketing_slug)
        files = {'banner_image': banner_image}

        if status == ProgramStatus.Active:
            if can_be_activated:
                # Transitioning to an active status should require a marketing slug and banner image.
                self.assert_form_valid(data, files)
            else:
                self.assert_form_invalid(data, files)
        else:
            # All other status transitions should be valid regardless of marketing slug and banner image.
            self.assert_form_valid(data, files)

    def test_new_program_without_courses(self):
        """ Verify that new program can be added without `courses`."""
        data = self._post_data()
        data['courses'] = []
        form = ProgramAdminForm(data)
        self.assertTrue(form.is_valid())
        program = form.save()
        self.assertEqual(0, program.courses.all().count())
        response = self.client.get(reverse('admin:course_metadata_program_change', args=(program.id,)))
        self.assertEqual(response.status_code, 200)
Esempio n. 31
0
class AdminTests(TestCase):
    """ Tests Admin page."""

    def setUp(self):
        super(AdminTests, self).setUp()
        self.user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=self.user.username, password=USER_PASSWORD)
        self.course_runs = factories.CourseRunFactory.create_batch(3)
        self.courses = [course_run.course for course_run in self.course_runs]

        self.excluded_course_run = factories.CourseRunFactory(course=self.courses[0])
        self.program = factories.ProgramFactory(
            courses=self.courses, excluded_course_runs=[self.excluded_course_run]
        )

    def _post_data(self, status=ProgramStatus.Unpublished, marketing_slug='/foo'):
        return {
            'title': 'some test title',
            'courses': [self.courses[0].id],
            'type': self.program.type.id,
            'status': status,
            'marketing_slug': marketing_slug,
            'partner': self.program.partner.id
        }

    def assert_form_valid(self, data, files):
        form = ProgramAdminForm(data=data, files=files)
        self.assertTrue(form.is_valid())
        program = form.save()
        response = self.client.get(reverse('admin:course_metadata_program_change', args=(program.id,)))
        self.assertEqual(response.status_code, 200)

    def assert_form_invalid(self, data, files):
        form = ProgramAdminForm(data=data, files=files)
        self.assertFalse(form.is_valid())
        self.assertEqual(
            form.errors['__all__'],
            ['Programs can only be activated if they have a banner image.']
        )
        with self.assertRaises(ValueError):
            form.save()

    def test_program_detail_form(self):
        """ Verify in admin panel program detail form load successfully. """
        response = self.client.get(reverse('admin:course_metadata_program_change', args=(self.program.id,)))
        self.assertEqual(response.status_code, 200)

    def test_custom_course_selection_page(self):
        """ Verify that course selection page loads successfully. """
        response = self.client.get(reverse('admin_metadata:update_course_runs', args=(self.program.id,)))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, reverse('admin:course_metadata_program_change', args=(self.program.id,)))
        self.assertContains(response, reverse('admin:course_metadata_program_changelist'))

    def test_custom_course_selection_page_with_invalid_id(self):
        """ Verify that course selection page will return 404 for invalid program id. """
        response = self.client.get(reverse('admin_metadata:update_course_runs', args=(10,)))
        self.assertEqual(response.status_code, 404)

    def test_custom_course_selection_page_with_non_staff(self):
        """ Verify that course selection page will return 404 for non authorized user. """
        self.client.logout()
        self.user.is_superuser = False
        self.user.is_staff = False
        self.user.save()
        self.client.login(username=self.user.username, password=USER_PASSWORD)
        response = self.client.get(reverse('admin_metadata:update_course_runs', args=(self.program.id,)))
        self.assertEqual(response.status_code, 404)

    def test_page_loads_only_course_related_runs(self):
        """ Verify that course selection page loads only all course runs. Also marked checkboxes with
        excluded courses runs only.
        """
        # add some new courses and course runs
        factories.CourseRunFactory.create_batch(2)
        response = self.client.get(reverse('admin_metadata:update_course_runs', args=(self.program.id,)))
        html = '<input checked="checked" id="id_excluded_course_runs_0" '
        html += 'name="excluded_course_runs" type="checkbox" value="{id}" />'.format(
            id=self.excluded_course_run.id
        )
        self.assertContains(response, html)
        for run in self.course_runs:
            self.assertContains(response, run.key)

    def test_page_with_post_new_course_run(self):
        """ Verify that course selection page with posting the data. """

        self.assertEqual(1, self.program.excluded_course_runs.all().count())
        self.assertEqual(3, sum(1 for _ in self.program.course_runs))

        params = {
            'excluded_course_runs': [self.excluded_course_run.id, self.course_runs[0].id],
        }
        post_url = reverse('admin_metadata:update_course_runs', args=(self.program.id,))
        response = self.client.post(post_url, params)
        self.assertRedirects(
            response,
            expected_url=reverse('admin:course_metadata_program_change', args=(self.program.id,)),
            status_code=302,
            target_status_code=200
        )
        self.assertEqual(2, self.program.excluded_course_runs.all().count())
        self.assertEqual(2, sum(1 for _ in self.program.course_runs))

    def test_page_with_post_without_course_run(self):
        """ Verify that course selection page without posting any selected excluded check run. """

        self.assertEqual(1, self.program.excluded_course_runs.all().count())
        params = {
            'excluded_course_runs': [],
        }
        post_url = reverse('admin_metadata:update_course_runs', args=(self.program.id,))
        response = self.client.post(post_url, params)
        self.assertRedirects(
            response,
            expected_url=reverse('admin:course_metadata_program_change', args=(self.program.id,)),
            status_code=302,
            target_status_code=200
        )
        self.assertEqual(0, self.program.excluded_course_runs.all().count())
        self.assertEqual(4, sum(1 for _ in self.program.course_runs))
        response = self.client.get(reverse('admin_metadata:update_course_runs', args=(self.program.id,)))
        self.assertNotContains(response, '<input checked="checked")')

    @ddt.data(
        *itertools.product(
            (
                (False, False),
                (True, True)
            ),
            ProgramStatus.labels
        )
    )
    @ddt.unpack
    def test_program_activation_restrictions(self, booleans, label):
        """Verify that program activation requires both a marketing slug and a banner image."""
        has_banner_image, can_be_activated = booleans
        status = getattr(ProgramStatus, label)

        banner_image = make_image_file('test_banner.jpg') if has_banner_image else ''

        data = self._post_data(status=status, marketing_slug='/foo')
        files = {'banner_image': banner_image}

        if status == ProgramStatus.Active:
            if can_be_activated:
                # Transitioning to an active status should require a marketing slug and banner image.
                self.assert_form_valid(data, files)
            else:
                self.assert_form_invalid(data, files)
        else:
            # All other status transitions should be valid regardless of marketing slug and banner image.
            self.assert_form_valid(data, files)

    def test_new_program_without_courses(self):
        """ Verify that new program can be added without `courses`."""
        data = self._post_data()
        data['courses'] = []
        form = ProgramAdminForm(data)
        self.assertTrue(form.is_valid())
        program = form.save()
        self.assertEqual(0, program.courses.all().count())
        response = self.client.get(reverse('admin:course_metadata_program_change', args=(program.id,)))
        self.assertEqual(response.status_code, 200)
Esempio n. 32
0
 def setUp(self):
     super(IsOwnerTests, self).setUp()
     self.permissions_class = IsOwner()
     self.user = UserFactory.create()
     self.comment = CommentFactory.create(user=self.user, comment='test comment')
Esempio n. 33
0
 def test_authenticated(self):
     """ Verify the view raises `PermissionDenied` if the request is authenticated. """
     user = UserFactory()
     self.request.user = user
     self.assertRaises(PermissionDenied, api_docs_permission_denied_handler,
                       self.request)
class CourseEditorsViewSetTests(SerializationMixin, APITestCase):
    list_path = reverse('api:v1:course_editor-list')

    def setUp(self):
        super().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)

    def test_list(self):
        """Verify GET endpoint returns list of editors"""
        CourseEditorFactory()
        response = self.client.get(self.list_path)

        assert len(response.data['results']) == 1

        # Test for non staff user
        self.client.login(username=self.user.username, password=USER_PASSWORD)
        response = self.client.get(self.list_path)

        assert not response.data['results']

    def test_course_query_param(self):
        """Verify GET endpoint with course query param returns editors relative to that course"""
        CourseEditorFactory(course=self.course)
        CourseEditorFactory()

        response = self.client.get(self.list_path)

        assert len(response.data['results']) == 2

        response = self.client.get(self.list_path,
                                   {'course': self.course.uuid})

        assert len(response.data['results']) == 1
        assert response.data['results'][0]['course'] == self.course.uuid

    @ddt.data(
        (True, True),  # Staff User on Draft Course
        (True, False),  # Staff User on Official Course
        (False, True),  # Non-staff User on Draft Course
        (False, False),  # Non-staff User on Official Course
    )
    @ddt.unpack
    def test_create_for_self_and_draft_course(self, is_staff, is_draft):
        """Verify can make self an editor. Test cases: as staff and non-staff, on official and draft course"""

        self.user.is_staff = is_staff
        self.user.save()
        partner = Partner.objects.first()
        course = CourseFactory(draft=is_draft, partner=partner)
        self.user.groups.add(self.org_ext.group)
        course.authoring_organizations.add(self.org_ext.organization)

        self.client.login(username=self.user.username, password=USER_PASSWORD)
        self.client.post(self.list_path, {'course': course.uuid},
                         format='json')
        course_editor = CourseEditor.objects.first()

        assert course_editor.course == course
        assert course_editor.user == self.user

    def test_create_for_self_as_non_staff_with_invalid_course(self):
        """Verify non staff user cannot make them self an editor of a course they dont belong to"""

        self.client.login(username=self.user.username, password=USER_PASSWORD)

        response = self.client.post(self.list_path,
                                    {'course': self.course.uuid},
                                    format='json')

        assert response.status_code == 403

    def test_create_for_other_user_as_staff(self):
        """Verify staff user can make another user an editor"""

        self.user.groups.add(self.org_ext.group)
        self.client.post(self.list_path, {
            'course': self.course.uuid,
            'user_id': self.user.id
        },
                         format='json')
        course_editor = CourseEditor.objects.first()

        assert course_editor.course == self.course
        assert course_editor.user == self.user

    def test_create_for_other_user_as_non_staff(self):
        """Verify non staff can make another user an editor"""

        user2 = UserFactory()

        self.user.groups.add(self.org_ext.group)
        user2.groups.add(self.org_ext.group)

        self.client.login(username=self.user.username, password=USER_PASSWORD)
        self.client.post(self.list_path, {
            'course': self.course.uuid,
            'user_id': user2.id
        },
                         format='json')
        course_editor = CourseEditor.objects.first()

        assert course_editor.course == self.course
        assert course_editor.user == user2

    def test_create_for_invalid_other_user(self):
        """Verify a user can't be made an editor of a course if both are not under the same organization"""

        response = self.client.post(self.list_path, {
            'course': self.course.uuid,
            'user_id': self.user.id
        },
                                    format='json')

        assert response.status_code == 403
Esempio n. 35
0
class CourseRunViewSetTests(SerializationMixin, ElasticsearchTestMixin,
                            OAuth2Mixin, APITestCase):
    def setUp(self):
        super(CourseRunViewSetTests, self).setUp()
        self.user = UserFactory(is_staff=True)
        self.client.force_authenticate(self.user)
        self.course_run = CourseRunFactory(course__partner=self.partner)
        self.course_run_2 = CourseRunFactory(course__key='Test+Course',
                                             course__partner=self.partner)
        self.draft_course = CourseFactory(partner=self.partner, draft=True)
        self.draft_course_run = CourseRunFactory(course=self.draft_course,
                                                 draft=True)
        self.draft_course_run.course.authoring_organizations.add(
            OrganizationFactory(key='course-id'))
        self.refresh_index()
        self.request = APIRequestFactory().get('/')
        self.request.user = self.user

    def mock_patch_to_studio(self, key, access_token=True, status=200):
        if access_token:
            self.mock_access_token()
        studio_url = '{root}/api/v1/course_runs/{key}/'.format(
            root=self.partner.studio_url.strip('/'), key=key)
        responses.add(responses.PATCH, studio_url, status=status)
        responses.add(responses.POST,
                      '{url}images/'.format(url=studio_url),
                      status=status)

    def mock_post_to_studio(self, key, access_token=True):
        if access_token:
            self.mock_access_token()
        studio_url = '{root}/api/v1/course_runs/'.format(
            root=self.partner.studio_url.strip('/'))
        responses.add(responses.POST, studio_url, status=200)
        responses.add(responses.POST,
                      '{url}{key}/images/'.format(url=studio_url, key=key),
                      status=200)

    def test_get(self):
        """ Verify the endpoint returns the details for a single course. """
        url = reverse('api:v1:course_run-detail',
                      kwargs={'key': self.course_run.key})

        with self.assertNumQueries(11):
            response = self.client.get(url)

        assert response.status_code == 200
        self.assertEqual(response.data,
                         self.serialize_course_run(self.course_run))

    def test_get_exclude_deleted_programs(self):
        """ Verify the endpoint returns no associated deleted programs """
        ProgramFactory(courses=[self.course_run.course],
                       status=ProgramStatus.Deleted)

        url = reverse('api:v1:course_run-detail',
                      kwargs={'key': self.course_run.key})

        with self.assertNumQueries(12):
            response = self.client.get(url)
        assert response.status_code == 200
        assert response.data.get('programs') == []

    def test_get_include_deleted_programs(self):
        """
        Verify the endpoint returns associated deleted programs
        with the 'include_deleted_programs' flag set to True
        """
        ProgramFactory(courses=[self.course_run.course],
                       status=ProgramStatus.Deleted)

        url = reverse('api:v1:course_run-detail',
                      kwargs={'key': self.course_run.key})
        url += '?include_deleted_programs=1'

        with self.assertNumQueries(17):
            response = self.client.get(url)
        assert response.status_code == 200
        assert response.data == \
            self.serialize_course_run(self.course_run, extra_context={'include_deleted_programs': True})

    def test_get_exclude_unpublished_programs(self):
        """ Verify the endpoint returns no associated unpublished programs """
        ProgramFactory(courses=[self.course_run.course],
                       status=ProgramStatus.Unpublished)

        url = reverse('api:v1:course_run-detail',
                      kwargs={'key': self.course_run.key})

        with self.assertNumQueries(12):
            response = self.client.get(url)
            assert response.status_code == 200
            assert response.data.get('programs') == []

    def test_get_include_unpublished_programs(self):
        """
        Verify the endpoint returns associated unpublished programs
        with the 'include_unpublished_programs' flag set to True
        """
        ProgramFactory(courses=[self.course_run.course],
                       status=ProgramStatus.Unpublished)

        url = reverse('api:v1:course_run-detail',
                      kwargs={'key': self.course_run.key})
        url += '?include_unpublished_programs=1'

        with self.assertNumQueries(17):
            response = self.client.get(url)
        assert response.status_code == 200
        assert response.data == \
            self.serialize_course_run(self.course_run, extra_context={'include_unpublished_programs': True})

    @responses.activate
    def test_create_minimum(self):
        """ Verify the endpoint supports creating a course_run with the least info. """
        course = self.draft_course_run.course
        new_key = 'course-v1:{}+1T2000'.format(course.key.replace('/', '+'))
        self.mock_post_to_studio(new_key)
        url = reverse('api:v1:course_run-list')

        # Send nothing - expect complaints
        response = self.client.post(url, {}, format='json')
        self.assertEqual(response.status_code, 400)
        self.assertDictEqual(
            response.data, {
                'course': ['This field is required.'],
                'start': ['This field is required.'],
                'end': ['This field is required.'],
            })

        # Send minimum requested
        response = self.client.post(url, {
            'course': course.key,
            'start': '2000-01-01T00:00:00Z',
            'end': '2001-01-01T00:00:00Z',
        },
                                    format='json')
        self.assertEqual(response.status_code, 201)
        new_course_run = CourseRun.everything.get(key=new_key)
        self.assertDictEqual(response.data,
                             self.serialize_course_run(new_course_run))
        self.assertEqual(new_course_run.pacing_type,
                         'instructor_paced')  # default we provide
        self.assertEqual(
            str(new_course_run.end),
            '2001-01-01 00:00:00+00:00')  # spot check that input made it
        self.assertTrue(new_course_run.draft)

    @ddt.data(True, False, "bogus")
    @responses.activate
    def test_create_draft_ignored(self, draft):
        """ Verify the endpoint supports creating a course_run, but always as a draft. """
        course = self.draft_course_run.course
        new_key = 'course-v1:{}+1T2000'.format(course.key.replace('/', '+'))
        self.mock_post_to_studio(new_key)
        url = reverse('api:v1:course_run-list')

        # Send minimum + draft: True/False/bogus
        response = self.client.post(url, {
            'course': course.key,
            'start': '2000-01-01T00:00:00Z',
            'end': '2001-01-01T00:00:00Z',
            'draft': draft,
        },
                                    format='json')

        self.assertEqual(response.status_code, 201)
        new_course_run = CourseRun.everything.get(key=new_key)
        self.assertDictEqual(response.data,
                             self.serialize_course_run(new_course_run))
        self.assertTrue(new_course_run.draft)

    @responses.activate
    def test_create_with_key(self):
        """ Verify the endpoint supports creating a course_run when specifying a key (if allowed). """
        course = self.draft_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.everything.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.everything.get(key=desired_key)
        self.assertDictEqual(response.data,
                             self.serialize_course_run(new_course_run))

    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.draft_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_create_fails_with_missing_fields(self):
        course = self.draft_course_run.course
        new_key = 'course-v1:{}+1T2000'.format(course.key.replace('/', '+'))
        self.mock_post_to_studio(new_key)
        url = reverse('api:v1:course_run-list')

        # Send nothing - expect complaints
        response = self.client.post(url, {}, format='json')
        self.assertEqual(response.status_code, 400)
        self.assertDictEqual(
            response.data, {
                'course': ['This field is required.'],
                'start': ['This field is required.'],
                'end': ['This field is required.'],
            })

    @responses.activate
    def test_update_operates_on_drafts(self):
        self.assertFalse(
            CourseRun.everything.filter(key=self.course_run.key,
                                        draft=True).exists())  # sanity check
        self.mock_patch_to_studio(self.course_run.key)
        expected_original_max_effort = self.course_run.max_effort

        url = reverse('api:v1:course_run-detail',
                      kwargs={'key': self.course_run.key})
        response = self.client.patch(url, {'max_effort': 777}, format='json')
        self.assertEqual(response.status_code, 200)

        course_run = CourseRun.everything.get(key=self.course_run.key,
                                              draft=True)
        self.assertEqual(course_run.max_effort, 777)

        self.course_run.refresh_from_db()
        self.assertFalse(self.course_run.draft)
        self.assertEqual(self.course_run.max_effort,
                         expected_original_max_effort)

    @responses.activate
    def test_partial_update(self):
        """ Verify the endpoint supports partially updating a course_run's fields, provided user has permission. """
        self.mock_patch_to_studio(self.draft_course_run.key)

        url = reverse('api:v1:course_run-detail',
                      kwargs={'key': self.draft_course_run.key})

        expected_min_effort = 867
        expected_max_effort = 5309
        data = {
            'max_effort': expected_max_effort,
            'min_effort': expected_min_effort,
        }

        # Update this course_run with the new info
        response = self.client.patch(url, data, format='json')
        assert response.status_code == 200

        # refresh and make sure we have the new effort levels
        self.draft_course_run.refresh_from_db()

        assert self.draft_course_run.max_effort == expected_max_effort
        assert self.draft_course_run.min_effort == expected_min_effort

    def test_partial_update_no_studio_url(self):
        """ Verify we skip pushing when no studio url is set. """
        self.partner.studio_url = None
        self.partner.save()

        url = reverse('api:v1:course_run-detail',
                      kwargs={'key': self.draft_course_run.key})

        with mock.patch(
                'course_discovery.apps.api.v1.views.course_runs.log.info'
        ) as mock_logger:
            response = self.client.patch(url, {}, format='json')

        self.assertEqual(response.status_code, 200)
        mock_logger.assert_called_with(
            'Not pushing course run info for %s to Studio as partner %s has no studio_url set.',
            self.draft_course_run.key,
            self.partner.short_code,
        )

    def test_partial_update_bad_permission(self):
        """ Verify partially updating will fail if user doesn't have permission. """
        user = UserFactory(is_staff=False, is_superuser=False)
        self.client.force_authenticate(user)
        url = reverse('api:v1:course_run-detail',
                      kwargs={'key': self.draft_course_run.key})

        response = self.client.patch(url, {}, format='json')
        assert response.status_code == 404

    @ddt.data(
        (
            {
                'start': '2010-01-01T00:00:00Z',
                'end': '2000-01-01T00:00:00Z'
            },
            'Start date cannot be after the End date',
        ),
        (
            {
                'key': 'course-v1:Blarg+Hello+Run'
            },
            'Key cannot be changed',
        ),
        (
            {
                'course': 'Test+Course'
            },
            'Course cannot be changed',
        ),
        (
            {
                'min_effort': 10000
            },
            'Minimum effort cannot be greater than Maximum effort',
        ),
        (
            {
                'min_effort': 10000,
                'max_effort': 10000
            },
            'Minimum effort and Maximum effort cannot be the same',
        ),
        (
            {
                'max_effort': None
            },
            'Maximum effort cannot be empty',
        ),
    )
    @ddt.unpack
    def test_partial_update_common_errors(self, data, error):
        """ Verify partially updating will fail depending on various validation checks. """
        url = reverse('api:v1:course_run-detail',
                      kwargs={'key': self.draft_course_run.key})
        response = self.client.patch(url, data, format='json')
        self.assertContains(response, error, status_code=400)

    def test_partial_update_staff(self):
        """ Verify partially updating allows staff updates. """
        self.mock_patch_to_studio(self.draft_course_run.key)

        p1 = PersonFactory()
        p2 = PersonFactory()
        PersonFactory()

        url = reverse('api:v1:course_run-detail',
                      kwargs={'key': self.draft_course_run.key})
        response = self.client.patch(url, {'staff': [p2.uuid, p1.uuid]},
                                     format='json')
        self.assertEqual(response.status_code, 200)

        self.draft_course_run.refresh_from_db()
        self.assertListEqual(list(self.draft_course_run.staff.all()), [p2, p1])

    @responses.activate
    def test_partial_update_video(self):
        """ Verify partially updating allows video updates. """
        self.mock_patch_to_studio(self.draft_course_run.key)

        url = reverse('api:v1:course_run-detail',
                      kwargs={'key': self.draft_course_run.key})
        response = self.client.patch(
            url, {'video': {
                'src': 'https://example.com/blarg'
            }},
            format='json')
        self.assertEqual(response.status_code, 200)

        self.draft_course_run.refresh_from_db()
        self.assertEqual(self.draft_course_run.video.src,
                         'https://example.com/blarg')

    @responses.activate
    def test_update_if_editor(self):
        """ Verify the endpoint supports updating a course_run with editor permissions. """
        self.mock_patch_to_studio(self.draft_course_run.key)
        url = reverse('api:v1:course_run-detail',
                      kwargs={'key': self.draft_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, 404)

        # Add as editor
        org_ext = OrganizationExtensionFactory(
            organization=self.draft_course_run.course.authoring_organizations.
            first())
        self.user.groups.add(org_ext.group)
        CourseEditorFactory(user=self.user,
                            course=self.draft_course_run.course)

        # now allowed to patch
        response = self.client.patch(url, {}, format='json')
        self.assertEqual(response.status_code, 200)

    @responses.activate
    def test_studio_update_failure(self):
        """ Verify we bubble up error correctly if studio is giving us static. """
        self.mock_patch_to_studio(self.draft_course_run.key, status=400)

        url = reverse('api:v1:course_run-detail',
                      kwargs={'key': self.draft_course_run.key})
        response = self.client.patch(url, {'title': 'New Title'},
                                     format='json')
        self.assertContains(response,
                            'Failed to set course run data: Client Error 400',
                            status_code=400)

        self.draft_course_run.refresh_from_db()
        self.assertEqual(self.draft_course_run.title_override,
                         None)  # prove we didn't touch the course run object

    @responses.activate
    def test_full_update(self):
        """ Verify full updating is allowed. """
        self.mock_patch_to_studio(self.draft_course_run.key)

        url = reverse('api:v1:course_run-detail',
                      kwargs={'key': self.draft_course_run.key})
        response = self.client.put(
            url,
            {
                'course': self.draft_course_run.course.
                key,  # required, so we need for a put
                'start':
                self.draft_course_run.start,  # required, so we need for a put
                'end':
                self.draft_course_run.end,  # required, so we need for a put
                'title': 'New Title',
            },
            format='json')
        self.assertEqual(response.status_code, 200)

        self.draft_course_run.refresh_from_db()
        self.assertEqual(self.draft_course_run.title_override, 'New Title')

    @ddt.data(
        CourseRunStatus.LegalReview,
        CourseRunStatus.InternalReview,
    )
    def test_patch_put_restrict_when_reviewing(self, status):
        self.draft_course_run.status = status
        self.draft_course_run.save()
        url = reverse('api:v1:course_run-detail',
                      kwargs={'key': self.draft_course_run.key})
        response = self.client.put(
            url,
            {
                'course': self.draft_course_run.course.
                key,  # required, so we need for a put
                'start':
                self.draft_course_run.start,  # required, so we need for a put
                'end':
                self.draft_course_run.end,  # required, so we need for a put
            },
            format='json')
        assert response.status_code == 403

        response = self.client.patch(url, {}, format='json')
        assert response.status_code == 403

    @responses.activate
    def test_patch_put_reset_status(self):
        self.mock_patch_to_studio(self.draft_course_run.key)
        self.draft_course_run.status = CourseRunStatus.Reviewed
        self.draft_course_run.save()
        url = reverse('api:v1:course_run-detail',
                      kwargs={'key': self.draft_course_run.key})
        response = self.client.put(
            url,
            {
                'course': self.draft_course_run.course.
                key,  # required, so we need for a put
                'start':
                self.draft_course_run.start,  # required, so we need for a put
                'end':
                self.draft_course_run.end,  # required, so we need for a put
                'status': 'reviewed',
            },
            format='json')
        assert response.status_code == 200
        self.draft_course_run.refresh_from_db()
        draft_course_run = CourseRun.everything.get(
            key=self.draft_course_run.key, draft=True)
        assert draft_course_run.status == CourseRunStatus.Unpublished

    @ddt.data(
        CourseRunStatus.Unpublished,
        CourseRunStatus.Reviewed,
    )
    @responses.activate
    def test_patch_put_draft_false(self, status):
        """ Verify that setting draft to False moves status to LegalReview. """
        self.mock_patch_to_studio(self.draft_course_run.key)
        self.draft_course_run.status = status
        self.draft_course_run.save()
        url = reverse('api:v1:course_run-detail',
                      kwargs={'key': self.draft_course_run.key})
        response = self.client.put(
            url,
            {
                'course': self.draft_course_run.course.
                key,  # required, so we need for a put
                'start':
                self.draft_course_run.start,  # required, so we need for a put
                'end':
                self.draft_course_run.end,  # required, so we need for a put
                'draft': False,
            },
            format='json')
        assert response.status_code == 200, "Status {}: {}".format(
            response.status_code, response.content)
        draft_course_run = CourseRun.everything.get(
            key=self.draft_course_run.key, draft=True)
        assert draft_course_run.status == CourseRunStatus.LegalReview

    def test_list(self):
        """ Verify the endpoint returns a list of all course runs. """
        url = reverse('api:v1:course_run-list')

        with self.assertNumQueries(13):
            response = self.client.get(url)

        assert response.status_code == 200
        self.assertListEqual(
            response.data['results'],
            self.serialize_course_run(CourseRun.objects.all().order_by(
                Lower('key')),
                                      many=True))

    def test_list_sorted_by_course_start_date(self):
        """ Verify the endpoint returns a list of all course runs sorted by start date. """
        url = '{root}?ordering=start'.format(
            root=reverse('api:v1:course_run-list'))

        with self.assertNumQueries(13):
            response = self.client.get(url)

        assert response.status_code == 200
        self.assertListEqual(
            response.data['results'],
            self.serialize_course_run(
                CourseRun.objects.all().order_by('start'), many=True))

    def test_list_query(self):
        """ Verify the endpoint returns a filtered list of courses """
        course_runs = CourseRunFactory.create_batch(
            3, title='Some random title', course__partner=self.partner)
        CourseRunFactory(title='non-matching name')
        query = 'title:Some random title'
        url = '{root}?q={query}'.format(root=reverse('api:v1:course_run-list'),
                                        query=query)

        with self.assertNumQueries(14):
            response = self.client.get(url)

        actual_sorted = sorted(response.data['results'],
                               key=lambda course_run: course_run['key'])
        expected_sorted = sorted(self.serialize_course_run(course_runs,
                                                           many=True),
                                 key=lambda course_run: course_run['key'])
        self.assertListEqual(actual_sorted, expected_sorted)

    def assert_list_results(self, url, expected, extra_context=None):
        expected = sorted(expected,
                          key=lambda course_run: course_run.key.lower())
        response = self.client.get(url)
        assert response.status_code == 200
        self.assertListEqual(
            response.data['results'],
            self.serialize_course_run(expected,
                                      many=True,
                                      extra_context=extra_context))

    def test_filter_by_keys(self):
        """ Verify the endpoint returns a list of course runs filtered by the specified keys. """
        CourseRun.objects.all().delete()
        expected = CourseRunFactory.create_batch(3,
                                                 course__partner=self.partner)
        keys = ','.join([course.key for course in expected])
        url = '{root}?keys={keys}'.format(
            root=reverse('api:v1:course_run-list'), keys=keys)
        self.assert_list_results(url, expected)

    def test_filter_by_marketable(self):
        """ Verify the endpoint filters course runs to those that are marketable. """
        CourseRun.objects.all().delete()
        expected = CourseRunFactory.create_batch(3,
                                                 course__partner=self.partner)
        for course_run in expected:
            SeatFactory(course_run=course_run)

        CourseRunFactory.create_batch(3,
                                      slug=None,
                                      course__partner=self.partner)
        CourseRunFactory.create_batch(3, slug='', course__partner=self.partner)

        url = reverse('api:v1:course_run-list') + '?marketable=1'
        self.assert_list_results(url, expected)

    def test_filter_by_hidden(self):
        """ Verify the endpoint filters course runs that are hidden. """
        CourseRun.objects.all().delete()
        course_runs = CourseRunFactory.create_batch(
            3, course__partner=self.partner)
        hidden_course_runs = CourseRunFactory.create_batch(
            3, hidden=True, course__partner=self.partner)
        url = reverse('api:v1:course_run-list')
        self.assert_list_results(url, course_runs + hidden_course_runs)
        url = reverse('api:v1:course_run-list') + '?hidden=False'
        self.assert_list_results(url, course_runs)

    def test_filter_by_active(self):
        """ Verify the endpoint filters course runs to those that are active. """
        CourseRun.objects.all().delete()

        # Create course with end date in future and enrollment_end in past.
        end = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=2)
        enrollment_end = datetime.datetime.now(
            pytz.UTC) - datetime.timedelta(days=1)
        CourseRunFactory(end=end,
                         enrollment_end=enrollment_end,
                         course__partner=self.partner)

        # Create course with end date in past and no enrollment_end.
        end = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=2)
        CourseRunFactory(end=end,
                         enrollment_end=None,
                         course__partner=self.partner)

        # Create course with end date in future and enrollment_end in future.
        end = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=2)
        enrollment_end = datetime.datetime.now(
            pytz.UTC) + datetime.timedelta(days=1)
        active_enrollment_end = CourseRunFactory(end=end,
                                                 enrollment_end=enrollment_end,
                                                 course__partner=self.partner)

        # Create course with end date in future and no enrollment_end.
        active_no_enrollment_end = CourseRunFactory(
            end=end, enrollment_end=None, course__partner=self.partner)

        expected = [active_enrollment_end, active_no_enrollment_end]
        url = reverse('api:v1:course_run-list') + '?active=1'
        self.assert_list_results(url, expected)

    def test_filter_by_license(self):
        CourseRun.objects.all().delete()
        course_runs_cc = CourseRunFactory.create_batch(
            3, course__partner=self.partner, license='cc-by-sa')
        CourseRunFactory.create_batch(3,
                                      course__partner=self.partner,
                                      license='')

        url = reverse('api:v1:course_run-list') + '?license=cc-by-sa'
        self.assert_list_results(url, course_runs_cc)

    def test_list_exclude_utm(self):
        """ Verify the endpoint returns marketing URLs without UTM parameters. """
        url = reverse('api:v1:course_run-list') + '?exclude_utm=1'
        self.assert_list_results(url,
                                 CourseRun.objects.all(),
                                 extra_context={'exclude_utm': 1})

    def test_contains_single_course_run(self):
        """ Verify that a single course_run is contained in a query """
        qs = urllib.parse.urlencode({
            'query': 'id:course*',
            'course_run_ids': self.course_run.key,
        })
        url = '{}?{}'.format(reverse('api:v1:course_run-contains'), qs)
        response = self.client.get(url)
        assert response.status_code == 200
        self.assertEqual(response.data,
                         {'course_runs': {
                             self.course_run.key: True
                         }})

    def test_contains_multiple_course_runs(self):
        qs = urllib.parse.urlencode({
            'query':
            'id:course*',
            'course_run_ids':
            '{},{},{}'.format(self.course_run.key, self.course_run_2.key,
                              'abc')
        })
        url = '{}?{}'.format(reverse('api:v1:course_run-contains'), qs)

        response = self.client.get(url)
        assert response.status_code == 200
        self.assertDictEqual(
            response.data, {
                'course_runs': {
                    self.course_run.key: True,
                    self.course_run_2.key: True,
                    'abc': False
                }
            })

    @ddt.data({'params': {
        'course_run_ids': 'a/b/c'
    }}, {'params': {
        'query': 'id:course*'
    }}, {'params': {}})
    @ddt.unpack
    def test_contains_missing_parameter(self, params):
        qs = urllib.parse.urlencode(params)
        url = '{}?{}'.format(reverse('api:v1:course_run-contains'), qs)

        response = self.client.get(url)
        assert response.status_code == 400

    def test_options(self):
        url = reverse('api:v1:course_run-detail',
                      kwargs={'key': self.course_run.key})
        response = self.client.options(url)
        self.assertEqual(response.status_code, 200)

        data = response.data['actions']['PUT']
        self.assertEqual(
            data['level_type']['choices'],
            [{
                'display_name': self.course_run.level_type.name,
                'value': self.course_run.level_type.name
            }, {
                'display_name': self.course_run_2.level_type.name,
                'value': self.course_run_2.level_type.name
            }, {
                'display_name': self.draft_course_run.level_type.name,
                'value': self.draft_course_run.level_type.name
            }])
        self.assertEqual(data['content_language']['choices'],
                         [{
                             'display_name': x.name,
                             'value': x.code
                         } for x in LanguageTag.objects.all()])
        self.assertTrue(LanguageTag.objects.count() > 0)

    def test_editable_list_gives_drafts(self):
        # We delete self.course_run_2 and self.draft_course_run here so we can test that specifically
        # draft and extra are the only ones showing up.
        self.course_run_2.delete()
        self.draft_course_run.delete()

        draft = CourseRunFactory(course__partner=self.partner,
                                 uuid=self.course_run.uuid,
                                 key=self.course_run.key,
                                 draft=True)
        self.course_run.draft_version = draft
        self.course_run.save()
        extra = CourseRunFactory(course__partner=self.partner)

        response = self.client.get(
            reverse('api:v1:course_run-list') + '?editable=1')
        actual_sorted = sorted(response.data['results'],
                               key=lambda course_run: course_run['key'])
        expected_sorted = sorted(self.serialize_course_run([draft, extra],
                                                           many=True),
                                 key=lambda course_run: course_run['key'])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(actual_sorted, expected_sorted)

    def test_editable_get_gives_drafts(self):
        draft = CourseRunFactory(course__partner=self.partner,
                                 uuid=self.course_run.uuid,
                                 key=self.course_run.key,
                                 draft=True)
        self.course_run.draft_version = draft
        self.course_run.save()
        extra = CourseRunFactory(course__partner=self.partner)

        response = self.client.get(
            reverse('api:v1:course_run-detail',
                    kwargs={'key': self.course_run.key}) + '?editable=1')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data,
                         self.serialize_course_run(draft, many=False))

        response = self.client.get(
            reverse('api:v1:course_run-detail', kwargs={'key': extra.key}) +
            '?editable=1')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data,
                         self.serialize_course_run(extra, many=False))

    def test_list_query_with_editable_raises_exception(self):
        """ Verify the endpoint raises an exception if both a q param and editable=1 are passed in """
        query = 'title:Some random title'
        url = '{root}?q={query}&editable=1'.format(
            root=reverse('api:v1:course_run-list'), query=query)

        with pytest.raises(EditableAndQUnsupported) as exc:
            self.client.get(url)

        self.assertEqual(
            str(exc.value),
            'Specifying both editable=1 and a q parameter is not supported.')
Esempio n. 36
0
class AutocompleteTests(TestCase):
    """ Tests for autocomplete lookups."""
    def setUp(self):
        super(AutocompleteTests, self).setUp()
        self.user = UserFactory(is_staff=True)
        self.client.login(username=self.user.username, password=USER_PASSWORD)
        self.courses = factories.CourseFactory.create_batch(3, title='Some random course title')
        for course in self.courses:
            factories.CourseRunFactory(course=course)
        self.organizations = factories.OrganizationFactory.create_batch(3)

    @ddt.data('dum', 'ing')
    def test_course_autocomplete(self, search_key):
        """ Verify course autocomplete returns the data. """
        response = self.client.get(reverse('admin_metadata:course-autocomplete'))
        data = json.loads(response.content.decode('utf-8'))
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(data['results']), 3)
        # update the first course title
        self.courses[0].key = 'edx/dummy/key'
        self.courses[0].title = 'this is some thing new'
        self.courses[0].save()
        response = self.client.get(
            reverse('admin_metadata:course-autocomplete') + '?q={title}'.format(title=search_key)
        )
        data = json.loads(response.content.decode('utf-8'))
        self.assertEqual(data['results'][0]['text'], str(self.courses[0]))

    def test_course_autocomplete_un_authorize_user(self):
        """ Verify course autocomplete returns empty list for un-authorized users. """
        self._make_user_non_staff()
        response = self.client.get(reverse('admin_metadata:course-autocomplete'))
        data = json.loads(response.content.decode('utf-8'))
        self.assertEqual(data['results'], [])

    @ddt.data('ing', 'dum')
    def test_course_run_autocomplete(self, search_key):
        """ Verify course run autocomplete returns the data. """
        response = self.client.get(reverse('admin_metadata:course-run-autocomplete'))
        data = json.loads(response.content.decode('utf-8'))
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(data['results']), 3)
        # update the first course title
        course = self.courses[0]
        course.title = 'this is some thing new'
        course.save()
        course_run = self.courses[0].course_runs.first()
        course_run.key = 'edx/dummy/testrun'
        course_run.save()

        response = self.client.get(
            reverse('admin_metadata:course-run-autocomplete') + '?q={q}'.format(q=search_key)
        )
        data = json.loads(response.content.decode('utf-8'))
        self.assertEqual(data['results'][0]['text'], str(course_run))

    def test_course_run_autocomplete_un_authorize_user(self):
        """ Verify course run autocomplete returns empty list for un-authorized users. """
        self._make_user_non_staff()
        response = self.client.get(reverse('admin_metadata:course-run-autocomplete'))
        data = json.loads(response.content.decode('utf-8'))
        self.assertEqual(data['results'], [])

    @ddt.data('irc', 'ing')
    def test_organization_autocomplete(self, search_key):
        """ Verify Organization autocomplete returns the data. """
        response = self.client.get(reverse('admin_metadata:organisation-autocomplete'))
        data = json.loads(response.content.decode('utf-8'))
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(data['results']), 3)

        self.organizations[0].key = 'Mirco'
        self.organizations[0].name = 'testing name'
        self.organizations[0].save()

        response = self.client.get(
            reverse('admin_metadata:organisation-autocomplete') + '?q={key}'.format(
                key=search_key
            )
        )
        data = json.loads(response.content.decode('utf-8'))
        self.assertEqual(data['results'][0]['text'], str(self.organizations[0]))
        self.assertEqual(len(data['results']), 1)

    def test_organization_autocomplete_un_authorize_user(self):
        """ Verify Organization autocomplete returns empty list for un-authorized users. """
        self._make_user_non_staff()
        response = self.client.get(reverse('admin_metadata:organisation-autocomplete'))
        data = json.loads(response.content.decode('utf-8'))
        self.assertEqual(data['results'], [])

    @ddt.data('dummyurl', 'testing')
    def test_video_autocomplete(self, search_key):
        """ Verify video autocomplete returns the data. """
        response = self.client.get(reverse('admin_metadata:video-autocomplete'))
        data = json.loads(response.content.decode('utf-8'))
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(data['results']), 6)

        self.courses[0].video.src = 'http://www.youtube.com/dummyurl'
        self.courses[0].video.description = 'testing description'
        self.courses[0].video.save()

        response = self.client.get(
            reverse('admin_metadata:video-autocomplete') + '?q={key}'.format(
                key=search_key
            )
        )
        data = json.loads(response.content.decode('utf-8'))
        self.assertEqual(data['results'][0]['text'], str(self.courses[0].video))
        self.assertEqual(len(data['results']), 1)

    def test_video_autocomplete_un_authorize_user(self):
        """ Verify video autocomplete returns empty list for un-authorized users. """
        self._make_user_non_staff()
        response = self.client.get(reverse('admin_metadata:video-autocomplete'))
        data = json.loads(response.content.decode('utf-8'))
        self.assertEqual(data['results'], [])

    def _make_user_non_staff(self):
        self.client.logout()
        self.user = UserFactory(is_staff=False)
        self.user.save()
        self.client.login(username=self.user.username, password=USER_PASSWORD)
Esempio n. 37
0
 def test_team_admin_without_full_name(self):
     """
     Verify that UserModelChoiceField returns `username` if `full_name` is empty.
     """
     user = UserFactory(username='******', full_name='', first_name='', last_name='')
     self._assert_choice_label(user.username)
Esempio n. 38
0
 def setUp(self):
     super(SendForReviewEmailTests, self).setUp()
     self.user = UserFactory()
     self.course_state = factories.CourseStateFactory()
 def setUp(self):
     super(JournalBundleViewSetTests, self).setUp()
     self.user = UserFactory(is_staff=True)
     self.journal_bundle = JournalBundleFactory()
     self.client.login(username=self.user.username, password=USER_PASSWORD)
     self.journal_bundle_url = reverse('journal:api:v1:journal_bundle-list')
Esempio n. 40
0
 def setUp(self):
     super(CourseMarkAsReviewedEmailTests, self).setUp()
     self.user = UserFactory()
     self.course_state = factories.CourseStateFactory()
Esempio n. 41
0
 def setUp(self):
     super(ManagementCommandViewTestMixin, self).setUp()
     self.superuser = UserFactory(is_superuser=True)
     self.client.force_authenticate(self.superuser)  # pylint: disable=no-member