def test_data(self):
        course = CourseFactory()
        image = course.image
        video = course.video

        request = make_request()

        CourseRunFactory.create_batch(3, course=course)
        serializer = CourseSerializer(course, context={'request': request})

        expected = {
            'key': course.key,
            'title': course.title,
            'short_description': course.short_description,
            'full_description': course.full_description,
            'level_type': course.level_type.name,
            'subjects': [],
            'prerequisites': [],
            'expected_learning_items': [],
            'image': ImageSerializer(image).data,
            'video': VideoSerializer(video).data,
            'owners': [],
            'sponsors': [],
            'modified': json_date_format(course.modified),  # pylint: disable=no-member
            'course_runs': CourseRunSerializer(course.course_runs, many=True, context={'request': request}).data,
            'marketing_url': '{url}?{params}'.format(
                url=course.marketing_url,
                params=urlencode({
                    'utm_source': request.user.username,
                    'utm_medium': request.user.referral_tracking_id,
                })
            )
        }

        self.assertDictEqual(serializer.data, expected)
示例#2
0
    def test_exclude_utm(self):
        request = make_request()
        course = CourseFactory()
        CourseRunFactory.create_batch(3, course=course)
        serializer = self.serializer_class(course, context={'request': request, 'exclude_utm': 1})

        self.assertEqual(serializer.data['marketing_url'], course.marketing_url)
示例#3
0
 def test_data(self):
     request = make_request()
     organizations = OrganizationFactory()
     course = CourseFactory(authoring_organizations=[organizations])
     CourseRunFactory.create_batch(2, course=course)
     serializer = self.serializer_class(course, context={'request': request})
     expected = self.get_expected_data(course, request)
     self.assertDictEqual(serializer.data, expected)
示例#4
0
 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)
     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 setUp(self):
        super(SearchQuerySetWrapperTests, self).setUp()
        title = 'Some random course'
        query = 'title:' + title

        CourseRunFactory.create_batch(3, title=title)
        self.search_queryset = SearchQuerySet().models(CourseRun).raw_search(query).load_all()
        self.course_runs = [e.object for e in self.search_queryset]
        self.wrapper = SearchQuerySetWrapper(self.search_queryset)
示例#6
0
 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_list_key_filter(self):
        """ Verify the endpoint returns a list of course runs filtered by the specified keys. """
        course_runs = CourseRunFactory.create_batch(3)
        course_runs = sorted(course_runs, key=lambda course: course.key.lower())
        keys = ','.join([course.key for course in course_runs])
        url = '{root}?keys={keys}'.format(root=reverse('api:v1:course_run-list'), keys=keys)

        response = self.client.get(url)
        self.assertListEqual(response.data['results'], self.serialize_course_run(course_runs, many=True))
示例#8
0
    def create_program(self):
        organizations = OrganizationFactory.create_batch(2)
        person = PersonFactory()

        courses = CourseFactory.create_batch(3)
        for course in courses:
            CourseRunFactory.create_batch(2, course=course, staff=[person], start=datetime.now())

        return ProgramFactory(
            courses=courses,
            authoring_organizations=organizations,
            credit_backing_organizations=organizations,
            corporate_endorsements=CorporateEndorsementFactory.create_batch(1),
            individual_endorsements=EndorsementFactory.create_batch(1),
            expected_learning_items=ExpectedLearningItemFactory.create_batch(1),
            job_outlook_items=JobOutlookItemFactory.create_batch(1),
            banner_image=make_image_file('test_banner.jpg'),
            video=VideoFactory(),
            order_courses_by_start_date=False,
        )
 def setUp(self):
     super(CourseRunSerializerTests, self).setUp()
     self.course_run = CourseRunFactory()
     self.course_run.lms_course_id = 'course-v1:edX+DemoX+Demo_Course'
     self.person = PersonFactory()
     self.discovery_course_run = DiscoveryCourseRunFactory(
         key=self.course_run.lms_course_id, staff=[self.person])
     self.request = RequestFactory()
     self.user = UserFactory()
     self.request.user = self.user
     self.course_state = CourseRunStateFactory(
         course_run=self.course_run, owner_role=PublisherUserRole.Publisher)
