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_access_token_response() 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_get_offers_for_single_course_voucher(self): """ Verify that the course offers data is returned for a single course voucher. """ self.mock_access_token_response() course, seat = self.create_course_and_seat() new_range = RangeFactory(products=[seat, ]) voucher, __ = prepare_voucher(_range=new_range, benefit_value=10) benefit = voucher.offers.first().benefit request = self.prepare_offers_listing_request(voucher.code) self.mock_course_run_detail_endpoint( course, discovery_api_url=self.site_configuration.discovery_api_url ) offers = VoucherViewSet().get_offers(request=request, voucher=voucher)['results'] first_offer = offers[0] self.assertEqual(len(offers), 1) self.assertDictEqual(first_offer, { 'benefit': { 'type': benefit.type, 'value': benefit.value }, 'contains_verified': True, 'course_start_date': '2013-02-05T05:00:00Z', 'id': course.id, 'image_url': '/path/to/image.jpg', 'multiple_credit_providers': False, 'organization': CourseKey.from_string(course.id).org, 'credit_provider_price': None, 'seat_type': course.type, 'stockrecords': serializers.StockRecordSerializer(seat.stockrecords.first()).data, 'title': course.name, 'voucher_end_date': voucher.end_datetime, })
def test_no_product(self): """ Verify an error is returned for voucher with no product. """ no_product_range = RangeFactory() prepare_voucher(code='NOPRODUCT', _range=no_product_range) url = self.path + '?code={}'.format('NOPRODUCT') response = self.client.get(url) self.assertEqual(response.context['error'], _('The voucher is not applicable to your current basket.'))
def create_benefits(self): """ Create all Benefit permutations - Benefit type: Percentage, Benefit value: 100% - Benefit type: Percentage, Benefit value: 50% - Benefit type: Value, Benefit value: seat price - Benefit type: Value, Benefit value: half the seat price """ _range = RangeFactory(products=[ self.verified_seat, ]) benefit_percentage_all = BenefitFactory(type=Benefit.PERCENTAGE, range=_range, value=100.00) benefit_percentage_half = BenefitFactory(type=Benefit.PERCENTAGE, range=_range, value=50.00) benefit_value_all = BenefitFactory(type=Benefit.FIXED, range=_range, value=self.seat_price) benefit_value_half = BenefitFactory(type=Benefit.FIXED, range=_range, value=self.seat_price / 2) return [ benefit_percentage_all, benefit_percentage_half, benefit_value_all, benefit_value_half ]
def test_proper_code(self): """ Verify that proper information is returned when a valid code is provided. """ 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) self.prepare_voucher(range_=range_) 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') url = self.offer_url + '?code={}'.format('COUPONTEST') response = self.client.get(url) self.assertEqual(response.context['course']['name'], _('edX Demonstration Course')) self.assertEqual(response.context['code'], _('COUPONTEST'))
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': '*:*', } if enterprise_customer: range_kwargs['enterprise_customer'] = enterprise_customer _range = RangeFactory(**range_kwargs) prepare_voucher(code=code, _range=_range) url = '{path}?code={code}'.format(path=self.path, code=code) return url
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_attrs': { 'slug': '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_success(self): 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) ppr = PaymentProcessorResponse.objects.create(basket=basket, transaction_id='abc', processor_name='paypal') with mock.patch.object(Paypal, 'issue_credit') as mock_issue_credit: mock_issue_credit.return_value = None assert refund_basket_transactions(self.site, [basket.id]) == ( 1, 0, ) total = product_price * (100 - percentage_discount) / 100. mock_issue_credit.assert_called_once_with(basket.order_number, basket, ppr.transaction_id, total, basket.currency)
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 prepare_course_information(self): """ Helper function to prepare an API endpoint that provides course information. """ course, __, stock_record = self.prepare_stock_record() catalog = Catalog.objects.create(name='Test catalog', partner=self.partner) catalog.stock_records.add(stock_record) _range = RangeFactory(catalog=catalog) self.mock_course_api_response(course=course) return _range
def test_offer(self): # Our offer is for 100%, so all lines should end up with a price of 0. offer = factories.ProgramOfferFactory( site=self.site, benefit=factories.PercentageDiscountBenefitWithoutRangeFactory(value=100) ) basket = factories.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) # 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)
def test_no_product(self): """ Verify an error is returned for voucher with no product. """ code = FuzzyText().fuzz() no_product_range = RangeFactory() prepare_voucher(code=code, _range=no_product_range) url = format_url(path=self.path, params={'code': code}) response = self.client.get(url) self.assertEqual(response.context['error'], 'The voucher is not applicable to your current basket.')
def prepare_voucher(code='COUPONTEST', _range=None, start_datetime=None, end_datetime=None, benefit_value=100, benefit_type=Benefit.PERCENTAGE, usage=Voucher.SINGLE_USE, max_usage=None, email_domains=None, enterprise_customer=None, site=None): """ Helper function to create a voucher and add an offer to it that contains a product. """ # NOTE (CCB): We use empty categories here to avoid unique-constraint issues that occur when we use # ProductCategoryFactory in conjunction with pre-created Category objects. if _range is None: product = ProductFactory(categories=[]) _range = RangeFactory(products=[product], enterprise_customer=enterprise_customer) elif _range.num_products() > 0: product = _range.all_products()[0] else: product = ProductFactory(categories=[]) if start_datetime is None: start_datetime = now() - timedelta(days=1) if end_datetime is None: end_datetime = now() + timedelta(days=10) voucher = VoucherFactory(code=code, start_datetime=start_datetime, end_datetime=end_datetime, usage=usage) benefit = BenefitFactory(type=benefit_type, range=_range, value=benefit_value) condition = ConditionFactory(value=1, range=_range, enterprise_customer_uuid=enterprise_customer) if max_usage: offer = ConditionalOfferFactory(offer_type=ConditionalOffer.VOUCHER, benefit=benefit, condition=condition, max_global_applications=max_usage, email_domains=email_domains, priority=OFFER_PRIORITY_VOUCHER) else: offer = ConditionalOfferFactory( offer_type=ConditionalOffer.VOUCHER, benefit=benefit, condition=condition, email_domains=email_domains, partner=site.siteconfiguration.partner if site else None, priority=OFFER_PRIORITY_VOUCHER) voucher.offers.add(offer) return voucher, product
def setUp(self): self.basket = create_basket(empty=True) self.product = create_product(price=100) range = RangeFactory(products=[self.product]) condition = ConditionFactory(range=range, value=2) self.voucher = VoucherFactory() self.voucher.offers.add( create_offer(offer_type='Voucher', range=range, condition=condition))
def setUp(self): self.applicator = Applicator() self.basket = BasketFactory() rng = RangeFactory(includes_all_products=True) self.condition = ConditionFactory( range=rng, type=ConditionFactory._meta.model.VALUE, value=D('100'), proxy_class=None) self.benefit = BenefitFactory( range=rng, type=BenefitFactory._meta.model.FIXED, value=D('10'))
def test_voucher_offers_listing_product_found(self): """ Verify the endpoint returns offers data for single product range. """ course, seat = self.create_course_and_seat() self.mock_dynamic_catalog_single_course_runs_api(course) new_range = RangeFactory(products=[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)
def prepare_course_information(self): """ Helper function to prepare an API endpoint that provides course information. """ course = CourseFactory(name='Test course') seat = course.create_or_update_seat('verified', True, 50, self.partner) stock_record = StockRecord.objects.get(product=seat) catalog = Catalog.objects.create(name='Test catalog', partner=self.partner) catalog.stock_records.add(stock_record) _range = RangeFactory(catalog=catalog) self.mock_course_api_response(course=course) return _range
def setUp(self): self.applicator = Applicator() rng = RangeFactory(includes_all_products=True) self.condition = ConditionFactory( range=rng, type=ConditionFactory._meta.model.VALUE, value=D('100'), proxy_class=None) self.benefit = BenefitFactory( range=rng, type=BenefitFactory._meta.model.FIXED, value=D('10'), max_affected_items=1) self.basket = factories.create_basket() self.line = self.basket.all_lines()[0]
def test_voucher_offers_listing_product_found(self): """ Verify the endpoint returns offers data for single product range. """ self.mock_dynamic_catalog_course_runs_api() product = ProductFactory(stockrecords__price_excl_tax=100) new_range = RangeFactory(products=[ product, ]) 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)
def test_voucher_offers_listing_api_exception_caught(self, exception): """ Verify the endpoint returns status 400 Bad Request when ConnectionError occurs """ __, seat = self.create_course_and_seat() new_range = RangeFactory(products=[seat, ]) voucher, __ = prepare_voucher(_range=new_range, benefit_value=10) with mock.patch( 'ecommerce.extensions.api.v2.views.vouchers.VoucherViewSet.get_offers', mock.Mock(side_effect=exception)): request = self.prepare_offers_listing_request(voucher.code) response = self.endpointView(request) self.assertEqual(response.status_code, 400)
def test_apply_voucher_on_basket_and_check_discount_with_invalid_product(self): """ Tests apply_voucher_on_basket_and_check_discount when called with invalid product does not apply voucher and returns the correct values. """ basket = BasketFactory(owner=self.request.user, site=self.request.site) product = ProductFactory(stockrecords__partner__short_code='test1', stockrecords__price_excl_tax=0) voucher, __ = prepare_voucher(_range=RangeFactory(products=[product])) basket.add_product(product, 1) applied, msg = apply_voucher_on_basket_and_check_discount(voucher, self.request, basket) self.assertEqual(applied, False) self.assertEqual(basket.applied_offers(), {}) self.assertEqual(msg, 'Basket does not qualify for coupon code {code}.'.format(code=voucher.code))
def test_get_offers_object_not_found(self): """ Verify the endpoint returns status 404 Not Found when product Course or Stock Record is not found """ __, seat = self.create_course_and_seat() new_range = RangeFactory(products=[seat, ]) voucher, __ = prepare_voucher(_range=new_range) with mock.patch( 'ecommerce.extensions.api.v2.views.vouchers.VoucherViewSet.get_offers', mock.Mock(side_effect=Http404)): request = self.prepare_offers_listing_request(voucher.code) response = self.endpointView(request) self.assertEqual(response.status_code, 404)
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_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} basket = prepare_basket(request, [product]) self.assertFalse(basket.vouchers.all())
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 prepare_url_for_credit_seat(self, code='CREDIT'): """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') # 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 = RangeFactory(products=[credit_seat, ], course_seat_types='credit', catalog_query='*:*') prepare_voucher(code=code, _range=_range) url = '{path}?code={code}'.format(path=self.path, code=code) return url
def test_is_voucher_applied(self): """ Verify is_voucher_applied return correct value. """ product = ProductFactory(stockrecords__price_excl_tax=100) voucher, product = prepare_voucher( _range=RangeFactory(products=[product]), benefit_value=10) basket = prepare_basket(self.request, [product], voucher) # Verify is_voucher_applied returns True when voucher is applied to the basket. self.assertTrue(is_voucher_applied(basket, voucher)) # Verify is_voucher_applied returns False when voucher can not be applied to the basket. self.assertFalse(is_voucher_applied(basket, VoucherFactory()))
def test_voucher_offers_listing_product_found(self): """ Verify the endpoint returns offers data for single product range. """ self.mock_access_token_response() course, seat = self.create_course_and_seat() self.mock_course_run_detail_endpoint( course, discovery_api_url=self.site_configuration.discovery_api_url ) new_range = RangeFactory(products=[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)
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, benefit_type='Percentage'): """ 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(type=benefit_type, range=range_, value=benefit_value) offer = ConditionalOfferFactory(benefit=benefit) voucher.offers.add(offer) return voucher, product