mirror of
				https://github.com/ae-utbm/sith.git
				synced 2025-10-31 00:53:08 +00:00 
			
		
		
		
	Test pay with sith
This commit is contained in:
		| @@ -47,6 +47,10 @@ from counter.fields import CurrencyField | |||||||
| from subscription.models import Subscription | from subscription.models import Subscription | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def get_eboutic() -> Counter: | ||||||
|  |     return Counter.objects.filter(type="EBOUTIC").order_by("id").first() | ||||||
|  |  | ||||||
|  |  | ||||||
| class CustomerQuerySet(models.QuerySet): | class CustomerQuerySet(models.QuerySet): | ||||||
|     def update_amount(self) -> int: |     def update_amount(self) -> int: | ||||||
|         """Update the amount of all customers selected by this queryset. |         """Update the amount of all customers selected by this queryset. | ||||||
|   | |||||||
| @@ -28,12 +28,20 @@ from django.utils.translation import gettext_lazy as _ | |||||||
|  |  | ||||||
| from core.models import User | from core.models import User | ||||||
| from counter.fields import CurrencyField | from counter.fields import CurrencyField | ||||||
| from counter.models import BillingInfo, Counter, Customer, Product, Refilling, Selling | from counter.models import ( | ||||||
|  |     BillingInfo, | ||||||
|  |     Counter, | ||||||
|  |     Customer, | ||||||
|  |     Product, | ||||||
|  |     Refilling, | ||||||
|  |     Selling, | ||||||
|  |     get_eboutic, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_eboutic_products(user: User) -> list[Product]: | def get_eboutic_products(user: User) -> list[Product]: | ||||||
|     products = ( |     products = ( | ||||||
|         Counter.objects.get(type="EBOUTIC") |         get_eboutic() | ||||||
|         .products.filter(product_type__isnull=False) |         .products.filter(product_type__isnull=False) | ||||||
|         .filter(archived=False) |         .filter(archived=False) | ||||||
|         .filter(limit_age__lte=user.age) |         .filter(limit_age__lte=user.age) | ||||||
| @@ -102,13 +110,6 @@ class Basket(models.Model): | |||||||
|             )["total"] |             )["total"] | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     @classmethod |  | ||||||
|     def from_session(cls, session) -> Basket | None: |  | ||||||
|         """The basket stored in the session object, if it exists.""" |  | ||||||
|         if "basket_id" in session: |  | ||||||
|             return cls.objects.filter(id=session["basket_id"]).first() |  | ||||||
|         return None |  | ||||||
|  |  | ||||||
|     def generate_sales(self, counter, seller: User, payment_method: str): |     def generate_sales(self, counter, seller: User, payment_method: str): | ||||||
|         """Generate a list of sold items corresponding to the items |         """Generate a list of sold items corresponding to the items | ||||||
|         of this basket WITHOUT saving them NOR deleting the basket. |         of this basket WITHOUT saving them NOR deleting the basket. | ||||||
|   | |||||||
| @@ -4,6 +4,14 @@ | |||||||
|   <h3>{% trans %}Eboutic{% endtrans %}</h3> |   <h3>{% trans %}Eboutic{% endtrans %}</h3> | ||||||
|  |  | ||||||
|   <div> |   <div> | ||||||
|  |     {% if messages %} | ||||||
|  |       {% for message in messages %} | ||||||
|  |         <div class="alert alert-{{ message.tags }}"> | ||||||
|  |           {{ message }} | ||||||
|  |         </div> | ||||||
|  |       {% endfor %} | ||||||
|  |     {% endif %} | ||||||
|  |  | ||||||
|     {% if success %} |     {% if success %} | ||||||
|       {% trans %}Payment successful{% endtrans %} |       {% trans %}Payment successful{% endtrans %} | ||||||
|     {% else %} |     {% else %} | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | import pytest | ||||||
| from django.http import HttpResponse | from django.http import HttpResponse | ||||||
| from django.test import TestCase | from django.test import TestCase | ||||||
| from django.test.client import Client | from django.test.client import Client | ||||||
| @@ -9,11 +10,20 @@ from pytest_django.asserts import assertRedirects | |||||||
| from core.baker_recipes import subscriber_user | from core.baker_recipes import subscriber_user | ||||||
| from core.models import Group, User | from core.models import Group, User | ||||||
| from counter.baker_recipes import product_recipe | from counter.baker_recipes import product_recipe | ||||||
| from counter.models import Counter, ProductType | from counter.models import Counter, ProductType, get_eboutic | ||||||
| from counter.tests.test_counter import BasketItem | from counter.tests.test_counter import BasketItem | ||||||
| from eboutic.models import Basket | from eboutic.models import Basket | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @pytest.mark.django_db | ||||||
|  | def test_get_eboutic(): | ||||||
|  |     assert Counter.objects.get(name="Eboutic") == get_eboutic() | ||||||
|  |  | ||||||
|  |     baker.make(Counter, type="EBOUTIC") | ||||||
|  |  | ||||||
|  |     assert Counter.objects.get(name="Eboutic") == get_eboutic() | ||||||
|  |  | ||||||
|  |  | ||||||
| class TestEboutic(TestCase): | class TestEboutic(TestCase): | ||||||
|     @classmethod |     @classmethod | ||||||
|     def setUpTestData(cls): |     def setUpTestData(cls): | ||||||
| @@ -51,7 +61,7 @@ class TestEboutic(TestCase): | |||||||
|         cls.new_customer.groups.add(cls.group_public) |         cls.new_customer.groups.add(cls.group_public) | ||||||
|         cls.new_customer_adult.groups.add(cls.group_public) |         cls.new_customer_adult.groups.add(cls.group_public) | ||||||
|  |  | ||||||
|         cls.eboutic = Counter.objects.get(name="Eboutic") |         cls.eboutic = get_eboutic() | ||||||
|         cls.eboutic.products.add(cls.cotiz, cls.beer, cls.snack) |         cls.eboutic.products.add(cls.cotiz, cls.beer, cls.snack) | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|   | |||||||
							
								
								
									
										122
									
								
								eboutic/tests/test_payment.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								eboutic/tests/test_payment.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | |||||||
|  | from decimal import Decimal | ||||||
|  |  | ||||||
|  | from django.contrib.messages import get_messages | ||||||
|  | from django.contrib.messages.constants import DEFAULT_LEVELS | ||||||
|  | from django.test import TestCase | ||||||
|  | from django.urls import reverse | ||||||
|  | from model_bakery import baker | ||||||
|  | from pytest_django.asserts import assertRedirects | ||||||
|  |  | ||||||
|  | from core.baker_recipes import subscriber_user | ||||||
|  | from counter.baker_recipes import product_recipe | ||||||
|  | from counter.models import Product, ProductType | ||||||
|  | from counter.tests.test_counter import force_refill_user | ||||||
|  | from eboutic.models import Basket, BasketItem | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestPaymentBase(TestCase): | ||||||
|  |     @classmethod | ||||||
|  |     def setUpTestData(cls): | ||||||
|  |         cls.customer = subscriber_user.make() | ||||||
|  |         cls.basket = baker.make(Basket, user=cls.customer) | ||||||
|  |         cls.refilling = Product.objects.get(code="15REFILL") | ||||||
|  |  | ||||||
|  |         product_type = baker.make(ProductType) | ||||||
|  |  | ||||||
|  |         cls.snack = product_recipe.make( | ||||||
|  |             selling_price=1.5, special_selling_price=1, product_type=product_type | ||||||
|  |         ) | ||||||
|  |         cls.beer = product_recipe.make( | ||||||
|  |             limit_age=18, | ||||||
|  |             selling_price=2.5, | ||||||
|  |             special_selling_price=1, | ||||||
|  |             product_type=product_type, | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         BasketItem.from_product(cls.snack, 1, cls.basket).save() | ||||||
|  |         BasketItem.from_product(cls.beer, 2, cls.basket).save() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestPaymentSith(TestPaymentBase): | ||||||
|  |     def test_anonymous(self): | ||||||
|  |         assert ( | ||||||
|  |             self.client.post( | ||||||
|  |                 reverse("eboutic:pay_with_sith", kwargs={"basket_id": self.basket.id}), | ||||||
|  |             ).status_code | ||||||
|  |             == 403 | ||||||
|  |         ) | ||||||
|  |         assert Basket.objects.filter(id=self.basket.id).first() is not None | ||||||
|  |  | ||||||
|  |     def test_unauthorized(self): | ||||||
|  |         self.client.force_login(subscriber_user.make()) | ||||||
|  |         assert ( | ||||||
|  |             self.client.post( | ||||||
|  |                 reverse("eboutic:pay_with_sith", kwargs={"basket_id": self.basket.id}), | ||||||
|  |             ).status_code | ||||||
|  |             == 403 | ||||||
|  |         ) | ||||||
|  |         assert Basket.objects.filter(id=self.basket.id).first() is not None | ||||||
|  |  | ||||||
|  |     def test_not_found(self): | ||||||
|  |         self.client.force_login(self.customer) | ||||||
|  |         assert ( | ||||||
|  |             self.client.post( | ||||||
|  |                 reverse( | ||||||
|  |                     "eboutic:pay_with_sith", kwargs={"basket_id": self.basket.id + 1} | ||||||
|  |                 ), | ||||||
|  |             ).status_code | ||||||
|  |             == 404 | ||||||
|  |         ) | ||||||
|  |         assert Basket.objects.filter(id=self.basket.id).first() is not None | ||||||
|  |  | ||||||
|  |     def test_buy_success(self): | ||||||
|  |         self.client.force_login(self.customer) | ||||||
|  |         force_refill_user(self.customer, self.basket.total + 1) | ||||||
|  |         assertRedirects( | ||||||
|  |             self.client.post( | ||||||
|  |                 reverse("eboutic:pay_with_sith", kwargs={"basket_id": self.basket.id}), | ||||||
|  |             ), | ||||||
|  |             reverse("eboutic:payment_result", kwargs={"result": "success"}), | ||||||
|  |         ) | ||||||
|  |         assert Basket.objects.filter(id=self.basket.id).first() is None | ||||||
|  |         self.customer.customer.refresh_from_db() | ||||||
|  |         assert self.customer.customer.amount == Decimal("1") | ||||||
|  |  | ||||||
|  |     def test_not_enough_money(self): | ||||||
|  |         self.client.force_login(self.customer) | ||||||
|  |         response = self.client.post( | ||||||
|  |             reverse("eboutic:pay_with_sith", kwargs={"basket_id": self.basket.id}), | ||||||
|  |         ) | ||||||
|  |         assertRedirects( | ||||||
|  |             response, | ||||||
|  |             reverse("eboutic:payment_result", kwargs={"result": "failure"}), | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         messages = list(get_messages(response.wsgi_request)) | ||||||
|  |         assert len(messages) == 1 | ||||||
|  |         assert messages[0].level == DEFAULT_LEVELS["ERROR"] | ||||||
|  |         assert messages[0].message == "Solde insuffisant" | ||||||
|  |  | ||||||
|  |         assert Basket.objects.filter(id=self.basket.id).first() is not None | ||||||
|  |  | ||||||
|  |     def test_refilling_in_basket(self): | ||||||
|  |         BasketItem.from_product(self.refilling, 1, self.basket).save() | ||||||
|  |         self.client.force_login(self.customer) | ||||||
|  |         force_refill_user(self.customer, self.basket.total) | ||||||
|  |         response = self.client.post( | ||||||
|  |             reverse("eboutic:pay_with_sith", kwargs={"basket_id": self.basket.id}), | ||||||
|  |         ) | ||||||
|  |         assertRedirects( | ||||||
|  |             response, | ||||||
|  |             reverse("eboutic:payment_result", kwargs={"result": "failure"}), | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         assert Basket.objects.filter(id=self.basket.id).first() is not None | ||||||
|  |         messages = list(get_messages(response.wsgi_request)) | ||||||
|  |         assert messages[0].level == DEFAULT_LEVELS["ERROR"] | ||||||
|  |         assert ( | ||||||
|  |             messages[0].message | ||||||
|  |             == "Vous ne pouvez pas acheter un rechargement avec de l'argent du sith" | ||||||
|  |         ) | ||||||
|  |         self.customer.customer.refresh_from_db() | ||||||
|  |         assert self.customer.customer.amount == self.basket.total | ||||||
| @@ -85,33 +85,6 @@ class TestEboutic(TestCase): | |||||||
|         ) |         ) | ||||||
|         return url |         return url | ||||||
|  |  | ||||||
|     def test_buy_with_sith_account(self): |  | ||||||
|         self.client.force_login(self.subscriber) |  | ||||||
|         self.subscriber.customer.amount = 100  # give money before test |  | ||||||
|         self.subscriber.customer.save() |  | ||||||
|         basket = self.get_busy_basket(self.subscriber) |  | ||||||
|         amount = basket.total |  | ||||||
|         response = self.client.post(reverse("eboutic:pay_with_sith")) |  | ||||||
|         self.assertRedirects(response, "/eboutic/pay/success/") |  | ||||||
|         new_balance = Customer.objects.get(user=self.subscriber).amount |  | ||||||
|         assert float(new_balance) == 100 - amount |  | ||||||
|         expected = 'basket_items=""; expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; Path=/eboutic' |  | ||||||
|         assert expected == self.client.cookies["basket_items"].OutputString() |  | ||||||
|  |  | ||||||
|     def test_buy_with_sith_account_no_money(self): |  | ||||||
|         self.client.force_login(self.subscriber) |  | ||||||
|         basket = self.get_busy_basket(self.subscriber) |  | ||||||
|         initial = basket.total - 1  # just not enough to complete the sale |  | ||||||
|         self.subscriber.customer.amount = initial |  | ||||||
|         self.subscriber.customer.save() |  | ||||||
|         response = self.client.post(reverse("eboutic:pay_with_sith")) |  | ||||||
|         self.assertRedirects(response, "/eboutic/pay/failure/") |  | ||||||
|         new_balance = Customer.objects.get(user=self.subscriber).amount |  | ||||||
|         assert float(new_balance) == initial |  | ||||||
|         # this cookie should be removed after payment |  | ||||||
|         expected = 'basket_items=""; expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; Path=/eboutic' |  | ||||||
|         assert expected == self.client.cookies["basket_items"].OutputString() |  | ||||||
|  |  | ||||||
|     def test_buy_subscribe_product_with_credit_card(self): |     def test_buy_subscribe_product_with_credit_card(self): | ||||||
|         self.client.force_login(self.old_subscriber) |         self.client.force_login(self.old_subscriber) | ||||||
|         response = self.client.get( |         response = self.client.get( | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ from django.contrib.auth.mixins import ( | |||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
| ) | ) | ||||||
| from django.contrib.messages.views import SuccessMessageMixin | from django.contrib.messages.views import SuccessMessageMixin | ||||||
| from django.core.exceptions import SuspiciousOperation | from django.core.exceptions import SuspiciousOperation, ValidationError | ||||||
| from django.db import DatabaseError, transaction | from django.db import DatabaseError, transaction | ||||||
| from django.db.models.fields import forms | from django.db.models.fields import forms | ||||||
| from django.db.utils import cached_property | from django.db.utils import cached_property | ||||||
| @@ -48,7 +48,7 @@ from django_countries.fields import Country | |||||||
| from core.auth.mixins import CanViewMixin, IsSubscriberMixin | from core.auth.mixins import CanViewMixin, IsSubscriberMixin | ||||||
| from core.views.mixins import FragmentMixin, UseFragmentsMixin | from core.views.mixins import FragmentMixin, UseFragmentsMixin | ||||||
| from counter.forms import BaseBasketForm, BillingInfoForm, ProductForm | from counter.forms import BaseBasketForm, BillingInfoForm, ProductForm | ||||||
| from counter.models import BillingInfo, Counter, Customer, Product, Selling | from counter.models import BillingInfo, Customer, Product, Selling, get_eboutic | ||||||
| from eboutic.models import ( | from eboutic.models import ( | ||||||
|     Basket, |     Basket, | ||||||
|     BasketItem, |     BasketItem, | ||||||
| @@ -90,7 +90,7 @@ class EbouticMainView(LoginRequiredMixin, FormView): | |||||||
|         kwargs = super().get_form_kwargs() |         kwargs = super().get_form_kwargs() | ||||||
|         kwargs["form_kwargs"] = { |         kwargs["form_kwargs"] = { | ||||||
|             "customer": self.customer, |             "customer": self.customer, | ||||||
|             "counter": Counter.objects.get(type="EBOUTIC"), |             "counter": get_eboutic(), | ||||||
|             "allowed_products": {product.id: product for product in self.products}, |             "allowed_products": {product.id: product for product in self.products}, | ||||||
|         } |         } | ||||||
|         return kwargs |         return kwargs | ||||||
| @@ -246,9 +246,9 @@ class EbouticPayWithSith(CanViewMixin, SingleObjectMixin, View): | |||||||
|                 self.request, |                 self.request, | ||||||
|                 _("You can't buy a refilling with sith money"), |                 _("You can't buy a refilling with sith money"), | ||||||
|             ) |             ) | ||||||
|             return redirect("eboutic:main") |             return redirect("eboutic:payment_result", "failure") | ||||||
|  |  | ||||||
|         eboutic = Counter.objects.get(type="EBOUTIC") |         eboutic = get_eboutic() | ||||||
|         sales = basket.generate_sales(eboutic, basket.user, "SITH_ACCOUNT") |         sales = basket.generate_sales(eboutic, basket.user, "SITH_ACCOUNT") | ||||||
|         try: |         try: | ||||||
|             with transaction.atomic(): |             with transaction.atomic(): | ||||||
| @@ -260,6 +260,8 @@ class EbouticPayWithSith(CanViewMixin, SingleObjectMixin, View): | |||||||
|             return redirect("eboutic:payment_result", "success") |             return redirect("eboutic:payment_result", "success") | ||||||
|         except DatabaseError as e: |         except DatabaseError as e: | ||||||
|             sentry_sdk.capture_exception(e) |             sentry_sdk.capture_exception(e) | ||||||
|  |         except ValidationError as e: | ||||||
|  |             messages.error(self.request, e.message) | ||||||
|         return redirect("eboutic:payment_result", "failure") |         return redirect("eboutic:payment_result", "failure") | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user