def test_allocate_does_nothing_for_child_product(self): parent_product = factories.ProductFactory( structure='parent', product_class=self.product_class) child_product = factories.ProductFactory( parent=parent_product, product_class=None, structure='child') stockrecord = factories.create_stockrecord( child_product, price_excl_tax=D('10.00'), num_in_stock=10) self.assertFalse(stockrecord.can_track_allocations) stockrecord.allocate(5) self.assertEqual(stockrecord.num_allocated, None)
def test_works_for_child_product(self): parent = factories.ProductFactory(structure='parent', stockrecords=[]) for x in range(3): factories.ProductFactory(parent=parent, structure='child') detail_page = self.get(parent.get_absolute_url()) form = detail_page.forms['add_to_basket_form'] response = form.submit() self.assertIsRedirect(response) baskets = models.Basket.objects.all() self.assertEqual(1, len(baskets)) basket = baskets[0] self.assertEqual(1, basket.num_items)
def setUp(self): super(TestProductClass, self).setUp() self.pclass = ProductClassFactory(name='T-Shirts', slug='tshirts') for attribute_type, __ in ProductAttribute.TYPE_CHOICES: values = { 'type': attribute_type, 'code': attribute_type, 'product_class': self.pclass, 'name': attribute_type, } if attribute_type == ProductAttribute.OPTION: option_group = factories.AttributeOptionGroupFactory() self.option = factories.AttributeOptionFactory( group=option_group) values['option_group'] = option_group elif attribute_type == ProductAttribute.MULTI_OPTION: option_group = factories.AttributeOptionGroupFactory() self.multi_option = factories.AttributeOptionFactory( group=option_group) values['option_group'] = option_group ProductAttributeFactory(**values) self.product = factories.ProductFactory(product_class=self.pclass) self.url = reverse('dashboard:catalogue-product', kwargs={'pk': self.product.id})
def test_single_usage(self): user = AnonymousUser() creator = OrderCreator() product = factories.ProductFactory(stockrecords__num_in_stock=1000) # Make the order creator a bit more slow too reliable trigger # concurrency issues org_create_order_model = OrderCreator.create_order_model def new_create_order_model(*args, **kwargs): time.sleep(0.5) return org_create_order_model(creator, *args, **kwargs) creator.create_order_model = new_create_order_model # Start 5 threads to place an order concurrently def worker(): order_number = threading.current_thread().name basket = factories.BasketFactory() basket.add_product(product) place_order( creator, basket=basket, order_number=order_number, user=user) exceptions = run_concurrently(worker, num_threads=5) assert all(isinstance(x, ValueError) for x in exceptions), exceptions assert len(exceptions) == 0 assert Order.objects.count() == 5 stockrecord = product.stockrecords.first() assert stockrecord.num_allocated == 5
def test_consume_stock_allocations_track_stock_on(self): product_class = factories.ProductClassFactory( requires_shipping=False, track_stock=True) product = factories.ProductFactory(product_class=product_class) basket = factories.create_basket(empty=True) add_product(basket, D('10.00'), 5, product=product) order = factories.create_order(basket=basket) stockrecord = product.stockrecords.get() num_in_stock = stockrecord.num_in_stock num_allocated = stockrecord.num_allocated lines = order.lines.all() self.handler.consume_stock_allocations( order, lines, [line.quantity for line in lines]) stockrecord.refresh_from_db() self.assertEqual( stockrecord.num_allocated, num_allocated - 5, "Allocated stock should have decreased, but didn't." ) self.assertEqual( stockrecord.num_in_stock, num_in_stock - 5, "Stock should have decreased, but didn't." )
def create_digital_product(self): product_class = factories.ProductClassFactory(requires_shipping=False, track_stock=False) product = factories.ProductFactory(product_class=product_class) factories.StockRecordFactory(num_in_stock=None, price_excl_tax=D('12.00'), product=product) return product
def test_allocate_does_nothing(self): product = factories.ProductFactory(product_class=self.product_class) stockrecord = factories.create_stockrecord( product, price_excl_tax=D('10.00'), num_in_stock=10) self.assertFalse(stockrecord.can_track_allocations) stockrecord.allocate(5) self.assertEqual(stockrecord.num_allocated, None)
def add_product_to_basket(self, product=None): if product is None: product = factories.ProductFactory() factories.StockRecordFactory(num_in_stock=10, price_excl_tax=D('12.00'), product=product) detail_page = self.get(product.get_absolute_url()) form = detail_page.forms['add_to_basket_form'] form.submit()
def test_redirects_customers_with_invalid_basket(self): # Add product to basket but then remove its stock so it is not # purchasable. product = factories.ProductFactory() self.add_product_to_basket(product) product.stockrecords.all().update(num_in_stock=0) response = self.get(reverse('checkout:index')) self.assertRedirectsTo(response, 'basket:summary')
def test_variant_images_fallback_to_parent(self): parent = factories.ProductFactory(structure='parent') variant = factories.create_product(parent=parent) factories.create_product_image(product=parent, caption='Parent Product Image') all_images = variant.get_all_images() self.assertEquals(all_images.count(), 1) product_image = all_images.first() self.assertEquals(product_image.caption, 'Parent Product Image')
def test_availability_does_not_require_price(self): # regression test for https://github.com/vituocgia/wshop-core/issues/2664 # The availability policy should be independent of price. product_class = factories.ProductClassFactory(track_stock=False) product = factories.ProductFactory(product_class=product_class, stockrecords=[]) factories.StockRecordFactory(price_excl_tax=None, product=product) info = self.strategy.fetch_for_product(product) self.assertTrue(info.availability.is_available_to_buy)
def setUp(self): self.condition_product = factories.ProductFactory() condition_range = factories.RangeFactory() condition_range.add_product(self.condition_product) self.condition = models.CountCondition.objects.create( range=condition_range, type=models.Condition.COUNT, value=2) self.benefit_product = factories.ProductFactory() benefit_range = factories.RangeFactory() benefit_range.add_product(self.benefit_product) self.benefit = models.AbsoluteDiscountBenefit.objects.create( range=benefit_range, type=models.Benefit.FIXED, value=D('3.00')) self.offer = models.ConditionalOffer(id=1, condition=self.condition, benefit=self.benefit) self.basket = factories.create_basket(empty=True) self.applicator = Applicator()
def test_works_for_standalone_product(self): product = factories.ProductFactory() detail_page = self.get(product.get_absolute_url()) response = detail_page.forms['add_to_basket_form'].submit() self.assertIsRedirect(response) baskets = models.Basket.objects.all() self.assertEqual(1, len(baskets)) basket = baskets[0] self.assertEqual(1, basket.num_items)
def test_product_update_form(self): self.product = factories.ProductFactory() url = reverse('dashboard:catalogue-product', kwargs={'pk': self.product.id}) page = self.get(url) product_form = page.form product_form['title'] = expected_title = 'Nice T-Shirt' page = product_form.submit() product = Product.objects.get(id=self.product.id) self.assertEqual(page.context['product'], self.product) self.assertEqual(product.title, expected_title)
def test_are_stock_allocations_available_track_stock_off(self): product_class = factories.ProductClassFactory( requires_shipping=False, track_stock=False) product = factories.ProductFactory(product_class=product_class) basket = factories.create_basket(empty=True) add_product(basket, D('10.00'), 5, product=product) order = factories.create_order(basket=basket) line = order.lines.get() self.assertEqual( self.handler.are_stock_allocations_available( [line], [105]), True, )
def test_mixed_currency_baskets_are_not_permitted(self): # Ensure basket is one currency basket = mock.Mock() basket.currency = 'GBP' basket.num_items = 1 # Ensure new product has different currency info = mock.Mock() info.price.currency = 'EUR' basket.strategy.fetch_for_product = mock.Mock(return_value=info) product = factories.ProductFactory() data = {'quantity': 1} form = forms.AddToBasketForm(basket=basket, product=product, data=data) self.assertFalse(form.is_valid())
def test_checks_if_purchase_is_permitted(self): basket = factories.BasketFactory() product = factories.ProductFactory() # Build a 4-level mock monster so we can force the return value of # whether the product is available to buy. This is a serious code smell # and needs to be remedied. info = mock.Mock() info.availability = mock.Mock() info.availability.is_purchase_permitted = mock.Mock( return_value=(False, "Not on your nelly!")) basket.strategy.fetch_for_product = mock.Mock(return_value=info) data = {'quantity': 1} form = forms.AddToBasketForm(basket=basket, product=product, data=data) self.assertFalse(form.is_valid()) self.assertEqual('Not on your nelly!', form.errors['__all__'][0])
def test_cancel_stock_allocations_without_line_arguments(self): product_class = factories.ProductClassFactory( requires_shipping=False, track_stock=True) product = factories.ProductFactory(product_class=product_class) basket = factories.create_basket(empty=True) add_product(basket, D('10.00'), 5, product=product) order = factories.create_order(basket=basket) stockrecord = product.stockrecords.get() num_allocated = stockrecord.num_allocated self.handler.cancel_stock_allocations(order) stockrecord.refresh_from_db() self.assertEqual( stockrecord.num_allocated, num_allocated - 5, "Allocated stock should have decreased, but didn't." )
def test_voucher_single_usage(self): user = AnonymousUser() creator = OrderCreator() product = factories.ProductFactory() voucher = factories.VoucherFactory(usage=Voucher.SINGLE_USE) voucher.offers.add(factories.create_offer(offer_type='Voucher')) # Make the order creator a bit more slow too reliable trigger # concurrency issues org_create_order_model = OrderCreator.create_order_model def new_create_order_model(*args, **kwargs): time.sleep(0.5) return org_create_order_model(creator, *args, **kwargs) creator.create_order_model = new_create_order_model org_record_voucher_usage = OrderCreator.record_voucher_usage def record_voucher_usage(*args, **kwargs): time.sleep(0.5) return org_record_voucher_usage(creator, *args, **kwargs) creator.record_voucher_usage = record_voucher_usage # Start 5 threads to place an order concurrently def worker(): order_number = threading.current_thread().name basket = factories.BasketFactory() basket.add_product(product) basket.vouchers.add(voucher) place_order( creator, basket=basket, order_number=order_number, user=user) exceptions = run_concurrently(worker, num_threads=5) voucher.refresh_from_db() assert all(isinstance(x, ValueError) for x in exceptions), exceptions assert len(exceptions) == 4 assert voucher.applications.count() == 1 assert Order.objects.count() == 1
def test_validates_that_child_products_dont_need_a_title(self): parent = factories.ProductFactory( product_class=self.product_class, structure='parent') form = self.submit({'structure': 'child'}, parent=parent) self.assertTrue(form.is_valid())
def test_pricing_policy_unavailable_if_no_price_excl_tax(self): product = factories.ProductFactory(stockrecords=[]) factories.StockRecordFactory(price_excl_tax=None, product=product) info = strategy.US().fetch_for_product(product) self.assertFalse(info.price.exists)