nk20/apps/note/tests/test_transactions.py

437 lines
18 KiB
Python

# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from api.tests import TestAPI
from member.models import Club, Membership
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from django.urls import reverse
from django.utils import timezone
from permission.models import Role
from ..api.views import AliasViewSet, ConsumerViewSet, NotePolymorphicViewSet, TemplateCategoryViewSet, \
TransactionTemplateViewSet, TransactionViewSet
from ..models import NoteUser, Transaction, TemplateCategory, TransactionTemplate, RecurrentTransaction, \
MembershipTransaction, SpecialTransaction, NoteSpecial, Alias, Note
class TestTransactions(TestCase):
fixtures = ('initial', )
def setUp(self) -> None:
self.user = User.objects.create_superuser(
username="toto",
password="totototo",
email="toto@example.com",
)
sess = self.client.session
sess["permission_mask"] = 42
sess.save()
self.client.force_login(self.user)
membership = Membership.objects.create(club=Club.objects.get(name="BDE"), user=self.user)
membership.roles.add(Role.objects.get(name="Respo info"))
membership.save()
Membership.objects.create(club=Club.objects.get(name="Kfet"), user=self.user)
self.user.note.refresh_from_db()
self.second_user = User.objects.create(
username="toto2",
)
# Non superusers have no note until the registration get validated
NoteUser.objects.create(user=self.second_user)
self.club = Club.objects.create(
name="clubtoto",
)
self.transaction = Transaction.objects.create(
source=self.second_user.note,
destination=self.user.note,
amount=4200,
reason="Test transaction",
)
self.user.note.refresh_from_db()
self.second_user.note.refresh_from_db()
self.category = TemplateCategory.objects.create(name="Test")
self.template = TransactionTemplate.objects.create(
name="Test",
destination=self.club.note,
category=self.category,
amount=100,
description="Test template",
)
def test_admin_pages(self):
"""
Load some admin pages to check that they render successfully.
"""
response = self.client.get(reverse("admin:index") + "note/note/")
self.assertEqual(response.status_code, 200)
response = self.client.get(reverse("admin:index") + "note/transaction/")
self.assertEqual(response.status_code, 200)
response = self.client.get(reverse("admin:index") + "note/transaction/" + str(self.transaction.pk) + "/change/")
self.assertEqual(response.status_code, 200)
response = self.client.get(reverse("admin:index") + "note/transaction/add/?ct_id="
+ str(ContentType.objects.get_for_model(Transaction).id))
self.assertEqual(response.status_code, 200)
response = self.client.get(reverse("admin:index") + "note/transaction/add/?ct_id="
+ str(ContentType.objects.get_for_model(RecurrentTransaction).id))
self.assertEqual(response.status_code, 200)
response = self.client.get(reverse("admin:index") + "note/transaction/add/?ct_id="
+ str(ContentType.objects.get_for_model(MembershipTransaction).id))
self.assertEqual(response.status_code, 200)
response = self.client.get(reverse("admin:index") + "note/transaction/add/?ct_id="
+ str(ContentType.objects.get_for_model(SpecialTransaction).id))
self.assertEqual(response.status_code, 200)
response = self.client.get(reverse("admin:index") + "note/transactiontemplate/")
self.assertEqual(response.status_code, 200)
response = self.client.get(reverse("admin:index") + "note/templatecategory/")
self.assertEqual(response.status_code, 200)
def test_render_transfer_page(self):
response = self.client.get(reverse("note:transfer"))
self.assertEqual(response.status_code, 200)
def test_transfer_api(self):
old_user_balance = self.user.note.balance
old_second_user_balance = self.second_user.note.balance
quantity = 3
amount = 314
total = quantity * amount
response = self.client.post("/api/note/transaction/transaction/", data=dict(
quantity=quantity,
amount=amount,
reason="Transaction through API",
valid=True,
polymorphic_ctype=ContentType.objects.get_for_model(Transaction).id,
resourcetype="Transaction",
source=self.user.note.id,
source_alias=self.user.username,
destination=self.second_user.note.id,
destination_alias=self.second_user.username,
))
self.assertEqual(response.status_code, 201) # 201 = Created
self.assertTrue(Transaction.objects.filter(reason="Transaction through API").exists())
self.user.note.refresh_from_db()
self.second_user.note.refresh_from_db()
self.assertTrue(self.user.note.balance == old_user_balance - total)
self.assertTrue(self.second_user.note.balance == old_second_user_balance + total)
self.test_render_transfer_page()
def test_credit_api(self):
old_user_balance = self.user.note.balance
amount = 4242
special_type = NoteSpecial.objects.first()
response = self.client.post("/api/note/transaction/transaction/", data=dict(
quantity=1,
amount=amount,
reason="Credit through API",
valid=True,
polymorphic_ctype=ContentType.objects.get_for_model(SpecialTransaction).id,
resourcetype="SpecialTransaction",
source=special_type.id,
source_alias=str(special_type),
destination=self.user.note.id,
destination_alias=self.user.username,
last_name="TOTO",
first_name="Toto",
))
self.assertEqual(response.status_code, 201) # 201 = Created
self.assertTrue(Transaction.objects.filter(reason="Credit through API").exists())
self.user.note.refresh_from_db()
self.assertTrue(self.user.note.balance == old_user_balance + amount)
self.test_render_transfer_page()
def test_debit_api(self):
old_user_balance = self.user.note.balance
amount = 4242
special_type = NoteSpecial.objects.first()
response = self.client.post("/api/note/transaction/transaction/", data=dict(
quantity=1,
amount=amount,
reason="Debit through API",
valid=True,
polymorphic_ctype=ContentType.objects.get_for_model(SpecialTransaction).id,
resourcetype="SpecialTransaction",
source=self.user.note.id,
source_alias=self.user.username,
destination=special_type.id,
destination_alias=str(special_type),
last_name="TOTO",
first_name="Toto",
))
self.assertEqual(response.status_code, 201) # 201 = Created
self.assertTrue(Transaction.objects.filter(reason="Debit through API").exists())
self.user.note.refresh_from_db()
self.assertTrue(self.user.note.balance == old_user_balance - amount)
self.test_render_transfer_page()
def test_render_consos_page(self):
response = self.client.get(reverse("note:consos"))
self.assertEqual(response.status_code, 200)
def test_consumption_api(self):
old_user_balance = self.user.note.balance
old_club_balance = self.club.note.balance
quantity = 2
template = self.template
total = quantity * template.amount
response = self.client.post("/api/note/transaction/transaction/", data=dict(
quantity=quantity,
amount=template.amount,
reason="Consumption through API (" + template.name + ")",
valid=True,
polymorphic_ctype=ContentType.objects.get_for_model(RecurrentTransaction).id,
resourcetype="RecurrentTransaction",
source=self.user.note.id,
source_alias=self.user.username,
destination=self.club.note.id,
destination_alias=self.second_user.username,
template=template.id,
))
self.assertEqual(response.status_code, 201) # 201 = Created
self.assertTrue(Transaction.objects.filter(destination=self.club.note).exists())
self.user.note.refresh_from_db()
self.club.note.refresh_from_db()
self.assertTrue(self.user.note.balance == old_user_balance - total)
self.assertTrue(self.club.note.balance == old_club_balance + total)
self.test_render_consos_page()
def test_invalidate_transaction(self):
old_second_user_balance = self.second_user.note.balance
old_user_balance = self.user.note.balance
total = self.transaction.total
response = self.client.patch("/api/note/transaction/transaction/" + str(self.transaction.pk) + "/", data=dict(
valid=False,
resourcetype="Transaction",
invalidity_reason="Test invalidate",
), content_type="application/json")
self.assertEqual(response.status_code, 200)
self.assertTrue(Transaction.objects.filter(valid=False, invalidity_reason="Test invalidate").exists())
self.second_user.note.refresh_from_db()
self.user.note.refresh_from_db()
self.assertTrue(self.second_user.note.balance == old_second_user_balance + total)
self.assertTrue(self.user.note.balance == old_user_balance - total)
self.test_render_transfer_page()
self.test_render_consos_page()
# Now we check if we can revalidate
old_second_user_balance = self.second_user.note.balance
old_user_balance = self.user.note.balance
total = self.transaction.total
response = self.client.patch("/api/note/transaction/transaction/" + str(self.transaction.pk) + "/", data=dict(
valid=True,
resourcetype="Transaction",
), content_type="application/json")
self.assertEqual(response.status_code, 200)
self.assertTrue(Transaction.objects.filter(valid=True, pk=self.transaction.pk).exists())
self.second_user.note.refresh_from_db()
self.user.note.refresh_from_db()
self.assertTrue(self.second_user.note.balance == old_second_user_balance - total)
self.assertTrue(self.user.note.balance == old_user_balance + total)
self.test_render_transfer_page()
self.test_render_consos_page()
def test_render_template_list(self):
response = self.client.get(reverse("note:template_list") + "?search=test")
self.assertEqual(response.status_code, 200)
def test_render_template_create(self):
response = self.client.get(reverse("note:template_create"))
self.assertEqual(response.status_code, 200)
response = self.client.post(reverse("note:template_create"), data=dict(
name="Test create button",
destination=self.club.note.pk,
category=self.category.pk,
amount=4200,
description="We have created a button",
highlighted=True,
display=True,
))
self.assertRedirects(response, reverse("note:template_list"), 302, 200)
self.assertTrue(TransactionTemplate.objects.filter(name="Test create button").exists())
def test_render_template_update(self):
response = self.client.get(self.template.get_absolute_url())
self.assertEqual(response.status_code, 200)
response = self.client.post(self.template.get_absolute_url(), data=dict(
name="Test update button",
destination=self.club.note.pk,
category=self.category.pk,
amount=4200,
description="We have updated a button",
highlighted=True,
display=True,
))
self.assertRedirects(response, reverse("note:template_list"), 302, 200)
self.assertTrue(TransactionTemplate.objects.filter(name="Test update button", pk=self.template.pk).exists())
# Check that the price history renders properly
response = self.client.post(self.template.get_absolute_url(), data=dict(
name="Test price history",
destination=self.club.note.pk,
category=self.category.pk,
amount=4200,
description="We have updated a button",
highlighted=True,
display=True,
))
self.assertRedirects(response, reverse("note:template_list"), 302, 200)
self.assertTrue(TransactionTemplate.objects.filter(name="Test price history", pk=self.template.pk).exists())
response = self.client.get(reverse("note:template_update", args=(self.template.pk,)))
self.assertEqual(response.status_code, 200)
def test_render_search_transactions(self):
response = self.client.get(reverse("note:transactions", args=(self.user.note.pk,)), data=dict(
source=self.second_user.note.alias.first().id,
destination=self.user.note.alias.first().id,
type=[ContentType.objects.get_for_model(Transaction).id],
reason="test",
valid=True,
amount_gte=0,
amount_lte=42424242,
created_after="2000-01-01 00:00",
created_before="2042-12-31 21:42",
))
self.assertEqual(response.status_code, 200)
def test_delete_transaction(self):
# Transactions can't be deleted with a normal usage, but it is possible through the admin interface.
old_second_user_balance = self.second_user.note.balance
old_user_balance = self.user.note.balance
total = self.transaction.total
self.transaction.delete()
self.second_user.note.refresh_from_db()
self.user.note.refresh_from_db()
self.assertTrue(self.second_user.note.balance == old_second_user_balance + total)
self.assertTrue(self.user.note.balance == old_user_balance - total)
def test_calculate_last_negative_duration(self):
self.assertIsNone(self.user.note.last_negative_duration)
self.assertIsNotNone(self.second_user.note.last_negative_duration)
self.assertIsNone(self.club.note.last_negative_duration)
Transaction.objects.create(
source=self.club.note,
destination=self.user.note,
amount=2 * self.club.note.balance + 100,
reason="Club balance is negative",
)
self.club.note.refresh_from_db()
self.assertIsNotNone(self.club.note.last_negative_duration)
def test_api_search(self):
response = self.client.get("/api/note/note/")
self.assertEqual(response.status_code, 200)
response = self.client.get("/api/note/alias/?alias=.*")
self.assertEqual(response.status_code, 200)
response = self.client.get("/api/note/consumer/")
self.assertEqual(response.status_code, 200)
response = self.client.get("/api/note/transaction/transaction/")
self.assertEqual(response.status_code, 200)
response = self.client.get("/api/note/transaction/template/")
self.assertEqual(response.status_code, 200)
def test_api_alias(self):
response = self.client.post("/api/note/alias/", data=dict(
name="testalias",
note=self.user.note.id,
))
self.assertEqual(response.status_code, 201)
self.assertTrue(Alias.objects.filter(name="testalias").exists())
alias = Alias.objects.get(name="testalias")
response = self.client.patch("/api/note/alias/" + str(alias.pk) + "/", dict(name="test_updated_alias"),
content_type="application/json")
self.assertEqual(response.status_code, 200)
self.assertTrue(Alias.objects.filter(name="test_updated_alias").exists())
response = self.client.delete("/api/note/alias/" + str(alias.pk) + "/")
self.assertEqual(response.status_code, 204)
class TestNoteAPI(TestAPI):
def setUp(self) -> None:
super().setUp()
membership = Membership.objects.create(club=Club.objects.get(name="BDE"), user=self.user)
membership.roles.add(Role.objects.get(name="Respo info"))
membership.save()
Membership.objects.create(club=Club.objects.get(name="Kfet"), user=self.user)
self.user.note.last_negative = timezone.now()
self.user.note.save()
self.transaction = Transaction.objects.create(
source=Note.objects.first(),
destination=self.user.note,
amount=4200,
reason="Test transaction",
)
self.user.note.refresh_from_db()
Alias.objects.create(note=self.user.note, name="I am a ¢omplex alias")
self.category = TemplateCategory.objects.create(name="Test")
self.template = TransactionTemplate.objects.create(
name="Test",
destination=Club.objects.get(name="BDE").note,
category=self.category,
amount=100,
description="Test template",
)
def test_alias_api(self):
"""
Load Alias API page and test all filters and permissions
"""
self.check_viewset(AliasViewSet, "/api/note/alias/")
def test_consumer_api(self):
"""
Load Consumer API page and test all filters and permissions
"""
self.check_viewset(ConsumerViewSet, "/api/note/consumer/")
def test_note_api(self):
"""
Load Note API page and test all filters and permissions
"""
self.check_viewset(NotePolymorphicViewSet, "/api/note/note/")
def test_template_category_api(self):
"""
Load TemplateCategory API page and test all filters and permissions
"""
self.check_viewset(TemplateCategoryViewSet, "/api/note/transaction/category/")
def test_transaction_template_api(self):
"""
Load TemplateTemplate API page and test all filters and permissions
"""
self.check_viewset(TransactionTemplateViewSet, "/api/note/transaction/template/")
def test_transaction_api(self):
"""
Load Transaction API page and test all filters and permissions
"""
self.check_viewset(TransactionViewSet, "/api/note/transaction/transaction/")