def test_courses_with_honor_seats(self): honor_seat_type = factories.SeatTypeFactory.honor() honor_track = factories.TrackFactory(seat_type=honor_seat_type) honor_run_type = factories.CourseRunTypeFactory( name='Honor Only', slug=CourseRunType.HONOR, tracks=[honor_track]) # Tests that Honor Only will not match with Verified and Honor despite being a subset of that CourseRunType honor_run = factories.CourseRunFactory(course=self.course, type=self.empty_run_type, key='course-v1:Org1+Course1+H') factories.SeatFactory(course_run=honor_run, type=honor_seat_type) vh_run_type = factories.CourseRunTypeFactory( name='Verified and Honor', slug=CourseRunType.VERIFIED_HONOR, tracks=[honor_track, self.verified_track]) vh_run = factories.CourseRunFactory(course=self.course, type=self.empty_run_type, key='course-v1:Org1+Course1+VH') factories.SeatFactory(course_run=vh_run, type=honor_seat_type) factories.SeatFactory(course_run=vh_run, type=self.verified_seat_type) allow_for = [ CourseType.VERIFIED_AUDIT + ':' + CourseRunType.HONOR, CourseType.VERIFIED_AUDIT + ':' + CourseRunType.VERIFIED_HONOR ] self.run_command(allow_for=allow_for) self.assertEqual(self.course.type, self.va_course_type) honor_run.refresh_from_db() self.assertEqual(honor_run.type, honor_run_type) vh_run.refresh_from_db() self.assertEqual(vh_run.type, vh_run_type)
def test_price_ranges_multiple_course(self): """ Verifies the price_range property of a program with multiple courses """ currency = Currency.objects.get(code='USD') test_price = 100 for course_run in self.course_runs: factories.SeatFactory(type='audit', currency=currency, course_run=course_run, price=0) factories.SeatFactory(type='verified', currency=currency, course_run=course_run, price=test_price) test_price += 100 applicable_seat_types = SeatType.objects.filter(slug__in=['verified']) program_type = factories.ProgramTypeFactory( applicable_seat_types=applicable_seat_types) self.program.type = program_type expected_price_ranges = [{ 'currency': 'USD', 'min': Decimal(100), 'max': Decimal(300), 'total': Decimal(600) }] self.assertEqual(self.program.price_ranges, expected_price_ranges)
def test_one_click_purchase_eligible_with_unpublished_runs(self): """ Verify that program with unpublished course runs is one click purchase eligible. """ verified_seat_type, __ = SeatType.objects.get_or_create( name=Seat.VERIFIED) program_type = factories.ProgramTypeFactory( applicable_seat_types=[verified_seat_type]) published_course_run = factories.CourseRunFactory( end=None, enrollment_end=None, status=CourseRunStatus.Published) unpublished_course_run = factories.CourseRunFactory( end=None, enrollment_end=None, status=CourseRunStatus.Unpublished, course=published_course_run.course) factories.SeatFactory(course_run=published_course_run, type=Seat.VERIFIED, upgrade_deadline=None) factories.SeatFactory(course_run=unpublished_course_run, type=Seat.VERIFIED, upgrade_deadline=None) program = factories.ProgramFactory( courses=[published_course_run.course], one_click_purchase_enabled=True, type=program_type, ) self.assertTrue(program.is_program_eligible_for_one_click_purchase)
def test_enrollable_seats(self): """ Verify the expected seats get returned. """ course_run = factories.CourseRunFactory(start=None, end=None, enrollment_start=None, enrollment_end=None) verified_seat = factories.SeatFactory(course_run=course_run, type=Seat.VERIFIED, upgrade_deadline=None) professional_seat = factories.SeatFactory(course_run=course_run, type=Seat.PROFESSIONAL, upgrade_deadline=None) factories.SeatFactory(course_run=course_run, type=Seat.HONOR, upgrade_deadline=None) self.assertEqual( course_run.enrollable_seats([Seat.VERIFIED, Seat.PROFESSIONAL]), [verified_seat, professional_seat]) # The method should not care about the course run's start date. course_run.start = datetime.datetime.utcnow() + datetime.timedelta( days=1) course_run.save() self.assertEqual( course_run.enrollable_seats([Seat.VERIFIED, Seat.PROFESSIONAL]), [verified_seat, professional_seat])
def create_program_with_seats(self): currency = Currency.objects.get(code='USD') course_run = factories.CourseRunFactory() course_run.course.canonical_course_run = course_run course_run.course.save() factories.SeatFactory(type='audit', currency=currency, course_run=course_run, price=0) factories.SeatFactory(type='credit', currency=currency, course_run=course_run, price=600) factories.SeatFactory(type='verified', currency=currency, course_run=course_run, price=100) applicable_seat_types = SeatType.objects.filter( slug__in=['credit', 'verified']) program_type = factories.ProgramTypeFactory( applicable_seat_types=applicable_seat_types) return factories.ProgramFactory(type=program_type, courses=[course_run.course])
def create_program_with_multiple_course_runs(self, set_all_dates=True): currency = Currency.objects.get(code='USD') single_course_course_runs = factories.CourseRunFactory.create_batch(3) course = factories.CourseFactory() course_runs_same_course = factories.CourseRunFactory.create_batch( 3, course=course) for course_run in single_course_course_runs: factories.SeatFactory(type='audit', currency=currency, course_run=course_run, price=0) factories.SeatFactory(type='verified', currency=currency, course_run=course_run, price=10) course_run.course.canonical_course_run = course_run course_run.course.save() day_separation = 1 now = datetime.datetime.utcnow() for course_run in course_runs_same_course: if set_all_dates or day_separation < 2: date_delta = datetime.timedelta(days=day_separation) course_run.enrollment_start = now - date_delta course_run.end = now + datetime.timedelta(weeks=day_separation) else: course_run.enrollment_start = None course_run.end = None course_run.save() factories.SeatFactory(type='audit', currency=currency, course_run=course_run, price=0) factories.SeatFactory(type='verified', currency=currency, course_run=course_run, price=(day_separation * 100)) day_separation += 1 course.canonical_course_run = course_runs_same_course[2] course.save() applicable_seat_types = SeatType.objects.filter(slug__in=['verified']) program_type = factories.ProgramTypeFactory( applicable_seat_types=applicable_seat_types) program_courses = [ course_run.course for course_run in single_course_course_runs ] program_courses.append(course) return factories.ProgramFactory(type=program_type, courses=program_courses)
def test_queryset_method_returns_eligible_programs(self): """ Verify that one click purchase eligible programs pass the filter. """ verified_seat_type, __ = SeatType.objects.get_or_create( name=Seat.VERIFIED) program_type = factories.ProgramTypeFactory( applicable_seat_types=[verified_seat_type]) program_filter = ProgramEligibilityFilter(None, {self.parameter_name: 1}, None, None) course_run = factories.CourseRunFactory( end=None, enrollment_end=None, ) factories.SeatFactory(course_run=course_run, type='verified', upgrade_deadline=None) one_click_purchase_eligible_program = factories.ProgramFactory( type=program_type, courses=[course_run.course], one_click_purchase_enabled=True, ) with self.assertNumQueries(12): self.assertEqual( list(program_filter.queryset({}, Program.objects.all())), [one_click_purchase_eligible_program])
def test_one_click_purchase_eligible(self): """ Verify that program is one click purchase eligible. """ verified_seat_type, __ = SeatType.objects.get_or_create( name=Seat.VERIFIED) program_type = factories.ProgramTypeFactory( applicable_seat_types=[verified_seat_type]) # Program has one_click_purchase_enabled set to True, # all courses have one course run, all course runs have # verified seat types courses = [] for __ in range(3): course_run = factories.CourseRunFactory(end=None, enrollment_end=None) factories.SeatFactory(course_run=course_run, type=Seat.VERIFIED, upgrade_deadline=None) courses.append(course_run.course) program = factories.ProgramFactory( courses=courses, one_click_purchase_enabled=True, type=program_type, ) self.assertTrue(program.is_program_eligible_for_one_click_purchase) # Program has one_click_purchase_enabled set to True, # course has all course runs excluded except one which # has verified seat type course_run = factories.CourseRunFactory(end=None, enrollment_end=None) factories.SeatFactory(course_run=course_run, type=Seat.VERIFIED, upgrade_deadline=None) course = course_run.course excluded_course_runs = [ factories.CourseRunFactory(course=course), factories.CourseRunFactory(course=course) ] program = factories.ProgramFactory( courses=[course], excluded_course_runs=excluded_course_runs, one_click_purchase_enabled=True, type=program_type, ) self.assertTrue(program.is_program_eligible_for_one_click_purchase)
def setUp(self): super().setUp() # Disable marketing site password just to save us from having to mock the responses self.partner = factories.PartnerFactory(marketing_site_api_password=None) # Fill out a bunch of types and modes. Exact mode parameters don't matter, just the resulting seat types. self.audit_seat_type = factories.SeatTypeFactory.audit() self.verified_seat_type = factories.SeatTypeFactory.verified() self.audit_mode = Mode.objects.get(slug=Seat.AUDIT) self.verified_mode = Mode.objects.get(slug=Seat.VERIFIED) self.audit_track = Track.objects.get(seat_type=self.audit_seat_type, mode=self.audit_mode) self.verified_track = Track.objects.get(seat_type=self.verified_seat_type, mode=self.verified_mode) self.empty_run_type = CourseRunType.objects.get(slug=CourseRunType.EMPTY) self.audit_run_type = CourseRunType.objects.get(slug=CourseRunType.AUDIT) self.va_run_type = CourseRunType.objects.get(slug=CourseRunType.VERIFIED_AUDIT) self.empty_course_type = CourseType.objects.get(slug=CourseType.EMPTY) self.va_course_type = CourseType.objects.get(slug=CourseType.VERIFIED_AUDIT) self.audit_course_type = CourseType.objects.get(slug=CourseType.AUDIT) # Now create some courses and orgs that will be found to match the above, in the simple happy path case. self.org = factories.OrganizationFactory(partner=self.partner, key='Org1') self.course = factories.CourseFactory(partner=self.partner, authoring_organizations=[self.org], type=self.empty_course_type, key=f'{self.org.key}+Course1') self.entitlement = factories.CourseEntitlementFactory(partner=self.partner, course=self.course, mode=self.verified_seat_type) self.audit_run = factories.CourseRunFactory(course=self.course, type=self.empty_run_type, key='course-v1:Org1+Course1+A') self.audit_seat = factories.SeatFactory(course_run=self.audit_run, type=self.audit_seat_type) self.verified_run = factories.CourseRunFactory(course=self.course, type=self.empty_run_type, key='course-v1:Org1+Course1+V') self.verified_seat = factories.SeatFactory(course_run=self.verified_run, type=self.verified_seat_type) self.verified_audit_seat = factories.SeatFactory(course_run=self.verified_run, type=self.audit_seat_type) # Create parallel obj / course for argument testing self.org2 = factories.OrganizationFactory(partner=self.partner, key='Org2') self.org3 = factories.OrganizationFactory(partner=self.partner, key='Org3') self.course2 = factories.CourseFactory(partner=self.partner, authoring_organizations=[self.org2, self.org3], type=self.empty_course_type, key=f'{self.org2.key}+Course1') self.c2_audit_run = factories.CourseRunFactory(course=self.course2, type=self.empty_run_type) self.c2_audit_seat = factories.SeatFactory(course_run=self.c2_audit_run, type=self.audit_seat_type)
def test_courses_with_honor_seats(self): honor_seat_type = factories.SeatTypeFactory.honor() honor_run_type = CourseRunType.objects.get(slug=CourseRunType.HONOR) # Tests that Honor Only will not match with Verified and Honor despite being a subset of that CourseRunType honor_run = factories.CourseRunFactory(course=self.course, type=self.empty_run_type, key='course-v1:Org1+Course1+H') factories.SeatFactory(course_run=honor_run, type=honor_seat_type) vh_run_type = CourseRunType.objects.get(slug=CourseRunType.VERIFIED_HONOR) vh_run = factories.CourseRunFactory(course=self.course, type=self.empty_run_type, key='course-v1:Org1+Course1+VH') factories.SeatFactory(course_run=vh_run, type=honor_seat_type) factories.SeatFactory(course_run=vh_run, type=self.verified_seat_type) allow_for = [ CourseType.VERIFIED_AUDIT + ':' + CourseRunType.HONOR, CourseType.VERIFIED_AUDIT + ':' + CourseRunType.VERIFIED_HONOR ] self.run_command(allow_for=allow_for) assert self.course.type == self.va_course_type honor_run.refresh_from_db() assert honor_run.type == honor_run_type vh_run.refresh_from_db() assert vh_run.type == vh_run_type
def test_queryset_method_returns_all_programs(self): """ Verify that all programs pass the filter. """ verified_seat_type = factories.SeatTypeFactory.verified() program_type = factories.ProgramTypeFactory(applicable_seat_types=[verified_seat_type]) program_filter = ProgramEligibilityFilter(None, {}, None, None) course_run = factories.CourseRunFactory() factories.SeatFactory(course_run=course_run, type=verified_seat_type, upgrade_deadline=None) one_click_purchase_eligible_program = factories.ProgramFactory( type=program_type, courses=[course_run.course], one_click_purchase_enabled=True ) one_click_purchase_ineligible_program = factories.ProgramFactory(courses=[course_run.course]) with self.assertNumQueries(1): assert list(program_filter.queryset({}, Program.objects.all())) == \ [one_click_purchase_eligible_program, one_click_purchase_ineligible_program]
def test_canonical_course_seats(self): """ Test canonical course seats returns only canonical course run's applicable seats """ currency = Currency.objects.get(code='USD') course = factories.CourseFactory() course_runs_same_course = factories.CourseRunFactory.create_batch( 3, course=course) for course_run in course_runs_same_course: factories.SeatFactory(type='verified', currency=currency, course_run=course_run, price=100) course.canonical_course_run = course_runs_same_course[0] course.save() applicable_seat_types = SeatType.objects.filter(slug__in=['verified']) program_type = factories.ProgramTypeFactory( applicable_seat_types=applicable_seat_types) program = factories.ProgramFactory(type=program_type, courses=[course]) self.assertEqual(set(course.canonical_course_run.seats.all()), set(program.canonical_seats))
def assert_one_click_purchase_ineligible_program( self, end=None, enrollment_start=None, enrollment_end=None, seat_type=Seat.VERIFIED, upgrade_deadline=None, one_click_purchase_enabled=True, excluded_course_runs=None, program_type=None): course_run = factories.CourseRunFactory( end=end, enrollment_start=enrollment_start, enrollment_end=enrollment_end) factories.SeatFactory(course_run=course_run, type=seat_type, upgrade_deadline=upgrade_deadline) program = factories.ProgramFactory( courses=[course_run.course], excluded_course_runs=excluded_course_runs, one_click_purchase_enabled=one_click_purchase_enabled, type=program_type, ) self.assertFalse(program.is_program_eligible_for_one_click_purchase)
def test_type(self, seat_types, expected_course_run_type): """ Verify the property returns the appropriate type string for the CourseRun. """ for seat_type in seat_types: factories.SeatFactory(course_run=self.course_run, type=seat_type) self.assertEqual(self.course_run.type, expected_course_run_type)
def test_one_click_purchase_ineligible(self): """ Verify that program is one click purchase ineligible. """ yesterday = datetime.datetime.utcnow() - datetime.timedelta(days=1) tomorrow = datetime.datetime.utcnow() + datetime.timedelta(days=1) verified_seat_type, __ = SeatType.objects.get_or_create( name=Seat.VERIFIED) program_type = factories.ProgramTypeFactory( applicable_seat_types=[verified_seat_type]) # Program has one_click_purchase_enabled set to False and # every course has one course run self.assert_one_click_purchase_ineligible_program( one_click_purchase_enabled=False, program_type=program_type, ) # Program has one_click_purchase_enabled set to True and # one course has two course runs course_run = factories.CourseRunFactory(end=None, enrollment_end=None) factories.CourseRunFactory(end=None, enrollment_end=None, course=course_run.course) factories.SeatFactory(course_run=course_run, type='verified', upgrade_deadline=None) program = factories.ProgramFactory( courses=[course_run.course], one_click_purchase_enabled=True, type=program_type, ) self.assertFalse(program.is_program_eligible_for_one_click_purchase) # Program has one_click_purchase_enabled set to True and # one course with one course run excluded from the program course_run = factories.CourseRunFactory(end=None, enrollment_end=None) factories.SeatFactory(course_run=course_run, type='verified', upgrade_deadline=None) program = factories.ProgramFactory( courses=[course_run.course], one_click_purchase_enabled=True, excluded_course_runs=[course_run], type=program_type, ) self.assertFalse(program.is_program_eligible_for_one_click_purchase) # Program has one_click_purchase_enabled set to True, one course # with one course run, course run end date passed self.assert_one_click_purchase_ineligible_program( end=yesterday, program_type=program_type, ) # Program has one_click_purchase_enabled set to True, one course # with one course run, course run enrollment start date not passed self.assert_one_click_purchase_ineligible_program( enrollment_start=tomorrow, program_type=program_type, ) # Program has one_click_purchase_enabled set to True, one course # with one course run, course run enrollment end date passed self.assert_one_click_purchase_ineligible_program( enrollment_end=yesterday, program_type=program_type, ) # Program has one_click_purchase_enabled set to True, one course # with one course run, seat upgrade deadline passed self.assert_one_click_purchase_ineligible_program( upgrade_deadline=yesterday, program_type=program_type, ) # Program has one_click_purchase_enabled set to True, one course # with one course run, seat type is not purchasable self.assert_one_click_purchase_ineligible_program( seat_type='incorrect', program_type=program_type, )