示例#10
0
    def test_update_run(self):
        run = CourseRunFactory()
        self.api.update_course_run_details_in_studio(run)

        expected_data = self.make_studio_data(run,
                                              add_pacing=False,
                                              add_schedule=False)
        self.assertEqual(self.client.course_runs.call_args_list,
                         [mock.call(run.key)])
        self.assertEqual(
            self.client.course_runs.return_value.patch.call_args_list[0][0][0],
            expected_data)
    def test_unpublished_status(self):
        """ Verify the endpoint does not return CourseRuns in a non-published state. """
        self.course_run.status = CourseRunStatus.Unpublished
        self.course_run.save()

        CourseRunFactory(course=self.course, status=CourseRunStatus.Unpublished)

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

        self.assertEqual(response.status_code, 200)
        root = ET.fromstring(response.content)
        self.assertEqual(0, len(root.findall('product')))
示例#12
0
    def create_course_with_basic_active_course_run(self):
        course = AlgoliaProxyCourseFactory(partner=self.__class__.edxPartner)

        course_run = CourseRunFactory(course=course,
                                      start=self.YESTERDAY,
                                      end=self.YESTERDAY,
                                      status=CourseRunStatus.Published)
        SeatFactory(
            course_run=course_run,
            type=SeatTypeFactory.audit(),
        )
        return course
示例#13
0
    def test_courses_with_time_range_query(self):
        catalog = CatalogFactory(query='start:[2015-01-01 TO 2015-12-01]')
        course_run_1 = CourseRunFactory(
            start=datetime.datetime(2015, 9, 1, tzinfo=pytz.UTC),
            status=CourseRunStatus.Published,
            type__is_marketable=True,
        )
        course_run_2 = CourseRunFactory(
            start=datetime.datetime(2015, 10, 13, tzinfo=pytz.UTC),
            status=CourseRunStatus.Published,
            type__is_marketable=True,
        )
        SeatFactory.create(course_run=course_run_1)
        SeatFactory.create(course_run=course_run_2)
        call_command('search_index', '--rebuild', '-f')
        url = reverse('api:v1:catalog-courses', kwargs={'id': catalog.id})
        response = self.client.get(url)

        assert response.status_code == 200
        assert response.data['results'] == self.serialize_catalog_course(
            [course_run_1.course, course_run_2.course], many=True)
示例#14
0
 def test_retrieve_with_sorting_flag(self, order_courses_by_start_date):
     """ Verify the number of queries is the same with sorting flag set to true. """
     course_list = CourseFactory.create_batch(3)
     for course in course_list:
         CourseRunFactory(course=course)
     program = ProgramFactory(
         courses=course_list,
         order_courses_by_start_date=order_courses_by_start_date)
     with self.assertNumQueries(26):
         response = self.assert_retrieve_success(program)
     assert response.data == self.serialize_program(program)
     self.assertEqual(course_list, list(program.courses.all()))  # pylint: disable=no-member
示例#15
0
    def setUp(self):
        super(ImportCoursesTests, self).setUp()
        self.course = CourseFactory()
        self.course_runs = CourseRunFactory.create_batch(3, course=self.course)
        self.course.canonical_course_run = self.course_runs[2]
        self.course.save()

        # add multiple courses.
        self.course_2 = CourseFactory()

        self.command_name = 'import_metadata_courses'
        self.command_args = ['--start_id={}'.format(self.course.id), '--end_id={}'.format(self.course.id)]
示例#16
0
    def test_enrollable(self):
        """ Verify the method returns only course runs currently open for enrollment. """
        past = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=2)
        future = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=2)

        enrollable = CourseRunFactory(enrollment_start=past,
                                      enrollment_end=future)
        enrollable_no_enrollment_end = CourseRunFactory(enrollment_start=past,
                                                        enrollment_end=None)
        enrollable_no_enrollment_start = CourseRunFactory(
            enrollment_start=None, enrollment_end=future)
        CourseRunFactory(enrollment_start=future)
        CourseRunFactory(enrollment_end=past)

        # order doesn't matter
        assert list(CourseRun.objects.enrollable().order_by('id')) == sorted(
            [
                enrollable, enrollable_no_enrollment_end,
                enrollable_no_enrollment_start
            ],
            key=lambda x: x.id)
示例#17
0
 def test_with_no_org(self):
     org1 = OrganizationFactory()
     course = CourseFactory(authoring_organizations=[org1])
     with mock.patch.object(MarketingSitePeople,
                            'update_or_publish_person'):
         person1 = PersonFactory(partner=self.partner)
         PersonFactory(partner=self.partner)
         CourseRunFactory(staff=[person1], course=course)
         url = '{url}?org='.format(url=self.people_list_url)
         response = self.client.get(url)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(len(response.data['results']), 0)
