Unit test user app

* adds unit test for User model and forms
* refactors functions from user_checks.py into User class and drops user_checks.py
This commit is contained in:
mpeltriaux 2023-09-13 09:49:40 +02:00
parent 21db8227f8
commit d9046eb2b9
14 changed files with 263 additions and 70 deletions

View File

@ -4,7 +4,6 @@ from django.urls import reverse
from konova.settings import DEFAULT_GROUP
from konova.tests.test_views import BaseTestCase
from konova.utils.user_checks import is_default_group_only
class BaseAPIV1TestCase(BaseTestCase):
@ -138,7 +137,7 @@ class APIV1SharingTestCase(BaseAPIV1TestCase):
# Give the user only default group rights
default_group = self.groups.get(name=DEFAULT_GROUP)
self.superuser.groups.set([default_group])
self.assertTrue(is_default_group_only(self.superuser))
self.assertTrue(self.superuser.is_default_group_only())
# Add only him as shared_users an object
self.intervention.users.set([self.superuser])

View File

@ -18,7 +18,6 @@ from compensation.models import EcoAccount
from ema.models import Ema
from intervention.models import Intervention
from konova.utils.message_templates import DATA_UNSHARED
from konova.utils.user_checks import is_default_group_only
from user.models import User, Team
@ -321,7 +320,7 @@ class AbstractModelShareAPIView(AbstractAPIView):
for team_name in new_teams:
new_teams_objs.append(Team.objects.get(name=team_name))
if is_default_group_only(self.user):
if self.user.is_default_group_only():
# Default only users are not allowed to remove other users from having access. They can only add new ones!
new_users_to_be_added = User.objects.filter(
username__in=new_users

View File

@ -10,8 +10,6 @@ from django.db import models
from intervention.models import Intervention
from konova.models import BaseResource
from konova.utils.message_templates import PAYMENT_REMOVED
from user.models import UserActionLogEntry
class Payment(BaseResource):

View File

@ -27,7 +27,6 @@ from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE, DATA_CHECKED_PREVIOUSLY_TEMPLATE, \
RECORDED_BLOCKS_EDIT, CHECK_STATE_RESET, FORM_INVALID, PARAMS_INVALID, IDENTIFIER_REPLACED, \
COMPENSATION_ADDED_TEMPLATE, DO_NOT_FORGET_TO_SHARE, GEOMETRY_SIMPLIFIED
from konova.utils.user_checks import in_group
@login_required
@ -265,9 +264,9 @@ def detail_view(request: HttpRequest, id: str):
"sum_before_states": sum_before_states,
"sum_after_states": sum_after_states,
"diff_states": diff_states,
"is_default_member": in_group(_user, DEFAULT_GROUP),
"is_zb_member": in_group(_user, ZB_GROUP),
"is_ets_member": in_group(_user, ETS_GROUP),
"is_default_member": _user.in_group(DEFAULT_GROUP),
"is_zb_member": _user.in_group(ZB_GROUP),
"is_ets_member": _user.in_group(ETS_GROUP),
"LANIS_LINK": comp.get_LANIS_link(),
TAB_TITLE_IDENTIFIER: f"{comp.identifier} - {comp.title}",
"has_finished_deadlines": comp.get_finished_deadlines().exists(),

View File

@ -23,7 +23,6 @@ from konova.settings import ETS_GROUP, DEFAULT_GROUP, ZB_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import CANCEL_ACC_RECORDED_OR_DEDUCTED, RECORDED_BLOCKS_EDIT, FORM_INVALID, \
IDENTIFIER_REPLACED, DO_NOT_FORGET_TO_SHARE, GEOMETRY_SIMPLIFIED
from konova.utils.user_checks import in_group
@login_required
@ -244,9 +243,9 @@ def detail_view(request: HttpRequest, id: str):
"diff_states": diff_states,
"available": available_relative,
"available_total": available_total,
"is_default_member": in_group(_user, DEFAULT_GROUP),
"is_zb_member": in_group(_user, ZB_GROUP),
"is_ets_member": in_group(_user, ETS_GROUP),
"is_default_member": _user.in_group(DEFAULT_GROUP),
"is_zb_member": _user.in_group(ZB_GROUP),
"is_ets_member": _user.in_group(ETS_GROUP),
"LANIS_LINK": acc.get_LANIS_link(),
"deductions": deductions,
"actions": actions,
@ -277,7 +276,7 @@ def remove_view(request: HttpRequest, id: str):
# default group user
if acc.recorded is not None or acc.deductions.exists():
user = request.user
if not in_group(user, ETS_GROUP):
if not user.in_group(ETS_GROUP):
messages.info(request, CANCEL_ACC_RECORDED_OR_DEDUCTED)
return redirect("compensation:acc:detail", id=id)

View File

@ -24,7 +24,6 @@ from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import RECORDED_BLOCKS_EDIT, IDENTIFIER_REPLACED, FORM_INVALID, \
DO_NOT_FORGET_TO_SHARE, GEOMETRY_SIMPLIFIED
from konova.utils.user_checks import in_group
@login_required
@ -172,9 +171,9 @@ def detail_view(request: HttpRequest, id: str):
"sum_before_states": sum_before_states,
"sum_after_states": sum_after_states,
"diff_states": diff_states,
"is_default_member": in_group(_user, DEFAULT_GROUP),
"is_zb_member": in_group(_user, ZB_GROUP),
"is_ets_member": in_group(_user, ETS_GROUP),
"is_default_member": _user.in_group(DEFAULT_GROUP),
"is_zb_member": _user.in_group(ZB_GROUP),
"is_ets_member": _user.in_group(ETS_GROUP),
"LANIS_LINK": ema.get_LANIS_link(),
TAB_TITLE_IDENTIFIER: f"{ema.identifier} - {ema.title}",
"has_finished_deadlines": ema.get_finished_deadlines().exists(),

View File

@ -12,7 +12,6 @@ from django.utils.translation import gettext_lazy as _
from intervention.inputs import TextToClipboardInput
from konova.forms.modals import BaseModalForm
from konova.utils.message_templates import ENTRY_REMOVE_MISSING_PERMISSION
from konova.utils.user_checks import is_default_group_only
from user.models import Team, User
@ -80,7 +79,7 @@ class ShareModalForm(BaseModalForm):
teams = self.cleaned_data.get("teams", Team.objects.none())
_is_valid = True
if is_default_group_only(self.user):
if self.user.is_default_group_only():
shared_users = self.instance.shared_users
shared_teams = self.instance.shared_teams

View File

@ -23,7 +23,6 @@ from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import DATA_CHECKED_PREVIOUSLY_TEMPLATE, RECORDED_BLOCKS_EDIT, \
CHECK_STATE_RESET, FORM_INVALID, IDENTIFIER_REPLACED, DO_NOT_FORGET_TO_SHARE, GEOMETRY_SIMPLIFIED
from konova.utils.user_checks import in_group
@login_required
@ -186,9 +185,9 @@ def detail_view(request: HttpRequest, id: str):
"compensations": compensations,
"has_access": is_data_shared,
"geom_form": geom_form,
"is_default_member": in_group(_user, DEFAULT_GROUP),
"is_zb_member": in_group(_user, ZB_GROUP),
"is_ets_member": in_group(_user, ETS_GROUP),
"is_default_member": _user.in_group(DEFAULT_GROUP),
"is_zb_member": _user.in_group(ZB_GROUP),
"is_ets_member": _user.in_group(ETS_GROUP),
"LANIS_LINK": intervention.get_LANIS_link(),
"has_payment_without_document": has_payment_without_document,
TAB_TITLE_IDENTIFIER: f"{intervention.identifier} - {intervention.title}",

View File

@ -640,12 +640,11 @@ class ShareableObjectMixin(models.Model):
Returns:
"""
from konova.utils.user_checks import is_default_group_only
users = self.shared_users
cleaned_users = []
default_users = []
for user in users:
if not is_default_group_only(user):
if not user.is_default_group_only():
cleaned_users.append(user)
else:
default_users.append(user)

View File

@ -6,12 +6,12 @@ Created on: 08.09.23
"""
from django.test import RequestFactory
from django.utils.timezone import now
from intervention.forms.modals.share import ShareModalForm
from konova.models import DeadlineType
from konova.models import DeadlineType, Resubmission
from konova.settings import ZB_GROUP
from konova.tests.test_views import BaseTestCase
from konova.utils.user_checks import is_default_group_only
from user.models import UserAction
@ -171,8 +171,8 @@ class ShareableObjectMixinTestCase(BaseTestCase):
self.intervention.share_with_user(self.user)
self.intervention.share_with_user(self.superuser)
self.assertTrue(is_default_group_only(self.user))
self.assertFalse(is_default_group_only(self.superuser))
self.assertTrue(self.user.is_default_group_only())
self.assertFalse(self.superuser.is_default_group_only())
self.assertTrue(self.intervention.is_shared_with(self.user))
self.assertTrue(self.intervention.is_shared_with(self.superuser))
@ -180,3 +180,22 @@ class ShareableObjectMixinTestCase(BaseTestCase):
self.intervention.unshare_with_default_users()
self.assertFalse(self.intervention.is_shared_with(self.user))
self.assertTrue(self.intervention.is_shared_with(self.superuser))
class ResubmissionTestCase(BaseTestCase):
def test_send_resubmission_mail(self):
resubmission = Resubmission.objects.create(
user=self.user,
resubmit_on=now().date(),
comment="Test",
)
self.intervention.resubmissions.add(resubmission)
self.assertFalse(resubmission.resubmission_sent)
resubmission.send_resubmission_mail(
self.intervention.identifier,
[
"Test_municipal_1"
],
)
self.assertTrue(resubmission.resubmission_sent)

View File

@ -1,37 +0,0 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 02.07.21
"""
from user.models import User
from konova.settings import ETS_GROUP, ZB_GROUP
def in_group(user: User, group: str) -> bool:
""" Checks if the user is part of a group
Args:
user (User): The user object
group (str): The group's name
Returns:
bool
"""
return user.groups.filter(
name=group
)
def is_default_group_only(user: User) -> bool:
""" Checks if the user is only part of the default group
Args:
user (User): The user object
Returns:
bool
"""
return not in_group(user, ZB_GROUP) and not in_group(user, ETS_GROUP)

View File

@ -60,6 +60,29 @@ class User(AbstractUser):
name=ETS_GROUP
).exists()
def is_default_group_only(self) -> bool:
""" Checks if the user is only part of the default group
Args:
Returns:
bool
"""
return not self.in_group(ZB_GROUP) and not self.in_group(ETS_GROUP)
def in_group(self, group: str) -> bool:
""" Checks if the user is part of a group
Args:
group (str): The group's name
Returns:
bool
"""
return self.groups.filter(
name=group
)
def send_mail_shared_access_removed(self, obj_identifier, obj_title, municipals_names):
""" Sends a mail to the user in case of removed shared access

View File

@ -5,13 +5,16 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 12.09.23
"""
from django.core.exceptions import ObjectDoesNotExist
from django.test import RequestFactory
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from api.models import APIUserToken
from konova.tests.test_views import BaseTestCase
from user.forms.modals.team import NewTeamModalForm, EditTeamModalForm
from user.models import Team
from user.forms.modals.team import NewTeamModalForm, EditTeamModalForm, RemoveTeamModalForm, LeaveTeamModalForm
from user.forms.user import UserNotificationForm, UserAPITokenForm
from user.models import Team, UserAction, UserNotification
class NewTeamModalFormTestCase(BaseTestCase):
@ -147,3 +150,137 @@ class EditTeamModalFormTestCase(NewTeamModalFormTestCase):
self.assertEqual(obj.admins.count(), 1)
self.assertEqual(obj.users.count(), 2)
class RemoveTeamModalFormTestCase(BaseTestCase):
def setUp(self) -> None:
super().setUp()
self.request = RequestFactory().request()
self.request.user = self.user
def test_init(self):
form = RemoveTeamModalForm(
request=self.request,
instance=self.team
)
self.assertEqual(form.form_caption, str(_("ATTENTION!\n\nRemoving the team means all members will lose their access to data, based on this team! \n\nAre you sure to remove this team?")))
self.assertEqual(form.user, self.request.user)
self.assertEqual(form.request, self.request)
def test_save(self):
data = {
"confirm": True
}
form = RemoveTeamModalForm(
data,
request=self.request,
instance=self.team
)
self.assertTrue(form.is_valid(), msg=form.errors)
form.save()
self.team.refresh_from_db()
self.assertIsNotNone(self.team.deleted)
self.assertEqual(self.team.deleted.user, self.request.user)
self.assertEqual(self.team.deleted.action, UserAction.DELETED)
class LeaveTeamModalFormTestCase(BaseTestCase):
def setUp(self) -> None:
super().setUp()
self.request = RequestFactory().request()
self.request.user = self.user
def test_init(self):
form = LeaveTeamModalForm(
request=self.request,
instance=self.team
)
self.assertEqual(form.form_title, str(_("Leave team")))
def test_save(self):
self.team.users.add(self.user)
data = {
"confirm": True,
}
form = LeaveTeamModalForm(
data,
request=self.request,
instance=self.team
)
self.assertTrue(form.is_valid(), msg=form.errors)
self.assertIn(self.request.user, self.team.users.all())
form.save()
self.assertNotIn(self.request.user, self.team.users.all())
class UserNotificationFormTestCase(BaseTestCase):
def setUp(self) -> None:
super().setUp()
if not UserNotification.objects.all().exists():
self.notifications = UserNotification.objects.bulk_create(
[
UserNotification(id="notification_1", name="notification_1", is_active=True),
UserNotification(id="notification_2", name="notification_2", is_active=True),
UserNotification(id="notification_3", name="notification_3", is_active=True),
UserNotification(id="notification_4", name="notification_4", is_active=True),
]
)
def test_init(self):
form = UserNotificationForm(
user=self.user
)
self.assertEqual(form.form_title, str(_("Edit notifications")))
self.assertEqual(form.form_caption, "")
self.assertEqual(form.action_url, reverse("user:notifications"))
self.assertEqual(form.cancel_redirect, reverse("user:index"))
def test_save(self):
selected_notification = UserNotification.objects.first()
data = {
"notifications": [selected_notification.id,]
}
form = UserNotificationForm(
data=data,
user=self.user
)
self.assertTrue(form.is_valid(), msg=form.errors)
self.assertEqual(self.user.notifications.count(), 0)
form.save()
self.assertEqual(self.user.notifications.count(), 1)
self.assertIn(selected_notification, self.user.notifications.all())
class UserAPITokenFormTestCase(BaseTestCase):
def test_init(self):
form = UserAPITokenForm(
instance=self.user
)
self.assertEqual(form.form_title, str(_("Create new token")))
self.assertEqual(form.form_caption, str(_("A new token needs to be validated by an administrator!")))
self.assertEqual(form.action_url, reverse("user:api-token"))
self.assertEqual(form.cancel_redirect, reverse("user:index"))
self.assertIsNone(form.fields["token"].initial)
self.assertTrue(form.fields["token"].widget.attrs["readonly"])
def test_save(self):
data = {
"token": APIUserToken().token
}
form = UserAPITokenForm(
data,
instance=self.user
)
self.assertTrue(form.is_valid(), msg=form.errors)
self.assertIsNone(self.user.api_token)
token = form.save()
self.assertEqual(self.user.api_token, token)
new_token = form.save()
self.assertEqual(self.user.api_token, new_token)
try:
token.refresh_from_db()
self.fail("Token should be deleted and not be fetchable anymore")
except ObjectDoesNotExist:
pass

View File

@ -0,0 +1,61 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 13.09.23
"""
from konova.settings import ZB_GROUP, DEFAULT_GROUP, ETS_GROUP
from konova.tests.test_views import BaseTestCase
from user.enums import UserNotificationEnum
from user.models import UserNotification
class UserTestCase(BaseTestCase):
def test_is_notification_setting_set(self):
notification = UserNotification.objects.create(
id=UserNotificationEnum.NOTIFY_ON_DEDUCTION_CHANGES.name,
name=UserNotificationEnum.NOTIFY_ON_DEDUCTION_CHANGES.value,
)
self.assertFalse(self.user.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_DEDUCTION_CHANGES))
self.user.notifications.add(notification)
self.assertTrue(self.user.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_DEDUCTION_CHANGES))
def test_is_group_member(self):
zb_group = self.groups.get(name=ZB_GROUP)
ets_group = self.groups.get(name=ETS_GROUP)
default_group = self.groups.get(name=DEFAULT_GROUP)
self.user.groups.set([])
self.assertFalse(self.user.is_zb_user())
self.assertFalse(self.user.is_ets_user())
self.assertFalse(self.user.is_default_user())
self.user.groups.add(zb_group)
self.assertTrue(self.user.is_zb_user())
self.user.groups.add(ets_group)
self.assertTrue(self.user.is_ets_user())
self.user.groups.add(default_group)
self.assertTrue(self.user.is_default_user())
def test_get_API_token(self):
self.assertIsNone(self.user.api_token)
token = self.user.get_API_token()
self.assertIsNotNone(self.user.api_token)
self.assertEqual(self.user.api_token, token)
# Make sure the same token is returned if command is called twice
token = self.user.get_API_token()
self.assertEqual(self.user.api_token, token)
def test_shared_teams_property(self):
shared_teams = self.user.shared_teams
self.assertEqual(shared_teams.count(), 0)
self.team.users.add(self.user)
shared_teams = self.user.shared_teams
self.assertEqual(shared_teams.count(), 1)
self.assertIn(self.team, shared_teams)