# -*- coding:utf-8 -*- # # Copyright 2023 © AE UTBM # ae@utbm.fr / ae.info@utbm.fr # All contributors are listed in the CONTRIBUTORS file. # # This file is part of the website of the UTBM Student Association (AE UTBM), # https://ae.utbm.fr. # # You can find the whole source code at https://github.com/ae-utbm/sith3 # # LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3) # SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE # OR WITHIN THE LOCAL FILE "LICENSE" # # PREVIOUSLY LICENSED UNDER THE MIT LICENSE, # SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE.old # OR WITHIN THE LOCAL FILE "LICENSE.old" # import base64 import json import urllib from django.conf import settings from django.db.models import Max from django.test import TestCase from django.urls import reverse from OpenSSL import crypto from core.models import User from counter.models import Counter, Customer, Product, Selling from eboutic.models import Basket class EbouticTest(TestCase): @classmethod def setUpTestData(cls): cls.barbar = Product.objects.get(code="BARB") cls.refill = Product.objects.get(code="15REFILL") cls.cotis = Product.objects.get(code="1SCOTIZ") cls.eboutic = Counter.objects.get(name="Eboutic") cls.skia = User.objects.get(username="skia") cls.subscriber = User.objects.get(username="subscriber") cls.old_subscriber = User.objects.get(username="old_subscriber") cls.public = User.objects.get(username="public") def get_busy_basket(self, user) -> 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() basket.add_product(self.barbar, 3) basket.add_product(self.cotis) return basket def generate_bank_valid_answer(self) -> str: basket = Basket.from_session(self.client.session) basket_id = basket.id amount = int(basket.get_total() * 100) query = f"Amount={amount}&BasketID={basket_id}&Auto=42&Error=00000" with open("./eboutic/tests/private_key.pem") as f: PRIVKEY = f.read() with open("./eboutic/tests/public_key.pem") as f: settings.SITH_EBOUTIC_PUB_KEY = f.read() privkey = crypto.load_privatekey(crypto.FILETYPE_PEM, PRIVKEY) sig = crypto.sign(privkey, query.encode("utf-8"), "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_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.get_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.get_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_submit_basket(self): self.client.force_login(self.subscriber) self.client.cookies["basket_items"] = """[ {"id": 2, "name": "Cotis 2 semestres", "quantity": 1, "unit_price": 28}, {"id": 4, "name": "Barbar", "quantity": 3, "unit_price": 1.7} ]""" response = self.client.get(reverse("eboutic:command")) assert response.status_code == 200 self.assertInHTML( "Cotis 2 semestres128.00 €", response.content.decode(), ) self.assertInHTML( "Barbar31.70 €", response.content.decode(), ) assert "basket_id" in self.client.session basket = Basket.objects.get(id=self.client.session["basket_id"]) assert basket.items.count() == 2 barbar = basket.items.filter(product_name="Barbar").first() assert barbar is not None assert barbar.quantity == 3 cotis = basket.items.filter(product_name="Cotis 2 semestres").first() assert cotis is not None assert cotis.quantity == 1 assert basket.get_total() == 3 * 1.7 + 28 def test_submit_empty_basket(self): self.client.force_login(self.subscriber) self.client.cookies["basket_items"] = "[]" response = self.client.get(reverse("eboutic:command")) self.assertRedirects(response, "/eboutic/") def test_submit_invalid_basket(self): self.client.force_login(self.subscriber) max_id = Product.objects.aggregate(res=Max("id"))["res"] self.client.cookies["basket_items"] = f"""[ {{"id": {max_id + 1}, "name": "", "quantity": 1, "unit_price": 28}} ]""" response = self.client.get(reverse("eboutic:command")) cookie = self.client.cookies["basket_items"].OutputString() assert 'basket_items=""' in cookie assert "Path=/eboutic" in cookie self.assertRedirects(response, "/eboutic/") def test_submit_basket_illegal_quantity(self): self.client.force_login(self.subscriber) self.client.cookies["basket_items"] = """[ {"id": 4, "name": "Barbar", "quantity": -1, "unit_price": 1.7} ]""" response = self.client.get(reverse("eboutic:command")) self.assertRedirects(response, "/eboutic/") 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.content.decode(), ) 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.content.decode() == "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 assert ( "Basket processing failed with error: SuspiciousOperation('Basket total and amount do not match'" 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