示例#18
0
 def test_typeahead_multiple_results(self):
     """ Verify the typeahead responses always returns a limited number of results, even if there are more hits. """
     RESULT_COUNT = TypeaheadSearchView.RESULT_COUNT
     title = "Test"
     for i in range(RESULT_COUNT + 1):
         CourseRunFactory(title="{}{}".format(title, i), course__partner=self.partner)
         ProgramFactory(title="{}{}".format(title, i), status=ProgramStatus.Active, partner=self.partner)
     response = self.get_response({'q': title})
     self.assertEqual(response.status_code, 200)
     response_data = response.json()
     self.assertEqual(len(response_data['course_runs']), RESULT_COUNT)
     self.assertEqual(len(response_data['programs']), RESULT_COUNT)
示例#19
0
    def mock_courses_api(self):
        # Create existing seats to be removed by ingest
        audit_run = CourseRunFactory(title_override='audit', key='audit/course/run')
        verified_run = CourseRunFactory(title_override='verified', key='verified/course/run')
        credit_run = CourseRunFactory(title_override='credit', key='credit/course/run')
        no_currency_run = CourseRunFactory(title_override='no currency', key='nocurrency/course/run')

        SeatFactory(course_run=audit_run, type=Seat.PROFESSIONAL)
        SeatFactory(course_run=verified_run, type=Seat.PROFESSIONAL)
        SeatFactory(course_run=credit_run, type=Seat.PROFESSIONAL)
        SeatFactory(course_run=no_currency_run, type=Seat.PROFESSIONAL)

        bodies = mock_data.ECOMMERCE_API_BODIES
        url = self.api_url + 'courses/'
        responses.add_callback(
            responses.GET,
            url,
            callback=mock_api_callback(url, bodies),
            content_type=JSON
        )
        return bodies
示例#20
0
 def test_get_include_published_course_run(self,
                                           published_course_runs_only):
     """
     Verify the endpoint returns hides unpublished programs if
     the 'published_course_runs_only' flag is set to True
     """
     CourseRunFactory(status=CourseRunStatus.Published, course=self.course)
     CourseRunFactory(status=CourseRunStatus.Unpublished,
                      course=self.course)
     url = reverse('api:v1:course-detail', kwargs={'key': self.course.key})
     url = '{}?published_course_runs_only={}'.format(
         url, published_course_runs_only)
     response = self.client.get(url)
     self.assertEqual(response.status_code, 200)
     self.assertEqual(
         response.data,
         self.serialize_course(self.course,
                               extra_context={
                                   'published_course_runs_only':
                                   published_course_runs_only
                               }))
示例#21
0
 def test_active_course_run_beats_no_active_course_run(self):
     course_1 = self.create_course_with_basic_active_course_run()
     course_2 = AlgoliaProxyCourseFactory(partner=self.__class__.edxPartner)
     CourseRunFactory(
         course=course_2,
         start=self.YESTERDAY,
         end=self.YESTERDAY,
         enrollment_end=self.YESTERDAY,
         status=CourseRunStatus.Published
     )
     assert course_1.availability_rank
     assert not course_2.availability_rank
示例#22
0
    def test_typeahead_deduplicate_course_runs(self):
        """ Verify the typeahead response will only include the first course run per course. """
        RESULT_COUNT = TypeaheadSearchView.RESULT_COUNT
        title = "Test"
        course1 = CourseFactory(partner=self.partner)
        course2 = CourseFactory(partner=self.partner)
        for i in range(RESULT_COUNT):
            CourseRunFactory(title=f"{title}{course1.title}{i}",
                             course=course1)
        for i in range(RESULT_COUNT):
            CourseRunFactory(title=f"{title}{course2.title}{i}",
                             course=course2)
        response = self.get_response({'q': title})
        assert response.status_code == 200
        response_data = response.json()

        # There are many runs for both courses, but only one from each will be included
        course_runs = response_data['course_runs']
        assert len(course_runs) == 2
        # compare course titles embedded in course run title to ensure that course runs belong to different courses
        assert course_runs[0]['title'][4:-1] != course_runs[1]['title'][4:-1]
