#19 Tests
* adds workflow tests or deductions in InterventionWorkflowTestCase * fixes bugs detected by testing
This commit is contained in:
		
							parent
							
								
									d93ff4015b
								
							
						
					
					
						commit
						34fd78be5e
					
				@ -32,7 +32,7 @@ urlpatterns = [
 | 
			
		||||
    path('document/<doc_id>/remove/', remove_document_view, name='acc-remove-doc'),
 | 
			
		||||
 | 
			
		||||
    # Eco-account deductions
 | 
			
		||||
    path('<id>/remove/<deduction_id>', deduction_remove_view, name='deduction-remove'),
 | 
			
		||||
    path('<id>/remove/<deduction_id>', deduction_remove_view, name='acc-remove-deduction'),
 | 
			
		||||
    path('<id>/deduct/new', new_deduction_view, name='acc-new-deduction'),
 | 
			
		||||
 | 
			
		||||
]
 | 
			
		||||
@ -60,7 +60,7 @@
 | 
			
		||||
                <td class="align-middle">{{ deduction.created.timestamp|default_if_none:""|naturalday}}</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    {% if is_default_member and has_access  %}
 | 
			
		||||
                    <button data-form-url="{% url 'compensation:deduction-remove' deduction.account.id deduction.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove Deduction' %}">
 | 
			
		||||
                    <button data-form-url="{% url 'compensation:acc-remove-deduction' deduction.account.id deduction.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove Deduction' %}">
 | 
			
		||||
                        {% fa5_icon 'trash' %}
 | 
			
		||||
                    </button>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
 | 
			
		||||
@ -55,7 +55,7 @@
 | 
			
		||||
                <td class="align-middle">{{ deduction.created.timestamp|default_if_none:""|naturalday}}</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    {% if is_default_member and has_access  %}
 | 
			
		||||
                    <button data-form-url="{% url 'compensation:deduction-remove' deduction.account.id deduction.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove Deduction' %}">
 | 
			
		||||
                    <button data-form-url="{% url 'compensation:acc-remove-deduction' deduction.account.id deduction.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove Deduction' %}">
 | 
			
		||||
                        {% fa5_icon 'trash' %}
 | 
			
		||||
                    </button>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user