def test_create_line_reference(self): basket = BasketFactory() product = ProductFactory(title="A product") option = OptionFactory(name="product_option", code="product_option") option_product = ProductFactory(title='Asunción') options = [{'option': option, 'value': option_product}] basket.add_product(product, options=options)
def test_description(self): basket = BasketFactory() product = ProductFactory(title="A product") basket.add_product(product) line = basket.lines.first() self.assertEqual(line.description, "A product")
def filled_basket(): basket = BasketFactory() product1 = ProductFactory() product2 = ProductFactory() basket.add_product(product1, quantity=10) basket.add_product(product2, quantity=20) return basket
def test_description_with_attributes(self): basket = BasketFactory() product = ProductFactory(title="A product") basket.add_product(product) line = basket.lines.first() BasketLineAttributeFactory(line=line, value=u"\u2603", option__name="with") self.assertEqual(line.description, u"A product (with = '\u2603')")
def test_basket_lines_queryset_is_ordered(self): # This is needed to make sure a formset is not performing the query # again with an order_by clause (losing all calculated discounts) basket = BasketFactory() product = ProductFactory(title="A product") another_product = ProductFactory(title="Another product") basket.add_product(product) basket.add_product(another_product) queryset = basket.all_lines() self.assertTrue(queryset.ordered)
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_is_satisfied_with_exception_for_enrollments(self): """ The method should return True despite having an error at the enrollment check, given 1 course run seat corresponding to each course in the program. """ offer = factories.ProgramOfferFactory(partner=self.partner, condition=self.condition) basket = BasketFactory(site=self.site, owner=UserFactory()) program = self.mock_program_detail_endpoint( self.condition.program_uuid, self.site_configuration.discovery_api_url) 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: basket.add_product(seat) self.mock_user_data(basket.owner.username, mocked_api='enrollments', owned_products=None, response_code=400) self.assertTrue(self.condition.is_satisfied(offer, basket))
def test_is_satisfied_contains_content_items_failure(self): """ Ensure the condition returns false if the contains_content_item call fails. """ offer = factories.EnterpriseOfferFactory(partner=self.partner, condition=self.condition) basket = BasketFactory(site=self.site, owner=self.user) basket.add_product(self.course_run.seat_products[0]) self.mock_enterprise_learner_api( learner_id=self.user.id, enterprise_customer_uuid=str( self.condition.enterprise_customer_uuid), course_run_id=self.course_run.id, ) self.mock_catalog_contains_course_runs( [self.course_run.id], self.condition.enterprise_customer_uuid, enterprise_customer_catalog_uuid=self.condition. enterprise_customer_catalog_uuid, contains_content=False, raise_exception=True) self.assertFalse(self.condition.is_satisfied(offer, basket))
def test_voucher_errors(self): """ Test data when voucher error happen""" basket = BasketFactory(site=self.site) voucher, product = prepare_voucher(code='test101') basket.vouchers.add(voucher) basket.add_product(product) with mock.patch( 'ecommerce.extensions.api.serializers.VoucherSerializer', side_effect=ValueError): response = self.client.get(self.path, HTTP_AUTHORIZATION=self.token) self.assertIsNone(response.json()['results'][0]['vouchers']) with mock.patch( 'ecommerce.extensions.api.serializers.VoucherSerializer', side_effect=AttributeError): response = self.client.get(self.path, HTTP_AUTHORIZATION=self.token) self.assertIsNone(response.json()['results'][0]['vouchers'])
def test_absolute_benefit_offer_availability(self): """ Verify that enterprise offer condition returns correct result for an absolute benefit with discount value greater than course price. """ offer = factories.EnterpriseOfferFactory( partner=self.partner, benefit=factories.EnterpriseAbsoluteDiscountBenefitFactory( value=150), max_discount=Decimal(300), total_discount=Decimal(200)) basket = BasketFactory(site=self.site, owner=self.user) basket.add_product(self.course_run.seat_products[0]) self.mock_catalog_contains_course_runs( [self.course_run.id], self.condition.enterprise_customer_uuid, enterprise_customer_catalog_uuid=self.condition. enterprise_customer_catalog_uuid, ) self.assertTrue(self.condition.is_satisfied(offer, basket))
def test_stop_sailthru_update_on_multi_product_baskets(self, mock_log_error, mock_update_course_enrollment): """ Verify Sailthru is not contacted for multi-product baskets. """ # Create multi-product basket seat = self.course.create_or_update_seat('verified', False, 100, None) other_course = CourseFactory(partner=self.partner) other_seat = other_course.create_or_update_seat('verified', False, 100, None) basket = BasketFactory(owner=self.user, site=self.site) basket.add_product(seat) basket.add_product(other_seat) multi_product_order = create_order(number=2, basket=basket, user=self.user, site=self.site) # This method takes an argument to determine whether that product is part of a multi-product basket process_basket_addition(None, request=self.request, user=self.user, product=seat, is_multi_product_basket=True) self.assertFalse(mock_update_course_enrollment.called) self.assertFalse(mock_log_error.called) # This method looks at the number of lines in the order to determine if the basket has multiple products process_checkout_complete(None, order=multi_product_order, request=None) self.assertFalse(mock_update_course_enrollment.called) self.assertFalse(mock_log_error.called)
def test_is_satisfied_true(self): """ Ensure the condition returns true if all basket requirements are met. """ offer = factories.EnterpriseOfferFactory(partner=self.partner, condition=self.condition) basket = BasketFactory(site=self.site, owner=self.user) basket.add_product(self.course_run.seat_products[0]) self.mock_enterprise_learner_api( learner_id=self.user.id, enterprise_customer_uuid=str( self.condition.enterprise_customer_uuid), course_run_id=self.course_run.id, ) self.mock_catalog_contains_course_runs( [self.course_run.id], self.condition.enterprise_customer_uuid, self.site.siteconfiguration.enterprise_api_url, enterprise_customer_catalog_uuid=self.condition. enterprise_customer_catalog_uuid, ) self.assertTrue(self.condition.is_satisfied(offer, basket))
def test_is_satisfied_no_enrollments(self): """ The method should return True if the basket contains one course run seat corresponding to each course in the program. """ offer = factories.ProgramOfferFactory(partner=self.partner, condition=self.condition) basket = BasketFactory(site=self.site, owner=UserFactory()) program = self.mock_program_detail_endpoint( self.condition.program_uuid, self.site_configuration.discovery_api_url) # Extract one audit and one verified seat for each course audit_seats = [] verified_seats = [] 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: verified_seats.append(seat) else: audit_seats.append(seat) self.mock_user_data(basket.owner.username) # Empty baskets should never be satisfied basket.flush() self.assertTrue(basket.is_empty) self.assertFalse(self.condition.is_satisfied(offer, basket)) # Adding seats of all the courses with the wrong seat type should NOT satisfy the condition. basket.flush() for seat in audit_seats: basket.add_product(seat) self.assertFalse(self.condition.is_satisfied(offer, basket)) # All courses must be represented in the basket. # NOTE: We add all but the first verified seat to ensure complete branch coverage of the method. basket.flush() for verified_seat in verified_seats[1:len(verified_seats)]: basket.add_product(verified_seat) self.assertFalse(self.condition.is_satisfied(offer, basket)) # The condition should be satisfied if one valid course run from each course is in the basket. basket.add_product(verified_seats[0]) self.assertTrue(self.condition.is_satisfied(offer, basket)) # If the user is enrolled with the wrong seat type for courses missing from their basket that are # needed for the program, the condition should NOT be satisfied basket.flush() for verified_seat in verified_seats[1:len(verified_seats)]: basket.add_product(verified_seat) self.assertFalse(self.condition.is_satisfied(offer, basket))
def test_absolute_benefit_offer_availability_with_max_user_discount(self): """ Verify that enterprise offer condition returns correct result for an absolute benefit with discount value greater than course price. """ offer = factories.EnterpriseOfferFactory( partner=self.partner, benefit=factories.EnterpriseAbsoluteDiscountBenefitFactory(value=150), max_user_discount=150 ) for _ in range(5): order = OrderFactory(user=self.user, status=ORDER.COMPLETE) OrderDiscountFactory(order=order, offer_id=offer.id, amount=10) basket = BasketFactory(site=self.site, owner=self.user) basket.add_product(self.course_run.seat_products[0]) self.mock_catalog_contains_course_runs( [self.course_run.id], self.condition.enterprise_customer_uuid, enterprise_customer_catalog_uuid=self.condition.enterprise_customer_catalog_uuid, ) self.assertTrue(self.condition.is_satisfied(offer, basket))
def test_is_satisfied_with_course_entitlement_request_error(self): """ Ensure the condition returns False if an error occurs while fetching course details. """ offer = factories.EnterpriseOfferFactory(partner=self.partner, condition=self.condition) basket = BasketFactory(site=self.site, owner=self.user) basket.add_product(self.entitlement) self.mock_course_detail_endpoint_error( self.entitlement, discovery_api_url=self.site_configuration.discovery_api_url, error=ReqConnectionError ) self.mock_enterprise_learner_api( learner_id=self.user.id, enterprise_customer_uuid=str(self.condition.enterprise_customer_uuid), course_run_id=self.course_run.id, ) self.mock_catalog_contains_course_runs( [self.entitlement.attr.UUID], self.condition.enterprise_customer_uuid, enterprise_customer_catalog_uuid=self.condition.enterprise_customer_catalog_uuid, ) self.assertFalse(self.condition.is_satisfied(offer, basket))
def test_is_satisfied_with_course_entitlement(self): """ Ensure the condition returns true if the basket contains a course entitlement. """ offer = factories.EnterpriseOfferFactory(partner=self.partner, condition=self.condition) basket = BasketFactory(site=self.site, owner=self.user) basket.add_product(self.entitlement) self.mock_course_detail_endpoint( discovery_api_url=self.site_configuration.discovery_api_url, course=self.entitlement ) self.mock_enterprise_learner_api( learner_id=self.user.id, enterprise_customer_uuid=str(self.condition.enterprise_customer_uuid), course_run_id=self.course_run.id, ) self.mock_catalog_contains_course_runs( [self.entitlement.attr.UUID], self.condition.enterprise_customer_uuid, enterprise_customer_catalog_uuid=self.condition.enterprise_customer_catalog_uuid, ) self.assertTrue(self.condition.is_satisfied(offer, basket))
def test_prof_ed_stale_product_removal_with_orders(self): """ Verify that professional education seats are never deleted if they have been purchased. """ user = self.create_user() course = CourseFactory() professional_product_no_verification = course.create_or_update_seat('professional', False, 0, self.partner) self.assertEqual(course.products.count(), 2) basket = BasketFactory(owner=user, site=self.site) basket.add_product(professional_product_no_verification) create_order(basket=basket, user=user) course.create_or_update_seat('professional', True, 0, self.partner) self.assertEqual(course.products.count(), 3) product_mode = course.products.all()[0] self.assertEqual(product_mode.attr.id_verification_required, True) self.assertEqual(product_mode.attr.certificate_type, 'professional') product_mode = course.products.all()[1] self.assertEqual(product_mode.attr.id_verification_required, False) self.assertEqual(product_mode.attr.certificate_type, 'professional')
def test_handle_post_order_for_seat_purchase(self, __): """ Ensure that the single seat purchase order is not linked any business client when the method `handle_post_order` is invoked. """ toggle_switch(ENROLLMENT_CODE_SWITCH, False) course = CourseFactory(partner=self.partner) verified_product = course.create_or_update_seat('verified', True, 50) user = UserFactory() basket = BasketFactory(owner=user, site=self.site) basket.add_product(verified_product, quantity=1) order = create_order(number=1, basket=basket, user=user) request_data = {'organization': 'Dummy Business Client'} # Manually add organization attribute on the basket for testing basket_add_organization_attribute(basket, request_data) EdxOrderPlacementMixin().handle_post_order(order) # Now verify that the single seat order is not linked to business # client by checking that there is no record for BusinessClient. assert not BusinessClient.objects.all()
def test_offer_availability_with_max_user_discount(self, discount_type, num_prev_orders, benefit_value, is_satisfied): """ Verify that enterprise offer with discount type percentage and absolute, condition returns correct result based on user limits in the offer. """ benefits = { Benefit.PERCENTAGE: factories.EnterprisePercentageDiscountBenefitFactory( value=benefit_value), Benefit.FIXED: factories.EnterpriseAbsoluteDiscountBenefitFactory( value=benefit_value), } offer = factories.EnterpriseOfferFactory( partner=self.partner, benefit=benefits[discount_type], max_user_discount=150) for _ in range(num_prev_orders): order = OrderFactory(user=self.user, status=ORDER.COMPLETE) OrderDiscountFactory(order=order, offer_id=offer.id, amount=10) basket = BasketFactory(site=self.site, owner=self.user) basket.add_product(self.course_run.seat_products[0]) basket.add_product(self.entitlement) self.mock_course_detail_endpoint( discovery_api_url=self.site_configuration.discovery_api_url, course=self.entitlement) self.mock_catalog_contains_course_runs( [self.course_run.id], self.condition.enterprise_customer_uuid, self.site.siteconfiguration.enterprise_api_url, enterprise_customer_catalog_uuid=self.condition. enterprise_customer_catalog_uuid, ) self.assertEqual(self.condition.is_satisfied(offer, basket), is_satisfied)
def test_get_enterprise_customer_from_enterprise_offer( self, discount_value): """ Verify that "get_enterprise_customer_from_enterprise_offer" returns `None` if expected conditions are not met. """ course = CourseFactory(name='EnterpriseConsentErrorTest', partner=PartnerFactory()) product = course.create_or_update_seat('verified', False, 50) benefit = EnterprisePercentageDiscountBenefitFactory( value=discount_value) offer = EnterpriseOfferFactory(benefit=benefit) # set wrong priority to invalidate the condition in util offer.priority = 111 self.mock_enterprise_learner_api( learner_id=self.learner.id, enterprise_customer_uuid=str( offer.condition.enterprise_customer_uuid), course_run_id=course.id, ) self.mock_catalog_contains_course_runs( [course.id], str(offer.condition.enterprise_customer_uuid), self.site.siteconfiguration.enterprise_api_url, enterprise_customer_catalog_uuid=str( offer.condition.enterprise_customer_catalog_uuid), contains_content=True, ) basket = BasketFactory(site=self.site, owner=self.create_user()) basket.add_product(product) basket.strategy = DefaultStrategy() Applicator().apply_offers(basket, [offer]) self.assertIsNone( get_enterprise_customer_from_enterprise_offer(basket))
def test_handle_post_order_for_bulk_purchase(self, __): """ Ensure that the bulk purchase order is linked to the provided business client when the method `handle_post_order` is invoked. """ toggle_switch(ENROLLMENT_CODE_SWITCH, True) course = CourseFactory(partner=self.partner) course.create_or_update_seat('verified', True, 50, create_enrollment_code=True) enrollment_code = Product.objects.get(product_class__name=ENROLLMENT_CODE_PRODUCT_CLASS_NAME) user = UserFactory() basket = BasketFactory(owner=user, site=self.site) basket.add_product(enrollment_code, quantity=1) order = create_order(number=1, basket=basket, user=user) request_data = {'organization': 'Dummy Business Client'} # Manually add organization attribute on the basket for testing basket_add_organization_attribute(basket, request_data) EdxOrderPlacementMixin().handle_post_order(order) # Now verify that a new business client has been created in current # order is now linked with that client through Invoice model. business_client = BusinessClient.objects.get(name=request_data['organization']) assert Invoice.objects.get(order=order).business_client == business_client
def test_offer_availability_with_max_discount(self, discount_type, total_discount, benefit_value, is_satisfied): """ Verify that enterprise offer with discount type percentage and absolute, condition returns correct result based on total_discount(consumed discount so far) and discount on course price covered by the offer. """ benefits = { Benefit.PERCENTAGE: factories.EnterprisePercentageDiscountBenefitFactory( value=benefit_value), Benefit.FIXED: factories.EnterpriseAbsoluteDiscountBenefitFactory( value=benefit_value), } offer = factories.EnterpriseOfferFactory( partner=self.partner, benefit=benefits[discount_type], max_discount=Decimal(5000), total_discount=total_discount) basket = BasketFactory(site=self.site, owner=self.user) basket.add_product(self.course_run.seat_products[0]) basket.add_product(self.entitlement) self.mock_course_detail_endpoint( discovery_api_url=self.site_configuration.discovery_api_url, course=self.entitlement) self.mock_catalog_contains_course_runs( [self.course_run.id], self.condition.enterprise_customer_uuid, self.site.siteconfiguration.enterprise_api_url, enterprise_customer_catalog_uuid=self.condition. enterprise_customer_catalog_uuid, ) self.assertEqual(self.condition.is_satisfied(offer, basket), is_satisfied)
def create_order(self, user=None, credit=False, multiple_lines=False, free=False, entitlement=False, status=ORDER.COMPLETE, id_verification_required=False): user = user or self.user basket = BasketFactory(owner=user, site=self.site) if credit: basket.add_product(self.credit_product) elif multiple_lines: basket.add_product(self.verified_product) basket.add_product(self.honor_product) elif free: basket.add_product(self.honor_product) elif entitlement: course_entitlement = create_or_update_course_entitlement( certificate_type='verified', price=100, partner=self.partner, UUID='111', title='Foo', id_verification_required=id_verification_required) basket.add_product(course_entitlement) else: basket.add_product(self.verified_product) order = create_order(basket=basket, user=user) order.status = status if entitlement: entitlement_option = Option.objects.get(code='course_entitlement') line = order.lines.first() line.attributes.create(option=entitlement_option, value='111') order.save() return order
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)
class DynamicConditionTests(TestCase): """ Tests to make sure that the dynamic discount condition correctly compute whether to give a discount """ def setUp(self): super(DynamicConditionTests, self).setUp() self.condition = Condition.objects.get( proxy_class= 'ecommerce.extensions.offer.dynamic_conditional_offer.DynamicDiscountCondition' ).proxy() self.offer = ConditionalOffer.objects.get( name='dynamic_conditional_offer') self.basket = BasketFactory(site=self.site, owner=self.create_user()) self.seat_product_class, __ = ProductClass.objects.get_or_create( name=SEAT_PRODUCT_CLASS_NAME) def test_name(self): self.assertTrue(self.condition.name == 'dynamic_discount_condition') @override_flag(DYNAMIC_DISCOUNT_FLAG, active=True) @patch('crum.get_current_request') @patch( 'ecommerce.extensions.offer.dynamic_conditional_offer.jwt_decode_handler', side_effect=_mock_jwt_decode_handler) @ddt.data( { 'discount_applicable': True, 'discount_percent': 15 }, { 'discount_applicable': False, 'discount_percent': 15 }, None, ) def test_is_satisfied_true(self, discount_jwt, jwt_decode_handler, request): # pylint: disable=unused-argument product = ProductFactory(product_class=self.seat_product_class, stockrecords__price_excl_tax=10, categories=[]) self.basket.add_product(product) request.return_value = Mock(method='GET', GET={'discount_jwt': discount_jwt}) if discount_jwt and discount_jwt.get('discount_applicable') is True: self.assertTrue( self.condition.is_satisfied(self.offer, self.basket)) else: self.assertFalse( self.condition.is_satisfied(self.offer, self.basket)) @override_flag(DYNAMIC_DISCOUNT_FLAG, active=True) @patch('crum.get_current_request') def test_is_satisfied_quantity_more_than_1(self, request): # pylint: disable=unused-argument """ This discount should not apply if are buying more than one of the same course. """ product = ProductFactory(stockrecords__price_excl_tax=10, categories=[]) self.basket.add_product(product, quantity=2) self.assertFalse(self.condition.is_satisfied(self.offer, self.basket)) @override_flag(DYNAMIC_DISCOUNT_FLAG, active=True) @patch('crum.get_current_request') def test_is_satisfied_not_seat_product(self, request): # pylint: disable=unused-argument """ This discount should not apply if are not purchasing a seat product. """ product = ProductFactory(stockrecords__price_excl_tax=10, categories=[]) self.basket.add_product(product) self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))
def test_is_satisfied_with_entitlements(self): """ The condition should be satisfied if, for each course in the program, their is either an entitlement sku in the basket or the user already has an entitlement for the course and the site has enabled partial program offers. """ offer = factories.ProgramOfferFactory(partner=self.partner, condition=self.condition) basket = BasketFactory(site=self.site, owner=UserFactory()) program = self.mock_program_detail_endpoint( self.condition.program_uuid, self.site_configuration.discovery_api_url) entitlements_response = { "count": 0, "num_pages": 1, "current_page": 1, "results": [{ 'mode': 'verified', 'course_uuid': '268afbfc-cc1e-415b-a5d8-c58d955bcfc3' }, { 'mode': 'verified', 'course_uuid': '268afbfc-cc1e-415b-a5d8-c58d955bcfc4' }], "next": None, "start": 0, "previous": None } # Extract one verified seat for each course verified_entitlements = [] course_uuids = set([course['uuid'] for course in program['courses']]) for parent_entitlement in Product.objects.filter( product_class__name=COURSE_ENTITLEMENT_PRODUCT_CLASS_NAME, structure=Product.PARENT): for entitlement in Product.objects.filter( parent=parent_entitlement): if entitlement.attr.UUID in course_uuids and entitlement.attr.certificate_type == 'verified': verified_entitlements.append(entitlement) self.mock_user_data(basket.owner.username, mocked_api='entitlements', owned_products=entitlements_response) self.mock_user_data(basket.owner.username) # If the user has not added all of the remaining courses in program to their basket, # the condition should not be satisfied basket.flush() for entitlement in verified_entitlements[2:len(verified_entitlements) - 1]: basket.add_product(entitlement) self.assertFalse(self.condition.is_satisfied(offer, basket)) # When all courses in the program that the user is not already enrolled in are in their basket # and the site allows partial program completion, the condition should be satisfied basket.add_product(verified_entitlements[-1]) self.assertTrue(self.condition.is_satisfied(offer, basket)) # If the site does not allow partial program completion and the user does not have all of the program # courses in their basket, the condition should not be satisfied basket.site.siteconfiguration.enable_partial_program = False self.assertFalse(self.condition.is_satisfied(offer, basket)) # Verify the user enrollments are cached basket.site.siteconfiguration.enable_partial_program = True httpretty.disable() with mock.patch('ecommerce.programs.conditions.get_program', return_value=program): self.assertTrue(self.condition.is_satisfied(offer, basket))
class ManualEnrollmentOrderDiscountConditionTests(TestCase): """ Test the `ManualEnrollmentOrderDiscountCondition` functionality. """ def setUp(self): super(ManualEnrollmentOrderDiscountConditionTests, self).setUp() self.user = self.create_user(is_staff=True) self.learner = self.create_user(username='******', is_staff=False) self.condition = ManualEnrollmentOrderDiscountConditionFactory() self.basket = BasketFactory(site=self.site, owner=self.learner) self.course = CourseFactory(id='course-v1:MAX+CX+Course', partner=self.partner) self.course.create_or_update_seat( certificate_type='verified', id_verification_required=True, price=50 ) self.course.create_or_update_seat( certificate_type='audit', id_verification_required=False, price=0 ) self.seat_product = self.course.seat_products.filter( attributes__name='certificate_type' ).exclude( attribute_values__value_text='audit' ).first() request_patcher = patch('crum.get_current_request') self.request_patcher = request_patcher.start() self.request_patcher.return_value = RequestFactory().post( reverse('api:v2:manual-course-enrollment-order-list') ) self.addCleanup(request_patcher.stop) def test_is_satisfied_with_wrong_offer(self): """ Test `ManualEnrollmentOrderDiscountCondition.is_satisfied` works as expected for wrong offer. """ offer = EnterpriseOfferFactory(partner=self.partner, condition=self.condition) status = self.condition.is_satisfied(offer, self.basket) assert not status def test_is_satisfied_with_wrong_order_lines(self): """ Test `ManualEnrollmentOrderDiscountCondition.is_satisfied` works as expected when there wrong number of order lines. """ for seat_product in self.course.seat_products: self.basket.add_product(seat_product) offer = ManualEnrollmentOrderOfferFactory() status = self.condition.is_satisfied(offer, self.basket) assert not status def test_is_satisfied_with_non_seat_type_product(self): """ Test `ManualEnrollmentOrderDiscountCondition.is_satisfied` works as expected when there basket contains non seat type product. """ product = ProductFactory() self.basket.add_product(product) offer = ManualEnrollmentOrderOfferFactory() status = self.condition.is_satisfied(offer, self.basket) assert not status def test_is_satisfied_with_non_verified_seat_type_product(self): """ Test `ManualEnrollmentOrderDiscountCondition.is_satisfied` works as expected when there basket contains seat type product but seat is not verified. """ seat_product = self.course.seat_products.filter( attribute_values__value_text='audit' ).first() self.basket.add_product(seat_product) offer = ManualEnrollmentOrderOfferFactory() status = self.condition.is_satisfied(offer, self.basket) assert not status def test_is_satisfied_success(self): """ Test `ManualEnrollmentOrderDiscountCondition.is_satisfied` works as expected when condition satisfies. """ self.basket.add_product(self.seat_product) offer = ManualEnrollmentOrderOfferFactory() status = self.condition.is_satisfied(offer, self.basket) assert status def test_is_satisfied_with_wrong_path_info(self): """ Test `ManualEnrollmentOrderDiscountCondition.is_satisfied` works as expected when request path_info is wrong. """ with patch('crum.get_current_request') as request_patcher: request_patcher.return_value = RequestFactory().post('some_view_path') offer = ManualEnrollmentOrderOfferFactory() self.basket.add_product(self.seat_product) status = self.condition.is_satisfied(offer, self.basket) assert not status
class JournalBundleConditionTests(TestCase, JournalMixin): def setUp(self): super(JournalBundleConditionTests, self).setUp() user = self.create_user(is_staff=True) self.client.login(username=user.username, password=self.password) self.condition = factories.JournalConditionFactory() self.offer = factories.JournalBundleOfferFactory( partner=self.partner, condition=self.condition) self.basket = BasketFactory(site=self.site, owner=UserFactory()) self.basket.add_product(self.create_product(self.client), 1) def test_name(self, mocked_journal_api_response): """ The name should contain the program's UUID. """ mocked_journal_api_response.return_value = None expected = 'Basket contains every product in bundle {}'.format( self.condition.journal_bundle_uuid) self.assertEqual(self.condition.name, expected) def test_is_satisfied_with_empty_basket(self, mocked_journal_api_response): """ Test the 'is_satisfied' with empty basket """ mocked_journal_api_response.return_value = None self.basket.flush() self.assertTrue(self.basket.is_empty) self.assertFalse(self.condition.is_satisfied(self.offer, self.basket)) def test_is_satisfied_with_exception(self, mocked_journal_api_response): """ Test the 'is_satisfied' with 'HttpNotFoundError' exception """ mocked_journal_api_response.side_effect = HttpNotFoundError self.assertFalse(self.condition.is_satisfied(self.offer, self.basket)) def test_is_satisfied_with_slumber_exception(self, mocked_journal_api_response): """ Test the 'is_satisfied' with 'SlumberBaseException' exception """ mocked_journal_api_response.side_effect = SlumberBaseException self.assertFalse(self.condition.is_satisfied(self.offer, self.basket)) def test_is_satisfied_with_timeout(self, mocked_journal_api_response): """ Test the 'is_satisfied' with 'Timeout' exception """ mocked_journal_api_response.side_effect = Timeout self.assertFalse(self.condition.is_satisfied(self.offer, self.basket)) def test_is_satisfied_without_journal_bundle(self, mocked_journal_api_response): """ Test the 'is_satisfied' without Journal bundle """ mocked_journal_api_response.return_value = None self.assertFalse(self.condition.is_satisfied(self.offer, self.basket)) def test_is_satisfied_without_courses(self, mocked_journal_api_response): """ Test the 'is_satisfied' without courses in Journal bundle """ mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle( empty_courses=True) self.assertFalse(self.condition.is_satisfied(self.offer, self.basket)) def test_is_satisfied_with_some_but_not_all_courses( self, mocked_journal_api_response): """ Test the 'is_satisfied' with only some of the courses in the Journal bundle """ mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle( multiple_courses=True) self.assertFalse(self.condition.is_satisfied(self.offer, self.basket)) def test_is_satisfied_with_dummy_product(self, mocked_journal_api_response): """ Test the 'is_satisfied' with dummy product in basket """ mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle( ) self.basket.flush() self.basket.add_product( self.create_product( self.client, data=self.get_data_for_create(sku="dummy-sku")), 1) self.assertFalse(self.condition.is_satisfied(self.offer, self.basket)) def test_is_satisfied_with_valid_data(self, mocked_journal_api_response): """ Test the 'is_satisfied' with valid Journal bundle """ mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle( empty_journals=True) self.assertTrue(self.condition.is_satisfied(self.offer, self.basket)) def test_get_applicable_lines(self, mocked_journal_api_response): """ Test the 'get_applicable_lines' with valid product in basket """ mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle( ) applicable_lines = [(line.product.stockrecords.first().price_excl_tax, line) for line in self.basket.all_lines()] self.assertEqual( self.condition.get_applicable_lines(self.offer, self.basket), applicable_lines) def test_get_applicable_lines_with_empty_basket( self, mocked_journal_api_response): """ Test the 'get_applicable_lines' with empty basket """ mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle( ) self.basket.flush() self.assertEqual( self.condition.get_applicable_lines(self.offer, self.basket), []) def test_get_applicable_lines_sku_not_in_basket( self, mocked_journal_api_response): """ Test the 'get_applicable_lines' where the sku is not in the basket """ mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle( ) self.basket.flush() self.basket.add_product( self.create_product( self.client, data=self.get_data_for_create(sku="dummy-sku")), 1) self.assertEqual( self.condition.get_applicable_lines(self.offer, self.basket), []) @mock.patch( "ecommerce.extensions.catalogue.models.Product.get_is_discountable") def test_get_applicable_lines_product_is_not_discountable( self, mocked_product_is_discountable, mocked_journal_api_response): """ Test the 'get_applicable_lines' where the product is not discountable """ mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle( ) mocked_product_is_discountable.return_value = False self.assertEqual( self.condition.get_applicable_lines(self.offer, self.basket), []) @mock.patch("oscar.apps.offer.utils.unit_price") def test_get_applicable_lines_no_price(self, mocked_unit_price, mocked_journal_api_response): """ Test the 'get_applicable_lines' where there is no price """ mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle( ) mocked_unit_price.return_value = None self.assertEqual( self.condition.get_applicable_lines(self.offer, self.basket), []) def test_get_applicable_lines_no_journal_bundle(self, mock_journal_api_response): """ Test 'get_applicable_lines' where the journal bundle is None """ mock_journal_api_response.return_value = None self.assertEqual( self.condition.get_applicable_lines(self.offer, self.basket), [])
def test_is_satisfied_site_mismatch(self): """ Ensure the condition returns False if the offer partner does not match the basket site partner. """ offer = factories.EnterpriseOfferFactory(partner=SiteConfigurationFactory().partner, condition=self.condition) basket = BasketFactory(site=self.site, owner=self.user) basket.add_product(self.test_product) self.assertFalse(self.condition.is_satisfied(offer, basket))