示例#23
0
    def test_active(self):
        """ Verify the method filters the Courses to those with active course runs. """
        now = datetime.datetime.now(pytz.UTC)
        active_course_end = now + datetime.timedelta(days=60)
        inactive_course_end = now - datetime.timedelta(days=15)
        open_enrollment_end = now + datetime.timedelta(days=30)
        closed_enrollment_end = now - datetime.timedelta(days=30)

        # Create an active enrollable course
        active_course = CourseRunFactory(enrollment_end=open_enrollment_end,
                                         end=active_course_end).course

        # Create an active unenrollable course
        CourseRunFactory(enrollment_end=closed_enrollment_end,
                         end=active_course_end,
                         course__title='ABC Test Course 2')

        # Create an inactive unenrollable course
        CourseRunFactory(enrollment_end=closed_enrollment_end,
                         end=inactive_course_end)

        # Create an active course with unrestricted enrollment
        course_without_enrollment_end = CourseRunFactory(
            enrollment_end=None, end=active_course_end).course

        # Create an inactive course with unrestricted enrollment
        CourseRunFactory(enrollment_end=None, end=inactive_course_end)

        # Create course with end date is NULL
        course_without_end = CourseRunFactory(
            enrollment_end=open_enrollment_end, end=None).course

        self.assertEqual(
            set(Course.objects.active()),
            {active_course, course_without_enrollment_end, course_without_end})
示例#24
0
    def test_published(self):
        person = PersonFactory()
        org = OrganizationFactory()
        primary = DiscoveryCourseRunFactory(key=self.course_run.lms_course_id, staff=[person],
                                            status=CourseRunStatus.Unpublished, announcement=None,
                                            course__partner=self.partner, end=None, enrollment_end=None)
        second = DiscoveryCourseRunFactory(course=primary.course, status=CourseRunStatus.Published, end=None,
                                           enrollment_end=None, start=(primary.start + datetime.timedelta(days=1)))
        third = DiscoveryCourseRunFactory(course=primary.course, status=CourseRunStatus.Published,
                                          end=datetime.datetime(2010, 1, 1, tzinfo=UTC), enrollment_end=None)
        primary.course.authoring_organizations.add(org)
        self.course.organizations.add(org)
        ensure_draft_world(DiscoveryCourse.objects.get(pk=primary.course.pk))

        pc = UserFactory()
        factories.CourseUserRoleFactory(course=self.course, role=PublisherUserRole.ProjectCoordinator, user=pc)
        factories.OrganizationUserRoleFactory(organization=org, role=InternalUserRole.ProjectCoordinator, user=pc)

        self.mock_api_client()

        lookup_value = getattr(primary, self.publisher.unique_field)
        self.mock_node_retrieval(self.publisher.node_lookup_field, lookup_value)
        lookup_value = getattr(third, self.publisher.unique_field)
        self.mock_node_retrieval(self.publisher.node_lookup_field, lookup_value)

        self.mock_get_redirect_form()
        self.mock_add_redirect()

        self.course_run.course_run_state.name = CourseRunStateChoices.Approved
        self.course_run.course_run_state.change_state(CourseRunStateChoices.Published, self.user, self.site)
        primary.refresh_from_db()
        second.refresh_from_db()
        third.refresh_from_db()

        self.assertIsNotNone(primary.announcement)
        self.assertEqual(primary.status, CourseRunStatus.Published)
        self.assertEqual(second.status, CourseRunStatus.Published)  # doesn't change end=None runs
        self.assertEqual(third.status, CourseRunStatus.Unpublished)  # does change archived runs

        # Check email was sent (only one - from old publisher, not new publisher flow)
        assert len(mail.outbox) == 1
        message = mail.outbox[0]
        self.assertTrue(message.subject.startswith('Publication complete: '))
        self.assertEqual(message.to, [self.user.email])
        self.assertEqual(message.cc, [pc.email])
