From 34fd78be5e3a49107998bc47342fdd58931ce931 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Wed, 10 Nov 2021 15:36:18 +0100 Subject: [PATCH] #19 Tests * adds workflow tests or deductions in InterventionWorkflowTestCase * fixes bugs detected by testing --- compensation/account_urls.py | 2 +- .../eco_account/includes/deductions.html | 2 +- .../detail/includes/deductions.html | 2 +- intervention/tests/test_workflow.py | 190 ++++++++++++++++-- konova/tests/test_views.py | 20 ++ 5 files changed, 198 insertions(+), 18 deletions(-) diff --git a/compensation/account_urls.py b/compensation/account_urls.py index 97e5938e..a2bfd6f8 100644 --- a/compensation/account_urls.py +++ b/compensation/account_urls.py @@ -32,7 +32,7 @@ urlpatterns = [ path('document//remove/', remove_document_view, name='acc-remove-doc'), # Eco-account deductions - path('/remove/', deduction_remove_view, name='deduction-remove'), + path('/remove/', deduction_remove_view, name='acc-remove-deduction'), path('/deduct/new', new_deduction_view, name='acc-new-deduction'), ] \ No newline at end of file diff --git a/compensation/templates/compensation/detail/eco_account/includes/deductions.html b/compensation/templates/compensation/detail/eco_account/includes/deductions.html index b35cef1e..16b9cb75 100644 --- a/compensation/templates/compensation/detail/eco_account/includes/deductions.html +++ b/compensation/templates/compensation/detail/eco_account/includes/deductions.html @@ -60,7 +60,7 @@ {{ deduction.created.timestamp|default_if_none:""|naturalday}} {% if is_default_member and has_access %} - {% endif %} diff --git a/intervention/templates/intervention/detail/includes/deductions.html b/intervention/templates/intervention/detail/includes/deductions.html index cb2f8179..6bb602d6 100644 --- a/intervention/templates/intervention/detail/includes/deductions.html +++ b/intervention/templates/intervention/detail/includes/deductions.html @@ -55,7 +55,7 @@ {{ deduction.created.timestamp|default_if_none:""|naturalday}} {% if is_default_member and has_access %} - {% endif %} diff --git a/intervention/tests/test_workflow.py b/intervention/tests/test_workflow.py index 8646adf6..6f8bac6b 100644 --- a/intervention/tests/test_workflow.py +++ b/intervention/tests/test_workflow.py @@ -11,10 +11,11 @@ from django.contrib.auth.models import Group from django.core.exceptions import ObjectDoesNotExist from django.urls import reverse -from compensation.models import Payment +from compensation.models import Payment, EcoAccountDeduction from intervention.models import Intervention from konova.settings import DEFAULT_GROUP from konova.tests.test_views import BaseWorkflowTestCase +from user.models import UserActionLogEntry, UserAction class InterventionWorkflowTestCase(BaseWorkflowTestCase): @@ -31,7 +32,7 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase): default_group = Group.objects.get(name=DEFAULT_GROUP) cls.superuser.groups.set([default_group]) - def test_new_normal_case(self): + def test_new(self): """ Checks a 'normal' case of creating a new intervention. We expect the user to be redirected as expected right away to the detail page of the new intervention. @@ -93,9 +94,6 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase): ## BUT: Payments are added on the intervention detail page. Therefore it's part of a regular intervention workflow. new_payment_url = reverse("compensation:pay-new", args=(self.intervention.id,)) - # Create default detail page of intervention - detail_url = reverse("intervention:detail", args=(self.intervention.id,)) - # Make sure there are no payments on the intervention, yet self.assertEqual(0, self.intervention.payments.count()) @@ -145,22 +143,16 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase): ) # Expect the payment to be gone from the db and therefore from the intervention as well - try: - payment.refresh_from_db() - # Well, we should not reach this next line of code, since the payment should be gone, therefore not - # refreshable -> fail! - self.fail() - except ObjectDoesNotExist: - # If we get in here, the test was fine - pass + self.assert_object_is_deleted(payment) + # Now make sure the intervention has no payments anymore self.assertEqual(0, self.intervention.payments.count()) - def test_changing_payment_test_case(self): + def test_payments(self): """ Checks a 'normal' case of adding a payment. We expect a new payment to be addable to an existing intervention - We expect a payment to be deletable to an existing intervention + We expect a payment to be deletable from an existing intervention Returns: @@ -170,3 +162,171 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase): # Now remove the payment again self.subtest_delete_payment(payment) + + def subtest_add_deduction_fail_positive(self, new_url: str, post_data: dict, test_surface: float): + """ Holds tests for postivie fails of new deduction creation + + Reasons for failing are: + * EcoAccount does not provide enough 'deductable_surface' + * EcoAccount is not recorded (not "approved"), yet + * EcoAccount is not shared with performing user + + Args: + new_url (str): The url to send the post data to + post_data (dict): The form post data to be sent + + Returns: + + """ + # Before running fail positive tests, we need to have an account in a (normally) fine working state + self.assertIsNotNone(self.eco_account.recorded) # -> is recorded + self.assertGreater(self.eco_account.deductable_surface, test_surface) # -> has more deductable surface than we need + self.assertIn(self.superuser, self.eco_account.users.all()) # -> is shared with the performing user + + # Count the number of already existing deductions in total and for the account for later comparison + num_deductions = self.eco_account.deductions.count() + num_deductions_total = EcoAccountDeduction.objects.count() + + # First test that a deduction can not be created, if the account does not provide + # enough surface for the deduction. So we modify the deductable surface of the account + self.eco_account.deductable_surface = 0 + self.eco_account.save() + + # Now perform the (expected) failing request + self.client_user.post(new_url, post_data) + + # Expect no changes at all, since the deduction should not have been created + self.assertEqual(num_deductions, self.eco_account.deductions.count()) + self.assertEqual(num_deductions_total, EcoAccountDeduction.objects.count()) + + # Now restore the deductable surface to a valid size back again but remove the user from the shared list + self.eco_account.deductable_surface = test_surface + 100.00 + self.eco_account.users.set([]) + self.eco_account.save() + + # Now perform the (expected) failing request (again) + self.client_user.post(new_url, post_data) + + # Expect no changes at all, since the account is not shared + self.assertEqual(num_deductions, self.eco_account.deductions.count()) + self.assertEqual(num_deductions_total, EcoAccountDeduction.objects.count()) + + # Restore the sharing but remove the recording state + self.eco_account.users.set([self.superuser]) + self.eco_account.recorded.delete() + self.eco_account.refresh_from_db() + self.eco_account.save() + + # Now perform the (expected) failing request (again) + self.client_user.post(new_url, post_data) + + # Expect no changes at all, since the account is no shared with the user, yet + self.assertEqual(num_deductions, self.eco_account.deductions.count()) + self.assertEqual(num_deductions_total, EcoAccountDeduction.objects.count()) + + def subtest_add_deduction_normal(self, new_url: str, post_data: dict, test_surface: float): + """ Holds tests on working ("normal") deduction creation + + Args: + new_url (str): The url to send the post data to + post_data (dict): The form post data to be sent + test_surface (float): The expected surface of the deduction + + Returns: + + """ + # Prepare the account for a working situation (enough deductable surface, recorded and shared) + self.eco_account.deductable_surface = 10000.00 + if self.eco_account.recorded is None: + rec_action = UserActionLogEntry.objects.create( + user=self.superuser, + action=UserAction.RECORDED + ) + self.eco_account.recorded = rec_action + self.eco_account.users.set([self.superuser]) + self.eco_account.save() + + # Run the request + self.client_user.post(new_url, post_data) + + # Expect the deduction to be created, since all constraints are fulfilled + self.assertEqual(1, self.eco_account.deductions.count()) + self.assertEqual(1, EcoAccountDeduction.objects.count()) + + # Make sure the deduction contains the expected data + deduction = EcoAccountDeduction.objects.first() + self.assertEqual(deduction.surface, test_surface) + self.assertEqual(deduction.intervention, self.intervention) + self.assertEqual(deduction.account, self.eco_account) + + # Return deduction for further usage in tests + return deduction + + def subtest_add_deduction(self): + """ Holds test for adding a new deduction + + Contains tests for + * positive fails (as expected) + * normal cases + + Returns: + + """ + # Create the url for creating a new deduction + new_url = reverse("compensation:acc-new-deduction", args=(self.eco_account.id,)) + + # Prepare the form data + test_surface = 100.00 + post_data = { + "surface": test_surface, + "account": self.eco_account.id, + "intervention": self.intervention.id, + } + # Run some tests for regular, working cases + deduction = self.subtest_add_deduction_normal(new_url, post_data, test_surface) + + # Run some tests where we expect the creation of a deduction to fail (as expected) + self.subtest_add_deduction_fail_positive(new_url, post_data, test_surface) + + # Return deduction for further usage in tests + return deduction + + def subtest_delete_deduction(self, deduction: EcoAccountDeduction): + """ Holds test for deleting a deduction + + Returns: + + """ + # Prepare url for deleting of this deduction + delete_url = reverse("compensation:acc-remove-deduction", args=(self.eco_account.id, deduction.id,)) + post_data = { + "confirm": True + } + # Save number of current deductions for later comparison + num_deductions = self.eco_account.deductions.count() + num_deductions_total = EcoAccountDeduction.objects.count() + + # Run request + self.client_user.post(delete_url, post_data) + + # Expect the deduction to be gone from the db and relations + self.assertEqual(num_deductions - 1, self.eco_account.deductions.count()) + self.assertEqual(num_deductions_total - 1, EcoAccountDeduction.objects.count()) + + # Expect the deduction to be totally gone + self.assert_object_is_deleted(deduction) + + def test_deduction(self): + """ + Checks a 'normal case of adding a deduction. + We expect a new deduction to be addable to an existing intervention + We expect a deduction to be deletable + + Returns: + + """ + # Create a new deduction for the default intervention + deduction = self.subtest_add_deduction() + + # Now remove the deduction again + self.subtest_delete_deduction(deduction) diff --git a/konova/tests/test_views.py b/konova/tests/test_views.py index c6d1986f..c4937996 100644 --- a/konova/tests/test_views.py +++ b/konova/tests/test_views.py @@ -8,6 +8,7 @@ Created on: 26.10.21 from abc import abstractmethod from django.contrib.auth.models import User, Group +from django.core.exceptions import ObjectDoesNotExist from django.test import TestCase, Client from django.urls import reverse @@ -355,3 +356,22 @@ class BaseWorkflowTestCase(BaseTestCase): cls.client_user = Client() cls.client_user.login(username=cls.superuser.username, password=cls.superuser_pw) cls.client_anon = Client() + + def assert_object_is_deleted(self, obj): + """ Provides a quick check whether an object has been removed from the database or not + + Args: + obj (): + + Returns: + + """ + # Expect the object to be gone from the db + try: + obj.refresh_from_db() + # Well, we should not reach this next line of code, since the object should be gone, therefore not + # refreshable -> fail! + self.fail() + except ObjectDoesNotExist: + # If we get in here, the test was fine + pass \ No newline at end of file