From 2e527d3985b8df829078f635e59a4b8fe465856a Mon Sep 17 00:00:00 2001 From: Andreas Ellewsen <ae@uio.no> Date: Mon, 1 Nov 2021 15:27:28 +0100 Subject: [PATCH] Add fixtures for use with frontend To make testing of the frontend easier, we add a script that generates Person objects with the appropriate Consents and Identities for the different stages of the registration process. This includes Invitations, InvitationLinks, Sponsors and other things that would be present in a production environment. Resolves: GREG-93 --- README.md | 8 + greg/tests/populate_fixtures.py | 343 ++++++++++++++++++++++++++++++++ 2 files changed, 351 insertions(+) create mode 100644 greg/tests/populate_fixtures.py diff --git a/README.md b/README.md index a6babf2d..b9b8f371 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Greg + [](https://git.app.uib.no/it-bott-integrasjoner/greg/-/commits/master) [](https://git.app.uib.no/it-bott-integrasjoner/greg/-/commits/master) @@ -50,6 +51,13 @@ Use pytest with the pytest-django library to run unit tests. pytest +There are two scripts for adding data to the database: + +- greg/tests/populate_fixtures.py +- greg/tests/populate_database.py + +where the former uses randomized data, and the latter uses specific data useful in combination with the frontend. See the respective files for how to use them. + ## Static type analysis Use [mypy](http://mypy-lang.org/) to run static type checks using type hints. diff --git a/greg/tests/populate_fixtures.py b/greg/tests/populate_fixtures.py new file mode 100644 index 00000000..d7e03f4e --- /dev/null +++ b/greg/tests/populate_fixtures.py @@ -0,0 +1,343 @@ +""" +Adds a few more specific models for testing purposes. Alternative to the similiar +script for adding random data to various fields. + +WARNING: This script removes all entries in most tables. Do not execute it unless you +are absolutely certain you know what you are doing. + +There are 4 guests: + - One has been invited and not done anything yet + - One has followed the invite link and provided a passport number, and is waiting for + the sponsor to act + - One has had their passport number confirmed by the sponsor and the role is active + - One like the previous but the role has ended + +There is an OU tree with 4 OUs on three levels, with respective OUIdentifiers. +There is an invitation object related to each guest with InvitationLinks that are +expired for all but they invited guest that has not responded. + +There are Consent Types, and the active guests have consented to the mandatory one, but +one of them has denied the other one. + +""" + +import datetime +import logging + +from django.db import connection +from django.conf import settings +from django.utils import timezone + +from greg.models import ( + Consent, + ConsentType, + Identity, + Invitation, + InvitationLink, + OrganizationalUnit, + OuIdentifier, + Person, + Role, + RoleType, + Sponsor, + SponsorOrganizationalUnit, +) + + +ROLE_TYPE_EXT_SCI = "extsci" +ROLE_TYPE_EMERITUS = "emeritus" +TESTDATA_SOURCE = "testsource" +SPONSOR_FEIDEID = "sponsor@feide.no" +OU_EUROPE_NAME_EN = "Europe" +CONSENT_IDENT_MANDATORY = "mandatory" +CONSENT_IDENT_OPTIONAL = "optional" + +logger = logging.getLogger(__name__) + + +class DatabasePopulation: + """ + Helper class for populating database with specific data + + Run the file in the Django shell: exec(open('greg/tests/populate_fixtures.py').read()) + """ + + def truncate_tables(self): + logger.info("truncating tables...") + with connection.cursor() as cursor: + for table in ( + "greg_consent", + "greg_consenttype", + "greg_notification", + "greg_identity", + "greg_invitationlink", + "greg_invitation", + "greg_role", + "greg_sponsororganizationalunit", + "greg_roletype", + "greg_ouidentifier", + "greg_organizationalunit", + "greg_person", + "gregui_greguserprofile", + "greg_sponsor", + ): + logging.info("purging table %s", table) + cursor.execute(f"DELETE FROM {table}") + logger.info("...tables purged") + + def _add_consenttypes(self): + ConsentType.objects.create( + identifier=CONSENT_IDENT_MANDATORY, + name_en="Mandatory consent type", + name_nb="Påkrevd samtykketype", + user_allowed_to_change=False, + mandatory=True, + ) + ConsentType.objects.create( + identifier=CONSENT_IDENT_OPTIONAL, + name_en="Optional consent type", + name_nb="Valgfri samtykketype", + user_allowed_to_change=False, + ) + + def _add_ous_with_identifiers(self): + """ + Create a simple tree + + earth - america + - europe - norway + """ + earth = OrganizationalUnit.objects.create( + name_nb="Universitetet i Jorden", name_en="University of Earth" + ) + OuIdentifier.objects.create( + name=settings.ORGREG_NAME, + source=settings.ORGREG_SOURCE, + value="2", + orgunit=earth, + ) + europe = OrganizationalUnit.objects.create( + name_nb="Europa", name_en=OU_EUROPE_NAME_EN, parent=earth + ) + OuIdentifier.objects.create( + name=settings.ORGREG_NAME, + source=settings.ORGREG_SOURCE, + value="3", + orgunit=europe, + ) + america = OrganizationalUnit.objects.create( + name_nb="Amerika", name_en="America", parent=earth + ) + OuIdentifier.objects.create( + name=settings.ORGREG_NAME, + source=settings.ORGREG_SOURCE, + value="4", + orgunit=america, + ) + norway = OrganizationalUnit.objects.create( + name_nb="Norge", name_en="Norway", parent=europe + ) + OuIdentifier.objects.create( + name=settings.ORGREG_NAME, + source=settings.ORGREG_SOURCE, + value="5", + orgunit=norway, + ) + + def _add_roletypes(self): + RoleType.objects.create( + identifier=ROLE_TYPE_EXT_SCI, + name_nb="Gjesteforsker", + name_en="Guest researcher", + description_nb="Gjesteforsker som ikke skal ha lønn", + description_en="Guest reasearcher without payment", + ) + RoleType.objects.create( + identifier=ROLE_TYPE_EMERITUS, + name_nb="Emeritus", + name_en="Emeritus", + description_nb="Emeritus", + description_en="Emeritus", + max_days=700, + ) + + def _add_sponsors(self): + """Add a sponsor connected to the Europe unit""" + sam = Sponsor.objects.create( + feide_id=SPONSOR_FEIDEID, first_name="Sam", last_name="Sponsorson" + ) + SponsorOrganizationalUnit.objects.create( + sponsor=sam, + organizational_unit=OrganizationalUnit.objects.get( + name_en=OU_EUROPE_NAME_EN + ), + hierarchical_access=False, + ) + + def _add_invited_person(self): + """Person that has been invited and has not followed their invite""" + iggy = Person.objects.create(first_name="Iggy", last_name="Invited") + role = Role.objects.create( + person=iggy, + type=RoleType.objects.get(identifier=ROLE_TYPE_EXT_SCI), + orgunit=OrganizationalUnit.objects.get(name_en=OU_EUROPE_NAME_EN), + start_date=datetime.date.today() + datetime.timedelta(days=2), + end_date=datetime.date.today() + datetime.timedelta(days=100), + sponsor=Sponsor.objects.get(feide_id=SPONSOR_FEIDEID), + ) + invitation = Invitation.objects.create( + role=role, + ) + InvitationLink.objects.create( + invitation=invitation, + expire=timezone.now() + datetime.timedelta(days=30), + ) + + def _add_waiting_person(self): + """ + A person with an active role but missing a verified identity of type national + id or passport. + """ + walter = Person.objects.create( + first_name="Walter", + last_name="Waiting", + registration_completed_date=datetime.date.today() + - datetime.timedelta(days=10), + ) + role = Role.objects.create( + person=walter, + type=RoleType.objects.get(identifier=ROLE_TYPE_EXT_SCI), + orgunit=OrganizationalUnit.objects.get(name_en=OU_EUROPE_NAME_EN), + start_date=datetime.date.today() - datetime.timedelta(days=30), + end_date=datetime.date.today() + datetime.timedelta(days=100), + sponsor=Sponsor.objects.get(feide_id=SPONSOR_FEIDEID), + ) + Identity.objects.create( + person=walter, + type=Identity.IdentityType.PASSPORT_NUMBER, + source=TESTDATA_SOURCE, + value="SE-123456789", + ) + invitation = Invitation.objects.create( + role=role, + ) + InvitationLink.objects.create( + invitation=invitation, + expire=timezone.now() - datetime.timedelta(days=35), + ) + + def _add_active_person(self): + """ + A person with an active role and a verified identity of type national id or + passport. + """ + adam = Person.objects.create( + first_name="Adam", + last_name="Active", + registration_completed_date=datetime.date.today() + - datetime.timedelta(days=10), + ) + role = Role.objects.create( + person=adam, + type=RoleType.objects.get(identifier=ROLE_TYPE_EXT_SCI), + orgunit=OrganizationalUnit.objects.get(name_en=OU_EUROPE_NAME_EN), + start_date=datetime.date.today() - datetime.timedelta(days=30), + end_date=datetime.date.today() + datetime.timedelta(days=100), + sponsor=Sponsor.objects.get(feide_id=SPONSOR_FEIDEID), + ) + Identity.objects.create( + person=adam, + type=Identity.IdentityType.PASSPORT_NUMBER, + source=TESTDATA_SOURCE, + value="NO-123456789", + verified_at=timezone.now() - datetime.timedelta(days=31), + ) + Identity.objects.create( + person=adam, + type=Identity.IdentityType.PRIVATE_MOBILE_NUMBER, + source=TESTDATA_SOURCE, + value="+4792492412", + verified_at=timezone.now() - datetime.timedelta(days=205), + ) + invitation = Invitation.objects.create( + role=role, + ) + InvitationLink.objects.create( + invitation=invitation, + expire=timezone.now() - datetime.timedelta(days=32), + ) + Consent.objects.create( + person=adam, + type=ConsentType.objects.get(identifier=CONSENT_IDENT_MANDATORY), + consent_given_at=datetime.date.today() - datetime.timedelta(days=10), + ) + Consent.objects.create( + person=adam, + type=ConsentType.objects.get(identifier=CONSENT_IDENT_OPTIONAL), + consent_given_at=datetime.date.today() - datetime.timedelta(days=10), + ) + + def _add_expired_person(self): + """ + A person with an inactive role, and a verified identity of type national id or + passport. + """ + esther = Person.objects.create( + first_name="Esther", + last_name="Expired", + registration_completed_date=timezone.now() - datetime.timedelta(days=206), + ) + role = Role.objects.create( + person=esther, + type=RoleType.objects.get(identifier=ROLE_TYPE_EXT_SCI), + orgunit=OrganizationalUnit.objects.get(name_en=OU_EUROPE_NAME_EN), + start_date=datetime.date.today() - datetime.timedelta(days=200), + end_date=datetime.date.today() - datetime.timedelta(days=100), + sponsor=Sponsor.objects.get(feide_id=SPONSOR_FEIDEID), + ) + Identity.objects.create( + person=esther, + type=Identity.IdentityType.PASSPORT_NUMBER, + source=TESTDATA_SOURCE, + value="DK-123456789", + verified_at=timezone.now() - datetime.timedelta(days=205), + ) + Identity.objects.create( + person=esther, + type=Identity.IdentityType.NORWEGIAN_NATIONAL_ID_NUMBER, + source=TESTDATA_SOURCE, + value="12345678901", + verified_at=timezone.now() - datetime.timedelta(days=205), + ) + invitation = Invitation.objects.create( + role=role, + ) + InvitationLink.objects.create( + invitation=invitation, + expire=timezone.now() - datetime.timedelta(days=204), + ) + Consent.objects.create( + person=esther, + type=ConsentType.objects.get(identifier=CONSENT_IDENT_MANDATORY), + consent_given_at=datetime.date.today() - datetime.timedelta(days=206), + ) + + def populate_database(self): + logger.info("populating db...") + # Add the types, sponsors and ous + self._add_consenttypes() + self._add_ous_with_identifiers() + self._add_roletypes() + self._add_sponsors() + # Add the four guests + self._add_active_person() + self._add_waiting_person() + self._add_invited_person() + self._add_expired_person() + logger.info("...done populating db") + + +if __name__ == "__main__": + database_population = DatabasePopulation() + database_population.truncate_tables() + database_population.populate_database() -- GitLab