示例#25
0
    def test_facet_counts_includes_distinct_counts(self):
        """ Verify that facet_counts include distinct counts. """
        course = CourseFactory()
        CourseRunFactory(title='foo',
                         pacing_type='self_paced',
                         hidden=True,
                         course=course)
        CourseRunFactory(title='foo',
                         pacing_type='self_paced',
                         hidden=True,
                         course=course)
        CourseRunFactory(title='foo',
                         pacing_type='instructor_paced',
                         hidden=False,
                         course=course)

        # Make sure to add both a field facet and a query facet so that we can be sure that both work.
        queryset = SearchQuerySet().filter(title='foo').models(CourseRun)
        queryset = queryset.facet('pacing_type').query_facet(
            'hidden', 'hidden:true')
        dc_queryset = DistinctCountsSearchQuerySet.from_queryset(
            queryset).with_distinct_counts('aggregation_key')
        facet_counts = dc_queryset.facet_counts()

        # Field facets are expected to be formatted as a list of three-tuples (field_value, count, distinct_count)
        for val, count, distinct_count in facet_counts['fields'][
                'pacing_type']:
            assert val in {'self_paced', 'instructor_paced'}
            if val == 'self_paced':
                assert count == 2
                assert distinct_count == 1
            elif val == 'instructor_paced':
                assert count == 1
                assert distinct_count == 1

        # Query facets are expected to be formatted as a dictionary mapping facet_names to two-tuples (count,
        # distinct_count)
        hidden_count, hidden_distinct_count = facet_counts['queries']['hidden']
        assert hidden_count == 2
        assert hidden_distinct_count == 1
示例#26
0
    def test_requires_salesforce_update(self):
        org = OrganizationFactoryNoSignals(
            partner=self.salesforce_config.partner)
        org.description = 'changed'
        assert requires_salesforce_update('organization', org) is False

        org.name = 'changed'
        assert requires_salesforce_update('organization', org) is True

        course = CourseFactory()
        course.short_description = 'changed'
        assert requires_salesforce_update('course', course) is False

        course.title = 'changed'
        assert requires_salesforce_update('course', course) is True

        course_run = CourseRunFactory()
        course_run.full_description = 'changed'
        assert requires_salesforce_update('course_run', course_run) is False

        course_run.key = 'changed'
        assert requires_salesforce_update('course_run', course_run) is True
示例#27
0
 def test_org_synonyms(self):
     """ Test that synonyms work for organization names """
     title = 'UniversityX'
     authoring_organizations = [OrganizationFactory(name='University')]
     CourseRunFactory(title=title,
                      course__partner=self.partner,
                      authoring_organizations=authoring_organizations)
     ProgramFactory(title=title,
                    partner=self.partner,
                    authoring_organizations=authoring_organizations)
     response1 = self.process_response({'q': title})
     response2 = self.process_response({'q': 'University'})
     self.assertDictEqual(response1, response2)
示例#28
0
    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(self):
        """ Verify the endpoint returns a filtered list of courses """
        title = 'Some random title'
        course_runs = CourseRunFactory.create_batch(3, title=title)
        CourseRunFactory(title='non-matching name')
        query = 'title:' + title
        url = '{root}?q={query}'.format(root=reverse('api:v1:course_run-list'), query=query)

        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)
示例#30
0
    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)
示例#31
0
    def test_course_run_autocomplete(self, admin_client):
        course_runs = CourseRunFactory.create_batch(3)
        path = reverse('admin_metadata:course-run-autocomplete')
        response = admin_client.get(path)
        data = json.loads(response.content.decode('utf-8'))
        assert response.status_code == 200
        assert len(data['results']) == 3

        # Search for substrings of course run keys and titles
        course_run = course_runs[0]
        self.assert_valid_query_result(admin_client, path, course_run.key[14:],
                                       course_run)
        self.assert_valid_query_result(admin_client, path,
                                       course_run.title[12:], course_run)

        course = course_run.course
        CourseRunFactory.create_batch(3, course=course)
        response = admin_client.get(path + '?forward={f}'.format(
            f=json.dumps({'course': course.pk})))
        data = json.loads(response.content.decode('utf-8'))
        assert response.status_code == 200
        assert len(data['results']) == 4
