* adds workflow tests for checkability and recordability for interventions
* fixes/improves code snippets detected by testing
pull/40/head
mpeltriaux 3 years ago
parent bcee58700a
commit 7d611a60d8

@ -182,7 +182,10 @@ class NewRevocationModalForm(BaseModalForm):
return revocation
class RunCheckModalForm(BaseModalForm):
class CheckModalForm(BaseModalForm):
""" The modal form for running a check on interventions and their compensations
"""
checked_intervention = forms.BooleanField(
label=_("Checked intervention data"),
label_suffix="",

@ -16,7 +16,7 @@
{% fa5_icon 'share-alt' %}
</button>
{% if is_zb_member %}
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Run check' %}" data-form-url="{% url 'intervention:run-check' obj.id %}">
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Run check' %}" data-form-url="{% url 'intervention:check' obj.id %}">
{% fa5_icon 'star' %}
</button>
{% endif %}

@ -30,7 +30,7 @@ class InterventionViewTestCase(BaseViewTestCase):
cls.remove_url = reverse("intervention:remove", args=(cls.intervention.id,))
cls.share_url = reverse("intervention:share", args=(cls.intervention.id, cls.intervention.access_token,))
cls.share_create_url = reverse("intervention:share-create", args=(cls.intervention.id,))
cls.run_check_url = reverse("intervention:run-check", args=(cls.intervention.id,))
cls.run_check_url = reverse("intervention:check", args=(cls.intervention.id,))
cls.record_url = reverse("intervention:record", args=(cls.intervention.id,))
cls.report_url = reverse("intervention:report", args=(cls.intervention.id,))

@ -7,13 +7,12 @@ Created on: 10.11.21
"""
import datetime
from django.contrib.auth.models import Group
from django.core.exceptions import ObjectDoesNotExist
from django.urls import reverse
from compensation.models import Payment, EcoAccountDeduction
from intervention.models import Intervention
from konova.settings import DEFAULT_GROUP
from konova.settings import DEFAULT_GROUP, ETS_GROUP, ZB_GROUP
from konova.tests.test_views import BaseWorkflowTestCase
from user.models import UserActionLogEntry, UserAction
@ -26,11 +25,19 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
@classmethod
def setUpTestData(cls):
super().setUpTestData()
cls.new_url = reverse("intervention:new", args=())
# Add user to the default group -> give default permissions
default_group = Group.objects.get(name=DEFAULT_GROUP)
cls.superuser.groups.set([default_group])
# Give the user shared access to the dummy intervention
cls.intervention.users.add(cls.superuser)
def setUp(self) -> None:
""" Setup data before each test run
Returns:
"""
# Set the default group as only group for the user
default_group = self.groups.get(name=DEFAULT_GROUP)
self.superuser.groups.set([default_group])
def test_new(self):
"""
@ -45,6 +52,8 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
# Define the intervention identifier for easier handling on the next lines
test_id = "Test_IDENTIFIER"
new_url = reverse("intervention:new", args=())
# Expect the new intervention does not exist yet
obj_exists = Intervention.objects.filter(
identifier=test_id
@ -58,7 +67,7 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
"geometry": "",
}
response = self.client_user.post(
self.new_url,
new_url,
post_data
)
@ -80,6 +89,118 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
self.assertIn(self.superuser, obj.users.all())
self.assertEqual(1, obj.users.count())
def test_checkability(self):
""" Tests that the intervention can only be checked if all required data has been added
Returns:
"""
check_url = reverse("intervention:check", args=(self.intervention.id,))
post_data = {
"checked_intervention": True,
"checked_comps": True,
}
# First of all, the intervention should not be checked, yet
if self.intervention.checked:
self.intervention.checked.delete()
self.intervention.refresh_from_db()
# Make sure the dummy compensation is currently not linked to the intervention,
# since the system would check on it's quality as well (and it would fail)
self.intervention.compensations.set([])
# Run request with an incomplete intervention and missing user privileges --> expect to fail
self.client_user.post(check_url, post_data)
# We expect that the intervention is still not checked now
self.intervention.refresh_from_db()
self.assertIsNone(self.intervention.checked)
# Now give the user the required privileges by adding to the registration office group
group = self.groups.get(name=ZB_GROUP)
self.superuser.groups.add(group)
# Now fill in the missing data, so the intervention is 'valid' for checking
self.intervention = self.fill_out_intervention(self.intervention)
# Then add a dummy payment, so we pass the quality check (Checks whether any kind of valid compensation exists)
payment = Payment.objects.create(amount=10.00, due_on=None, comment="No due date because test")
self.intervention.payments.add(payment)
# Run request again
self.client_user.post(check_url, post_data)
# Update intervention from db
self.intervention.refresh_from_db()
# We expect the intervention to be checked now and contains the proper data
# Attention: We check the timestamp only on the date, not the time, since the microseconds delay would result
# in an unwanted assertion error
checked = self.intervention.checked
self.assertIsNotNone(checked)
self.assertEqual(self.superuser, checked.user)
self.assertEqual(datetime.date.today(), checked.timestamp.date())
self.assertEqual(UserAction.CHECKED, checked.action)
# Expect the user action now to live in the log
self.assertIn(checked, self.intervention.log.all())
def test_recordability(self):
""" Tests that the intervention can only be recorded if all required data has been added
Returns:
"""
record_url = reverse("intervention:record", args=(self.intervention.id,))
post_data = {
"confirm": True,
}
# Make sure the dummy compensation is currently not linked to the intervention,
# since we would check on it's quality as well then
self.intervention.compensations.set([])
# First of all, the intervention should not be recorded, yet
if self.intervention.recorded:
self.intervention.recorded.delete()
self.intervention.refresh_from_db()
# Run request with an incomplete intervention and missing user privileges --> expect to fail
self.client_user.post(record_url, post_data)
# We expect that the intervention is still not recorded now
self.intervention.refresh_from_db()
self.assertIsNone(self.intervention.recorded)
# Now give the user the required privileges by adding to the ETS group
group = self.groups.get(name=ETS_GROUP)
self.superuser.groups.add(group)
# Now fill in the missing data, so the intervention is 'valid' for recording
self.intervention = self.fill_out_intervention(self.intervention)
# Then add a dummy payment, so we pass the quality check (Checks whether any kind of valid compensation exists)
payment = Payment.objects.create(amount=10.00, due_on=None, comment="No due date because test")
self.intervention.payments.add(payment)
# Run request again
self.client_user.post(record_url, post_data)
# Update intervention from db
self.intervention.refresh_from_db()
# We expect the intervention to be recorded now and contains the proper data
# Attention: We check the timestamp only on the date, not the time, since the microseconds delay would result
# in an unwanted assertion error
self.assertIsNotNone(self.intervention.recorded)
self.assertEqual(self.superuser, self.intervention.recorded.user)
self.assertEqual(datetime.date.today(), self.intervention.recorded.timestamp.date())
self.assertEqual(UserAction.RECORDED, self.intervention.recorded.action)
# Expect the user action now to live in the log
self.assertIn(self.intervention.recorded, self.intervention.log.all())
def subtest_add_payment(self):
""" Subroutine for 'normal' payment tests
@ -89,9 +210,9 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
Returns:
"""
## Attention: Despite the fact, this url refers to a compensation app route, we test it here for the interventions.
## Reason: A payment is some kind of compensation for an intervention. Therefore it lives inside the compensation app.
## BUT: Payments are added on the intervention detail page. Therefore it's part of a regular intervention workflow.
# Attention: Despite the fact, this url refers to a compensation app route, we test it here for the interventions.
# Reason: A payment is some kind of compensation for an intervention. Therefore it lives inside the compensation app.
# 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,))
# Make sure there are no payments on the intervention, yet

@ -8,7 +8,7 @@ Created on: 30.11.20
from django.urls import path
from intervention.views import index_view, new_view, detail_view, edit_view, remove_view, new_document_view, share_view, \
create_share_view, remove_revocation_view, new_revocation_view, run_check_view, log_view, new_deduction_view, \
create_share_view, remove_revocation_view, new_revocation_view, check_view, log_view, new_deduction_view, \
record_view, remove_document_view, get_document_view, get_revocation_view, new_id_view, report_view
app_name = "intervention"
@ -22,7 +22,7 @@ urlpatterns = [
path('<id>/remove', remove_view, name='remove'),
path('<id>/share/<token>', share_view, name='share'),
path('<id>/share', create_share_view, name='share-create'),
path('<id>/check', run_check_view, name='run-check'),
path('<id>/check', check_view, name='check'),
path('<id>/record', record_view, name='record'),
path('<id>/report', report_view, name='report'),

@ -5,7 +5,7 @@ from django.shortcuts import render
from intervention.forms.forms import NewInterventionForm, EditInterventionForm
from intervention.forms.modalForms import ShareInterventionModalForm, NewRevocationModalForm, \
RunCheckModalForm, NewDeductionModalForm
CheckModalForm, NewDeductionModalForm
from intervention.models import Intervention, Revocation, InterventionDocument, RevocationDocument
from intervention.tables import InterventionTable
from konova.contexts import BaseContext
@ -413,7 +413,7 @@ def create_share_view(request: HttpRequest, id: str):
@login_required
@registration_office_group_required
@shared_access_required(Intervention, "id")
def run_check_view(request: HttpRequest, id: str):
def check_view(request: HttpRequest, id: str):
""" Renders check form for an intervention
Args:
@ -424,7 +424,7 @@ def run_check_view(request: HttpRequest, id: str):
"""
intervention = get_object_or_404(Intervention, id=id)
form = RunCheckModalForm(request.POST or None, instance=intervention, user=request.user)
form = CheckModalForm(request.POST or None, instance=intervention, user=request.user)
return form.process_request(
request,
msg_success=_("Check performed"),

@ -5,13 +5,15 @@ Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 26.10.21
"""
from abc import abstractmethod
import datetime
from django.contrib.auth.models import User, Group
from django.contrib.gis.geos import MultiPolygon, Polygon
from django.core.exceptions import ObjectDoesNotExist
from django.test import TestCase, Client
from django.urls import reverse
from codelist.models import KonovaCode
from compensation.models import Compensation, CompensationState, CompensationAction, EcoAccount
from intervention.models import LegalData, ResponsibilityData, Intervention
from konova.management.commands.setup_data import GROUPS_DATA
@ -32,17 +34,22 @@ class BaseTestCase(TestCase):
eco_account = None
comp_state = None
comp_action = None
codes = None
superuser_pw = "root"
user_pw = "root"
class Meta:
abstract = True
@classmethod
def setUpTestData(cls):
cls.create_users()
cls.create_groups()
cls.create_dummy_intervention()
cls.create_dummy_compensation()
cls.create_dummy_eco_account()
cls.intervention = cls.create_dummy_intervention()
cls.compensation = cls.create_dummy_compensation()
cls.eco_account = cls.create_dummy_eco_account()
cls.codes = cls.create_dummy_codes()
@classmethod
def create_users(cls):
@ -69,9 +76,6 @@ class BaseTestCase(TestCase):
)
cls.groups = Group.objects.all()
class Meta:
abstract = True
@classmethod
def create_dummy_intervention(cls):
""" Creates an intervention which can be used for tests
@ -91,7 +95,7 @@ class BaseTestCase(TestCase):
responsibility_data = ResponsibilityData.objects.create()
geometry = Geometry.objects.create()
# Finally create main object, holding the other objects
cls.intervention = Intervention.objects.create(
intervention = Intervention.objects.create(
identifier="TEST",
title="Test_title",
responsible=responsibility_data,
@ -100,8 +104,8 @@ class BaseTestCase(TestCase):
geometry=geometry,
comment="Test",
)
cls.intervention.generate_access_token(make_unique=True)
return cls.intervention
intervention.generate_access_token(make_unique=True)
return intervention
@classmethod
def create_dummy_compensation(cls):
@ -111,7 +115,7 @@ class BaseTestCase(TestCase):
"""
if cls.intervention is None:
cls.create_dummy_intervention()
cls.intervention = cls.create_dummy_intervention()
# Create dummy data
# Create log entry
action = UserActionLogEntry.objects.create(
@ -120,7 +124,7 @@ class BaseTestCase(TestCase):
)
geometry = Geometry.objects.create()
# Finally create main object, holding the other objects
cls.compensation = Compensation.objects.create(
compensation = Compensation.objects.create(
identifier="TEST",
title="Test_title",
intervention=cls.intervention,
@ -128,8 +132,7 @@ class BaseTestCase(TestCase):
geometry=geometry,
comment="Test",
)
cls.intervention.generate_access_token(make_unique=True)
return cls.compensation
return compensation
@classmethod
def create_dummy_eco_account(cls):
@ -149,7 +152,7 @@ class BaseTestCase(TestCase):
lega_data = LegalData.objects.create()
responsible_data = ResponsibilityData.objects.create()
# Finally create main object, holding the other objects
cls.eco_account = EcoAccount.objects.create(
eco_account = EcoAccount.objects.create(
identifier="TEST",
title="Test_title",
legal=lega_data,
@ -158,7 +161,7 @@ class BaseTestCase(TestCase):
geometry=geometry,
comment="Test",
)
return cls.eco_account
return eco_account
@classmethod
def create_dummy_states(cls):
@ -185,6 +188,47 @@ class BaseTestCase(TestCase):
)
return cls.comp_action
@classmethod
def create_dummy_codes(cls):
""" Creates some dummy KonovaCodes which can be used for testing
Returns:
"""
codes = KonovaCode.objects.bulk_create([
KonovaCode(id=1, is_selectable=True, long_name="Test1"),
KonovaCode(id=2, is_selectable=True, long_name="Test2"),
KonovaCode(id=3, is_selectable=True, long_name="Test3"),
KonovaCode(id=4, is_selectable=True, long_name="Test4"),
])
return codes
@staticmethod
def fill_out_intervention(intervention: Intervention) -> Intervention:
""" Adds all required (dummy) data to an intervention
Args:
intervention (Intervention): The intervention which shall be filled out
Returns:
intervention (Intervention): The modified intervention
"""
intervention.responsible.registration_office = KonovaCode.objects.get(id=1)
intervention.responsible.conservation_office = KonovaCode.objects.get(id=2)
intervention.responsible.registration_file_number = "test"
intervention.responsible.conservation_file_number = "test"
intervention.responsible.handler = "handler"
intervention.responsible.save()
intervention.legal.registration_date = datetime.date.fromisoformat("1970-01-01")
intervention.legal.binding_date = datetime.date.fromisoformat("1970-01-01")
intervention.legal.process_type = KonovaCode.objects.get(id=3)
intervention.legal.save()
intervention.legal.laws.set([KonovaCode.objects.get(id=(4))])
intervention.geometry.geom = MultiPolygon(Polygon.from_bbox([-4.526367, 18.354526, -1.801758, 20.591652]))
intervention.geometry.save()
intervention.save()
return intervention
class BaseViewTestCase(BaseTestCase):
""" Wraps basic test functionality, reusable for every specialized ViewTestCase
@ -348,6 +392,9 @@ class BaseWorkflowTestCase(BaseTestCase):
client_user = None
client_anon = None
class Meta:
abstract = True
@classmethod
def setUpTestData(cls):
super().setUpTestData()

Loading…
Cancel
Save