Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

test_condition.py 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. from decimal import Decimal as D
  2. import mock
  3. import six
  4. from django.test import TestCase
  5. from oscar.apps.basket.models import Basket
  6. from oscar.apps.offer import custom, models
  7. from oscar.test import factories
  8. from oscar.test.basket import add_product
  9. class TestCountCondition(TestCase):
  10. def setUp(self):
  11. self.range = models.Range.objects.create(
  12. name="All products range", includes_all_products=True)
  13. self.basket = factories.create_basket(empty=True)
  14. self.condition = models.CountCondition(
  15. range=self.range, type="Count", value=2)
  16. self.offer = mock.Mock()
  17. def test_is_not_satified_by_empty_basket(self):
  18. self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))
  19. def test_not_discountable_product_fails_condition(self):
  20. prod1, prod2 = factories.create_product(), factories.create_product()
  21. prod2.is_discountable = False
  22. prod2.save()
  23. add_product(self.basket, product=prod1)
  24. add_product(self.basket, product=prod2)
  25. self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))
  26. def test_empty_basket_fails_partial_condition(self):
  27. self.assertFalse(self.condition.is_partially_satisfied(self.offer, self.basket))
  28. def test_smaller_quantity_basket_passes_partial_condition(self):
  29. add_product(self.basket)
  30. self.assertTrue(self.condition.is_partially_satisfied(self.offer, self.basket))
  31. def test_smaller_quantity_basket_upsell_message(self):
  32. add_product(self.basket)
  33. self.assertTrue('Buy 1 more product from ' in
  34. self.condition.get_upsell_message(self.offer, self.basket))
  35. def test_matching_quantity_basket_fails_partial_condition(self):
  36. add_product(self.basket, quantity=2)
  37. self.assertFalse(self.condition.is_partially_satisfied(self.offer, self.basket))
  38. def test_matching_quantity_basket_passes_condition(self):
  39. add_product(self.basket, quantity=2)
  40. self.assertTrue(self.condition.is_satisfied(self.offer, self.basket))
  41. def test_greater_quantity_basket_passes_condition(self):
  42. add_product(self.basket, quantity=3)
  43. self.assertTrue(self.condition.is_satisfied(self.offer, self.basket))
  44. def test_consumption(self):
  45. add_product(self.basket, quantity=3)
  46. self.condition.consume_items(self.offer, self.basket, [])
  47. self.assertEqual(1, self.basket.all_lines()[0].quantity_without_discount)
  48. def test_is_satisfied_accounts_for_consumed_items(self):
  49. add_product(self.basket, quantity=3)
  50. self.condition.consume_items(self.offer, self.basket, [])
  51. self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))
  52. class TestValueCondition(TestCase):
  53. def setUp(self):
  54. self.range = models.Range.objects.create(
  55. name="All products range", includes_all_products=True)
  56. self.basket = factories.create_basket(empty=True)
  57. self.condition = models.ValueCondition(
  58. range=self.range, type="Value", value=D('10.00'))
  59. self.offer = mock.Mock()
  60. self.item = factories.create_product(price=D('5.00'))
  61. self.expensive_item = factories.create_product(price=D('15.00'))
  62. def test_empty_basket_fails_condition(self):
  63. self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))
  64. def test_empty_basket_fails_partial_condition(self):
  65. self.assertFalse(self.condition.is_partially_satisfied(self.offer, self.basket))
  66. def test_less_value_basket_fails_condition(self):
  67. add_product(self.basket, D('5'))
  68. self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))
  69. def test_not_discountable_item_fails_condition(self):
  70. product = factories.create_product(is_discountable=False)
  71. add_product(self.basket, D('15'), product=product)
  72. self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))
  73. def test_upsell_message(self):
  74. add_product(self.basket, D('5'))
  75. self.assertTrue('Spend' in self.condition.get_upsell_message(self.offer, self.basket))
  76. def test_matching_basket_fails_partial_condition(self):
  77. add_product(self.basket, D('5'), 2)
  78. self.assertFalse(self.condition.is_partially_satisfied(self.offer, self.basket))
  79. def test_less_value_basket_passes_partial_condition(self):
  80. add_product(self.basket, D('5'), 1)
  81. self.assertTrue(self.condition.is_partially_satisfied(self.offer, self.basket))
  82. def test_matching_basket_passes_condition(self):
  83. add_product(self.basket, D('5'), 2)
  84. self.assertTrue(self.condition.is_satisfied(self.offer, self.basket))
  85. def test_greater_than_basket_passes_condition(self):
  86. add_product(self.basket, D('5'), 3)
  87. self.assertTrue(self.condition.is_satisfied(self.offer, self.basket))
  88. def test_consumption(self):
  89. add_product(self.basket, D('5'), 3)
  90. self.condition.consume_items(self.offer, self.basket, [])
  91. self.assertEqual(1, self.basket.all_lines()[0].quantity_without_discount)
  92. def test_consumption_with_high_value_product(self):
  93. add_product(self.basket, D('15'), 1)
  94. self.condition.consume_items(self.offer, self.basket, [])
  95. self.assertEqual(0, self.basket.all_lines()[0].quantity_without_discount)
  96. def test_is_consumed_respects_quantity_consumed(self):
  97. add_product(self.basket, D('15'), 1)
  98. self.assertTrue(self.condition.is_satisfied(self.offer, self.basket))
  99. self.condition.consume_items(self.offer, self.basket, [])
  100. self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))
  101. class TestCoverageCondition(TestCase):
  102. def setUp(self):
  103. self.products = [factories.create_product(), factories.create_product()]
  104. self.range = models.Range.objects.create(name="Some products")
  105. for product in self.products:
  106. self.range.add_product(product)
  107. self.range.add_product(product)
  108. self.basket = factories.create_basket(empty=True)
  109. self.condition = models.CoverageCondition(
  110. range=self.range, type="Coverage", value=2)
  111. self.offer = mock.Mock()
  112. def test_empty_basket_fails(self):
  113. self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))
  114. def test_empty_basket_fails_partial_condition(self):
  115. self.assertFalse(self.condition.is_partially_satisfied(self.offer, self.basket))
  116. def test_single_item_fails(self):
  117. add_product(self.basket, product=self.products[0])
  118. self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))
  119. def test_not_discountable_item_fails(self):
  120. self.products[0].is_discountable = False
  121. self.products[0].save()
  122. add_product(self.basket, product=self.products[0])
  123. add_product(self.basket, product=self.products[1])
  124. self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))
  125. def test_single_item_passes_partial_condition(self):
  126. add_product(self.basket, product=self.products[0])
  127. self.assertTrue(self.condition.is_partially_satisfied(self.offer, self.basket))
  128. def test_upsell_message(self):
  129. add_product(self.basket, product=self.products[0])
  130. self.assertTrue(
  131. 'Buy 1 more' in self.condition.get_upsell_message(self.offer, self.basket))
  132. def test_duplicate_item_fails(self):
  133. add_product(self.basket, quantity=2, product=self.products[0])
  134. self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))
  135. def test_duplicate_item_passes_partial_condition(self):
  136. add_product(self.basket, quantity=2, product=self.products[0])
  137. self.assertTrue(self.condition.is_partially_satisfied(self.offer, self.basket))
  138. def test_covering_items_pass(self):
  139. add_product(self.basket, product=self.products[0])
  140. add_product(self.basket, product=self.products[1])
  141. self.assertTrue(self.condition.is_satisfied(self.offer, self.basket))
  142. def test_covering_items_fail_partial_condition(self):
  143. add_product(self.basket, product=self.products[0])
  144. add_product(self.basket, product=self.products[1])
  145. self.assertFalse(self.condition.is_partially_satisfied(self.offer, self.basket))
  146. def test_covering_items_are_consumed(self):
  147. add_product(self.basket, product=self.products[0])
  148. add_product(self.basket, product=self.products[1])
  149. self.condition.consume_items(self.offer, self.basket, [])
  150. self.assertEqual(0, self.basket.num_items_without_discount)
  151. def test_consumed_items_checks_affected_items(self):
  152. # Create new offer
  153. range = models.Range.objects.create(name="All products", includes_all_products=True)
  154. cond = models.CoverageCondition(range=range, type="Coverage", value=2)
  155. # Get 4 distinct products in the basket
  156. self.products.extend(
  157. [factories.create_product(), factories.create_product()])
  158. for product in self.products:
  159. add_product(self.basket, product=product)
  160. self.assertTrue(cond.is_satisfied(self.offer, self.basket))
  161. cond.consume_items(self.offer, self.basket, [])
  162. self.assertEqual(2, self.basket.num_items_without_discount)
  163. self.assertTrue(cond.is_satisfied(self.offer, self.basket))
  164. cond.consume_items(self.offer, self.basket, [])
  165. self.assertEqual(0, self.basket.num_items_without_discount)
  166. class TestConditionProxyModels(TestCase):
  167. def test_name_and_description(self):
  168. """
  169. Tests that the condition proxy classes all return a name and
  170. description. Unfortunately, the current implementations means
  171. a valid range and value are required.
  172. This test became necessary because the complex name/description logic
  173. broke with the python_2_unicode_compatible decorator.
  174. """
  175. range = factories.RangeFactory()
  176. for type, __ in models.Condition.TYPE_CHOICES:
  177. condition = models.Condition(type=type, range=range, value=5)
  178. self.assertTrue(all([
  179. condition.name,
  180. condition.description,
  181. six.text_type(condition)]))
  182. class BasketOwnerCalledBarry(models.Condition):
  183. class Meta:
  184. proxy = True
  185. app_label = 'tests'
  186. def is_satisfied(self, offer, basket):
  187. if not basket.owner:
  188. return False
  189. return basket.owner.first_name.lower() == 'barry'
  190. def can_apply_condition(self, product):
  191. return False
  192. class TestCustomCondition(TestCase):
  193. def setUp(self):
  194. self.condition = custom.create_condition(BasketOwnerCalledBarry)
  195. self.offer = models.ConditionalOffer(condition=self.condition)
  196. self.basket = Basket()
  197. def test_is_not_satified_by_non_match(self):
  198. self.basket.owner = factories.UserFactory(first_name="Alan")
  199. self.assertFalse(self.offer.is_condition_satisfied(self.basket))
  200. def test_is_satified_by_match(self):
  201. self.basket.owner = factories.UserFactory(first_name="Barry")
  202. self.assertTrue(self.offer.is_condition_satisfied(self.basket))