示例#32
0
    def test_ensure_draft_world_not_draft_course_run_given(self):
        course = CourseFactory()
        course_run = CourseRunFactory(course=course)
        verified_seat = SeatFactory(type='verified', course_run=course_run)
        audit_seat = SeatFactory(type='audit', course_run=course_run)
        course_run.seats.add(verified_seat, audit_seat)

        ensured_draft_course_run = utils.ensure_draft_world(course_run)
        not_draft_course_run = CourseRun.objects.get(uuid=course_run.uuid)

        self.assertNotEqual(ensured_draft_course_run, not_draft_course_run)
        self.assertEqual(ensured_draft_course_run.uuid,
                         not_draft_course_run.uuid)
        self.assertTrue(ensured_draft_course_run.draft)
        self.assertNotEqual(ensured_draft_course_run.course,
                            not_draft_course_run.course)
        self.assertEqual(ensured_draft_course_run.course.uuid,
                         not_draft_course_run.course.uuid)

        # Check slugs are equal
        self.assertEqual(ensured_draft_course_run.slug,
                         not_draft_course_run.slug)

        # Seat checks
        draft_seats = ensured_draft_course_run.seats.all()
        not_draft_seats = not_draft_course_run.seats.all()
        self.assertNotEqual(draft_seats, not_draft_seats)
        self.assertEqual(len(draft_seats), len(not_draft_seats))
        for i, __ in enumerate(draft_seats):
            self.assertEqual(draft_seats[i].price, not_draft_seats[i].price)
            self.assertEqual(draft_seats[i].sku, not_draft_seats[i].sku)
            self.assertNotEqual(draft_seats[i].course_run,
                                not_draft_seats[i].course_run)
            self.assertEqual(draft_seats[i].course_run.uuid,
                             not_draft_seats[i].course_run.uuid)
            self.assertEqual(draft_seats[i].official_version,
                             not_draft_seats[i])
            self.assertEqual(not_draft_seats[i].draft_version, draft_seats[i])

        # Check draft course is also created
        draft_course = ensured_draft_course_run.course
        not_draft_course = Course.objects.get(uuid=course.uuid)
        self.assertNotEqual(draft_course, not_draft_course)
        self.assertEqual(draft_course.uuid, not_draft_course.uuid)
        self.assertTrue(draft_course.draft)

        # Check official and draft versions match up
        self.assertEqual(ensured_draft_course_run.official_version,
                         not_draft_course_run)
        self.assertEqual(not_draft_course_run.draft_version,
                         ensured_draft_course_run)
    def setUp(self):
        super(AffiliateWindowViewSetTests, self).setUp()
        self.user = UserFactory()
        self.client.force_authenticate(self.user)
        self.catalog = CatalogFactory(query='*:*', viewers=[self.user])

        self.enrollment_end = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30)
        self.course_end = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=60)
        self.course_run = CourseRunFactory(enrollment_end=self.enrollment_end, end=self.course_end)

        self.seat_verified = SeatFactory(course_run=self.course_run, type=Seat.VERIFIED)
        self.course = self.course_run.course
        self.affiliate_url = reverse('api:v1:partners:affiliate_window-detail', kwargs={'pk': self.catalog.id})
        self.refresh_index()
示例#34
0
    def test_typeahead_authoring_organizations_partial_search(self):
        """ Test typeahead response with partial organization matching. """
        authoring_organizations = OrganizationFactory.create_batch(3)
        course_run = CourseRunFactory(authoring_organizations=authoring_organizations, course__partner=self.partner)
        program = ProgramFactory(authoring_organizations=authoring_organizations, partner=self.partner)
        partial_key = authoring_organizations[0].key[0:5]

        response = self.get_response({'q': partial_key})
        self.assertEqual(response.status_code, 200)
        expected = {
            'course_runs': [self.serialize_course_run_search(course_run)],
            'programs': [self.serialize_program_search(program)]
        }
        self.assertDictEqual(response.data, expected)
示例#35
0
 def test_partial_term_search(self):
     """ Test typeahead response with partial term search. """
     title = "Learn Data Science"
     course_run = CourseRunFactory(title=title, course__partner=self.partner)
     program = ProgramFactory(title=title, status=ProgramStatus.Active, partner=self.partner)
     query = "Data Sci"
     response = self.get_response({'q': query})
     self.assertEqual(response.status_code, 200)
     response_data = response.json()
     expected_response_data = {
         'course_runs': [self.serialize_course_run_search(course_run)],
         'programs': [self.serialize_program_search(program)]
     }
     self.assertDictEqual(response_data, expected_response_data)
 def create_current_upgradeable_course(self, **kwargs):
     course = AlgoliaProxyCourseFactory(partner=self.__class__.edxPartner)
     current_upgradeable_course_run = CourseRunFactory(
         course=course,
         start=self.YESTERDAY,
         end=self.IN_FIFTEEN_DAYS,
         enrollment_end=self.IN_FIFTEEN_DAYS,
         status=CourseRunStatus.Published,
         **kwargs)
     SeatFactory(course_run=current_upgradeable_course_run,
                 type=SeatTypeFactory.verified(),
                 upgrade_deadline=self.TOMORROW,
                 price=10)
     return course
    def test_get_include_published_course_run(self,
                                              published_course_runs_only):
        """
        Verify the endpoint returns hides unpublished programs if
        the 'published_course_runs_only' flag is set to True
        """
        CourseRunFactory(status=CourseRunStatus.Published, course=self.course)
        unpublished_course_run = CourseRunFactory(
            status=CourseRunStatus.Unpublished, course=self.course)

        url = reverse('api:v1:course-detail', kwargs={'key': self.course.key})
        url = '{}?published_course_runs_only={}'.format(
            url, published_course_runs_only)

        response = self.client.get(url)

        assert response.status_code == 200

        if published_course_runs_only:
            # Emulate prefetching behavior.
            unpublished_course_run.delete()

        assert response.data == self.serialize_course(self.course)
