From 07bf5ff53fef88e948a3a6af78e777ae88e2b64d Mon Sep 17 00:00:00 2001 From: Christian Merten Date: Sun, 4 May 2025 18:10:34 +0200 Subject: [PATCH 1/2] chore(members): add more tests --- docker/test/config/settings.toml | 3 + jdav_web/.coveragerc | 4 + jdav_web/members/csv.py | 22 +-- jdav_web/members/tests.py | 265 ++++++++++++++++++++++++++++++- 4 files changed, 276 insertions(+), 18 deletions(-) diff --git a/docker/test/config/settings.toml b/docker/test/config/settings.toml index f1e5f79..08414ab 100644 --- a/docker/test/config/settings.toml +++ b/docker/test/config/settings.toml @@ -29,3 +29,6 @@ password = 'password' [startpage] recent_section = 'aktuelles' reports_section = 'berichte' + +[misc] +allowed_email_domains_for_invite_as_user = ['test-organization.org'] diff --git a/jdav_web/.coveragerc b/jdav_web/.coveragerc index ef96063..46d095d 100644 --- a/jdav_web/.coveragerc +++ b/jdav_web/.coveragerc @@ -1,3 +1,7 @@ +[run] + source = + . + [report] omit = ./jet/* diff --git a/jdav_web/members/csv.py b/jdav_web/members/csv.py index 3f50377..3fec6c8 100644 --- a/jdav_web/members/csv.py +++ b/jdav_web/members/csv.py @@ -3,7 +3,7 @@ import re import csv -def import_from_csv(path, omit_groupless=True): +def import_from_csv(path, omit_groupless=True): # pragma: no cover with open(path, encoding='ISO-8859-1') as csvfile: reader = csv.DictReader(csvfile, delimiter=';') rows = list(reader) @@ -72,7 +72,7 @@ def import_from_csv(path, omit_groupless=True): transform_row(row) -def parse_group(value): +def parse_group(value): # pragma: no cover groups_raw = re.split(',', value) # need to determine if member is youth leader @@ -101,35 +101,35 @@ def parse_group(value): return groups -def parse_date(value): +def parse_date(value): # pragma: no cover if value == '': return None return datetime.strptime(value, '%d.%m.%Y').date() -def parse_datetime(value): +def parse_datetime(value): # pragma: no cover tz = pytz.timezone('Europe/Berlin') if value == '': return timezone.now() return tz.localize(datetime.strptime(value, '%d.%m.%Y %H:%M:%S')) -def parse_status(value): +def parse_status(value): # pragma: no cover return value != "Passivmitglied" -def parse_boolean(value): +def parse_boolean(value): # pragma: no cover return value.lower() == "ja" -def parse_nullable_boolean(value): +def parse_nullable_boolean(value): # pragma: no cover if value == '': return None else: return value.lower() == "ja" -def parse_gender(value): +def parse_gender(value): # pragma: no cover if value == 'männlich': return MALE elif value == 'weiblich': @@ -138,11 +138,11 @@ def parse_gender(value): return DIVERSE -def parse_can_swim(value): +def parse_can_swim(value): # pragma: no cover return True if len(value) > 0 else False -CLUBDESK_TO_KOMPASS = { +CLUBDESK_TO_KOMPASS = { # pragma: no cover 'Nachname': 'lastname', 'Vorname': 'prename', 'Adresse': 'street', @@ -188,7 +188,7 @@ CLUBDESK_TO_KOMPASS = { } -def import_from_csv_waitinglist(path): +def import_from_csv_waitinglist(path): # pragma: no cover with open(path, encoding='ISO-8859-1') as csvfile: reader = csv.DictReader(csvfile, delimiter=';') rows = list(reader) diff --git a/jdav_web/members/tests.py b/jdav_web/members/tests.py index e53e662..4e59f15 100644 --- a/jdav_web/members/tests.py +++ b/jdav_web/members/tests.py @@ -14,10 +14,13 @@ from django.conf import settings from django.urls import reverse from django import template from unittest import skip, mock -from .models import Member, Group, PermissionMember, PermissionGroup, Freizeit, GEMEINSCHAFTS_TOUR, MUSKELKRAFT_ANREISE,\ +from .models import Member, Group, PermissionMember, PermissionGroup, Freizeit, GEMEINSCHAFTS_TOUR,\ + MUSKELKRAFT_ANREISE, FUEHRUNGS_TOUR, AUSBILDUNGS_TOUR, OEFFENTLICHE_ANREISE,\ + FAHRGEMEINSCHAFT_ANREISE,\ MemberNoteList, NewMemberOnList, confirm_mail_by_key, EmergencyContact, MemberWaitingList,\ RegistrationPassword, MemberUnconfirmedProxy, InvitationToGroup, DIVERSE, MALE, FEMALE,\ - Klettertreff, KlettertreffAttendee, LJPProposal, ActivityCategory, WEEKDAYS + Klettertreff, KlettertreffAttendee, LJPProposal, ActivityCategory, WEEKDAYS,\ + TrainingCategory, Person from .admin import MemberWaitingListAdmin, MemberAdmin, FreizeitAdmin, MemberNoteListAdmin,\ MemberUnconfirmedAdmin, RegistrationFilter, FilteredMemberFieldMixin,\ MemberAdminForm, StatementOnListForm, KlettertreffAdmin, GroupAdmin @@ -34,6 +37,7 @@ import math import os.path +INTERNAL_EMAIL = "foobar@{domain}".format(domain=settings.ALLOWED_EMAIL_DOMAINS_FOR_INVITE_AS_USER[0]) REGISTRATION_DATA = { 'prename': 'Peter', 'lastname': 'Wulter', @@ -109,7 +113,7 @@ class BasicMemberTestCase(TestCase): self.peter.save() self.lara = Member.objects.create(prename="Lara", lastname="Wallis", birth_date=timezone.now().date(), - email=settings.TEST_MAIL, gender=DIVERSE) + email=INTERNAL_EMAIL, gender=DIVERSE) self.lara.group.add(self.alp) self.lara.save() self.fridolin = Member.objects.create(prename="Fridolin", lastname="Spargel", birth_date=timezone.now().date(), @@ -120,6 +124,8 @@ class BasicMemberTestCase(TestCase): self.lise = Member.objects.create(prename="Lise", lastname="Lotte", birth_date=timezone.now().date(), email=settings.TEST_MAIL, gender=FEMALE) + self.alp.leiters.add(self.lise) + self.alp.save() class MemberTestCase(BasicMemberTestCase): @@ -262,6 +268,41 @@ class MemberTestCase(BasicMemberTestCase): def test_association_email(self): self.assertIn(settings.DOMAIN, self.peter.association_email) + def test_registration_complete(self): + # this is currently a dummy that always returns True + self.assertTrue(self.peter.registration_complete()) + + def test_unconfirm(self): + self.assertTrue(self.peter.confirmed) + self.peter.unconfirm() + self.assertFalse(self.peter.confirmed) + + def test_generate_upload_registration_form_key(self): + self.peter.generate_upload_registration_form_key() + self.assertIsNotNone(self.peter.upload_registration_form_key) + + def test_has_internal_email(self): + self.peter.email = 'foobar' + self.assertFalse(self.peter.has_internal_email()) + + def test_invite_as_user(self): + self.assertTrue(self.lara.has_internal_email()) + self.lara.user = None + self.assertTrue(self.lara.invite_as_user()) + u = User.objects.create_user(username='user', password='secret', is_staff=True) + self.peter.user = u + self.assertFalse(self.peter.invite_as_user()) + + def test_birth_date_str(self): + self.fritz.birth_date = None + self.assertEqual(self.fritz.birth_date_str, '---') + date = timezone.now().date() + self.fritz.birth_date = date + self.assertEqual(self.fritz.birth_date_str, date.strftime('%d.%m.%Y')) + + def test_gender_str(self): + self.assertGreater(len(self.fritz.gender_str), 0) + class PDFTestCase(TestCase): def setUp(self): @@ -270,8 +311,12 @@ class PDFTestCase(TestCase): tour_approach=MUSKELKRAFT_ANREISE, difficulty=1) self.note = MemberNoteList.objects.create(title='Coolß! ‬löst') + self.cat = ActivityCategory.objects.create(name='Climbing', description='Climbing') + ActivityCategory.objects.create(name='Walking', description='Climbing') + self.ex.activity.add(self.cat) + self.ex.save() - for i in range(7): + for i in range(15): m = Member.objects.create(prename='Liääüuße {}'.format(i), lastname='Walter&co ‬: _ kg &', birth_date=timezone.now().date(), @@ -550,7 +595,7 @@ class MemberAdminTestCase(AdminTestCase): _("The configured email address for %(name)s is not an internal one.") % {'name': str(self.fritz)}) # update email to allowed email domain - self.fritz.email = 'foobar@{domain}'.format(domain=settings.ALLOWED_EMAIL_DOMAINS_FOR_INVITE_AS_USER[0]) + self.fritz.email = INTERNAL_EMAIL self.fritz.save() response = c.post(url) # expect: user is found and confirmation page is shown @@ -594,9 +639,9 @@ class MemberAdminTestCase(AdminTestCase): self.assertContains(response, _('Some members have been invited, others could not be invited.')) # confirm invite, expect: success - self.peter.email = 'foobar@{domain}'.format(domain=settings.ALLOWED_EMAIL_DOMAINS_FOR_INVITE_AS_USER[0]) + self.peter.email = INTERNAL_EMAIL self.peter.save() - self.fritz.email = 'foobar@{domain}'.format(domain=settings.ALLOWED_EMAIL_DOMAINS_FOR_INVITE_AS_USER[0]) + self.fritz.email = INTERNAL_EMAIL self.fritz.save() response = c.post(url, data={'action': 'invite_as_user_action', '_selected_action': [self.fritz.pk, self.peter.pk], 'apply': True}, follow=True) @@ -644,6 +689,7 @@ class FreizeitTestCase(BasicMemberTestCase): difficulty=1, date=timezone.localtime()) self.ex2.jugendleiter.add(self.fritz) + self.st = Statement.objects.create(excursion=self.ex2, night_cost=42, subsidy_to=None) self.ex2.save() def _setup_test_sjr_application_numbers(self, n_yl, n_b27_local, n_b27_non_local): @@ -769,6 +815,56 @@ class FreizeitTestCase(BasicMemberTestCase): self.ex2.send_crisis_intervention_list() self.assertTrue(self.ex2.crisis_intervention_list_sent) + def test_filter_queryset_by_permissions(self): + qs = Freizeit.filter_queryset_by_permissions(self.fritz) + self.assertIn(self.ex2, qs) + + def test_v32_fields(self): + self.assertIn('Textfeld 61', self.ex2.v32_fields().keys()) + + @skip("This currently throws a `RelatedObjectDoesNotExist` error.") + def test_no_statement(self): + self.assertEqual(self.ex.total_relative_costs, 0) + self.assertEqual(self.ex.payable_ljp_contributions, 0) + + def test_no_ljpproposal(self): + self.assertEqual(self.ex2.total_intervention_hours, 0) + self.assertEqual(self.ex2.seminar_time_per_day, []) + + def test_relative_costs(self): + # after deducting contributions, the total costs should still be non-negative + self.assertGreaterEqual(self.ex2.total_relative_costs, 0) + + def test_payable_ljp_contributions(self): + self.assertGreaterEqual(self.ex2.payable_ljp_contributions, 0) + + def test_get_tour_type(self): + self.ex2.tour_type = GEMEINSCHAFTS_TOUR + self.assertEqual(self.ex2.get_tour_type(), 'Gemeinschaftstour') + self.ex2.tour_type = FUEHRUNGS_TOUR + self.assertEqual(self.ex2.get_tour_type(), 'Führungstour') + self.ex2.tour_type = AUSBILDUNGS_TOUR + self.assertEqual(self.ex2.get_tour_type(), 'Ausbildung') + + def test_get_tour_approach(self): + self.ex2.tour_approach = MUSKELKRAFT_ANREISE + self.assertEqual(self.ex2.get_tour_approach(), 'Muskelkraft') + self.ex2.tour_approach = OEFFENTLICHE_ANREISE + self.assertEqual(self.ex2.get_tour_approach(), 'ÖPNV') + self.ex2.tour_approach = FAHRGEMEINSCHAFT_ANREISE + self.assertEqual(self.ex2.get_tour_approach(), 'Fahrgemeinschaften') + + def test_duration(self): + self.assertGreaterEqual(self.ex.duration, 0) + self.ex.date = timezone.datetime(2000, 1, 1, 8, 0, 0) + self.ex.end = timezone.datetime(2000, 1, 1, 10, 0, 0) + self.assertEqual(self.ex.duration, 0.5) + + # TODO: fix this in the model, the duration of this excursion should be 0 + self.ex.date = timezone.datetime(2000, 1, 1, 12, 0, 0) + self.ex.end = timezone.datetime(2000, 1, 1, 12, 0, 0) + self.assertEqual(self.ex.duration, 1) + class PDFActionMixin: def _test_pdf(self, name, pk, model='freizeit', invalid=False, username='superuser', post_data=None): @@ -1024,6 +1120,9 @@ class MemberNoteListAdminTestCase(AdminTestCase, PDFActionMixin): email=settings.TEST_MAIL, gender=FEMALE) NewMemberOnList.objects.create(member=m, comments='a' * i, memberlist=self.note) + def test_str(self): + self.assertEqual(str(self.note), 'Cool list') + def test_membernote_summary(self): self._test_pdf('summary', self.note.pk, model='membernotelist') self._test_pdf('summary', self.note.pk, model='membernotelist', username='standard', invalid=True) @@ -1197,6 +1296,24 @@ class MailConfirmationTestCase(BasicMemberTestCase): self.father = EmergencyContact.objects.create(prename='Olaf', lastname='Old', email=settings.TEST_MAIL, member=self.fritz) self.father.save() + self.reg = MemberUnconfirmedProxy.objects.create(**REGISTRATION_DATA, confirmed=False) + self.reg.group.add(self.alp) + file = SimpleUploadedFile("form.pdf", b"file_content", content_type="application/pdf") + self.reg.registration_form = file + self.reg.save() + + def test_request_mail_confirmation(self): + self.reg.confirmed_mail = True + self.reg.confirmed_alternative_mail = True + self.assertFalse(self.reg.request_mail_confirmation(rerequest=False)) + + def test_confirm_mail_memberunconfirmed(self): + requested = self.reg.request_mail_confirmation() + self.assertTrue(requested) + self.assertIsNone(self.reg.confirm_mail('foobar')) + self.assertTrue(self.reg.confirm_mail(self.reg.confirm_mail_key)) + self.assertTrue(self.reg.confirm_mail(self.reg.confirm_alternative_mail_key)) + self.assertTrue(self.reg.registration_ready()) def test_contact_confirmation(self): # request mail confirmation of father @@ -1519,6 +1636,58 @@ class InvitationToGroupViewTestCase(BasicMemberTestCase): self.assertEqual(response.status_code, HTTPStatus.OK) +class InvitationToGroupTestCase(BasicMemberTestCase): + def setUp(self): + super().setUp() + self.waiter = MemberWaitingList.objects.create(**WAITER_DATA) + self.waiter.invite_to_group(self.alp) + self.invitation = InvitationToGroup.objects.get(group=self.alp, waiter=self.waiter) + self.invitation.created_by = self.fritz + + def test_status(self): + self.assertEqual(self.invitation.status(), _('Undecided')) + # expire the invitation + self.invitation.date = (timezone.now() - timezone.timedelta(days=100)).date() + self.assertTrue(self.invitation.is_expired()) + self.assertEqual(self.invitation.status(), _('Expired')) + # reject the invitation + self.invitation.reject() + self.assertEqual(self.invitation.status(), _('Rejected')) + + def test_confirm(self): + self.invitation.confirm() + self.assertFalse(self.invitation.rejected) + + def test_notify_left_waitinglist(self): + self.invitation.notify_left_waitinglist() + + +class MemberWaitingListTestCase(BasicMemberTestCase): + def setUp(self): + super().setUp() + self.waiter = MemberWaitingList.objects.create(**WAITER_DATA) + self.waiter.invite_to_group(self.alp) + self.invitation = InvitationToGroup.objects.get(group=self.alp, waiter=self.waiter) + + def test_latest_group_invitation(self): + self.assertGreater(len(self.waiter.latest_group_invitation()), 1) + + @skip("This currently throws a 'TypeError'.") + def test_may_register(self): + self.assertTrue(self.waiter.may_register(self.invitation.key)) + + def test_may_register_invalid(self): + self.assertFalse(self.waiter.may_register('foobar')) + + @skip("This currently throws a 'NameError'.") + def test_waiting_confirmation_needed(self): + self.assertFalse(self.waiter.waiting_confirmation_needed()) + + def test_confirm_waiting_invalid(self): + self.assertEqual(self.waiter.confirm_waiting('foobar'), + MemberWaitingList.WAITING_CONFIRMATION_INVALID) + + class ConfirmWaitingViewTestCase(BasicMemberTestCase): def setUp(self): super().setUp() @@ -1613,6 +1782,7 @@ class MailConfirmationViewTestCase(BasicMemberTestCase): self.assertEqual(response.status_code, HTTPStatus.OK) self.assertContains(response, _("Mail confirmed")) + class EchoViewTestCase(BasicMemberTestCase): def setUp(self): super().setUp() @@ -1725,6 +1895,7 @@ class TestRegistrationFilterTestCase(AdminTestCase): Member.objects.filter(registration_complete=True), ordered=False) + class MemberAdminFormTestCase(TestCase): def test_clean_iban(self): form_data = dict(REGISTRATION_DATA, iban='foobar') @@ -1941,3 +2112,83 @@ class GroupTestCase(BasicMemberTestCase): self.assertNotIn(url, spiel_text) self.assertIn(str(WEEKDAYS[self.alp.weekday][1]), alp_text) + + +class NewMemberOnListTestCase(BasicMemberTestCase): + def setUp(self): + super().setUp() + self.ex = Freizeit.objects.create(name='Wild trip', kilometers_traveled=120, + tour_type=GEMEINSCHAFTS_TOUR, + tour_approach=MUSKELKRAFT_ANREISE, + difficulty=1) + self.cat = ActivityCategory.objects.create(name='crazy climbing', ljp_category='Klettern', + description='foobar') + self.ex.activity.add(self.cat) + self.ex.save() + self.mol = NewMemberOnList.objects.create(memberlist=self.ex, member=self.fritz) + + @skip("This currently throws a 'NameError'.") + def test_skills(self): + self.assertGreater(len(self.mol.skills), 0) + + @skip("This currently throws a 'NameError'.") + def test_qualities_tex(self): + self.assertGreater(len(self.mol.qualities_tex), 0) + + +class TrainingCategoryTestCase(TestCase): + def setUp(self): + self.cat = TrainingCategory.objects.create(name='school', permission_needed=True) + + def test_str(self): + self.assertEqual(str(self.cat), 'school') + +class PermissionMemberGroupTestCase(BasicMemberTestCase): + def setUp(self): + super().setUp() + self.gp = PermissionGroup.objects.create(group=self.alp) + self.gm = PermissionMember.objects.create(member=self.fritz) + + def test_str(self): + self.assertEqual(str(self.gp), _('Group permissions')) + self.assertEqual(str(self.gm), _('Permissions')) + + +class LJPProposalTestCase(TestCase): + def setUp(self): + self.proposal = LJPProposal.objects.create(title='Foo') + + def test_str(self): + self.assertEqual(str(self.proposal), 'Foo') + + +class KlettertreffTestCase(BasicMemberTestCase): + def setUp(self): + super().setUp() + self.kt = Klettertreff.objects.create(location='foo', topic='bar', group=self.alp) + self.kt.jugendleiter.add(self.fritz) + self.kt.save() + self.attendee = KlettertreffAttendee.objects.create(klettertreff=self.kt, member=self.peter) + + def test_str_attendee(self): + self.assertEqual(str(self.attendee), str(self.peter)) + + def test_get_jugendleiter(self): + self.assertIn(self.kt.get_jugendleiter(), self.fritz.name) + + def test_has_jugendleiter(self): + self.assertFalse(self.kt.has_jugendleiter(self.peter)) + self.assertTrue(self.kt.has_jugendleiter(self.fritz)) + + def test_has_attendee(self): + self.assertTrue(self.kt.has_attendee(self.peter)) + self.assertFalse(self.kt.has_attendee(self.fritz)) + + +class EmergencyContactTestCase(TestCase): + def setUp(self): + self.member = Member.objects.create(**REGISTRATION_DATA) + self.emergency_contact = EmergencyContact.objects.create(member=self.member) + + def test_str(self): + self.assertEqual(str(self.emergency_contact), str(self.member)) From 416af3607065fc50919ee3d2ff54a8d10d085ae2 Mon Sep 17 00:00:00 2001 From: Christian Merten Date: Sun, 4 May 2025 18:50:59 +0200 Subject: [PATCH 2/2] chore(members): reorganize tests --- jdav_web/members/tests/__init__.py | 1 + jdav_web/members/{tests.py => tests/basic.py} | 136 +------------ jdav_web/members/tests/utils.py | 191 ++++++++++++++++++ 3 files changed, 199 insertions(+), 129 deletions(-) create mode 100644 jdav_web/members/tests/__init__.py rename jdav_web/members/{tests.py => tests/basic.py} (93%) create mode 100644 jdav_web/members/tests/utils.py diff --git a/jdav_web/members/tests/__init__.py b/jdav_web/members/tests/__init__.py new file mode 100644 index 0000000..da0f2b6 --- /dev/null +++ b/jdav_web/members/tests/__init__.py @@ -0,0 +1 @@ +from .basic import * diff --git a/jdav_web/members/tests.py b/jdav_web/members/tests/basic.py similarity index 93% rename from jdav_web/members/tests.py rename to jdav_web/members/tests/basic.py index 4e59f15..20295a1 100644 --- a/jdav_web/members/tests.py +++ b/jdav_web/members/tests/basic.py @@ -14,17 +14,17 @@ from django.conf import settings from django.urls import reverse from django import template from unittest import skip, mock -from .models import Member, Group, PermissionMember, PermissionGroup, Freizeit, GEMEINSCHAFTS_TOUR,\ +from members.models import Member, Group, PermissionMember, PermissionGroup, Freizeit, GEMEINSCHAFTS_TOUR,\ MUSKELKRAFT_ANREISE, FUEHRUNGS_TOUR, AUSBILDUNGS_TOUR, OEFFENTLICHE_ANREISE,\ FAHRGEMEINSCHAFT_ANREISE,\ MemberNoteList, NewMemberOnList, confirm_mail_by_key, EmergencyContact, MemberWaitingList,\ RegistrationPassword, MemberUnconfirmedProxy, InvitationToGroup, DIVERSE, MALE, FEMALE,\ Klettertreff, KlettertreffAttendee, LJPProposal, ActivityCategory, WEEKDAYS,\ TrainingCategory, Person -from .admin import MemberWaitingListAdmin, MemberAdmin, FreizeitAdmin, MemberNoteListAdmin,\ +from members.admin import MemberWaitingListAdmin, MemberAdmin, FreizeitAdmin, MemberNoteListAdmin,\ MemberUnconfirmedAdmin, RegistrationFilter, FilteredMemberFieldMixin,\ MemberAdminForm, StatementOnListForm, KlettertreffAdmin, GroupAdmin -from .pdf import fill_pdf_form, render_tex, media_path, serve_pdf, find_template, merge_pdfs +from members.pdf import fill_pdf_form, render_tex, media_path, serve_pdf, find_template, merge_pdfs from mailer.models import EmailAddress from finance.models import Statement, Bill @@ -35,29 +35,9 @@ import datetime from dateutil.relativedelta import relativedelta import math import os.path +from members.tests.utils import * -INTERNAL_EMAIL = "foobar@{domain}".format(domain=settings.ALLOWED_EMAIL_DOMAINS_FOR_INVITE_AS_USER[0]) -REGISTRATION_DATA = { - 'prename': 'Peter', - 'lastname': 'Wulter', - 'street': 'Street 123', - 'plz': '12345 EJ', - 'town': 'Town 1', - 'phone_number': '+49 123456', - 'birth_date': '2010-05-17', - 'gender': '2', - 'email': settings.TEST_MAIL, - 'alternative_email': settings.TEST_MAIL, -} -WAITER_DATA = { - 'prename': 'Peter', - 'lastname': 'Wulter', - 'birth_date': '1999-02-16', - 'gender': '0', - 'email': settings.TEST_MAIL, - 'application_text': 'hoho', -} EMERGENCY_CONTACT_DATA = { 'emergencycontact_set-TOTAL_FORMS': '1', 'emergencycontact_set-INITIAL_FORMS': '0', @@ -73,61 +53,6 @@ EMERGENCY_CONTACT_DATA = { } -def create_custom_user(username, groups, prename, lastname): - user = User.objects.create_user( - username=username, password='secret' - ) - member = Member.objects.create(prename=prename, lastname=lastname, birth_date=timezone.localdate(), email=settings.TEST_MAIL, gender=DIVERSE) - member.user = user - member.save() - user.is_staff = True - user.save() - - for group in groups: - g = authmodels.Group.objects.get(name=group) - user.groups.add(g) - return user - - -class BasicMemberTestCase(TestCase): - def setUp(self): - self.jl = Group.objects.create(name="Jugendleiter") - self.alp = Group.objects.create(name="Alpenfuechse") - self.spiel = Group.objects.create(name="Spielkinder") - - self.fritz = Member.objects.create(prename="Fritz", lastname="Wulter", birth_date=timezone.now().date(), - email=settings.TEST_MAIL, gender=DIVERSE) - self.fritz.group.add(self.jl) - self.fritz.group.add(self.alp) - self.fritz.save() - - em = EmailAddress.objects.create(name='foobar') - self.alp.contact_email = em - self.alp.save() - - self.peter = Member.objects.create(prename="Peter", lastname="Wulter", - birth_date=timezone.now().date(), - email=settings.TEST_MAIL, gender=MALE) - self.peter.group.add(self.jl) - self.peter.group.add(self.alp) - self.peter.save() - - self.lara = Member.objects.create(prename="Lara", lastname="Wallis", birth_date=timezone.now().date(), - email=INTERNAL_EMAIL, gender=DIVERSE) - self.lara.group.add(self.alp) - self.lara.save() - self.fridolin = Member.objects.create(prename="Fridolin", lastname="Spargel", birth_date=timezone.now().date(), - email=settings.TEST_MAIL, gender=MALE) - self.fridolin.group.add(self.alp) - self.fridolin.group.add(self.spiel) - self.fridolin.save() - - self.lise = Member.objects.create(prename="Lise", lastname="Lotte", birth_date=timezone.now().date(), - email=settings.TEST_MAIL, gender=FEMALE) - self.alp.leiters.add(self.lise) - self.alp.save() - - class MemberTestCase(BasicMemberTestCase): def setUp(self): super().setUp() @@ -693,60 +618,13 @@ class FreizeitTestCase(BasicMemberTestCase): self.ex2.save() def _setup_test_sjr_application_numbers(self, n_yl, n_b27_local, n_b27_non_local): - for i in range(n_yl): - m = Member.objects.create(prename='Peter {}'.format(i), - lastname='Wulter', - birth_date=datetime.datetime.today() - relativedelta(years=50), - email=settings.TEST_MAIL, - gender=FEMALE) - self.ex.jugendleiter.add(m) - NewMemberOnList.objects.create(member=m, comments='a', memberlist=self.ex) - for i in range(n_b27_local): - m = Member.objects.create(prename='Lise {}'.format(i), - lastname='Walter', - birth_date=datetime.datetime.today() - relativedelta(years=10), - town=settings.SEKTION, - email=settings.TEST_MAIL, - gender=FEMALE) - NewMemberOnList.objects.create(member=m, comments='a', memberlist=self.ex) - for i in range(n_b27_non_local): - m = Member.objects.create(prename='Lise {}'.format(i), - lastname='Walter', - birth_date=datetime.datetime.today() - relativedelta(years=10), - email=settings.TEST_MAIL, - gender=FEMALE) - NewMemberOnList.objects.create(member=m, comments='a', memberlist=self.ex) + add_memberonlist_by_local(self.ex, n_yl, n_b27_local, n_b27_non_local) def _setup_test_ljp_participant_count(self, n_yl, n_correct_age, n_too_old): - for i in range(n_yl): - # a 50 years old - m = Member.objects.create(prename='Peter {}'.format(i), - lastname='Wulter', - birth_date=datetime.datetime.today() - relativedelta(years=50), - email=settings.TEST_MAIL, - gender=FEMALE) - self.ex.jugendleiter.add(m) - for i in range(n_correct_age): - # a 10 years old - m = Member.objects.create(prename='Lise {}'.format(i), - lastname='Walter', - birth_date=datetime.datetime.today() - relativedelta(years=10), - email=settings.TEST_MAIL, - gender=FEMALE) - NewMemberOnList.objects.create(member=m, comments='a', memberlist=self.ex) - for i in range(n_too_old): - # a 27 years old - m = Member.objects.create(prename='Lise {}'.format(i), - lastname='Walter', - birth_date=datetime.datetime.today() - relativedelta(years=27), - email=settings.TEST_MAIL, - gender=FEMALE) - NewMemberOnList.objects.create(member=m, comments='a', memberlist=self.ex) + add_memberonlist_by_age(self.ex, n_yl, n_correct_age, n_too_old) def _cleanup_excursion(self): - # delete all members on excursion for clean up - NewMemberOnList.objects.all().delete() - self.ex.jugendleiter.all().delete() + cleanup_excursion(self.ex) def _test_theoretic_ljp_participant_count_proportion(self, n_yl, n_correct_age, n_too_old): self._setup_test_ljp_participant_count(n_yl, n_correct_age, n_too_old) diff --git a/jdav_web/members/tests/utils.py b/jdav_web/members/tests/utils.py new file mode 100644 index 0000000..f7928e2 --- /dev/null +++ b/jdav_web/members/tests/utils.py @@ -0,0 +1,191 @@ +from http import HTTPStatus + +from django.core.files.uploadedfile import SimpleUploadedFile +from django.contrib.auth import models as authmodels +from django.contrib.admin.sites import AdminSite +from django.contrib.messages import get_messages +from django.contrib.auth.models import User +from django.contrib import admin +from django.core.exceptions import ValidationError +from django.utils.translation import gettext_lazy as _ +from django.test import TestCase, Client, RequestFactory +from django.utils import timezone, translation +from django.conf import settings +from django.urls import reverse +from django import template +from unittest import skip, mock +from members.models import Member, Group, PermissionMember, PermissionGroup, Freizeit, GEMEINSCHAFTS_TOUR,\ + MUSKELKRAFT_ANREISE, FUEHRUNGS_TOUR, AUSBILDUNGS_TOUR, OEFFENTLICHE_ANREISE,\ + FAHRGEMEINSCHAFT_ANREISE,\ + MemberNoteList, NewMemberOnList, confirm_mail_by_key, EmergencyContact, MemberWaitingList,\ + RegistrationPassword, MemberUnconfirmedProxy, InvitationToGroup, DIVERSE, MALE, FEMALE,\ + Klettertreff, KlettertreffAttendee, LJPProposal, ActivityCategory, WEEKDAYS,\ + TrainingCategory, Person +from members.admin import MemberWaitingListAdmin, MemberAdmin, FreizeitAdmin, MemberNoteListAdmin,\ + MemberUnconfirmedAdmin, RegistrationFilter, FilteredMemberFieldMixin,\ + MemberAdminForm, StatementOnListForm, KlettertreffAdmin, GroupAdmin +from members.pdf import fill_pdf_form, render_tex, media_path, serve_pdf, find_template, merge_pdfs +from mailer.models import EmailAddress +from finance.models import Statement, Bill + +from django.db import connection +from django.db.migrations.executor import MigrationExecutor +import random +import datetime +from dateutil.relativedelta import relativedelta +import math +import os.path + + +INTERNAL_EMAIL = "foobar@{domain}".format(domain=settings.ALLOWED_EMAIL_DOMAINS_FOR_INVITE_AS_USER[0]) +REGISTRATION_DATA = { + 'prename': 'Peter', + 'lastname': 'Wulter', + 'street': 'Street 123', + 'plz': '12345 EJ', + 'town': 'Town 1', + 'phone_number': '+49 123456', + 'birth_date': '2010-05-17', + 'gender': '2', + 'email': settings.TEST_MAIL, + 'alternative_email': settings.TEST_MAIL, +} +WAITER_DATA = { + 'prename': 'Peter', + 'lastname': 'Wulter', + 'birth_date': '1999-02-16', + 'gender': '0', + 'email': settings.TEST_MAIL, + 'application_text': 'hoho', +} + + +def create_custom_user(username, groups, prename, lastname): + user = User.objects.create_user( + username=username, password='secret' + ) + member = Member.objects.create(prename=prename, lastname=lastname, birth_date=timezone.localdate(), email=settings.TEST_MAIL, gender=DIVERSE) + member.user = user + member.save() + user.is_staff = True + user.save() + + for group in groups: + g = authmodels.Group.objects.get(name=group) + user.groups.add(g) + return user + + +class BasicMemberTestCase(TestCase): + """ + Utility base class for setting up a test environment for member-related tests. + It creates a few groups and members with different attributes. + """ + def setUp(self): + self.jl = Group.objects.create(name="Jugendleiter") + self.alp = Group.objects.create(name="Alpenfuechse") + self.spiel = Group.objects.create(name="Spielkinder") + + self.fritz = Member.objects.create(prename="Fritz", lastname="Wulter", birth_date=timezone.now().date(), + email=settings.TEST_MAIL, gender=DIVERSE) + self.fritz.group.add(self.jl) + self.fritz.group.add(self.alp) + self.fritz.save() + + em = EmailAddress.objects.create(name='foobar') + self.alp.contact_email = em + self.alp.save() + + self.peter = Member.objects.create(prename="Peter", lastname="Wulter", + birth_date=timezone.now().date(), + email=settings.TEST_MAIL, gender=MALE) + self.peter.group.add(self.jl) + self.peter.group.add(self.alp) + self.peter.save() + + self.lara = Member.objects.create(prename="Lara", lastname="Wallis", birth_date=timezone.now().date(), + email=INTERNAL_EMAIL, gender=DIVERSE) + self.lara.group.add(self.alp) + self.lara.save() + self.fridolin = Member.objects.create(prename="Fridolin", lastname="Spargel", birth_date=timezone.now().date(), + email=settings.TEST_MAIL, gender=MALE) + self.fridolin.group.add(self.alp) + self.fridolin.group.add(self.spiel) + self.fridolin.save() + + self.lise = Member.objects.create(prename="Lise", lastname="Lotte", birth_date=timezone.now().date(), + email=settings.TEST_MAIL, gender=FEMALE) + self.alp.leiters.add(self.lise) + self.alp.save() + + +def add_memberonlist_by_age(excursion, n_yl, n_correct_age, n_too_old): + """ + Utility function for setting up a test environment. Adds `n_yl` youth leaders, + `n_correct_age` members of correct age (i.e. 10 years olds) and + `n_too_old` members that are too old (i.e. 27 years olds) to `excursion`. + """ + for i in range(n_yl): + # a 50 years old + m = Member.objects.create(prename='Peter {}'.format(i), + lastname='Wulter', + birth_date=datetime.datetime.today() - relativedelta(years=50), + email=settings.TEST_MAIL, + gender=FEMALE) + excursion.jugendleiter.add(m) + for i in range(n_correct_age): + # a 10 years old + m = Member.objects.create(prename='Lise {}'.format(i), + lastname='Walter', + birth_date=datetime.datetime.today() - relativedelta(years=10), + email=settings.TEST_MAIL, + gender=FEMALE) + NewMemberOnList.objects.create(member=m, comments='a', memberlist=excursion) + for i in range(n_too_old): + # a 27 years old + m = Member.objects.create(prename='Lise {}'.format(i), + lastname='Walter', + birth_date=datetime.datetime.today() - relativedelta(years=27), + email=settings.TEST_MAIL, + gender=FEMALE) + NewMemberOnList.objects.create(member=m, comments='a', memberlist=excursion) + + +def add_memberonlist_by_local(excursion, n_yl, n_b27_local, n_b27_non_local): + """ + Utility function for setting up a test environment. Adds `n_yl` youth leaders, + `n_b27_local` local members of correct age and + `n_b27_non_local` non-local members of correct age to `excursion`. + """ + for i in range(n_yl): + m = Member.objects.create(prename='Peter {}'.format(i), + lastname='Wulter', + birth_date=datetime.datetime.today() - relativedelta(years=50), + email=settings.TEST_MAIL, + gender=FEMALE) + excursion.jugendleiter.add(m) + NewMemberOnList.objects.create(member=m, comments='a', memberlist=excursion) + for i in range(n_b27_local): + m = Member.objects.create(prename='Lise {}'.format(i), + lastname='Walter', + birth_date=datetime.datetime.today() - relativedelta(years=10), + town=settings.SEKTION, + email=settings.TEST_MAIL, + gender=FEMALE) + NewMemberOnList.objects.create(member=m, comments='a', memberlist=excursion) + for i in range(n_b27_non_local): + m = Member.objects.create(prename='Lise {}'.format(i), + lastname='Walter', + birth_date=datetime.datetime.today() - relativedelta(years=10), + email=settings.TEST_MAIL, + gender=FEMALE) + NewMemberOnList.objects.create(member=m, comments='a', memberlist=excursion) + + +def cleanup_excursion(excursion): + """ + Utility function for cleaning up a test environment. Deletes all members and + youth leaders from `excursion`. + """ + excursion.membersonlist.all().delete() + excursion.jugendleiter.all().delete()