def test_voucher_offers_listing_for_a_single_course_voucher(self): """ Verify the endpoint returns offers data when a single product is in voucher range. """ course, seat = self.create_course_and_seat() self.mock_dynamic_catalog_single_course_runs_api(course) new_range = RangeFactory(products=[seat, ]) new_range.catalog = Catalog.objects.create(partner=self.partner) new_range.catalog.stock_records.add(StockRecord.objects.get(product=seat)) voucher, __ = prepare_voucher(_range=new_range, benefit_value=10) request = self.prepare_offers_listing_request(voucher.code) response = self.endpointView(request) self.assertEqual(response.status_code, 200) new_range.remove_product(seat) response = self.endpointView(request) self.assertEqual(response.status_code, 404)
def prepare_voucher(self, range_=None, start_datetime=None, benefit_value=100): """ Create a voucher and add an offer to it that contains a created product. """ if range_ is None: product = ProductFactory(title='Test product') range_ = RangeFactory(products=[product, ]) else: product = range_.all_products()[0] if start_datetime is None: start_datetime = now() - datetime.timedelta(days=1) voucher = VoucherFactory(code='COUPONTEST', start_datetime=start_datetime, usage=Voucher.SINGLE_USE) benefit = BenefitFactory(range=range_, value=benefit_value) offer = ConditionalOfferFactory(benefit=benefit) voucher.offers.add(offer) return voucher, product
def test_multiple_vouchers(self): """ Verify only the last entered voucher is contained in the basket. """ product = ProductFactory(stockrecords__price_excl_tax=100) new_range = RangeFactory(products=[ product, ]) voucher1, __ = prepare_voucher(code='TEST1', _range=new_range, benefit_value=10) basket = prepare_basket(self.request, [product], voucher1) self.assertEqual(basket.vouchers.count(), 1) self.assertEqual(basket.vouchers.first(), voucher1) voucher2, __ = prepare_voucher(code='TEST2', _range=new_range, benefit_value=20) new_basket = prepare_basket(self.request, [product], voucher2) self.assertEqual(basket, new_basket) self.assertEqual(new_basket.vouchers.count(), 1) self.assertEqual(new_basket.vouchers.first(), voucher2)
def test_prepare_basket_applies_valid_voucher_argument(self): """ Tests that prepare_basket applies a valid voucher passed as an an argument, even when there is also a valid voucher already on the basket. """ product = ProductFactory(stockrecords__partner__short_code='test1', stockrecords__price_excl_tax=100) new_range = RangeFactory(products=[product]) new_voucher, __ = prepare_voucher(code='xyz', _range=new_range, benefit_value=10) existing_voucher, __ = prepare_voucher(code='test', _range=new_range, benefit_value=50) basket = BasketFactory(owner=self.request.user, site=self.request.site) basket.vouchers.add(existing_voucher) self.assertEqual(basket.vouchers.count(), 1) basket = prepare_basket(self.request, [product], new_voucher) self.assertIsNotNone(basket) self.assertEqual(basket.vouchers.count(), 1) self.assertEqual(basket.vouchers.first().code, 'XYZ') self.assertEqual(basket.total_discount, 10.00)
def test_prepare_basket_applies_existing_basket_valid_voucher(self): """ Tests that prepare_basket applies an existing basket voucher that is valid for multiple products when used to purchase any of those products. """ product = ProductFactory(stockrecords__partner__short_code='test1', stockrecords__price_excl_tax=100) new_range = RangeFactory(products=[product]) voucher, __ = prepare_voucher(_range=new_range, benefit_value=10) basket = BasketFactory(owner=self.request.user, site=self.request.site) basket.vouchers.add(voucher) self.assertEqual(basket.vouchers.count(), 1) basket = prepare_basket(self.request, [product]) self.assertIsNotNone(basket) self.assertEqual(basket.vouchers.count(), 1) self.assertEqual(basket.lines.first().product, product) self.assertIsNotNone(basket.applied_offers()) self.assertEqual(basket.total_discount, 10.00)
def prepare_course_information(self): """ Helper function to prepare an API endpoint that provides course information. """ course = CourseFactory() seat = course.create_or_update_seat('verified', True, 50, self.partner) sr = StockRecord.objects.get(product=seat) catalog = Catalog.objects.create(name='Test catalog', partner=self.partner) catalog.stock_records.add(sr) range_ = RangeFactory(catalog=catalog) course_info = { "media": { "course_image": { "uri": "/asset-v1:edX+DemoX+Demo_Course+type@asset+block@images_course_image.jpg" } }, "name": "edX Demonstration Course", } course_info_json = json.dumps(course_info) course_url = get_lms_url('api/courses/v1/courses/{}/'.format(course.id)) httpretty.register_uri(httpretty.GET, course_url, body=course_info_json, content_type='application/json') return range_
def test_apply_voucher_on_basket_and_check_discount_with_multiple_vouchers( self): """ Tests apply_voucher_on_basket_and_check_discount when called with a basket already containing a valid voucher it only checks the new voucher. """ basket = BasketFactory(owner=self.request.user, site=self.request.site) product = ProductFactory(stockrecords__partner__short_code='test1', stockrecords__price_excl_tax=10) invalid_voucher, __ = prepare_voucher(code='TEST1') valid_voucher, __ = prepare_voucher( code='TEST2', _range=RangeFactory(products=[product])) basket.add_product(product, 1) basket.vouchers.add(valid_voucher) applied, msg = apply_voucher_on_basket_and_check_discount( invalid_voucher, self.request, basket) self.assertEqual(applied, False) self.assertEqual( msg, 'Basket does not qualify for coupon code {code}.'.format( code=invalid_voucher.code))
def test_prepare_basket_with_voucher(self): """ Verify a basket is returned and contains a voucher and the voucher is applied. """ # Prepare a product with price of 100 and a voucher with 10% discount for that product. product = ProductFactory(stockrecords__price_excl_tax=100) new_range = RangeFactory(products=[ product, ]) voucher, product = prepare_voucher(_range=new_range, benefit_value=10) stock_record = StockRecord.objects.get(product=product) self.assertEqual(stock_record.price_excl_tax, 100.00) basket = prepare_basket(self.request, product, voucher) self.assertIsNotNone(basket) self.assertEqual(basket.status, Basket.OPEN) self.assertEqual(basket.lines.count(), 1) self.assertEqual(basket.lines.first().product, product) self.assertEqual(basket.vouchers.count(), 1) self.assertIsNotNone(basket.applied_offers()) self.assertEqual(basket.total_discount, 10.00) self.assertEqual(basket.total_excl_tax, 90.00)
def prepare_url_for_credit_seat(self, code='CREDIT', enterprise_customer=None): """Helper method for creating a credit seat and construct the URL to its offer landing page. Returns: URL to its offer landing page. """ __, credit_seat = self.create_course_and_seat(seat_type='credit') self.credit_seat = credit_seat # Make sure to always pair `course_seat_types` and `catalog_query` parameters because # if one of them is missing it could result in a SEGFAULT error when running tests # with migrations enabled. range_kwargs = { 'products': [credit_seat], 'course_seat_types': 'credit', 'catalog_query': '*:*', 'enterprise_customer': enterprise_customer, } _range = RangeFactory(**range_kwargs) prepare_voucher(code=code, _range=_range) return format_url(path=self.path, params={'code': code})
def test_with_expired_voucher(self): """ Test creates order when called with basket with expired voucher""" basket = create_basket() product = ProductFactory(stockrecords__price_excl_tax=100, stockrecords__partner=self.partner, stockrecords__price_currency='USD') voucher, product = prepare_voucher( code='TEST101', _range=RangeFactory(products=[product])) self.request.user = UserFactory() basket.add_product(product) basket.vouchers.add(voucher) basket.status = 'Frozen' basket.save() PaymentProcessorResponse.objects.create(basket=basket, transaction_id='PAY-123', processor_name='paypal', response={'state': 'approved'}) with mock.patch('oscar.apps.offer.applicator.Applicator.apply', side_effect=ValueError): assert FulfillFrozenBaskets().fulfill_basket(basket.id, self.site)
def test_course_information_error(self): """ Verify a response is returned when course information is not accessable. """ course = CourseFactory() seat = course.create_or_update_seat('verified', True, 50, self.partner) _range = RangeFactory(products=[ seat, ]) prepare_voucher(code=COUPON_CODE, _range=_range) course_url = get_lms_url('api/courses/v1/courses/{}/'.format( course.id)) httpretty.register_uri(httpretty.GET, course_url, status=404, content_type=CONTENT_TYPE) response = self.client.get(self.path_with_code) response_text = ( 'Could not get course information. ' '[Client Error 404: http://lms.testserver.fake/api/courses/v1/courses/{}/]' ).format(course.id) self.assertEqual(response.context['error'], _(response_text))
def test_success_with_cybersource(self): """ Test basket with cybersource payment basket.""" product_price = 100 percentage_discount = 10 product = ProductFactory(stockrecords__price_excl_tax=product_price) voucher, product = prepare_voucher( _range=RangeFactory(products=[product]), benefit_value=percentage_discount) self.request.user = UserFactory() basket = prepare_basket(self.request, [product], voucher) basket.status = 'Frozen' basket.save() card_number = '4111111111111111' response = { 'req_card_number': card_number, 'req_card_type': CARD_TYPES['visa']['cybersource_code'], u'decision': u'ACCEPT', } PaymentProcessorResponse.objects.create(basket=basket, transaction_id='abc', processor_name='cybersource', response=response) assert FulfillFrozenBaskets().fulfill_basket(basket.id, self.site) order = Order.objects.get(number=basket.order_number) assert order.status == 'Complete' total = product_price * (100 - percentage_discount) / 100. Source.objects.get(source_type__name=Cybersource.NAME, currency=order.currency, amount_allocated=total, amount_debited=total, label=card_number, card_type='visa') PaymentEvent.objects.get(event_type__name=PaymentEventTypeName.PAID, amount=total, processor_name=Cybersource.NAME)
def test_prepare_basket_with_bundle_voucher(self): """ Test prepare_basket clears vouchers for a bundle """ product = ProductFactory(stockrecords__price_excl_tax=100) new_range = RangeFactory(products=[ product, ]) voucher, __ = prepare_voucher(_range=new_range, benefit_value=10) request = self.request basket = prepare_basket(request, [product], voucher) self.assertTrue(basket.vouchers.all()) request.GET = {'bundle': TEST_BUNDLE_ID} program_data = { 'marketing_slug': 'program-slug', 'title': 'program title', 'type': 'micromasters' } with mock.patch('ecommerce.extensions.basket.utils.get_program', return_value=program_data): basket = prepare_basket(request, [product]) self.assertFalse(basket.vouchers.all())
def test_course_information_error(self): """ Verify a response is returned when course information is not accessable. """ course = CourseFactory() seat = course.create_or_update_seat('verified', True, 50, self.partner) range_ = RangeFactory(products=[ seat, ]) self.prepare_voucher(range_=range_) course_url = get_lms_url('api/courses/v1/courses/{}/'.format( course.id)) httpretty.register_uri(httpretty.GET, course_url, status=404, content_type='application/json') url = self.offer_url + '?code={}'.format('COUPONTEST') response = self.client.get(url) response_text = ( 'Could not get course information. ' '[Client Error 404: http://127.0.0.1:8000/api/courses/v1/courses/{}/]' ).format(course.id) self.assertEqual(response.context['error'], _(response_text))
def test_provider_fields(self, benefit_type, discount): code = 'TEST' seat = self.course.create_or_update_seat( 'credit', True, self.price, self.provider, credit_hours=self.credit_hours) new_range = RangeFactory(products=[ seat, ]) prepare_voucher(code=code, _range=new_range, benefit_value=100, benefit_type=benefit_type) self._mock_eligibility_api(body=self.eligibilities) self._mock_providers_api(body=self.provider_data) response = self.client.get('{}?code={}'.format(self.path, code)) self.assertEqual(response.status_code, 200) provider_info = response.context['providers'][0] self.assertEqual(provider_info['new_price'], '0.00') self.assertEqual(provider_info['discount'], discount)
def test_site(self): """ Verify the site is stored in the session. """ user = self.create_user(is_staff=True) self.client.login(username=user.username, password=self.password) site_configuration = SiteConfigurationFactory() site = site_configuration.site self.assertEqual(ConditionalOffer.objects.count(), 0) # Start creating the offer by defining by setting the name and site metadata = { 'name': 'Test Offer', 'description': 'Blah!', 'site': site.id, } metadata_url = reverse('dashboard:offer-metadata') response = self.client.post(metadata_url, metadata) self.assertEqual(response.status_code, 302) self.assertEqual(response['Location'], reverse('dashboard:offer-benefit')) # Ensure the Site ID is stored in the session actual = json.loads( self.client.session['offer_wizard']['metadata'])['data']['site_id'] self.assertEqual(actual, site.id) # Set the offer benfit data offer_range = RangeFactory() data = { 'range': offer_range.id, 'type': Benefit.PERCENTAGE, 'value': 100 } response = self.client.post(response['Location'], data) self.assertEqual(response.status_code, 302) self.assertEqual(response['Location'], reverse('dashboard:offer-condition')) # Set the restrictions on the offer restrictions_url = reverse('dashboard:offer-restrictions') data = {'range': offer_range.id, 'type': Condition.COUNT, 'value': 1} response = self.client.post(response['Location'], data) self.assertEqual(response.status_code, 302) self.assertEqual(response['Location'], restrictions_url) # Reload the first page to exercise _fetch_form_kwargs, which should pull the site from the session response = self.client.get(metadata_url) self.assertEqual(response.status_code, 200) # Finish saving the offer data = {} response = self.client.post(restrictions_url, data) self.assertEqual(response.status_code, 302) self.assertEqual(ConditionalOffer.objects.count(), 1) offer = ConditionalOffer.objects.first() self.assertEqual( response['Location'], reverse('dashboard:offer-detail', kwargs={'pk': offer.pk})) # Ensure the offer is associated to the partner set in the first step of the wizard self.assertEqual(offer.partner, site_configuration.partner)
def setUp(self): product = create_product(price=100) self.offer_range = RangeFactory(products=[product]) self.offer_condition = ConditionFactory(range=self.offer_range, value=2)
def setUp(self): """ Create test data. """ super(MigrateEnterpriseConditionalOffersTests, self).setUp() # Set up vouchers that relate to a range with a enterprise_customer uuid = '123e4567-e89b-12d3-a456-426655440000' range_with_ent_customer = RangeFactory(enterprise_customer=uuid) condition = ConditionFactory(range=range_with_ent_customer) benefit_percent = BenefitFactory( range=range_with_ent_customer, type='Percentage', value=10.00, ) benefit_absolute = BenefitFactory( range=range_with_ent_customer, type='Absolute', value=47, ) for i in range(2): code = '{}EntUserPercentBenefit'.format(i) voucher = VoucherFactory(code=code) offer_name = "Coupon [{}]-{}-{}".format(voucher.pk, benefit_percent.type, benefit_percent.value) conditional_offer = ConditionalOfferFactory( condition=condition, benefit=benefit_percent, name=offer_name, ) voucher.offers.add(conditional_offer) for i in range(2): code = '{}EntUserAbsoluteBenefit'.format(i) voucher = VoucherFactory(code=code) offer_name = "Coupon [{}]-{}-{}".format(voucher.pk, benefit_absolute.type, benefit_absolute.value) conditional_offer = ConditionalOfferFactory( condition=condition, benefit=benefit_absolute, name=offer_name, ) voucher.offers.add(conditional_offer) # Set up vouchers that do not relate to a range with an enterprise_customer range_no_ent_customer = RangeFactory() condition = ConditionFactory(range=range_no_ent_customer) benefit = BenefitFactory( range=range_no_ent_customer, type='Percentage', value=10.00, ) for i in range(3): code = '{}NoEntUserPercentBenefit'.format(i) voucher = VoucherFactory(code=code) offer_name = "Coupon [{}]-{}-{}".format(voucher.pk, benefit.type, benefit.value) conditional_offer = ConditionalOfferFactory( condition=condition, benefit=benefit, name=offer_name, ) voucher.offers.add(conditional_offer) assert Voucher.objects.filter( offers__condition__range__enterprise_customer__isnull=False).count( ) == 4 assert Voucher.objects.filter( offers__condition__range__enterprise_customer__isnull=True).count( ) == 3 self.command = Command()
def test_offer(self): # Our offer is for 100%, so all lines should end up with a price of 0. offer = factories.ProgramOfferFactory( partner=self.partner, benefit=factories.PercentageDiscountBenefitWithoutRangeFactory( value=100)) basket = BasketFactory(site=self.site, owner=self.create_user()) program_uuid = offer.condition.program_uuid program = self.mock_program_detail_endpoint( program_uuid, self.site_configuration.discovery_api_url) self.mock_user_data(basket.owner.username) # Add one course run seat from each course to the basket. products = [] for course in program['courses']: course_run = Course.objects.get(id=course['course_runs'][0]['key']) for seat in course_run.seat_products: if seat.attr.id_verification_required: products.append(seat) basket.add_product(seat) # No discounts should be applied, and each line should have a price of 100.00. self.assertEqual(len(basket.offer_applications), 0) self.assertEqual(basket.total_discount, 0) for line in basket.all_lines(): self.assertEqual(line.line_price_incl_tax_incl_discounts, Decimal(100)) # Apply the offers as Oscar will in a request basket.strategy = DefaultStrategy() Applicator().apply(basket, basket.owner, bundle_id=program_uuid) # Our discount should be applied, and each line should have a price of 0 lines = basket.all_lines() self.assertEqual(len(basket.offer_applications), 1) self.assertEqual(basket.total_discount, Decimal(100) * len(lines)) for line in lines: self.assertEqual(line.line_price_incl_tax_incl_discounts, 0) # Reset the basket and add a voucher. basket.reset_offer_applications() product_range = RangeFactory(products=products) voucher, __ = factories.prepare_voucher(_range=product_range, benefit_value=50) self.mock_account_api(self.request, basket.owner.username, data={'is_active': True}) self.client.login(username=basket.owner.username, password=self.password) self.client.post(reverse('basket:vouchers-add'), data={'code': voucher.code}) response = self.client.get(reverse('basket:summary')) basket = response.context['basket'] # Verify that voucher-based offer takes precedence over program offer. actual_offer_discounts = [ discount['offer'] for discount in basket.offer_discounts ] actual_voucher_discounts = [ discount['offer'] for discount in basket.voucher_discounts ] self.assertEqual(actual_offer_discounts, []) self.assertEqual(actual_voucher_discounts, [voucher.offers.first()]) lines = basket.all_lines() self.assertEqual(len(basket.offer_applications), 1) self.assertEqual(basket.total_discount, Decimal(50) * len(lines)) for line in lines: self.assertEqual(line.line_price_incl_tax_incl_discounts, 50)