示例#38
0
 def test_list_with_org_multiple(self):
     org1 = OrganizationFactory()
     org2 = OrganizationFactory()
     course1 = CourseFactory(authoring_organizations=[org1])
     course2 = CourseFactory(authoring_organizations=[org2])
     with mock.patch.object(MarketingSitePeople,
                            'update_or_publish_person'):
         person1 = PersonFactory(partner=self.partner)
         person2 = PersonFactory(partner=self.partner)
         PersonFactory(partner=self.partner)
         CourseRunFactory(staff=[person1], course=course1)
         CourseRunFactory(staff=[person2], course=course2)
         url = '{url}?org={org1_key}&org={org2_key}'.format(
             url=self.people_list_url,
             org1_key=org1.key,
             org2_key=org2.key,
         )
         response = self.client.get(url)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(len(response.data['results']), 2)
         self.assertCountEqual(
             response.data['results'],
             self.serialize_person([person1, person2], many=True))
示例#39
0
 def setUp(self):
     super(CatalogViewSetTests, self).setUp()
     self.user = UserFactory(is_staff=True, is_superuser=True)
     self.client.force_authenticate(self.user)
     self.catalog = CatalogFactory(query='title:abc*')
     enrollment_end = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30)
     course_end = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=60)
     self.course_run = CourseRunFactory(
         enrollment_end=enrollment_end,
         end=course_end,
         course__title='ABC Test Course'
     )
     self.course = self.course_run.course
     self.refresh_index()
示例#40
0
    def create_upgradeable_course_starting_soon(self):
        course = AlgoliaProxyCourseFactory(partner=self.__class__.edxPartner)
        upgradeable_course_run = CourseRunFactory(
            course=course,
            start=self.TOMORROW,
            end=self.IN_FIFTEEN_DAYS,
            enrollment_end=self.IN_FIFTEEN_DAYS,
            status=CourseRunStatus.Published)

        SeatFactory(course_run=upgradeable_course_run,
                    type=SeatTypeFactory.verified(),
                    upgrade_deadline=self.IN_FIFTEEN_DAYS,
                    price=10)
        return course
示例#41
0
    def test_with_exclusions(self):
        """
        Test serializer with course_run exclusions within program
        """
        request = make_request()
        course = CourseFactory()
        excluded_runs = []
        course_runs = CourseRunFactory.create_batch(2, course=course)
        excluded_runs.append(course_runs[0])
        program = ProgramFactory(courses=[course], excluded_course_runs=excluded_runs)

        serializer_context = {'request': request, 'program': program, 'course_runs': list(program.course_runs)}
        serializer = MinimalProgramCourseSerializer(course, context=serializer_context)

        expected = MinimalCourseSerializer(course, context=serializer_context).data
        expected['course_runs'] = MinimalCourseRunSerializer(
            [course_runs[1]], many=True, context={'request': request}).data
        self.assertDictEqual(serializer.data, expected)
