class TestOfferApplicator(TestCase): def setUp(self): self.applicator = Applicator() self.basket = Basket.objects.create() self.product = create_product(price=D('100')) rng = G(models.Range, includes_all_products=True) self.condition = G(models.Condition, range=rng, type="Value", value=D('100'), proxy_class=None) self.benefit = G(models.Benefit, range=rng, type="Absolute", value=D('10')) def test_applies_offer_multiple_times_by_default(self): self.basket.add_product(self.product, 5) offer = models.ConditionalOffer(id="test", condition=self.condition, benefit=self.benefit) self.applicator.apply_offers(self.basket, [offer]) self.assertEqual( 5, self.basket.offer_applications.applications["test"]['freq']) def test_respects_maximum_applications_field(self): self.basket.add_product(self.product, 5) offer = models.ConditionalOffer(id="test", condition=self.condition, benefit=self.benefit, max_basket_applications=1) self.applicator.apply_offers(self.basket, [offer]) self.assertEqual( 1, self.basket.offer_applications.applications["test"]['freq'])
class TestOfferApplicator(TestCase): 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_applies_offer_multiple_times_by_default(self): add_product(self.basket, D('100'), 5) offer = ConditionalOfferFactory( pk=1, condition=self.condition, benefit=self.benefit) self.applicator.apply self.applicator.apply_offers(self.basket, [offer]) line = self.basket.all_lines()[0] self.assertTrue(line.quantity_with_offer_discount(offer) == 5) def test_respects_maximum_applications_field(self): add_product(self.basket, D('100'), 5) offer = ConditionalOfferFactory( pk=1, condition=self.condition, benefit=self.benefit, max_basket_applications=1) self.applicator.apply_offers(self.basket, [offer]) line = self.basket.all_lines()[0] self.assertTrue(line.quantity_with_offer_discount(offer) == 5) applications = self.basket.offer_applications.applications self.assertTrue(applications[1]['freq'] == 1) def test_uses_offers_in_order_of_descending_priority(self): self.applicator.get_site_offers = Mock( return_value=[models.ConditionalOffer( name="offer1", condition=self.condition, benefit=self.benefit, priority=1)]) self.applicator.get_user_offers = Mock( return_value=[models.ConditionalOffer( name="offer2", condition=self.condition, benefit=self.benefit, priority=-1)]) offers = self.applicator.get_offers(self.basket) priorities = [offer.priority for offer in offers] self.assertEqual(sorted(priorities, reverse=True), priorities) def test_get_site_offers(self): models.ConditionalOffer.objects.create( name="globaloffer", condition=self.condition, benefit=self.benefit, offer_type=models.ConditionalOffer.SITE) models.ConditionalOffer.objects.create( name="sessionoffer", condition=self.condition, benefit=self.benefit, offer_type=models.ConditionalOffer.SESSION) site_offers = Applicator().get_site_offers() # Only one offer should be returned self.assertEqual(len(site_offers), 1) self.assertEqual(site_offers[0].name, "globaloffer")
class TestOfferApplicator(TestCase): 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_applies_offer_multiple_times_by_default(self): add_product(self.basket, D('100'), 5) offer = ConditionalOfferFactory( pk=1, condition=self.condition, benefit=self.benefit) self.applicator.apply self.applicator.apply_offers(self.basket, [offer]) line = self.basket.all_lines()[0] self.assertTrue(line.quantity_with_offer_discount(offer) == 5) def test_respects_maximum_applications_field(self): add_product(self.basket, D('100'), 5) offer = ConditionalOfferFactory( pk=1, condition=self.condition, benefit=self.benefit, max_basket_applications=1) self.applicator.apply_offers(self.basket, [offer]) line = self.basket.all_lines()[0] self.assertTrue(line.quantity_with_offer_discount(offer) == 5) applications = self.basket.offer_applications.applications self.assertTrue(applications[1]['freq'] == 1) def test_uses_offers_in_order_of_descending_priority(self): self.applicator.get_site_offers = Mock( return_value=[models.ConditionalOffer( name="offer1", condition=self.condition, benefit=self.benefit, priority=1)]) self.applicator.get_user_offers = Mock( return_value=[models.ConditionalOffer( name="offer2", condition=self.condition, benefit=self.benefit, priority=-1)]) offers = self.applicator.get_offers(self.basket) priorities = [offer.priority for offer in offers] self.assertEqual(sorted(priorities, reverse=True), priorities) def test_get_site_offers(self): models.ConditionalOffer.objects.create( name="globaloffer", condition=self.condition, benefit=self.benefit, offer_type=models.ConditionalOffer.SITE) models.ConditionalOffer.objects.create( name="sessionoffer", condition=self.condition, benefit=self.benefit, offer_type=models.ConditionalOffer.SESSION) site_offers = Applicator().get_site_offers() # Only one offer should be returned self.assertEqual(len(site_offers), 1) self.assertEqual(site_offers[0].name, "globaloffer")
class TestOfferApplicator(TestCase): def setUp(self): self.applicator = Applicator() self.basket = Basket.objects.create() self.product = create_product(price=D('100')) rng = G(models.Range, includes_all_products=True) self.condition = G(models.Condition, range=rng, type="Value", value=D('100'), proxy_class=None) self.benefit = G(models.Benefit, range=rng, type="Absolute", value=D('10')) def test_applies_offer_multiple_times_by_default(self): self.basket.add_product(self.product, 5) offer = models.ConditionalOffer( id="test", condition=self.condition, benefit=self.benefit) self.applicator.apply_offers(self.basket, [offer]) self.assertEqual(5, self.basket.offer_applications.applications["test"]['freq']) def test_respects_maximum_applications_field(self): self.basket.add_product(self.product, 5) offer = models.ConditionalOffer( id="test", condition=self.condition, benefit=self.benefit, max_basket_applications=1) self.applicator.apply_offers(self.basket, [offer]) self.assertEqual(1, self.basket.offer_applications.applications["test"]['freq'])
class TestOfferApplicator(TestCase): 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'), max_affected_items=1) def test_applies_offer_multiple_times_by_default(self): add_product(self.basket, D('100'), 5) offer = ConditionalOfferFactory(pk=1, condition=self.condition, benefit=self.benefit) self.applicator.apply_offers(self.basket, [offer]) applications = self.basket.offer_applications.applications self.assertEqual(5, applications[1]['freq']) def test_respects_maximum_applications_field(self): add_product(self.basket, D('100'), 5) offer = ConditionalOfferFactory(pk=1, condition=self.condition, benefit=self.benefit, max_basket_applications=1) self.applicator.apply_offers(self.basket, [offer]) applications = self.basket.offer_applications.applications self.assertEqual(1, applications[1]['freq']) def test_uses_offers_in_order_of_descending_priority(self): self.applicator.get_site_offers = Mock(return_value=[ models.ConditionalOffer(name="offer1", condition=self.condition, benefit=self.benefit, priority=1) ]) self.applicator.get_user_offers = Mock(return_value=[ models.ConditionalOffer(name="offer2", condition=self.condition, benefit=self.benefit, priority=-1) ]) offers = self.applicator.get_offers(self.basket) priorities = [offer.priority for offer in offers] self.assertEqual(sorted(priorities, reverse=True), priorities)
class TestOfferApplicator(TestCase): def setUp(self): self.applicator = Applicator() self.basket = factories.create_basket(empty=True) rng = G(models.Range, includes_all_products=True) self.condition = G(models.Condition, range=rng, type="Value", value=D('100'), proxy_class=None) self.benefit = G(models.Benefit, range=rng, type="Absolute", value=D('10')) def test_applies_offer_multiple_times_by_default(self): add_product(self.basket, D('100'), 5) offer = models.ConditionalOffer(id="test", condition=self.condition, benefit=self.benefit) self.applicator.apply_offers(self.basket, [offer]) applications = self.basket.offer_applications.applications self.assertEqual(5, applications["test"]['freq']) def test_respects_maximum_applications_field(self): add_product(self.basket, D('100'), 5) offer = models.ConditionalOffer(id="test", condition=self.condition, benefit=self.benefit, max_basket_applications=1) self.applicator.apply_offers(self.basket, [offer]) applications = self.basket.offer_applications.applications self.assertEqual(1, applications["test"]['freq']) def test_uses_offers_in_order_of_descending_priority(self): self.applicator.get_site_offers = Mock(return_value=[ models.ConditionalOffer(name="offer1", condition=self.condition, benefit=self.benefit, priority=1) ]) self.applicator.get_user_offers = Mock(return_value=[ models.ConditionalOffer(name="offer2", condition=self.condition, benefit=self.benefit, priority=-1) ]) offers = self.applicator.get_offers(self.basket) priorities = [offer.priority for offer in offers] self.assertEqual(sorted(priorities, reverse=True), priorities)
class TestAnAbsoluteDiscountAppliedWithCountConditionOnDifferentRange(TestCase): def setUp(self): # Flush the cache conn = get_redis_connection('redis') conn.flushall() self.condition_product = factories.ProductFactory() condition_range = factories.RangeFactory() condition_range.add_product(self.condition_product) self.condition = BluelightCountCondition.objects.create( range=condition_range, type=Condition.COUNT, value=2) self.benefit_product = factories.ProductFactory() benefit_range = factories.RangeFactory() benefit_range.add_product(self.benefit_product) self.benefit = BluelightAbsoluteDiscountBenefit.objects.create( range=benefit_range, type=Benefit.FIXED, value=D('3.00')) self.offer = ConditionalOffer( id=1, condition=self.condition, benefit=self.benefit) self.basket = factories.create_basket(empty=True) self.applicator = Applicator() def test_succcessful_application_consumes_correctly(self): add_product(self.basket, product=self.condition_product, quantity=2) add_product(self.basket, product=self.benefit_product, quantity=1) self.applicator.apply_offers(self.basket, [self.offer]) discounts = self.basket.offer_applications.offer_discounts self.assertEqual(len(discounts), 1) self.assertEqual(discounts[0]['freq'], 1) def test_condition_is_consumed_correctly(self): # Testing an error case reported on the mailing list add_product(self.basket, product=self.condition_product, quantity=3) add_product(self.basket, product=self.benefit_product, quantity=2) self.applicator.apply_offers(self.basket, [self.offer]) discounts = self.basket.offer_applications.offer_discounts self.assertEqual(len(discounts), 1) self.assertEqual(discounts[0]['freq'], 1)
class TestAnAbsoluteDiscountAppliedWithCountConditionOnDifferentRange(TestCase): def setUp(self): self.condition_product = factories.ProductFactory() condition_range = factories.RangeFactory() condition_range.add_product(self.condition_product) self.condition = BluelightCountCondition.objects.create( range=condition_range, type=Condition.COUNT, value=2) self.benefit_product = factories.ProductFactory() benefit_range = factories.RangeFactory() benefit_range.add_product(self.benefit_product) self.benefit = BluelightAbsoluteDiscountBenefit.objects.create( range=benefit_range, type=Benefit.FIXED, value=D('3.00')) self.offer = ConditionalOffer( id=1, condition=self.condition, benefit=self.benefit) self.basket = factories.create_basket(empty=True) self.applicator = Applicator() def test_succcessful_application_consumes_correctly(self): add_product(self.basket, product=self.condition_product, quantity=2) add_product(self.basket, product=self.benefit_product, quantity=1) self.applicator.apply_offers(self.basket, [self.offer]) discounts = self.basket.offer_applications.offer_discounts self.assertEqual(len(discounts), 1) self.assertEqual(discounts[0]['freq'], 1) def test_condition_is_consumed_correctly(self): # Testing an error case reported on the mailing list add_product(self.basket, product=self.condition_product, quantity=3) add_product(self.basket, product=self.benefit_product, quantity=2) self.applicator.apply_offers(self.basket, [self.offer]) discounts = self.basket.offer_applications.offer_discounts self.assertEqual(len(discounts), 1) self.assertEqual(discounts[0]['freq'], 1)
class TestOfferApplicator(TestCase): 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'), max_affected_items=1) def test_applies_offer_multiple_times_by_default(self): add_product(self.basket, D('100'), 5) offer = ConditionalOfferFactory( pk=1, condition=self.condition, benefit=self.benefit) self.applicator.apply_offers(self.basket, [offer]) applications = self.basket.offer_applications.applications self.assertEqual(5, applications[1]['freq']) def test_respects_maximum_applications_field(self): add_product(self.basket, D('100'), 5) offer = ConditionalOfferFactory( pk=1, condition=self.condition, benefit=self.benefit, max_basket_applications=1) self.applicator.apply_offers(self.basket, [offer]) applications = self.basket.offer_applications.applications self.assertEqual(1, applications[1]['freq']) def test_uses_offers_in_order_of_descending_priority(self): self.applicator.get_site_offers = Mock( return_value=[models.ConditionalOffer( name="offer1", condition=self.condition, benefit=self.benefit, priority=1)]) self.applicator.get_user_offers = Mock( return_value=[models.ConditionalOffer( name="offer2", condition=self.condition, benefit=self.benefit, priority=-1)]) offers = self.applicator.get_offers(self.basket) priorities = [offer.priority for offer in offers] self.assertEqual(sorted(priorities, reverse=True), priorities)
def test_apply_offer_with_multibuy_benefit_and_count_condition(self): rng = RangeFactory(includes_all_products=True) condition = ConditionFactory(range=rng, type=ConditionFactory._meta.model.COUNT, value=1) benefit = BenefitFactory(range=rng, type=BenefitFactory._meta.model.MULTIBUY, value=1) offer = ConditionalOfferFactory(condition=condition, benefit=benefit) add_product(self.basket, D('100'), 5) applicator = Applicator() applicator.apply_offers(self.basket, [offer]) line = self.basket.all_lines()[0] assert line.quantity_with_offer_discount(offer) == 1 self.basket.refresh_from_db() assert self.basket.total_discount == D('100')
class TestOfferApplicator(TestCase): def setUp(self): self.applicator = Applicator() self.basket = factories.create_basket(empty=True) rng = G(models.Range, includes_all_products=True) self.condition = G(models.Condition, range=rng, type="Value", value=D('100'), proxy_class=None) self.benefit = G(models.Benefit, range=rng, type="Absolute", value=D('10')) def test_applies_offer_multiple_times_by_default(self): add_product(self.basket, D('100'), 5) offer = models.ConditionalOffer( id="test", condition=self.condition, benefit=self.benefit) self.applicator.apply_offers(self.basket, [offer]) applications = self.basket.offer_applications.applications self.assertEqual(5, applications["test"]['freq']) def test_respects_maximum_applications_field(self): add_product(self.basket, D('100'), 5) offer = models.ConditionalOffer( id="test", condition=self.condition, benefit=self.benefit, max_basket_applications=1) self.applicator.apply_offers(self.basket, [offer]) applications = self.basket.offer_applications.applications self.assertEqual(1, applications["test"]['freq']) def test_uses_offers_in_order_of_descending_priority(self): self.applicator.get_site_offers = Mock( return_value=[models.ConditionalOffer( name="offer1", condition=self.condition, benefit=self.benefit, priority=1)]) self.applicator.get_user_offers = Mock( return_value=[models.ConditionalOffer( name="offer2", condition=self.condition, benefit=self.benefit, priority=-1)]) offers = self.applicator.get_offers(Mock(), self.basket) priorities = [offer.priority for offer in offers] self.assertEqual(sorted(priorities, reverse=True), priorities)
class TestBasketLineForm(TestCase): 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 mock_availability_return_value(self, is_available, reason=''): policy = self.line.purchase_info.availability policy.is_purchase_permitted = mock.MagicMock( return_value=(is_available, reason)) def build_form(self, quantity=None): if quantity is None: quantity = self.line.quantity return forms.BasketLineForm(strategy=self.basket.strategy, data={'quantity': quantity}, instance=self.line) def test_enforces_availability_policy_for_valid_quantities(self): self.mock_availability_return_value(True) form = self.build_form() self.assertTrue(form.is_valid()) def test_enforces_availability_policy_for_invalid_quantities(self): self.mock_availability_return_value(False, "Some reason") form = self.build_form() self.assertFalse(form.is_valid()) self.assertEqual(form.errors['quantity'], ['Some reason']) def test_skips_availability_policy_for_zero_quantities(self): self.mock_availability_return_value(True) form = self.build_form(quantity=0) self.assertTrue(form.is_valid()) def test_enforces_max_line_quantity_for_new_product(self): invalid_qty = settings.OSCAR_MAX_BASKET_QUANTITY_THRESHOLD + 1 form = self.build_form(quantity=invalid_qty) self.assertFalse(form.is_valid()) @override_settings(OSCAR_MAX_BASKET_QUANTITY_THRESHOLD=10) def test_enforce_max_line_quantity_for_existing_product(self): self.basket.flush() product = factories.create_product(num_in_stock=20) add_product(self.basket, D('100'), 4, product) self.line = self.basket.all_lines()[0] form = self.build_form(quantity=6) self.assertTrue(form.is_valid()) form.save() form = self.build_form(quantity=11) self.assertFalse(form.is_valid()) def test_line_quantity_max_attribute_per_num_available(self): self.basket.flush() product = factories.create_product(num_in_stock=20) add_product(self.basket, D('100'), 4, product) self.line = self.basket.all_lines()[0] form = self.build_form() self.assertIn('max="20"', str(form['quantity'])) @override_settings(OSCAR_MAX_BASKET_QUANTITY_THRESHOLD=10) def test_line_quantity_max_attribute_per_basket_threshold(self): self.basket.flush() product = factories.create_product(num_in_stock=20) add_product(self.basket, D('100'), 4, product) self.line = self.basket.all_lines()[0] form = self.build_form() self.assertIn('max="6"', str(form['quantity'])) def test_basketline_formset_ordering(self): # when we use a unordered queryset in the Basketlineformset, the # discounts will be lost because django will query the database # again to enforce ordered results add_product(self.basket, D('100'), 5) offer = ConditionalOfferFactory(pk=1, condition=self.condition, benefit=self.benefit) # now we force an unordered queryset so we can see that our discounts # will disappear due to a new ordering query (see django/forms/model.py) default_line_ordering = Line._meta.ordering Line._meta.ordering = [] self.basket._lines = self.basket.lines.all() self.applicator.apply_offers(self.basket, [offer]) formset = formsets.BasketLineFormSet(strategy=self.basket.strategy, queryset=self.basket.all_lines()) # the discount is in all_lines(): self.assertTrue(self.basket.all_lines()[0].has_discount) # but not in the formset self.assertFalse(formset.forms[0].instance.has_discount) # Restore the ordering on the line Line._meta.ordering = default_line_ordering # clear the cached lines and apply the offer again self.basket._lines = None self.applicator.apply_offers(self.basket, [offer]) formset = formsets.BasketLineFormSet(strategy=self.basket.strategy, queryset=self.basket.all_lines()) self.assertTrue(formset.forms[0].instance.has_discount)
class TestBasketLineForm(TestCase): 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 mock_availability_return_value(self, is_available, reason=''): policy = self.line.purchase_info.availability policy.is_purchase_permitted = mock.MagicMock( return_value=(is_available, reason)) def build_form(self, quantity=None): if quantity is None: quantity = self.line.quantity return forms.BasketLineForm( strategy=self.basket.strategy, data={'quantity': quantity}, instance=self.line) def test_enforces_availability_policy_for_valid_quantities(self): self.mock_availability_return_value(True) form = self.build_form() self.assertTrue(form.is_valid()) def test_enforces_availability_policy_for_invalid_quantities(self): self.mock_availability_return_value(False, "Some reason") form = self.build_form() self.assertFalse(form.is_valid()) self.assertEqual( form.errors['quantity'], ['Some reason']) def test_skips_availability_policy_for_zero_quantities(self): self.mock_availability_return_value(True) form = self.build_form(quantity=0) self.assertTrue(form.is_valid()) def test_enforces_max_line_quantity_for_new_product(self): invalid_qty = settings.OSCAR_MAX_BASKET_QUANTITY_THRESHOLD + 1 form = self.build_form(quantity=invalid_qty) self.assertFalse(form.is_valid()) @override_settings(OSCAR_MAX_BASKET_QUANTITY_THRESHOLD=10) def test_enforce_max_line_quantity_for_existing_product(self): self.basket.flush() product = factories.create_product(num_in_stock=20) add_product(self.basket, D('100'), 4, product) self.line = self.basket.all_lines()[0] form = self.build_form(quantity=6) self.assertTrue(form.is_valid()) form.save() form = self.build_form(quantity=11) self.assertFalse(form.is_valid()) def test_line_quantity_max_attribute_per_num_available(self): self.basket.flush() product = factories.create_product(num_in_stock=20) add_product(self.basket, D('100'), 4, product) self.line = self.basket.all_lines()[0] form = self.build_form() self.assertIn('max="20"', str(form['quantity'])) @override_settings(OSCAR_MAX_BASKET_QUANTITY_THRESHOLD=10) def test_line_quantity_max_attribute_per_basket_threshold(self): self.basket.flush() product = factories.create_product(num_in_stock=20) add_product(self.basket, D('100'), 4, product) self.line = self.basket.all_lines()[0] form = self.build_form() self.assertIn('max="6"', str(form['quantity'])) def test_basketline_formset_ordering(self): # when we use a unordered queryset in the Basketlineformset, the # discounts will be lost because django will query the database # again to enforce ordered results add_product(self.basket, D('100'), 5) offer = ConditionalOfferFactory( pk=1, condition=self.condition, benefit=self.benefit) # now we force an unordered queryset so we can see that our discounts # will disappear due to a new ordering query (see django/forms/model.py) default_line_ordering = Line._meta.ordering Line._meta.ordering = [] self.basket._lines = self.basket.lines.all() self.applicator.apply_offers(self.basket, [offer]) formset = formsets.BasketLineFormSet( strategy=self.basket.strategy, queryset=self.basket.all_lines()) # the discount is in all_lines(): self.assertTrue(self.basket.all_lines()[0].has_discount) # but not in the formset self.assertFalse(formset.forms[0].instance.has_discount) # Restore the ordering on the line Line._meta.ordering = default_line_ordering # clear the cached lines and apply the offer again self.basket._lines = None self.applicator.apply_offers(self.basket, [offer]) formset = formsets.BasketLineFormSet( strategy=self.basket.strategy, queryset=self.basket.all_lines()) self.assertTrue(formset.forms[0].instance.has_discount)