* adds EMA tests
* extends compensation tests
* fixes bugs detected by testing
* restructured tests for performance boost
pull/40/head
mpeltriaux 3 years ago
parent 107cbeadee
commit c221d00c28

@ -20,6 +20,8 @@ urlpatterns = [
path('<id>/remove', remove_view, name='acc-remove'),
path('<id>/state/new', state_new_view, name='acc-new-state'),
path('<id>/action/new', action_new_view, name='acc-new-action'),
path('<id>/state/<state_id>/remove', state_remove_view, name='acc-state-remove'),
path('<id>/action/<action_id>/remove', action_remove_view, name='acc-action-remove'),
path('<id>/deadline/new', deadline_new_view, name="acc-new-deadline"),
path('<id>/share/<token>', share_view, name='share'),
path('<id>/share', create_share_view, name='share-create'),

@ -20,6 +20,8 @@ urlpatterns = [
path('<id>/remove', remove_view, name='remove'),
path('<id>/state/new', state_new_view, name='new-state'),
path('<id>/action/new', action_new_view, name='new-action'),
path('<id>/state/<state_id>/remove', state_remove_view, name='state-remove'),
path('<id>/action/<action_id>/remove', action_remove_view, name='action-remove'),
path('<id>/deadline/new', deadline_new_view, name="new-deadline"),
path('<id>/report', report_view, name='report'),
@ -28,10 +30,4 @@ urlpatterns = [
path('document/<doc_id>', get_document_view, name='get-doc'),
path('document/<doc_id>/remove/', remove_document_view, name='remove-doc'),
# Generic state routes
path('state/<id>/remove', state_remove_view, name='state-remove'),
# Generic action routes
path('action/<id>/remove', action_remove_view, name='action-remove'),
]

@ -50,7 +50,7 @@
<td class="align-middle">{{ action.comment|default_if_none:"" }}</td>
<td>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'compensation:action-remove' action.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove action' %}">
<button data-form-url="{% url 'compensation:action-remove' obj.id action.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove action' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}

@ -51,7 +51,7 @@
<td class="align-middle">{{ state.surface|floatformat:2 }} m²</td>
<td>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'compensation:state-remove' state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
<button data-form-url="{% url 'compensation:state-remove' obj.id state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}

@ -51,7 +51,7 @@
<td class="align-middle">{{ state.surface|floatformat:2 }} m²</td>
<td>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'compensation:state-remove' state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
<button data-form-url="{% url 'compensation:state-remove' obj.id state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}

@ -50,7 +50,7 @@
<td class="align-middle">{{ action.comment|default_if_none:"" }}</td>
<td>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'compensation:action-remove' action.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove action' %}">
<button data-form-url="{% url 'compensation:acc-action-remove' obj.id action.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove action' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}

@ -51,7 +51,7 @@
<td class="align-middle">{{ state.surface|floatformat:2 }} m²</td>
<td>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'compensation:state-remove' state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
<button data-form-url="{% url 'compensation:acc-state-remove' obj.id state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}

@ -51,7 +51,7 @@
<td class="align-middle">{{ state.surface|floatformat:2 }} m²</td>
<td>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'compensation:state-remove' state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
<button data-form-url="{% url 'compensation:acc-state-remove' obj.id state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}

@ -8,60 +8,76 @@ Created on: 27.10.21
from django.urls import reverse
from django.test import Client
from compensation.models import CompensationState, CompensationAction
from konova.settings import DEFAULT_GROUP
from konova.tests.test_views import BaseViewTestCase
class ViewTestCase(BaseViewTestCase):
class CompensationViewTestCase(BaseViewTestCase):
"""
These tests focus on proper returned views depending on the user's groups privileges and login status
"""
comp_state = None
comp_action = None
def setUp(self) -> None:
super().setUp()
self.create_dummy_states()
self.create_dummy_action()
@classmethod
def setUpTestData(cls) -> None:
super().setUpTestData()
state = cls.create_dummy_states()
cls.compensation.before_states.set([state])
cls.compensation.after_states.set([state])
# Prepare urls
self.index_url = reverse("compensation:index", args=())
self.new_url = reverse("compensation:new", args=(self.intervention.id,))
self.new_id_url = reverse("compensation:new-id", args=())
self.detail_url = reverse("compensation:detail", args=(self.compensation.id,))
self.log_url = reverse("compensation:log", args=(self.compensation.id,))
self.edit_url = reverse("compensation:edit", args=(self.compensation.id,))
self.remove_url = reverse("compensation:remove", args=(self.compensation.id,))
self.report_url = reverse("compensation:report", args=(self.compensation.id,))
self.state_new_url = reverse("compensation:new-state", args=(self.compensation.id,))
self.action_new_url = reverse("compensation:new-action", args=(self.compensation.id,))
self.deadline_new_url = reverse("compensation:new-deadline", args=(self.compensation.id,))
self.new_doc_url = reverse("compensation:new-doc", args=(self.compensation.id,))
self.state_remove_url = reverse("compensation:state-remove", args=(self.comp_state.id,))
self.action_remove_url = reverse("compensation:action-remove", args=(self.comp_action.id,))
def create_dummy_states(self):
""" Creates an intervention which can be used for tests
action = cls.create_dummy_action()
cls.compensation.actions.set([action])
Returns:
# Prepare urls
cls.index_url = reverse("compensation:index", args=())
cls.new_url = reverse("compensation:new", args=(cls.intervention.id,))
cls.new_id_url = reverse("compensation:new-id", args=())
cls.detail_url = reverse("compensation:detail", args=(cls.compensation.id,))
cls.log_url = reverse("compensation:log", args=(cls.compensation.id,))
cls.edit_url = reverse("compensation:edit", args=(cls.compensation.id,))
cls.remove_url = reverse("compensation:remove", args=(cls.compensation.id,))
cls.report_url = reverse("compensation:report", args=(cls.compensation.id,))
cls.state_new_url = reverse("compensation:new-state", args=(cls.compensation.id,))
cls.action_new_url = reverse("compensation:new-action", args=(cls.compensation.id,))
cls.deadline_new_url = reverse("compensation:new-deadline", args=(cls.compensation.id,))
cls.new_doc_url = reverse("compensation:new-doc", args=(cls.compensation.id,))
cls.state_remove_url = reverse("compensation:state-remove", args=(cls.compensation.id, cls.comp_state.id,))
cls.action_remove_url = reverse("compensation:action-remove", args=(cls.compensation.id, cls.comp_action.id,))
"""
self.comp_state = CompensationState.objects.create(
surface=10.00,
biotope_type=None,
)
self.compensation.before_states.set([self.comp_state])
self.compensation.after_states.set([self.comp_state])
def test_anonymous_user(self):
""" Check correct status code for all requests
def create_dummy_action(self):
""" Creates an intervention which can be used for tests
Assumption: User not logged in
Returns:
"""
self.comp_action = CompensationAction.objects.create(
amount=10
)
self.compensation.actions.set([self.comp_action])
client = Client()
success_urls = [
self.report_url,
]
fail_urls = [
self.index_url,
self.detail_url,
self.new_url,
self.new_id_url,
self.log_url,
self.edit_url,
self.remove_url,
self.state_new_url,
self.action_new_url,
self.deadline_new_url,
self.state_remove_url,
self.action_remove_url,
self.new_doc_url,
]
self.assert_url_success(client, success_urls)
self.assert_url_fail(client, fail_urls)
def test_logged_in_no_groups_shared(self):
""" Check correct status code for all requests
@ -111,6 +127,7 @@ class ViewTestCase(BaseViewTestCase):
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
self.superuser.groups.set([])
# Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state
self.intervention.users.set([])
# Since the user has no groups, it does not matter that data is unshared. There SHOULD not be any difference
@ -140,7 +157,8 @@ class ViewTestCase(BaseViewTestCase):
def test_logged_in_default_group_shared(self):
""" Check correct status code for all requests
Assumption: User logged in and has no groups and data is shared
Assumption: User logged in, is default group member and data is shared
--> Default group necessary since all base functionalities depend on this group membership
Returns:
@ -149,6 +167,7 @@ class ViewTestCase(BaseViewTestCase):
client.login(username=self.superuser.username, password=self.superuser_pw)
group = self.groups.get(name=DEFAULT_GROUP)
self.superuser.groups.set([group])
# Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state
self.intervention.users.set([self.superuser])
success_urls = [
@ -169,34 +188,39 @@ class ViewTestCase(BaseViewTestCase):
]
self.assert_url_success(client, success_urls)
def test_anonymous_user(self):
def test_logged_in_default_group_unshared(self):
""" Check correct status code for all requests
Assumption: User not logged in
Assumption: User logged in, is default group member and data is NOT shared
--> Default group necessary since all base functionalities depend on this group membership
Returns:
"""
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
group = self.groups.get(name=DEFAULT_GROUP)
self.superuser.groups.set([group])
# Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state
self.intervention.users.set([])
success_urls = [
self.index_url,
self.detail_url,
self.report_url,
self.new_id_url,
]
fail_urls = [
self.index_url,
self.detail_url,
self.new_url,
self.new_id_url,
self.log_url,
self.edit_url,
self.remove_url,
self.state_new_url,
self.action_new_url,
self.deadline_new_url,
self.state_remove_url,
self.action_remove_url,
self.new_doc_url,
self.log_url,
self.remove_url,
]
self.assert_url_success(client, success_urls)
self.assert_url_fail(client, fail_urls)
self.assert_url_success(client, success_urls)

@ -110,6 +110,7 @@ def new_id_view(request: HttpRequest):
@login_required
@default_group_required
@shared_access_required(Compensation, "id")
def edit_view(request: HttpRequest, id: str):
"""
Renders a view for editing compensations
@ -377,17 +378,19 @@ def deadline_new_view(request: HttpRequest, id: str):
@login_required
@default_group_required
def state_remove_view(request: HttpRequest, id: str):
@shared_access_required(Compensation, "id")
def state_remove_view(request: HttpRequest, id: str, state_id: str):
""" Renders a form for removing a compensation state
Args:
request (HttpRequest): The incoming request
id (str): The state's id
id (str): The compensation's id
state_id (str): The state's id
Returns:
"""
state = get_object_or_404(CompensationState, id=id)
state = get_object_or_404(CompensationState, id=state_id)
form = RemoveModalForm(request.POST or None, instance=state, user=request.user)
return form.process_request(
request,
@ -397,17 +400,19 @@ def state_remove_view(request: HttpRequest, id: str):
@login_required
@default_group_required
def action_remove_view(request: HttpRequest, id: str):
@shared_access_required(Compensation, "id")
def action_remove_view(request: HttpRequest, id: str, action_id: str):
""" Renders a form for removing a compensation action
Args:
request (HttpRequest): The incoming request
id (str): The compensation's id
id (str): The action's id
Returns:
"""
action = get_object_or_404(CompensationAction, id=id)
action = get_object_or_404(CompensationAction, id=action_id)
form = RemoveModalForm(request.POST or None, instance=action, user=request.user)
return form.process_request(
request,
@ -415,7 +420,7 @@ def action_remove_view(request: HttpRequest, id: str):
)
def report_view(request:HttpRequest, id: str):
def report_view(request: HttpRequest, id: str):
""" Renders the public report view
Args:

@ -16,11 +16,12 @@ from django.shortcuts import render, get_object_or_404, redirect
from compensation.forms.forms import NewEcoAccountForm, EditEcoAccountForm
from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm
from compensation.models import EcoAccount, EcoAccountDocument
from compensation.models import EcoAccount, EcoAccountDocument, CompensationState, CompensationAction
from compensation.tables import EcoAccountTable
from intervention.forms.modalForms import NewDeductionModalForm, ShareInterventionModalForm
from konova.contexts import BaseContext
from konova.decorators import any_group_check, default_group_required, conservation_office_group_required
from konova.decorators import any_group_check, default_group_required, conservation_office_group_required, \
shared_access_required
from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm, RecordModalForm
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.utils.documents import get_document, remove_document
@ -99,6 +100,7 @@ def new_view(request: HttpRequest):
@login_required
@default_group_required
def new_id_view(request: HttpRequest):
""" JSON endpoint
@ -353,6 +355,50 @@ def action_new_view(request: HttpRequest, id: str):
)
@login_required
@default_group_required
@shared_access_required(EcoAccount, "id")
def state_remove_view(request: HttpRequest, id: str, state_id: str):
""" Renders a form for removing a compensation state
Args:
request (HttpRequest): The incoming request
id (str): The compensation's id
state_id (str): The state's id
Returns:
"""
state = get_object_or_404(CompensationState, id=state_id)
form = RemoveModalForm(request.POST or None, instance=state, user=request.user)
return form.process_request(
request,
msg_success=_("State removed")
)
@login_required
@default_group_required
@shared_access_required(EcoAccount, "id")
def action_remove_view(request: HttpRequest, id: str, action_id: str):
""" Renders a form for removing a compensation action
Args:
request (HttpRequest): The incoming request
id (str): The compensation's id
id (str): The action's id
Returns:
"""
action = get_object_or_404(CompensationAction, id=action_id)
form = RemoveModalForm(request.POST or None, instance=action, user=request.user)
return form.process_request(
request,
msg_success=_("Action removed")
)
@login_required
def deadline_new_view(request: HttpRequest, id: str):
""" Renders a form for adding new states for an eco account

@ -48,7 +48,7 @@
<td class="align-middle">{{ action.comment|default_if_none:"" }}</td>
<td>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'ema:action-remove' action.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove action' %}">
<button data-form-url="{% url 'ema:action-remove' obj.id action.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove action' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}

@ -49,7 +49,7 @@
<td class="align-middle">{{ state.surface|floatformat:2 }} m²</td>
<td>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'ema:state-remove' state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
<button data-form-url="{% url 'ema:state-remove' obj.id state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}

@ -49,7 +49,7 @@
<td class="align-middle">{{ state.surface|floatformat:2 }} m²</td>
<td>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'ema:state-remove' state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
<button data-form-url="{% url 'ema:state-remove' obj.id state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}

@ -0,0 +1,7 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 26.10.21
"""

@ -0,0 +1,200 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 26.10.21
"""
from django.db.models import Q
from django.urls import reverse
from django.test.client import Client
from compensation.tests.test_views import CompensationViewTestCase
from ema.models import Ema
from intervention.models import ResponsibilityData
from konova.models import Geometry
from konova.settings import DEFAULT_GROUP, ETS_GROUP
from user.models import UserActionLogEntry, UserAction
class EmaViewTestCase(CompensationViewTestCase):
""" Test cases for EMA.
Since we inherit most tests functions from CompensationViewTestCase, we only need to add some EMA specific
test functions
"""
ema = None
@classmethod
def setUpTestData(cls) -> None:
super().setUpTestData()
# Create dummy data and related objects, like states or actions
cls.create_dummy_data()
state = cls.create_dummy_states()
action = cls.create_dummy_action()
cls.ema.before_states.set([state])
cls.ema.after_states.set([state])
cls.ema.actions.set([action])
# Prepare urls
cls.index_url = reverse("ema:index", args=())
cls.new_url = reverse("ema:new", args=())
cls.new_id_url = reverse("ema:new-id", args=())
cls.detail_url = reverse("ema:detail", args=(cls.ema.id,))
cls.log_url = reverse("ema:log", args=(cls.ema.id,))
cls.edit_url = reverse("ema:edit", args=(cls.ema.id,))
cls.remove_url = reverse("ema:remove", args=(cls.ema.id,))
cls.share_url = reverse("ema:share", args=(cls.ema.id, cls.ema.access_token,))
cls.share_create_url = reverse("ema:share-create", args=(cls.ema.id,))
cls.record_url = reverse("ema:record", args=(cls.ema.id,))
cls.report_url = reverse("ema:report", args=(cls.ema.id,))
cls.new_doc_url = reverse("ema:new-doc", args=(cls.ema.id,))
cls.state_new_url = reverse("ema:new-state", args=(cls.ema.id,))
cls.action_new_url = reverse("ema:new-action", args=(cls.ema.id,))
cls.deadline_new_url = reverse("ema:new-deadline", args=(cls.ema.id,))
cls.state_remove_url = reverse("ema:state-remove", args=(cls.ema.id, state.id,))
cls.action_remove_url = reverse("ema:action-remove", args=(cls.ema.id, action.id,))
@classmethod
def create_dummy_data(cls):
# Create dummy data
# Create log entry
action = UserActionLogEntry.objects.create(
user=cls.superuser,
action=UserAction.CREATED,
)
# Create responsible data object
responsibility_data = ResponsibilityData.objects.create()
geometry = Geometry.objects.create()
cls.ema = Ema.objects.create(
identifier="TEST",
title="Test_title",
created=action,
geometry=geometry,
responsible=responsibility_data,
comment="Test",
)
def test_logged_in_default_group_shared(self):
""" Check correct status code for all requests
OVERWRITES DEFAULT COMPENSATION TEST METHOD DUE TO SPECIFIC BEHAVIOUR OF EMAS
Assumption: User logged in, is default group member and data is shared
Normally default group would give access to all base functionalities. In case of EMAs we expect these
requests to fail, since a user must be part of the ets group as well, not only default.
Returns:
"""
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
group = self.groups.get(name=DEFAULT_GROUP)
self.superuser.groups.set([group])
# Sharing does not have any effect in here, since the default group will prohibit further functionality access
# to this user
self.ema.users.set([self.superuser])
success_urls = [
self.index_url,
self.detail_url,
self.report_url,
]
fail_urls = [
self.new_url,
self.new_id_url,
self.edit_url,
self.state_new_url,
self.action_new_url,
self.deadline_new_url,
self.state_remove_url,
self.action_remove_url,
self.new_doc_url,
self.log_url,
self.remove_url,
]
self.assert_url_fail(client, fail_urls)
self.assert_url_success(client, success_urls)
def test_logged_in_ets_group_shared(self):
""" Check correct status code for all requests
Assumption: User logged in, is conservation office group member and data is shared
For EMAs we expect a user to be ETS and default group member to have full access to all functionalities, normally
provided for default group members.
Returns:
"""
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
groups = self.groups.filter(Q(name=ETS_GROUP)|Q(name=DEFAULT_GROUP))
self.superuser.groups.set(groups)
# Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state
self.ema.users.set([self.superuser])
success_urls = [
self.index_url,
self.detail_url,
self.report_url,
self.new_url,
self.new_id_url,
self.edit_url,
self.state_new_url,
self.action_new_url,
self.deadline_new_url,
self.state_remove_url,
self.action_remove_url,
self.new_doc_url,
self.log_url,
self.remove_url,
]
self.assert_url_success(client, success_urls)
def test_logged_in_ets_group_unshared(self):
""" Check correct status code for all requests
Assumption: User logged in, is conservation office group member and data is NOT shared
For EMAs we expect a user to be ETS and default group member to have full access to all functionalities, normally
provided for default group members.
Returns:
"""
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
groups = self.groups.filter(Q(name=ETS_GROUP)|Q(name=DEFAULT_GROUP))
self.superuser.groups.set(groups)
# Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state
self.ema.users.set([])
success_urls = [
self.index_url,
self.detail_url,
self.report_url,
self.new_url,
self.new_id_url,
]
fail_urls = [
self.edit_url,
self.state_new_url,
self.action_new_url,
self.deadline_new_url,
self.state_remove_url,
self.action_remove_url,
self.new_doc_url,
self.log_url,
self.remove_url,
]
self.assert_url_success(client, success_urls)
self.assert_url_fail(client, fail_urls)
def test_logged_in_default_group_unshared(self):
# overwrite this test, since it's not relevant for EMA but is inherited by the superclass
pass

@ -21,6 +21,8 @@ urlpatterns = [
path('<id>/report', report_view, name='report'),
path('<id>/state/new', state_new_view, name='new-state'),
path('<id>/action/new', action_new_view, name='new-action'),
path('<id>/state/<state_id>/remove', state_remove_view, name='state-remove'),
path('<id>/action/<action_id>/remove', action_remove_view, name='action-remove'),
path('<id>/deadline/new', deadline_new_view, name="new-deadline"),
path('<id>/share/<token>', share_view, name='share'),
path('<id>/share', create_share_view, name='share-create'),
@ -31,9 +33,4 @@ urlpatterns = [
path('document/<doc_id>', get_document_view, name='get-doc'),
path('document/<doc_id>/remove/', remove_document_view, name='remove-doc'),
# Generic state routes
path('state/<id>/remove', state_remove_view, name='state-remove'),
# Generic action routes
path('action/<id>/remove', action_remove_view, name='action-remove'),
]

@ -6,13 +6,13 @@ from django.shortcuts import render, get_object_or_404, redirect
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
import compensation
from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm
from compensation.models import CompensationAction, CompensationState
from ema.forms import NewEmaForm, EditEmaForm
from ema.tables import EmaTable
from intervention.forms.modalForms import ShareInterventionModalForm
from konova.contexts import BaseContext
from konova.decorators import conservation_office_group_required, default_group_required
from konova.decorators import conservation_office_group_required, shared_access_required
from ema.models import Ema, EmaDocument
from konova.forms import RemoveModalForm, NewDocumentForm, SimpleGeomForm, RecordModalForm
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
@ -92,6 +92,7 @@ def new_view(request: HttpRequest):
@login_required
@conservation_office_group_required
def new_id_view(request: HttpRequest):
""" JSON endpoint
@ -159,6 +160,8 @@ def detail_view(request: HttpRequest, id: str):
@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def log_view(request: HttpRequest, id: str):
""" Renders a log view using modal
@ -183,6 +186,8 @@ def log_view(request: HttpRequest, id: str):
@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def edit_view(request: HttpRequest, id: str):
"""
Renders a view for editing compensations
@ -219,6 +224,8 @@ def edit_view(request: HttpRequest, id: str):
@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def remove_view(request: HttpRequest, id: str):
""" Renders a modal view for removing the EMA
@ -239,6 +246,8 @@ def remove_view(request: HttpRequest, id: str):
@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def record_view(request: HttpRequest, id: str):
""" Renders a modal view for recording the EMA
@ -259,6 +268,8 @@ def record_view(request: HttpRequest, id: str):
@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def state_new_view(request: HttpRequest, id: str):
""" Renders a form for adding new states for an EMA
@ -278,6 +289,8 @@ def state_new_view(request: HttpRequest, id: str):
@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def action_new_view(request: HttpRequest, id: str):
""" Renders a form for adding new actions for an EMA
@ -297,6 +310,8 @@ def action_new_view(request: HttpRequest, id: str):
@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def deadline_new_view(request: HttpRequest, id: str):
""" Renders a form for adding new states for an EMA
@ -316,6 +331,8 @@ def deadline_new_view(request: HttpRequest, id: str):
@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def document_new_view(request: HttpRequest, id: str):
""" Renders a form for uploading new documents
@ -334,6 +351,7 @@ def document_new_view(request: HttpRequest, id: str):
@login_required
@conservation_office_group_required
def get_document_view(request: HttpRequest, doc_id: str):
""" Returns the document as downloadable file
@ -360,6 +378,7 @@ def get_document_view(request: HttpRequest, doc_id: str):
@login_required
@conservation_office_group_required
def remove_document_view(request: HttpRequest, doc_id: str):
""" Removes the document from the database and file system
@ -380,37 +399,46 @@ def remove_document_view(request: HttpRequest, doc_id: str):
@login_required
def state_remove_view(request: HttpRequest, id: str):
@conservation_office_group_required
@shared_access_required(Ema, "id")
def state_remove_view(request: HttpRequest, id: str, state_id: str):
""" Renders a form for removing an EMA state
Args:
request (HttpRequest): The incoming request
id (str): The state's id
id (str): The ema id
state_id (str): The state's id
Returns:
"""
return compensation.views.compensation_views.state_remove_view(
state = get_object_or_404(CompensationState, id=state_id)
form = RemoveModalForm(request.POST or None, instance=state, user=request.user)
return form.process_request(
request,
id
msg_success=_("State removed")
)
@login_required
def action_remove_view(request: HttpRequest, id: str):
""" Renders a form for removing an EMA state
@conservation_office_group_required
@shared_access_required(Ema, "id")
def action_remove_view(request: HttpRequest, id: str, action_id: str):
""" Renders a form for removing an EMA action
Args:
request (HttpRequest): The incoming request
id (str): The state's id
id (str): The ema id
id (str): The action's id
Returns:
"""
# Reuses the route logic from compensation view
return compensation.views.compensation_views.action_remove_view(
action = get_object_or_404(CompensationAction, id=action_id)
form = RemoveModalForm(request.POST or None, instance=action, user=request.user)
return form.process_request(
request,
id
msg_success=_("Action removed")
)
@ -505,7 +533,8 @@ def share_view(request: HttpRequest, id: str, token: str):
@login_required
@default_group_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def create_share_view(request: HttpRequest, id: str):
""" Renders sharing form for an Ema

@ -10,62 +10,29 @@ from django.test import Client
from django.contrib.auth.models import Group
from django.urls import reverse
from intervention.models import Intervention, LegalData, ResponsibilityData
from konova.models import Geometry
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.tests.test_views import BaseViewTestCase
from user.models import UserActionLogEntry, UserAction
class ViewTestCase(BaseViewTestCase):
def setUp(self) -> None:
super().setUp()
class InterventionViewTestCase(BaseViewTestCase):
# Prepare urls
self.index_url = reverse("intervention:index", args=())
self.new_url = reverse("intervention:new", args=())
self.new_id_url = reverse("intervention:new-id", args=())
self.detail_url = reverse("intervention:detail", args=(self.intervention.id,))
self.log_url = reverse("intervention:log", args=(self.intervention.id,))
self.edit_url = reverse("intervention:edit", args=(self.intervention.id,))
self.remove_url = reverse("intervention:remove", args=(self.intervention.id,))
self.share_url = reverse("intervention:share", args=(self.intervention.id, self.intervention.access_token,))
self.share_create_url = reverse("intervention:share-create", args=(self.intervention.id,))
self.run_check_url = reverse("intervention:run-check", args=(self.intervention.id,))
self.record_url = reverse("intervention:record", args=(self.intervention.id,))
self.report_url = reverse("intervention:report", args=(self.intervention.id,))
def test_views_logged_in_no_groups(self):
""" Check correct status code for all requests
Assumption: User logged in but has no groups
Returns:
"""
# Login client
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
self.superuser.groups.set([])
success_urls = [
self.index_url,
self.report_url,
self.detail_url,
]
fail_urls = [
self.log_url,
self.new_id_url,
self.new_url,
self.edit_url,
self.remove_url,
self.share_url,
self.share_create_url,
self.run_check_url,
self.record_url,
]
@classmethod
def setUpTestData(cls) -> None:
super().setUpTestData()
self.assert_url_success(client, success_urls)
self.assert_url_fail(client, fail_urls)
# Prepare urls
cls.index_url = reverse("intervention:index", args=())
cls.new_url = reverse("intervention:new", args=())
cls.new_id_url = reverse("intervention:new-id", args=())
cls.detail_url = reverse("intervention:detail", args=(cls.intervention.id,))
cls.log_url = reverse("intervention:log", args=(cls.intervention.id,))
cls.edit_url = reverse("intervention:edit", args=(cls.intervention.id,))
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.record_url = reverse("intervention:record", args=(cls.intervention.id,))
cls.report_url = reverse("intervention:report", args=(cls.intervention.id,))
def test_views_anonymous_user(self):
""" Check correct status code for all requests
@ -102,6 +69,38 @@ class ViewTestCase(BaseViewTestCase):
response = client.get(url, follow=True)
self.assertEqual(response.redirect_chain[0], (f"{self.login_url}?next={url}", 302), msg=f"Failed for {url}. Redirect chain is {response.redirect_chain}")
def test_views_logged_in_no_groups(self):
""" Check correct status code for all requests
Assumption: User logged in but has no groups
Returns:
"""
# Login client
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
self.superuser.groups.set([])
success_urls = [
self.index_url,
self.report_url,
self.detail_url,
]
fail_urls = [
self.log_url,
self.new_id_url,
self.new_url,
self.edit_url,
self.remove_url,
self.share_url,
self.share_create_url,
self.run_check_url,
self.record_url,
]
self.assert_url_success(client, success_urls)
self.assert_url_fail(client, fail_urls)
def test_views_logged_in_default_group_shared(self):
""" Check correct status code for all requests
@ -194,7 +193,7 @@ class ViewTestCase(BaseViewTestCase):
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
# Add user to default group
# Add user to zb group
zb_group = self.groups.get(name=ZB_GROUP)
self.superuser.groups.set([zb_group])
self.intervention.users.set([self.superuser])
@ -234,7 +233,7 @@ class ViewTestCase(BaseViewTestCase):
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
# Add user to default group
# Add user to zb group
zb_group = self.groups.get(name=ZB_GROUP)
self.superuser.groups.set([zb_group])
self.intervention.users.set([])
@ -265,7 +264,7 @@ class ViewTestCase(BaseViewTestCase):
def test_views_logged_in_ets_group_shared(self):
""" Check correct status code for all requests
Assumption: User logged in and is registration office member and data is shared with
Assumption: User logged in and is conservation office member and data is shared with
Returns:
@ -274,7 +273,7 @@ class ViewTestCase(BaseViewTestCase):
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
# Add user to default group
# Add user to ets group
ets_group = Group.objects.get(name=ETS_GROUP)
self.superuser.groups.set([ets_group])
self.intervention.users.set([self.superuser])

@ -11,7 +11,7 @@ from django.contrib.auth.models import User, Group
from django.test import TestCase, Client
from django.urls import reverse
from compensation.models import Compensation
from compensation.models import Compensation, CompensationState, CompensationAction
from intervention.models import LegalData, ResponsibilityData, Intervention
from konova.management.commands.setup_data import GROUPS_DATA
from konova.models import Geometry
@ -30,33 +30,30 @@ class BaseTestCase(TestCase):
superuser_pw = "root"
user_pw = "root"
@abstractmethod
def setUp(self) -> None:
# To be implemented in the inheriting classes
raise NotImplementedError
def create_users(self):
@classmethod
def create_users(cls):
# Create superuser and regular user
self.superuser = User.objects.create_superuser(
cls.superuser = User.objects.create_superuser(
username="root",
email="root@root.com",
password=self.superuser_pw,
password=cls.superuser_pw,
)
self.user = User.objects.create_user(
cls.user = User.objects.create_user(
username="user1",
email="user@root.com",
password=self.user_pw
password=cls.user_pw
)
self.users = User.objects.all()
cls.users = User.objects.all()
def create_groups(self):
@classmethod
def create_groups(cls):
# Create groups
for group_data in GROUPS_DATA:
name = group_data.get("name")
Group.objects.get_or_create(
name=name,
)
self.groups = Group.objects.all()
cls.groups = Group.objects.all()
class Meta:
abstract = True
@ -69,13 +66,16 @@ class BaseViewTestCase(BaseTestCase):
login_url = None
intervention = None
compensation = None
comp_state = None
comp_action = None
def setUp(self) -> None:
self.create_users()
self.create_groups()
self.create_dummy_intervention()
self.create_dummy_compensation()
self.login_url = reverse("simple-sso-login")
@classmethod
def setUpTestData(cls) -> None:
cls.create_users()
cls.create_groups()
cls.create_dummy_intervention()
cls.create_dummy_compensation()
cls.login_url = reverse("simple-sso-login")
def assert_url_success(self, client: Client, urls: list):
""" Assert for all given urls a direct 200 response
@ -122,7 +122,8 @@ class BaseViewTestCase(BaseTestCase):
response = client.get(url)
self.assertEqual(response.status_code, 302, msg=f"Failed for {url}")
def create_dummy_intervention(self):
@classmethod
def create_dummy_intervention(cls):
""" Creates an intervention which can be used for tests
Returns:
@ -131,7 +132,7 @@ class BaseViewTestCase(BaseTestCase):
# Create dummy data
# Create log entry
action = UserActionLogEntry.objects.create(
user=self.superuser,
user=cls.superuser,
action=UserAction.CREATED,
)
# Create legal data object (without M2M laws first)
@ -140,7 +141,7 @@ class BaseViewTestCase(BaseTestCase):
responsibility_data = ResponsibilityData.objects.create()
geometry = Geometry.objects.create()
# Finally create main object, holding the other objects
self.intervention = Intervention.objects.create(
cls.intervention = Intervention.objects.create(
identifier="TEST",
title="Test_title",
responsible=responsibility_data,
@ -149,43 +150,70 @@ class BaseViewTestCase(BaseTestCase):
geometry=geometry,
comment="Test",
)
self.intervention.generate_access_token(make_unique=True)
cls.intervention.generate_access_token(make_unique=True)
def create_dummy_compensation(self):
@classmethod
def create_dummy_compensation(cls):
""" Creates an intervention which can be used for tests
Returns:
"""
if self.intervention is None:
self.create_dummy_intervention()
if cls.intervention is None:
cls.create_dummy_intervention()
# Create dummy data
# Create log entry
action = UserActionLogEntry.objects.create(
user=self.superuser,
user=cls.superuser,
action=UserAction.CREATED,
)
geometry = Geometry.objects.create()
# Finally create main object, holding the other objects
self.compensation = Compensation.objects.create(
cls.compensation = Compensation.objects.create(
identifier="TEST",
title="Test_title",
intervention=self.intervention,
intervention=cls.intervention,
created=action,
geometry=geometry,
comment="Test",
)
self.intervention.generate_access_token(make_unique=True)
cls.intervention.generate_access_token(make_unique=True)
@classmethod
def create_dummy_states(cls):
""" Creates an intervention which can be used for tests
Returns:
"""
cls.comp_state = CompensationState.objects.create(
surface=10.00,
biotope_type=None,
)
return cls.comp_state
@classmethod
def create_dummy_action(cls):
""" Creates an intervention which can be used for tests
Returns:
"""
cls.comp_action = CompensationAction.objects.create(
amount=10
)
return cls.comp_action
class KonovaViewTestCase(BaseViewTestCase):
""" Holds tests for all regular views, which are not app specific
"""
def setUp(self) -> None:
super().setUp()
@classmethod
def setUpTestData(cls) -> None:
super().setUpTestData()
self.home_url = reverse("home")
cls.home_url = reverse("home")
def test_views_logged_in_no_groups(self):
""" Check correct status code for all requests
@ -221,17 +249,18 @@ class KonovaViewTestCase(BaseViewTestCase):
class AutocompleteTestCase(BaseViewTestCase):
def setUp(self) -> None:
super().setUp()
self.atcmplt_accs = reverse("accounts-autocomplete")
self.atcmplt_interventions = reverse("interventions-autocomplete")
self.atcmplt_code_comp_action = reverse("codes-compensation-action-autocomplete")
self.atcmplt_code_comp_funding = reverse("codes-compensation-funding-autocomplete")
self.atcmplt_code_comp_biotope = reverse("codes-biotope-autocomplete")
self.atcmplt_code_comp_law = reverse("codes-law-autocomplete")
self.atcmplt_code_comp_process = reverse("codes-process-type-autocomplete")
self.atcmplt_code_comp_reg_off = reverse("codes-registration-office-autocomplete")
self.atcmplt_code_comp_cons_off = reverse("codes-conservation-office-autocomplete")
@classmethod
def setUpTestData(cls) -> None:
super().setUpTestData()
cls.atcmplt_accs = reverse("accounts-autocomplete")
cls.atcmplt_interventions = reverse("interventions-autocomplete")
cls.atcmplt_code_comp_action = reverse("codes-compensation-action-autocomplete")
cls.atcmplt_code_comp_funding = reverse("codes-compensation-funding-autocomplete")
cls.atcmplt_code_comp_biotope = reverse("codes-biotope-autocomplete")
cls.atcmplt_code_comp_law = reverse("codes-law-autocomplete")
cls.atcmplt_code_comp_process = reverse("codes-process-type-autocomplete")
cls.atcmplt_code_comp_reg_off = reverse("codes-registration-office-autocomplete")
cls.atcmplt_code_comp_cons_off = reverse("codes-conservation-office-autocomplete")
def _test_views_anonymous_user(self):
# ATTENTION: As of the current state of django-autocomplete-light, there is no way to check on authenticated

Loading…
Cancel
Save