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)
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)
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)
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)
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))
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)
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')))
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
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)
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
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)]
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)
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)
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)
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
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 }))
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
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]
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})
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])
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
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
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)
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)
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 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
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()
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)
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)
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))
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()
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
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)