class AffiliateWindowViewSetTests(ElasticsearchTestMixin, SerializationMixin, APITestCase):
    """ Tests for the AffiliateWindowViewSet. """

    def setUp(self):
        super(AffiliateWindowViewSetTests, self).setUp()
        self.user = UserFactory()
        self.client.force_authenticate(self.user)
        self.catalog = CatalogFactory(query='*:*', viewers=[self.user])

        self.enrollment_end = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30)
        self.course_end = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=60)
        self.course_run = CourseRunFactory(enrollment_end=self.enrollment_end, end=self.course_end)

        self.seat_verified = SeatFactory(course_run=self.course_run, type=Seat.VERIFIED)
        self.course = self.course_run.course
        self.affiliate_url = reverse('api:v1:partners:affiliate_window-detail', kwargs={'pk': self.catalog.id})
        self.refresh_index()

    def test_without_authentication(self):
        """ Verify authentication is required when accessing the endpoint. """
        self.client.logout()
        response = self.client.get(self.affiliate_url)
        self.assertEqual(response.status_code, 403)

    def test_affiliate_with_supported_seats(self):
        """ Verify that endpoint returns course runs for verified and professional seats only. """
        response = self.client.get(self.affiliate_url)
        self.assertEqual(response.status_code, 200)
        root = ET.fromstring(response.content)
        self.assertEqual(1, len(root.findall('product')))
        self.assert_product_xml(
            root.findall('product/[pid="{}-{}"]'.format(self.course_run.key, self.seat_verified.type))[0],
            self.seat_verified
        )

        # Add professional seat.
        seat_professional = SeatFactory(course_run=self.course_run, type=Seat.PROFESSIONAL)

        response = self.client.get(self.affiliate_url)
        root = ET.fromstring(response.content)
        self.assertEqual(2, len(root.findall('product')))

        self.assert_product_xml(
            root.findall('product/[pid="{}-{}"]'.format(self.course_run.key, self.seat_verified.type))[0],
            self.seat_verified
        )
        self.assert_product_xml(
            root.findall('product/[pid="{}-{}"]'.format(self.course_run.key, seat_professional.type))[0],
            seat_professional
        )

    @ddt.data(Seat.CREDIT, Seat.HONOR, Seat.AUDIT)
    def test_with_non_supported_seats(self, non_supporting_seat):
        """ Verify that endpoint returns no data for honor, credit and audit seats. """

        self.seat_verified.type = non_supporting_seat
        self.seat_verified.save()

        response = self.client.get(self.affiliate_url)
        self.assertEqual(response.status_code, 200)
        root = ET.fromstring(response.content)
        self.assertEqual(0, len(root.findall('product')))

    def test_with_closed_enrollment(self):
        """ Verify that endpoint returns no data if enrollment is close. """
        self.course_run.enrollment_end = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=100)
        self.course_run.end = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=100)
        self.course_run.save()

        # new course run with future end date and no enrollment_date.
        CourseRunFactory(end=self.course_end, course=self.course, enrollment_end=None)

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

        self.assertEqual(response.status_code, 200)
        root = ET.fromstring(response.content)
        self.assertEqual(0, len(root.findall('product')))

    def assert_product_xml(self, content, seat):
        """ Helper method to verify product data in xml format. """
        self.assertEqual(content.find('pid').text, '{}-{}'.format(self.course_run.key, seat.type))
        self.assertEqual(content.find('name').text, self.course_run.title)
        self.assertEqual(content.find('desc').text, self.course_run.short_description)
        self.assertEqual(content.find('purl').text, self.course_run.marketing_url)
        self.assertEqual(content.find('imgurl').text, self.course_run.image.src)
        self.assertEqual(content.find('price/actualp').text, str(seat.price))
        self.assertEqual(content.find('currency').text, seat.currency.code)
        self.assertEqual(content.find('category').text, AffiliateWindowSerializer.CATEGORY)

    def test_dtd_with_valid_data(self):
        """ Verify the XML data produced by the endpoint conforms to the DTD file. """
        response = self.client.get(self.affiliate_url)
        self.assertEqual(response.status_code, 200)
        filename = abspath(join(dirname(dirname(__file__)), 'affiliate_window_product_feed.1.4.dtd'))
        dtd = etree.DTD(open(filename))

        root = etree.XML(response.content)
        self.assertTrue(dtd.validate(root))

    def test_permissions(self):
        """ Verify only users with the appropriate permissions can access the endpoint. """
        catalog = CatalogFactory()
        superuser = UserFactory(is_superuser=True)
        url = reverse('api:v1:partners:affiliate_window-detail', kwargs={'pk': catalog.id})

        # Superusers can view all catalogs
        self.client.force_authenticate(superuser)
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)

        # Regular users can only view catalogs belonging to them
        self.client.force_authenticate(self.user)
        response = self.client.get(url)
        self.assertEqual(response.status_code, 403)

        catalog.viewers = [self.user]
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)