diff --git a/eboutic/tests/test_payment.py b/eboutic/tests/test_payment.py index a3ce15c1..1e817cd0 100644 --- a/eboutic/tests/test_payment.py +++ b/eboutic/tests/test_payment.py @@ -1,5 +1,13 @@ +import base64 +import urllib from decimal import Decimal +from typing import TYPE_CHECKING +from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15 +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey +from cryptography.hazmat.primitives.hashes import SHA1 +from cryptography.hazmat.primitives.serialization import load_pem_private_key +from django.conf import settings from django.contrib.messages import get_messages from django.contrib.messages.constants import DEFAULT_LEVELS from django.test import TestCase @@ -7,12 +15,15 @@ from django.urls import reverse from model_bakery import baker from pytest_django.asserts import assertRedirects -from core.baker_recipes import subscriber_user +from core.baker_recipes import old_subscriber_user, subscriber_user from counter.baker_recipes import product_recipe -from counter.models import Product, ProductType +from counter.models import Product, ProductType, Selling from counter.tests.test_counter import force_refill_user from eboutic.models import Basket, BasketItem +if TYPE_CHECKING: + from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey + class TestPaymentBase(TestCase): @classmethod @@ -82,6 +93,22 @@ class TestPaymentSith(TestPaymentBase): self.customer.customer.refresh_from_db() assert self.customer.customer.amount == Decimal("1") + sellings = Selling.objects.filter(customer=self.customer.customer).order_by( + "quantity" + ) + assert len(sellings) == 2 + assert sellings[0].payment_method == "SITH_ACCOUNT" + assert sellings[0].quantity == 1 + assert sellings[0].unit_price == self.snack.selling_price + assert sellings[0].counter.type == "EBOUTIC" + assert sellings[0].product == self.snack + + assert sellings[1].payment_method == "SITH_ACCOUNT" + assert sellings[1].quantity == 2 + assert sellings[1].unit_price == self.beer.selling_price + assert sellings[1].counter.type == "EBOUTIC" + assert sellings[1].product == self.beer + def test_not_enough_money(self): self.client.force_login(self.customer) response = self.client.post( @@ -120,3 +147,103 @@ class TestPaymentSith(TestPaymentBase): ) self.customer.customer.refresh_from_db() assert self.customer.customer.amount == self.basket.total + + +class TestPaymentCard(TestPaymentBase): + def generate_bank_valid_answer(self, basket: Basket): + query = ( + f"Amount={int(basket.total * 100)}&BasketID={basket.id}&Auto=42&Error=00000" + ) + with open("./eboutic/tests/private_key.pem", "br") as f: + PRIVKEY = f.read() + with open("./eboutic/tests/public_key.pem") as f: + settings.SITH_EBOUTIC_PUB_KEY = f.read() + key: RSAPrivateKey = load_pem_private_key(PRIVKEY, None) + sig = key.sign(query.encode("utf-8"), PKCS1v15(), SHA1()) + b64sig = base64.b64encode(sig).decode("ascii") + + url = reverse("eboutic:etransation_autoanswer") + "?%s&Sig=%s" % ( + query, + urllib.parse.quote_plus(b64sig), + ) + return url + + def test_buy_success(self): + response = self.client.get(self.generate_bank_valid_answer(self.basket)) + assert response.status_code == 200 + assert response.content.decode("utf-8") == "Payment successful" + assert Basket.objects.filter(id=self.basket.id).first() is None + + sellings = Selling.objects.filter(customer=self.customer.customer).order_by( + "quantity" + ) + assert len(sellings) == 2 + assert sellings[0].payment_method == "CARD" + assert sellings[0].quantity == 1 + assert sellings[0].unit_price == self.snack.selling_price + assert sellings[0].counter.type == "EBOUTIC" + assert sellings[0].product == self.snack + + assert sellings[1].payment_method == "CARD" + assert sellings[1].quantity == 2 + assert sellings[1].unit_price == self.beer.selling_price + assert sellings[1].counter.type == "EBOUTIC" + assert sellings[1].product == self.beer + + def test_buy_subscribe_product(self): + customer = old_subscriber_user.make() + assert customer.subscriptions.count() == 1 + assert not customer.subscriptions.first().is_valid_now() + + basket = baker.make(Basket, user=customer) + BasketItem.from_product(Product.objects.get(code="2SCOTIZ"), 1, basket).save() + assert ( + self.client.get(self.generate_bank_valid_answer(basket)).status_code == 200 + ) + + assert customer.subscriptions.count() == 2 + + subscription = customer.subscriptions.order_by("-subscription_end").first() + assert subscription.is_valid_now() + assert subscription.subscription_type == "deux-semestres" + assert subscription.location == "EBOUTIC" + + def test_buy_refilling(self): + BasketItem.from_product(self.refilling, 2, self.basket).save() + assert ( + self.client.get(self.generate_bank_valid_answer(self.basket)).status_code + == 200 + ) + + self.customer.customer.refresh_from_db() + assert self.customer.customer.amount == self.refilling.selling_price * 2 + + def test_multiple_responses(self): + bank_response = self.generate_bank_valid_answer(self.basket) + assert self.client.get(bank_response).status_code == 200 + response = self.client.get(bank_response) + assert response.status_code == 500 + assert ( + response.text + == "Basket processing failed with error: SuspiciousOperation('Basket does not exists')" + ) + + def test_unknown_basket(self): + bank_response = self.generate_bank_valid_answer(self.basket) + self.basket.delete() + response = self.client.get(bank_response) + assert response.status_code == 500 + assert ( + response.text + == "Basket processing failed with error: SuspiciousOperation('Basket does not exists')" + ) + + def test_altered_basket(self): + bank_response = self.generate_bank_valid_answer(self.basket) + BasketItem.from_product(self.snack, 1, self.basket).save() + response = self.client.get(bank_response) + assert response.status_code == 500 + assert ( + response.text == "Basket processing failed with error: " + "SuspiciousOperation('Basket total and amount do not match')" + ) diff --git a/eboutic/tests/tests.py b/eboutic/tests/tests.py deleted file mode 100644 index 52ddca92..00000000 --- a/eboutic/tests/tests.py +++ /dev/null @@ -1,173 +0,0 @@ -# -# Copyright 2016,2017 -# - Skia -# - Maréchal Basket: - """Create and return a basket with 3 barbar and 1 cotis in it. - - Edit the client session to store the basket id in it. - """ - session = self.client.session - basket = Basket.objects.create(user=user) - session["basket_id"] = basket.id - session.save() - BasketItem.from_product(self.barbar, 3, basket).save() - BasketItem.from_product(self.cotis, 1, basket).save() - return basket - - def generate_bank_valid_answer(self) -> str: - basket = Basket.from_session(self.client.session) - basket_id = basket.id - amount = int(basket.total * 100) - query = f"Amount={amount}&BasketID={basket_id}&Auto=42&Error=00000" - with open("./eboutic/tests/private_key.pem", "br") as f: - PRIVKEY = f.read() - with open("./eboutic/tests/public_key.pem") as f: - settings.SITH_EBOUTIC_PUB_KEY = f.read() - key: RSAPrivateKey = load_pem_private_key(PRIVKEY, None) - sig = key.sign(query.encode("utf-8"), PKCS1v15(), SHA1()) - b64sig = base64.b64encode(sig).decode("ascii") - - url = reverse("eboutic:etransation_autoanswer") + "?%s&Sig=%s" % ( - query, - urllib.parse.quote_plus(b64sig), - ) - return url - - def test_buy_subscribe_product_with_credit_card(self): - self.client.force_login(self.old_subscriber) - response = self.client.get( - reverse("core:user_profile", kwargs={"user_id": self.old_subscriber.id}) - ) - assert "Non cotisant" in str(response.content) - self.client.cookies["basket_items"] = """[ - {"id": 2, "name": "Cotis 2 semestres", "quantity": 1, "unit_price": 28} - ]""" - response = self.client.get(reverse("eboutic:command")) - self.assertInHTML( - "Cotis 2 semestres128.00 €", - response.text, - ) - basket = Basket.objects.get(id=self.client.session["basket_id"]) - assert basket.items.count() == 1 - response = self.client.get(self.generate_bank_valid_answer()) - assert response.status_code == 200 - assert response.content.decode("utf-8") == "Payment successful" - - subscriber = User.objects.get(id=self.old_subscriber.id) - assert subscriber.subscriptions.count() == 2 - sub = subscriber.subscriptions.order_by("-subscription_end").first() - assert sub.is_valid_now() - assert sub.member == subscriber - assert sub.subscription_type == "deux-semestres" - assert sub.location == "EBOUTIC" - - def test_buy_refill_product_with_credit_card(self): - self.client.force_login(self.subscriber) - # basket contains 1 refill item worth 15€ - self.client.cookies["basket_items"] = json.dumps( - [{"id": 3, "name": "Rechargement 15 €", "quantity": 1, "unit_price": 15}] - ) - initial_balance = self.subscriber.customer.amount - self.client.get(reverse("eboutic:command")) - - url = self.generate_bank_valid_answer() - response = self.client.get(url) - assert response.status_code == 200 - assert response.text == "Payment successful" - new_balance = Customer.objects.get(user=self.subscriber).amount - assert new_balance == initial_balance + 15 - - def test_alter_basket_after_submission(self): - self.client.force_login(self.subscriber) - self.client.cookies["basket_items"] = json.dumps( - [{"id": 4, "name": "Barbar", "quantity": 1, "unit_price": 1.7}] - ) - self.client.get(reverse("eboutic:command")) - et_answer_url = self.generate_bank_valid_answer() - self.client.cookies["basket_items"] = json.dumps( - [ # alter basket - {"id": 4, "name": "Barbar", "quantity": 3, "unit_price": 1.7} - ] - ) - self.client.get(reverse("eboutic:command")) - response = self.client.get(et_answer_url) - assert response.status_code == 500 - msg = ( - "Basket processing failed with error: " - "SuspiciousOperation('Basket total and amount do not match'" - ) - assert msg in response.content.decode("utf-8") - - def test_buy_simple_product_with_credit_card(self): - self.client.force_login(self.subscriber) - self.client.cookies["basket_items"] = json.dumps( - [{"id": 4, "name": "Barbar", "quantity": 1, "unit_price": 1.7}] - ) - self.client.get(reverse("eboutic:command")) - et_answer_url = self.generate_bank_valid_answer() - response = self.client.get(et_answer_url) - assert response.status_code == 200 - assert response.content.decode("utf-8") == "Payment successful" - - selling = ( - Selling.objects.filter(customer=self.subscriber.customer) - .order_by("-date") - .first() - ) - assert selling.payment_method == "CARD" - assert selling.quantity == 1 - assert selling.unit_price == self.barbar.selling_price - assert selling.counter.type == "EBOUTIC" - assert selling.product == self.barbar