Ejemplo n.º 1
0
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'])
Ejemplo n.º 2
0
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")
Ejemplo n.º 3
0
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")
Ejemplo n.º 4
0
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'])
Ejemplo n.º 5
0
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)
Ejemplo n.º 6
0
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)
Ejemplo n.º 7
0
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)
Ejemplo n.º 9
0
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)
Ejemplo n.º 10
0
    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')
Ejemplo n.º 11
0
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)
Ejemplo n.º 12
0
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)
Ejemplo n.º 13
0
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)