from django.contrib.auth import get_user_model from django.core import mail from django.db import IntegrityError from django.test import TestCase, override_settings from django.urls import reverse from items.models import Category, Item, ItemImage, Subcategory from orders.models import Cart, CartItem, Order, OrderItem User = get_user_model() @override_settings( MEDIA_URL="/media/", EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend", ADMINISTRATOR_EMAIL="admin@example.com", ) class CartFlowTests(TestCase): def setUp(self): self.user = User.objects.create_user( username="cart-user", email="cart@example.com", password="StrongPassword123", is_active=True, ) self.category = Category.objects.create(name="Сантехника", slug="plumbing") self.subcategory = Subcategory.objects.create(name="Мойки", slug="sinks", category=self.category) self.item = Item.objects.create( name="Кухонная мойка", slug="kitchen-sink", category=self.category, subcategory=self.subcategory, description="Искусственный камень", price="1600.00", is_available=True, ) self.unavailable_item = Item.objects.create( name="Смеситель", slug="tap", category=self.category, subcategory=self.subcategory, description="Недоступный товар", price="400.00", is_available=False, ) def checkout_data(self, **overrides): data = { "delivery_method": "courier", "region": "Минск", "address": "ул. Тимирязева, 123", "payment_method": "card", "customer_name": "Иван Петров", "phone": "+375296881819", "email": "buyer@example.com", "recipient_matches_customer": "on", "promo_code": "SPRING", "terms_accepted": "on", } data.update(overrides) return data def test_cart_item_is_unique_per_cart(self): cart = Cart.objects.create(user=self.user) CartItem.objects.create(cart=cart, item=self.item, quantity=1) with self.assertRaises(IntegrityError): CartItem.objects.create(cart=cart, item=self.item, quantity=2) def test_add_remove_and_update_quantity_for_authenticated_user(self): self.client.force_login(self.user) add_response = self.client.post( reverse("orders:add"), data={"item_id": self.item.id, "next": reverse("orders:cart")}, ) self.assertRedirects(add_response, reverse("orders:cart")) self.assertTrue(CartItem.objects.filter(cart__user=self.user, item=self.item, quantity=1).exists()) update_response = self.client.post( reverse("orders:update_quantity"), data={"item_id": self.item.id, "quantity": 3, "next": reverse("orders:cart")}, ) self.assertRedirects(update_response, reverse("orders:cart")) self.assertTrue(CartItem.objects.filter(cart__user=self.user, item=self.item, quantity=3).exists()) remove_response = self.client.post( reverse("orders:remove"), data={"item_id": self.item.id, "next": reverse("orders:cart")}, ) self.assertRedirects(remove_response, reverse("orders:cart")) self.assertFalse(CartItem.objects.filter(cart__user=self.user, item=self.item).exists()) def test_add_endpoint_rejects_unavailable_item(self): self.client.force_login(self.user) response = self.client.post( reverse("orders:add"), data={"item_id": self.unavailable_item.id, "next": reverse("orders:cart")}, follow=True, ) self.assertEqual(response.status_code, 200) self.assertFalse(CartItem.objects.filter(cart__user=self.user, item=self.unavailable_item).exists()) self.assertContains(response, "Недоступный товар нельзя добавить в корзину.") def test_cart_page_shows_authenticated_user_items(self): cart = Cart.objects.create(user=self.user) CartItem.objects.create(cart=cart, item=self.item, quantity=2) ItemImage.objects.create( item=self.item, image="items/cart-item.jpg", alt="Фото мойки", is_primary=True, ) self.client.force_login(self.user) response = self.client.get(reverse("orders:cart")) self.assertEqual(response.status_code, 200) self.assertContains(response, "Кухонная мойка") self.assertContains(response, "Оформление заказа") self.assertContains(response, "1600,00 руб.") self.assertContains(response, "3200,00 руб.") self.assertContains(response, "3745,00 руб.") self.assertContains(response, "/media/items/cart-item.jpg") def test_cart_page_redirects_guest_to_login(self): response = self.client.get(reverse("orders:cart")) self.assertEqual(response.status_code, 302) self.assertIn(reverse("users:login"), response["Location"]) def test_checkout_creates_paid_order_clears_cart_and_sends_email(self): cart = Cart.objects.create(user=self.user) CartItem.objects.create(cart=cart, item=self.item, quantity=2) self.client.force_login(self.user) response = self.client.post(reverse("orders:checkout"), data=self.checkout_data()) order = Order.objects.get() self.assertRedirects(response, reverse("orders:success", kwargs={"order_id": order.id})) self.assertEqual(order.status, Order.Status.PAID) self.assertEqual(order.products_total, 3200) self.assertEqual(order.discount_total, 0) self.assertEqual(order.delivery_total, 500) self.assertEqual(order.floor_lift_total, 45) self.assertEqual(order.total, 3745) self.assertEqual(order.promo_code, "SPRING") self.assertFalse(CartItem.objects.filter(cart=cart).exists()) order_item = OrderItem.objects.get(order=order) self.assertEqual(order_item.item_name, "Кухонная мойка") self.assertEqual(order_item.quantity, 2) self.assertEqual(order_item.line_total, 3200) self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].to, ["buyer@example.com"]) self.assertEqual(mail.outbox[0].bcc, ["admin@example.com"]) self.assertIn(f"заказ №{order.id}", mail.outbox[0].subject.lower()) self.assertIn("Кухонная мойка", mail.outbox[0].body) def test_checkout_rejects_empty_cart(self): self.client.force_login(self.user) response = self.client.post(reverse("orders:checkout"), data=self.checkout_data()) self.assertRedirects(response, reverse("orders:cart")) self.assertFalse(Order.objects.exists()) self.assertEqual(len(mail.outbox), 0) def test_checkout_requires_address_for_courier_delivery(self): cart = Cart.objects.create(user=self.user) CartItem.objects.create(cart=cart, item=self.item, quantity=1) self.client.force_login(self.user) response = self.client.post( reverse("orders:checkout"), data=self.checkout_data(region="", address=""), ) self.assertEqual(response.status_code, 400) self.assertContains(response, "Укажите регион доставки.", status_code=400) self.assertContains(response, "Укажите адрес доставки.", status_code=400) self.assertFalse(Order.objects.exists()) def test_checkout_pickup_does_not_require_address_or_add_services(self): cart = Cart.objects.create(user=self.user) CartItem.objects.create(cart=cart, item=self.item, quantity=1) self.client.force_login(self.user) response = self.client.post( reverse("orders:checkout"), data=self.checkout_data(delivery_method="pickup", region="", address=""), ) order = Order.objects.get() self.assertRedirects(response, reverse("orders:success", kwargs={"order_id": order.id})) self.assertEqual(order.delivery_total, 0) self.assertEqual(order.floor_lift_total, 0) self.assertEqual(order.total, 1600) def test_success_page_is_only_available_to_order_owner(self): other_user = User.objects.create_user( username="other-user", email="other@example.com", password="StrongPassword123", is_active=True, ) order = Order.objects.create( user=self.user, customer_name="Иван Петров", phone="+375296881819", email="buyer@example.com", delivery_method=Order.DeliveryMethod.COURIER, region="Минск", address="ул. Тимирязева, 123", payment_method=Order.PaymentMethod.CARD, products_total=1600, discount_total=0, delivery_total=500, floor_lift_total=45, total=2145, status=Order.Status.PAID, ) self.client.force_login(other_user) response = self.client.get(reverse("orders:success", kwargs={"order_id": order.id})) self.assertEqual(response.status_code, 404)