diff --git a/jdav_web/members/tests.py b/jdav_web/members/tests.py index 2f86987..596fcbf 100644 --- a/jdav_web/members/tests.py +++ b/jdav_web/members/tests.py @@ -1,7 +1,11 @@ +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.utils.translation import gettext_lazy as _ from django.test import TestCase, Client, RequestFactory from django.utils import timezone, translation from django.conf import settings @@ -9,8 +13,10 @@ from django.urls import reverse from unittest import skip, mock from .models import Member, Group, PermissionMember, PermissionGroup, Freizeit, GEMEINSCHAFTS_TOUR, MUSKELKRAFT_ANREISE,\ MemberNoteList, NewMemberOnList, confirm_mail_by_key, EmergencyContact, MemberWaitingList,\ - DIVERSE, MALE, FEMALE + RegistrationPassword, MemberUnconfirmedProxy, InvitationToGroup, DIVERSE, MALE, FEMALE from .admin import MemberWaitingListAdmin, MemberAdmin, FreizeitAdmin +from mailer.models import EmailAddress + from django.db import connection from django.db.migrations.executor import MigrationExecutor import random @@ -19,6 +25,41 @@ from dateutil.relativedelta import relativedelta import math +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', + 'emergencycontact_set-MIN_NUM_FORMS': '1', + 'emergencycontact_set-MAX_NUM_FORMS': '1000', + 'emergencycontact_set-0-prename': 'Papa', + 'emergencycontact_set-0-lastname': 'Wulter', + 'emergencycontact_set-0-email': settings.TEST_MAIL, + 'emergencycontact_set-0-phone_number': '-49 124125', + 'emergencycontact_set-0-id': '', + 'emergencycontact_set-0-DELETE': '', + 'emergencycontact_set-0-member': '', +} + + def create_custom_user(username, groups, prename, lastname): user = User.objects.create_user( username=username, password='secret' @@ -47,6 +88,10 @@ class BasicMemberTestCase(TestCase): 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) @@ -625,3 +670,420 @@ class MailConfirmationTestCase(BasicMemberTestCase): for em in self.fritz.emergencycontact_set.all(): self.assertTrue(em.confirmed_mail, msg='Mail of every emergency contact should be confirmed after manually confirming.') + + +class RegisterWaitingListViewTestCase(BasicMemberTestCase): + def test_register_waiting_list_get(self): + url = reverse('members:register_waiting_list') + response = self.client.get(url) + self.assertEqual(response.status_code, HTTPStatus.OK) + + def test_register_waiting_list_post(self): + url = reverse('members:register_waiting_list') + response = self.client.post(url, data=dict(WAITER_DATA, save='')) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _("Your registration for the waiting list was successful.")) + + def test_register_waiting_list_post_invalid(self): + url = reverse('members:register_waiting_list') + response = self.client.post(url, data={ + 'save': '', + }) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _("This field is required.")) + + # this is required to bump the test coverage, but this is probably dead code + response = self.client.post(url, data={}) + self.assertEqual(response.status_code, HTTPStatus.OK) + + +class RegisterViewTestCase(BasicMemberTestCase): + REGISTRATION_PASSWORD = "foobar" + + def setUp(self): + super().setUp() + RegistrationPassword.objects.create(group=self.alp, + password=RegisterViewTestCase.REGISTRATION_PASSWORD) + + def test_register_password_get(self): + url = reverse('members:register') + response = self.client.get(url) + self.assertEqual(response.status_code, HTTPStatus.OK) + + def test_register_password_post(self): + url = reverse('members:register') + response = self.client.post(url, data={ + 'password': RegisterViewTestCase.REGISTRATION_PASSWORD, + }) + self.assertEqual(response.status_code, HTTPStatus.OK) + + def test_register_password_post_save(self): + url = reverse('members:register') + response = self.client.post(url, data=dict( + REGISTRATION_DATA, + **EMERGENCY_CONTACT_DATA, + password=RegisterViewTestCase.REGISTRATION_PASSWORD, + save='', + )) + self.assertEqual(response.status_code, HTTPStatus.FOUND) + reg = MemberUnconfirmedProxy.objects.get(prename='Peter', lastname='Wulter', town='Town 1') + self.assertEqual(reg.street, 'Street 123') + + def test_register_password_post_incomplete(self): + url = reverse('members:register') + response = self.client.post(url, data={ + 'password': RegisterViewTestCase.REGISTRATION_PASSWORD, + 'save': '', + }) + self.assertEqual(response.status_code, HTTPStatus.OK) + + def test_register_password_post_missing_emergency_contact(self): + url = reverse('members:register') + response = self.client.post(url, data=dict( + REGISTRATION_DATA, + password=RegisterViewTestCase.REGISTRATION_PASSWORD, + save='', + )) + self.assertEqual(response.status_code, HTTPStatus.OK) + + def test_register_password_post_invalid(self): + url = reverse('members:register') + response = self.client.post(url, data={ + 'password': RegisterViewTestCase.REGISTRATION_PASSWORD + "_", + }) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _("The entered password is wrong.")) + + +class UploadRegistrationFormViewTestCase(BasicMemberTestCase): + def setUp(self): + super().setUp() + self.reg = MemberUnconfirmedProxy.objects.create(**REGISTRATION_DATA) + self.reg.create_from_registration(None, self.alp) + + def test_upload_registration_form_get(self): + url = self.reg.get_upload_registration_form_link() + response = self.client.get(url) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _('If you are not an adult yet, please let someone responsible for you sign the agreement.')) + + def test_upload_registration_form_get_invalid(self): + url = reverse('members:upload_registration_form') + response = self.client.get(url) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _('The supplied key for uploading a registration form is invalid.')) + + url = reverse('members:upload_registration_form') + '?key=foobar' + response = self.client.get(url) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _('The supplied key for uploading a registration form is invalid.')) + + def test_upload_registration_form_post_no_key(self): + url = reverse('members:upload_registration_form') + # no key + response = self.client.post(url, data={}) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _('The supplied key for uploading a registration form is invalid.')) + # invalid key + response = self.client.post(url, data={'key': 'foobar'}) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _('The supplied key for uploading a registration form is invalid.')) + + def test_upload_registration_form_post_incomplete(self): + url = reverse('members:upload_registration_form') + response = self.client.post(url, data={ + 'key': self.reg.upload_registration_form_key, + }) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _("This field is required.")) + + def test_upload_registration_form_post(self): + url = reverse('members:upload_registration_form') + file = SimpleUploadedFile("form.pdf", b"file_content", content_type="application/pdf") + response = self.client.post(url, data={ + 'key': self.reg.upload_registration_form_key, + 'registration_form': file, + }) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, + _("Thank you for uploading the registration form. Our team will process your registration shortly.")) + +class DownloadRegistrationFormViewTestCase(BasicMemberTestCase): + def setUp(self): + super().setUp() + self.reg = MemberUnconfirmedProxy.objects.create(**REGISTRATION_DATA) + self.reg.create_from_registration(None, self.alp) + + def test_download_registration_form_get_invalid(self): + url = reverse('members:download_registration_form') + response = self.client.get(url) + self.assertEqual(response.status_code, HTTPStatus.OK) + # this is how it is implemented, but it is questionable if this is the correct behaviour + self.assertContains(response, _('The supplied key for uploading a registration form is invalid.')) + + response = self.client.get(url, data={'key': 'foobar'}) + self.assertEqual(response.status_code, HTTPStatus.OK) + # this is how it is implemented, but it is questionable if this is the correct behaviour + self.assertContains(response, _('The supplied key for uploading a registration form is invalid.')) + + def test_download_registration_form_get(self): + url = reverse('members:download_registration_form') + response = self.client.get(url, data={'key': self.reg.upload_registration_form_key}) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertEqual(response.headers['Content-Type'], 'application/pdf') + + +class RegistrationFromWaiterViewTestCase(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_register_post_waiter_key_invalid(self): + url = reverse('members:register') + response = self.client.post(url, data={ + 'waiter_key': 'foobar', + }) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _('Something went wrong while processing your registration.')) + + def test_register_post(self): + url = reverse('members:register') + response = self.client.post(url, data=dict( + REGISTRATION_DATA, + **EMERGENCY_CONTACT_DATA, + waiter_key=self.invitation.key, + save='', + )) + self.assertEqual(response.status_code, HTTPStatus.FOUND) + + def test_register_post_invalid(self): + url = reverse('members:register') + response = self.client.post(url, data=dict( + REGISTRATION_DATA, + waiter_key=self.invitation.key, + save='', + )) + self.assertEqual(response.status_code, HTTPStatus.OK) + + @skip("This currently throws an 'AttributeError'.") + def test_register_post_no_save(self): + url = reverse('members:register') + response = self.client.post(url, data=dict( + waiter_key=self.invitation.key, + )) + self.assertEqual(response.status_code, HTTPStatus.OK) + + +class InvitationToGroupViewTestCase(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 _assert_reject_invalid(self, response): + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _('This invitation is invalid or expired.')) + + def test_accept_get_no_key(self): + url = reverse('members:registration') + response = self.client.get(url) + self.assertEqual(response.status_code, HTTPStatus.OK) + + def test_accept_get_invalid(self): + url = reverse('members:registration') + response = self.client.get(url, data={'key': 'foobar'}) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _('invalid')) + + url = reverse('members:registration') + self.invitation.rejected = True + self.invitation.save() + response = self.client.get(url, data={'key': self.invitation.key}) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _('expired')) + + def test_accept_get(self): + url = reverse('members:registration') + response = self.client.get(url, data={'key': self.invitation.key}) + self.assertEqual(response.status_code, HTTPStatus.OK) + + def test_reject_get(self): + url = reverse('members:reject_invitation') + response = self.client.get(url, data={'key': self.invitation.key}) + self.assertEqual(response.status_code, HTTPStatus.OK) + + def test_reject_get_invalid(self): + url = reverse('members:reject_invitation') + response = self.client.get(url, data={'key': 'foobar'}) + self._assert_reject_invalid(response) + + self.invitation.rejected = True + self.invitation.save() + response = self.client.get(url, data={'key': self.invitation.key}) + self._assert_reject_invalid(response) + + def test_reject_post_invalid(self): + url = reverse('members:reject_invitation') + response = self.client.post(url) + self._assert_reject_invalid(response) + response = self.client.post(url, data={'key': 'foobar'}) + self._assert_reject_invalid(response) + response = self.client.post(url, data={'key': self.invitation.key}) + self._assert_reject_invalid(response) + + def test_reject_post_reject(self): + url = reverse('members:reject_invitation') + response = self.client.post(url, data={ + 'key': self.invitation.key, + 'reject_invitation': '', + }) + self.assertEqual(response.status_code, HTTPStatus.OK) + + def test_reject_post_leave(self): + url = reverse('members:reject_invitation') + response = self.client.post(url, data={ + 'key': self.invitation.key, + 'leave_waitinglist': '', + }) + self.assertEqual(response.status_code, HTTPStatus.OK) + + +class ConfirmWaitingViewTestCase(BasicMemberTestCase): + def setUp(self): + super().setUp() + self.waiter = MemberWaitingList.objects.create(**WAITER_DATA) + self.waiter.ask_for_wait_confirmation() + self.key = self.waiter.generate_wait_confirmation_key() + + def test_get_no_key(self): + url = reverse('members:confirm_waiting') + response = self.client.get(url) + self.assertEqual(response.status_code, HTTPStatus.FOUND) + + url = reverse('members:confirm_waiting') + response = self.client.get(url, data={'key': 'foobar'}) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _('The supplied link is invalid.')) + + def test_get(self): + url = reverse('members:confirm_waiting') + response = self.client.get(url, data={'key': self.key}) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _('Waiting confirmed')) + + # modify the POST data, otherwise the request is cached + response = self.client.get(url, data={'key': self.key, 'foo': 'bar'}) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _('Waiting confirmed')) + + @skip("This currently fails, because `last_wait_confirmation` has `auto_now=True`, which is wrong.") + def test_get_expired(self): + self.waiter.last_wait_confirmation = datetime.date(1900, 1, 1) + self.waiter.save() + # after setting the last wait confirmation to an old date, the waiting status + # should be unconfirmed + self.assertFalse(self.waiter.waiting_confirmed()) + + url = reverse('members:confirm_waiting') + self.waiter.wait_confirmation_key_expire = timezone.now() - timezone.timedelta(days=10) + self.waiter.save() + response = self.client.get(url, data={'key': self.key}) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _('rejoin the waiting list')) + + +class MailConfirmationViewTestCase(BasicMemberTestCase): + def setUp(self): + super().setUp() + self.waiter = MemberWaitingList.objects.create(**WAITER_DATA) + self.waiter.request_mail_confirmation() + + def test_get_invalid(self): + url = reverse('members:confirm_mail') + response = self.client.get(url) + self.assertEqual(response.status_code, HTTPStatus.FOUND) + + url = reverse('members:confirm_mail') + response = self.client.get(url, data={'key': 'foobar'}) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _("Mail confirmation failed")) + + def test_get(self): + url = reverse('members:confirm_mail') + response = self.client.get(url, {'key': self.waiter.confirm_mail_key}) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _("Mail confirmed")) + +class EchoViewTestCase(BasicMemberTestCase): + def setUp(self): + super().setUp() + self.key = self.fritz.generate_echo_key() + + def _assert_failed(self, response): + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _('Echo failed')) + + def test_get_invalid(self): + url = reverse('members:echo') + response = self.client.get(url) + self.assertEqual(response.status_code, HTTPStatus.FOUND) + + url = reverse('members:echo') + response = self.client.get(url, data={'key': 'foobar'}) + self._assert_failed(response) + + def test_get(self): + url = reverse('members:echo') + response = self.client.get(url, data={'key': self.key}) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _('Thanks for echoing back. Please enter the password, which you can find in the email we sent you.\n')) + + def test_post_invalid(self): + url = reverse('members:echo') + # no key + response = self.client.post(url) + self._assert_failed(response) + # wrong key + response = self.client.post(url, data={'key': 'foobar', 'password': self.fritz.echo_password}) + self._assert_failed(response) + # wrong password + response = self.client.post(url, data={'key': self.key, 'password': 'foobar'}) + self.assertContains(response, _('The entered password is wrong.')) + # expired key + self.fritz.echo_expire = timezone.now() - timezone.timedelta(days=settings.ECHO_GRACE_PERIOD) + self.fritz.save() + response = self.client.post(url, data={'key': self.key, 'password': self.fritz.echo_password}) + self._assert_failed(response) + + def test_post(self): + url = reverse('members:echo') + response = self.client.post(url, data={'key': self.key, 'password': self.fritz.echo_password}) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _('Thanks for echoing back. Here is your current data:')) + + def test_post_save(self): + url = reverse('members:echo') + # provide data, but no emergency contacts + response = self.client.post(url, data=dict( + REGISTRATION_DATA, + key=self.key, + password=self.fritz.echo_password, + save='', + )) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _('Thanks for echoing back. Here is your current data:')) + + # provide everything correctly + url = reverse('members:echo') + response = self.client.post(url, data=dict( + REGISTRATION_DATA, + **EMERGENCY_CONTACT_DATA, + key=self.key, + password=self.fritz.echo_password, + save='', + )) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertContains(response, _('Your data was successfully updated.'))