konova/konova/tests/test_views.py
mpeltriaux 632fb0f48a #49 Calculation implementation
* implements update routine for Geometry model
* reorganizes fields of Parcel and District
* adds tests
* simplifies usage of ParcelWFSFetcher
2022-01-04 15:59:53 +01:00

493 lines
16 KiB
Python

"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 26.10.21
"""
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 Legal, Responsibility, Intervention
from konova.management.commands.setup_data import GROUPS_DATA
from konova.models import Geometry
from konova.settings import DEFAULT_GROUP
from konova.utils.generators import generate_random_string
from user.models import UserActionLogEntry
class BaseTestCase(TestCase):
""" Provides reusable functionality for specialized test cases
"""
users = None
groups = None
superuser = None
user = None
intervention = None
compensation = None
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.intervention = cls.create_dummy_intervention()
cls.compensation = cls.create_dummy_compensation()
cls.eco_account = cls.create_dummy_eco_account()
cls.create_dummy_states()
cls.create_dummy_action()
cls.codes = cls.create_dummy_codes()
@classmethod
def create_users(cls):
# Create superuser and regular user
cls.superuser = User.objects.create_superuser(
username="root",
email="root@root.com",
password=cls.superuser_pw,
)
cls.user = User.objects.create_user(
username="user1",
email="user@root.com",
password=cls.user_pw
)
cls.users = User.objects.all()
@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,
)
cls.groups = Group.objects.all()
@staticmethod
def create_dummy_string(prefix: str = ""):
""" Create
Returns:
"""
return f"{prefix}{generate_random_string(3, True)}"
@classmethod
def create_dummy_intervention(cls):
""" Creates an intervention which can be used for tests
Returns:
"""
# Create dummy data
# Create log entry
action = UserActionLogEntry.get_created_action(cls.superuser)
# Create legal data object (without M2M laws first)
legal_data = Legal.objects.create()
# Create responsible data object
responsibility_data = Responsibility.objects.create()
geometry = Geometry.objects.create()
# Finally create main object, holding the other objects
intervention = Intervention.objects.create(
identifier="TEST",
title="Test_title",
responsible=responsibility_data,
legal=legal_data,
created=action,
geometry=geometry,
comment="Test",
)
intervention.generate_access_token(make_unique=True)
return intervention
@classmethod
def create_dummy_compensation(cls):
""" Creates a compensation which can be used for tests
Returns:
"""
if cls.intervention is None:
cls.intervention = cls.create_dummy_intervention()
# Create dummy data
# Create log entry
action = UserActionLogEntry.get_created_action(cls.superuser)
geometry = Geometry.objects.create()
# Finally create main object, holding the other objects
compensation = Compensation.objects.create(
identifier="TEST",
title="Test_title",
intervention=cls.intervention,
created=action,
geometry=geometry,
comment="Test",
)
return compensation
@classmethod
def create_dummy_eco_account(cls):
""" Creates an eco account which can be used for tests
Returns:
"""
# Create dummy data
# Create log entry
action = UserActionLogEntry.get_created_action(cls.superuser)
geometry = Geometry.objects.create()
# Create responsible data object
lega_data = Legal.objects.create()
responsible_data = Responsibility.objects.create()
# Finally create main object, holding the other objects
eco_account = EcoAccount.objects.create(
identifier="TEST",
title="Test_title",
legal=lega_data,
responsible=responsible_data,
created=action,
geometry=geometry,
comment="Test",
)
return eco_account
@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
@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 create_dummy_geometry() -> MultiPolygon:
""" Creates some geometry
Returns:
"""
polygon = Polygon.from_bbox((7.592449, 50.359385, 7.593382, 50.359874))
polygon.srid = 4326
polygon = polygon.transform(3857, clone=True)
return MultiPolygon(polygon, srid=3857) # 3857 is the default srid used for MultiPolygonField in the form
@classmethod
def fill_out_intervention(cls, 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 = cls.create_dummy_geometry()
intervention.geometry.save()
intervention.save()
return intervention
@classmethod
def fill_out_compensation(cls, compensation: Compensation) -> Compensation:
""" Adds all required (dummy) data to a compensation
Args:
compensation (Compensation): The compensation which shall be filled out
Returns:
compensation (Compensation): The modified compensation
"""
compensation.after_states.add(cls.comp_state)
compensation.before_states.add(cls.comp_state)
compensation.actions.add(cls.comp_action)
compensation.geometry.geom = cls.create_dummy_geometry()
compensation.geometry.save()
return compensation
def assert_equal_geometries(self, geom1: MultiPolygon, geom2: MultiPolygon):
""" Assert for geometries to be equal
Transforms the geometries to matching srids before checking
Args:
geom1 (MultiPolygon): A geometry
geom2 (MultiPolygon): A geometry
Returns:
"""
# Two empty geometries are basically identical - no further testing
if geom1.empty and geom2.empty:
self.assertTrue(True)
return
if geom1.srid != geom2.srid:
# Due to prior possible transformation of any of these geometries, we need to make sure there exists a
# transformation from one coordinate system into the other, which is valid
geom1_t = geom1.transform(geom2.srid, clone=True)
geom2_t = geom2.transform(geom1.srid, clone=True)
self.assertTrue(geom1_t.equals(geom2) or geom2_t.equals(geom1))
else:
self.assertTrue(geom1.equals(geom2))
class BaseViewTestCase(BaseTestCase):
""" Wraps basic test functionality, reusable for every specialized ViewTestCase
"""
login_url = None
class Meta:
abstract = True
@classmethod
def setUpTestData(cls) -> None:
super().setUpTestData()
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
Args:
client (Client): The performing client
urls (list): An iterable list of urls to be checked
Returns:
"""
for url in urls:
response = client.get(url)
self.assertEqual(response.status_code, 200, msg=f"Failed for {url}")
def assert_url_success_redirect(self, client: Client, urls: dict):
""" Assert for all given urls a 302 response to a certain location.
Assert the redirect being the expected behaviour.
Args:
client (Client): The performing client
urls (dict): An iterable dict of (urls, redirect_to_url) pairs to be checked
Returns:
"""
for url, redirect_to in urls.items():
response = client.get(url, follow=True)
# Expect redirects to the landing page
self.assertEqual(response.redirect_chain[0], (redirect_to, 302), msg=f"Failed for {url}")
def assert_url_fail(self, client: Client, urls: list):
""" Assert for all given urls a direct 302 response
Args:
client (Client): The performing client
urls (list): An iterable list of urls to be checked
Returns:
"""
for url in urls:
response = client.get(url)
self.assertEqual(response.status_code, 302, msg=f"Failed for {url}")
class KonovaViewTestCase(BaseViewTestCase):
""" Holds tests for all regular views, which are not app specific
"""
@classmethod
def setUpTestData(cls) -> None:
super().setUpTestData()
cls.home_url = reverse("home")
def test_views_logged_in_no_groups(self):
""" Check correct status code for all requests
Assumption: User logged in but has no groups
Returns:
"""
# User logged in
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
self.superuser.groups.set([])
success_urls = [
self.home_url
]
self.assert_url_success(client, success_urls)
def test_views_anonymous_user(self):
""" Check correct status code for all requests
Assumption: User logged in but has no groups
Returns:
"""
# User not logged in
client = Client()
urls = [
self.home_url
]
self.assert_url_fail(client, urls)
class AutocompleteTestCase(BaseViewTestCase):
@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_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
# users in a way like @loing_required or anything else. The documentation considers to check on the user's
# authentication state during get_queryset() of the call. Therefore this test method here will stay here
# for future clarification but won't be run due to the prefix '_'
# User not logged in
client = Client()
urls = [
self.atcmplt_accs,
self.atcmplt_interventions,
self.atcmplt_code_comp_action,
self.atcmplt_code_comp_biotope,
self.atcmplt_code_comp_law,
self.atcmplt_code_comp_process,
self.atcmplt_code_comp_reg_off,
self.atcmplt_code_comp_cons_off,
]
self.assert_url_fail(client, urls)
def test_views_logged_in_no_groups(self):
# User logged in
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
self.superuser.groups.set([])
urls = [
self.atcmplt_accs,
self.atcmplt_interventions,
self.atcmplt_code_comp_action,
self.atcmplt_code_comp_biotope,
self.atcmplt_code_comp_law,
self.atcmplt_code_comp_process,
self.atcmplt_code_comp_reg_off,
self.atcmplt_code_comp_cons_off,
]
self.assert_url_success(client, urls)
class BaseWorkflowTestCase(BaseTestCase):
"""
Holds base methods and attributes for workflow testing
"""
client_user = None
client_anon = None
class Meta:
abstract = True
@classmethod
def setUpTestData(cls):
super().setUpTestData()
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])
# Create fresh logged in client and a non-logged in client (anon) for each test
self.client_user = Client()
self.client_user.login(username=self.superuser.username, password=self.superuser_pw)
self.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