def setUp(self): super(EdxOrderPlacementMixinTests, self).setUp() self.user = UserFactory(lms_user_id=61710) self.order = self.create_order(status=ORDER.OPEN) # Ensure that the basket attribute type exists for these tests self.basket_attribute_type, _ = BasketAttributeType.objects.get_or_create( name=EMAIL_OPT_IN_ATTRIBUTE)
def test_generate_coupon_report(self): """ Verify the coupon report is generated properly. """ self.setup_coupons_for_report() client = UserFactory() basket = Basket.get_basket(client, self.site) basket.add_product(self.coupon) vouchers = self.coupon_vouchers.first().vouchers.all() self.use_voucher('TESTORDER1', vouchers[1], self.user) user2 = UserFactory() self.use_voucher('TESTORDER2', vouchers[2], self.user) self.use_voucher('TESTORDER3', vouchers[2], user2) self.mock_course_api_response(course=self.course) field_names, rows = generate_coupon_report(self.coupon_vouchers) self.assertEqual(field_names, [ 'Code', 'Coupon Name', 'Maximum Coupon Usage', 'Redemption Count', 'Coupon Type', 'URL', 'Course ID', 'Organization', 'Client', 'Category', 'Note', 'Price', 'Invoiced Amount', 'Discount Percentage', 'Discount Amount', 'Status', 'Order Number', 'Redeemed By Username', 'Create Date', 'Coupon Start Date', 'Coupon Expiry Date', 'Email Domains', ]) voucher = Voucher.objects.get(name=rows[0]['Coupon Name']) self.assert_report_first_row(rows.pop(0), self.coupon, voucher) for row in rows: voucher = Voucher.objects.get(code=row['Code']) self.assert_report_row(row, voucher) self.assertNotIn('Catalog Query', field_names) self.assertNotIn('Course Seat Types', field_names) self.assertNotIn('Redeemed For Course ID', field_names)
def setUp(self): super(EnrollmentFulfillmentModuleTests, self).setUp() self.user = UserFactory() self.user.tracking_context = { 'ga_client_id': 'test-client-id', 'lms_user_id': 'test-user-id', 'lms_ip': '127.0.0.1' } self.user.save() self.course = CourseFactory(id=self.course_id, name='Demo Course', partner=self.partner) self.seat = self.course.create_or_update_seat(self.certificate_type, False, 100, self.provider) basket = factories.BasketFactory(owner=self.user, site=self.site) basket.add_product(self.seat, 1) self.order = create_order(number=1, basket=basket, user=self.user)
def test_notify_purchaser_course_entielement(self, mock_task): """ Verify the notification is scheduled if the site has notifications enabled and the refund is for a course entitlement. """ site_configuration = self.site.siteconfiguration site_configuration.send_refund_notifications = True user = UserFactory() course_entitlement = create_or_update_course_entitlement( 'verified', 100, self.partner, '111-222-333-444', 'Course Entitlement') basket = create_basket(site=self.site, owner=user, empty=True) basket.add_product(course_entitlement, 1) order = create_order(number=1, basket=basket, user=user) order_url = get_receipt_page_url(site_configuration, order.number) refund = Refund.create_with_lines(order, order.lines.all()) with LogCapture(REFUND_MODEL_LOGGER_NAME) as logger: refund._notify_purchaser() # pylint: disable=protected-access msg = 'Course refund notification scheduled for Refund [{}].'.format(refund.id) logger.check_present( (REFUND_MODEL_LOGGER_NAME, 'INFO', msg) ) amount = format_currency(order.currency, 100) mock_task.assert_called_once_with( user.email, refund.id, amount, course_entitlement.title, order.number, order_url, site_code=self.partner.short_code )
def create_order_with_billing_address(self): """ Creates an order object with a bit of extra information for HubSpot unit tests""" enrollment_code = Product.objects.get( product_class__name=ENROLLMENT_CODE_PRODUCT_CLASS_NAME) user = UserFactory() basket = factories.BasketFactory(owner=user, site=self.site) basket.add_product(enrollment_code, self.QUANTITY) # add organization and purchaser attributes manually to the basket for testing purposes basket_data = { 'organization': 'Dummy Business Client', PURCHASER_BEHALF_ATTRIBUTE: 'True' } basket_add_organization_attribute(basket, basket_data) # add some additional data the billing address to exercise some of the code paths in the unit we are testing billing_address = factories.BillingAddressFactory() billing_address.line2 = 'Suite 321' billing_address.line4 = "City" billing_address.state = "State" billing_address.country.name = "United States of America" # create new order adding in the additional billing address info return create_order(number=2, basket=basket, user=user, billing_address=billing_address)
def test_is_single_use_range_condition_satisfied(self): """ Verify that the condition for a single use coupon is only satisfied by single-product baskets. """ valid_user_email = 'valid@{domain}'.format(domain=self.valid_sub_domain) basket = factories.BasketFactory(site=self.site, owner=UserFactory(email=valid_user_email)) product1 = self.create_entitlement_product() product2 = self.create_entitlement_product() _range = factories.RangeFactory() _range.course_seat_types = ','.join(Range.ALLOWED_SEAT_TYPES) _range.catalog_query = 'uuid:*' benefit = factories.BenefitFactory(range=_range) offer = factories.ConditionalOfferFactory(benefit=benefit) offer.set_voucher( factories.VoucherFactory(usage='Single-use') ) self.mock_access_token_response() self.mock_catalog_query_contains_endpoint( course_run_ids=[], course_uuids=[product1.attr.UUID, product2.attr.UUID], absent_ids=[], query=benefit.range.catalog_query, discovery_api_url=self.site_configuration.discovery_api_url ) # Verify that each product individually satisfies the condition basket.add_product(product1) self.assertTrue(offer.is_condition_satisfied(basket)) basket.flush() basket.add_product(product2) self.assertTrue(offer.is_condition_satisfied(basket)) # Verify that the offer cannot be applied to a multi-product basket basket.add_product(product1) self.assertFalse(offer.is_condition_satisfied(basket))
def test_is_satisfied_program_without_entitlements(self): """ User entitlements should not be retrieved if no course in the program has a course entitlement product """ 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, include_entitlements=False ) enrollments = [{'mode': 'verified', 'course_details': {'course_id': 'course-v1:test-org+course+1'}}, {'mode': 'verified', 'course_details': {'course_id': 'course-v1:test-org+course+2'}}] 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 } self.mock_user_data(basket.owner.username, owned_products=enrollments) self.mock_user_data(basket.owner.username, mocked_api='entitlements', owned_products=entitlements_response) for course in program['courses'][2:len(program['courses']) - 1]: 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) with mock.patch('ecommerce.programs.conditions.deprecated_traverse_pagination') as mock_processing_entitlements: self.assertFalse(self.condition.is_satisfied(offer, basket)) mock_processing_entitlements.assert_not_called()
def test_is_satisfied_program_retrieval_failure(self): """ The method should return False if no program is retrieved """ offer = factories.ProgramOfferFactory(partner=self.partner, condition=self.condition) basket = BasketFactory(site=self.site, owner=UserFactory()) basket.add_product(self.test_product) self.condition.program_uuid = None self.assertFalse(self.condition.is_satisfied(offer, basket))
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_is_satisfied_when_owner_has_no_assignment(self, mock_request): """ Ensure that condition returns expected result the basket owner has no assignments. # voucher has free slots(3) available, no offer assignment for basket owner, # assignments(2) exist for other users, voucher has some redemptions(num_orders = 2) # basket owner is allowed to redeem the voucher """ mock_request.return_value = self.request code = 'TA7WCQD3T4C7GHZ4' num_orders = 2 max_global_applications = 7 enterprise_offer = factories.EnterpriseOfferFactory( max_global_applications=max_global_applications) voucher = factories.VoucherFactory(usage=Voucher.MULTI_USE, code=code, num_orders=num_orders) voucher.offers.add(enterprise_offer) factories.OfferAssignmentFactory(offer=enterprise_offer, code=code, user_email='*****@*****.**') factories.OfferAssignmentFactory(offer=enterprise_offer, code=code, user_email='*****@*****.**') basket = BasketFactory(site=self.site, owner=UserFactory(email='*****@*****.**')) basket.vouchers.add(voucher) assert self.condition.is_satisfied(enterprise_offer, basket) is True
def test_is_satisfied_with_different_users(self, mock_request): """ Ensure that condition returns expected result when wrong user is try to redeem the voucher. # code = 'ASD' assigned_to = '*****@*****.**' # code = 'ZXC' assigned_to = '*****@*****.**' # [email protected] try to redeem `ASD` code # `is_satisfied` should return False """ mock_request.return_value = self.request voucher1 = factories.VoucherFactory(usage=Voucher.SINGLE_USE, code='ASD') voucher2 = factories.VoucherFactory(usage=Voucher.SINGLE_USE, code='ZXC') enterprise_offers = factories.EnterpriseOfferFactory.create_batch(2) voucher1.offers.add(enterprise_offers[0]) voucher2.offers.add(enterprise_offers[1]) basket = BasketFactory(site=self.site, owner=UserFactory(email='*****@*****.**')) basket.vouchers.add(voucher1) factories.OfferAssignmentFactory(offer=enterprise_offers[0], code=voucher1.code, user_email='*****@*****.**') factories.OfferAssignmentFactory(offer=enterprise_offers[1], code=voucher2.code, user_email='*****@*****.**') assert self.condition.is_satisfied(enterprise_offers[1], basket) is False
def _create_user(self): """ Create a new user. """ user = UserFactory(username='******', password=self.PASSWORD) UserSocialAuth.objects.create(user=user, provider='edx-oauth2', uid=user.username) return user
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', PURCHASER_BEHALF_ATTRIBUTE: 'False', } # Manually add organization and purchaser attributes 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 assert_condition(self, voucher_type, assignments, expected_condition_result): """ Verify that condition works as expected for different vouchers and assignments. """ for assignment in assignments: code = assignment['code'] email = assignment['user_email'] # In some cases individual assignments have their own expected result expected_condition_result = assignment.get( 'result', expected_condition_result) voucher = Voucher.objects.get(usage=voucher_type, code=code) basket = BasketFactory(site=self.site, owner=UserFactory(email=email)) basket.vouchers.add(voucher) is_condition_satisfied = self.condition.is_satisfied( voucher.enterprise_offer, basket) assert is_condition_satisfied == expected_condition_result # update the `num_orders` so that we can also verify the redemptions check # also update the offer assignment status if expected_condition_result: voucher.num_orders += 1 voucher.save() assignment = OfferAssignment.objects.filter( offer=voucher.enterprise_offer, code=code, user_email=email).exclude( status__in=[OFFER_REDEEMED, OFFER_ASSIGNMENT_REVOKED ]).first() if assignment: assignment.status = OFFER_REDEEMED assignment.save()
def test_zero_dollar_refund(self, mock_revoke_line): """ Given an order and order lines which total $0 and are not refunded, Refund.create_with_lines should create and approve a Refund with corresponding RefundLines. """ httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=200, body='{}', content_type='application/json') order = self.create_order(user=UserFactory(), free=True) # Verify that the order totals $0. self.assertEqual(order.total_excl_tax, 0) refund = Refund.create_with_lines(order, list(order.lines.all())) # Verify that refund lines are not revoked. self.assertFalse(mock_revoke_line.called) # Verify that the refund has been successfully approved. self.assertEqual(refund.status, REFUND.COMPLETE) self.assertEqual({line.status for line in refund.lines.all()}, {REFUND_LINE.COMPLETE})
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', PURCHASER_BEHALF_ATTRIBUTE: 'False', } # Manually add organization and purchaser attributes 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_is_range_condition_satisfied(self): """ Verify that a basket satisfies a condition only when all of its products are in its range's catalog queryset. """ valid_user_email = 'valid@{domain}'.format( domain=self.valid_sub_domain) basket = factories.BasketFactory( site=self.site, owner=UserFactory(email=valid_user_email)) product = self.create_entitlement_product() another_product = self.create_entitlement_product() _range = factories.RangeFactory() _range.course_seat_types = ','.join(Range.ALLOWED_SEAT_TYPES) _range.catalog_query = 'uuid:{course_uuid}'.format( course_uuid=product.attr.UUID) benefit = factories.BenefitFactory(range=_range) offer = factories.ConditionalOfferFactory(benefit=benefit) self.mock_access_token_response() self.mock_catalog_query_contains_endpoint( course_run_ids=[], course_uuids=[product.attr.UUID], absent_ids=[another_product.attr.UUID], query=benefit.range.catalog_query, discovery_api_url=self.site_configuration.discovery_api_url) basket.add_product(product) self.assertTrue(offer.is_condition_satisfied(basket)) basket.add_product(another_product) self.assertFalse(offer.is_condition_satisfied(basket)) # Verify that API return values are cached httpretty.disable() self.assertFalse(offer.is_condition_satisfied(basket))
def setUp(self): super(CouponFulfillmentModuleTest, self).setUp() coupon = self.create_coupon() user = UserFactory() basket = factories.BasketFactory(owner=user, site=self.site) basket.add_product(coupon, 1) self.order = create_order(number=1, basket=basket, user=user)
def setUp(self): super(RefundAdminTests, self).setUp() self.user = UserFactory(is_staff=True, is_superuser=True, password=self.password) self.client.login(username=self.user.username, password=self.password) self.role = EcommerceFeatureRole.objects.get(name=ORDER_MANAGER_ROLE)
def setUp(self): super(EnterpriseCustomerConditionTests, self).setUp() self.user = UserFactory() self.condition = factories.EnterpriseCustomerConditionFactory() self.test_product = ProductFactory(stockrecords__price_excl_tax=10, categories=[]) self.course_run = CourseFactory(partner=self.partner) self.course_run.create_or_update_seat('verified', True, Decimal(100))
def create_user(self, lms_user_id=lms_user_id, **kwargs): """Create a user, with overrideable defaults.""" not_provided = object() if kwargs.get('username', not_provided) is None: kwargs.pop('username') return UserFactory(password=self.password, lms_user_id=lms_user_id, **kwargs)
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.ProgramOfferFactory( partner=SiteConfigurationFactory().partner, condition=self.condition) basket = BasketFactory(site=self.site, owner=UserFactory()) basket.add_product(self.test_product) self.assertFalse(self.condition.is_satisfied(offer, basket))
def test_is_satisfied_free_basket(self): """ Ensure the basket returns False if the basket total is zero. """ offer = factories.ProgramOfferFactory(partner=self.partner, condition=self.condition) basket = BasketFactory(site=self.site, owner=UserFactory()) test_product = factories.ProductFactory(stockrecords__price_excl_tax=0, stockrecords__partner__short_code='test') basket.add_product(test_product) self.assertFalse(self.condition.is_satisfied(offer, basket))
def test_create_voucher_with_multi_use_per_customer_usage(self): """ Verify voucher is created with `MULTI_USE_PER_CUSTOMER` usage type. """ voucher_data = dict(self.data, usage=Voucher.MULTI_USE_PER_CUSTOMER) voucher = Voucher.objects.create(**voucher_data) user = UserFactory() self.assertEqual(voucher.usage, Voucher.MULTI_USE_PER_CUSTOMER) is_available, message = voucher.is_available_to_user(user) self.assertTrue(is_available) self.assertEqual(message, '')
def setUp(self): super(EnrollmentCodeFulfillmentModuleTests, self).setUp() 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 = factories.BasketFactory(owner=user, site=self.site) basket.add_product(enrollment_code, self.QUANTITY) self.order = create_order(number=1, basket=basket, user=user)
def setUp(self): super(BenefitTests, self).setUp() _range = factories.RangeFactory(course_seat_types=','.join( Range.ALLOWED_SEAT_TYPES[1:]), catalog_query='uuid:*') self.benefit = factories.BenefitFactory(range=_range) self.offer = factories.ConditionalOfferFactory(benefit=self.benefit) self.user = UserFactory()
def test_is_satisfied_with_exception_for_programs(self, value): """ The method should return False if there is an exception when trying to get program details. """ offer = factories.ProgramOfferFactory(partner=self.partner, condition=self.condition) basket = BasketFactory(site=self.site, owner=UserFactory()) basket.add_product(self.test_product) with mock.patch('ecommerce.programs.conditions.get_program', side_effect=value): self.assertFalse(self.condition.is_satisfied(offer, basket))
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_get_transaction_parameters_with_quoted_product_title(self): """ Verify quotes are removed from item name """ course = CourseFactory(id='a/b/c/d', name='Course with "quotes"') product = course.create_or_update_seat(self.CERTIFICATE_TYPE, False, 20) basket = create_basket(owner=UserFactory(), site=self.site, empty=True) basket.add_product(product) response = self.processor.get_transaction_parameters(basket) self.assertEqual(response['item_0_name'], 'Seat in Course with quotes with test-certificate-type certificate')
def request_specific_voucher_report(self, coupon): client = UserFactory() basket = Basket.get_basket(client, self.site) basket.add_product(coupon) request = RequestFactory() response = CouponReportCSVView().get(request, coupon_id=coupon.id) self.assertEqual(response.status_code, 200) self.assertEqual(len(response.content.splitlines()), 7)