chore(members/tests): various tests

Co-authored by: Claude
pull/154/head^2
Christian Merten 4 months ago
parent 44354cb681
commit 7ea500ebaa
Signed by: christian.merten
GPG Key ID: D953D69721B948B3

@ -1 +1,4 @@
from .basic import * from .basic import *
from .views import *
from .tasks import *
from .rules import *

@ -416,7 +416,7 @@ class AdminTestCase(TestCase):
self.em = EmailAddress.objects.create(name='foobar') self.em = EmailAddress.objects.create(name='foobar')
self.staff = Group.objects.create(name='Jugendleiter', contact_email=self.em) self.staff = Group.objects.create(name='Jugendleiter', contact_email=self.em)
cool_kids = Group.objects.create(name='cool kids') cool_kids = Group.objects.create(name='cool kids', show_website=True)
super_kids = Group.objects.create(name='super kids') super_kids = Group.objects.create(name='super kids')
p1 = PermissionMember.objects.create(member=paul) p1 = PermissionMember.objects.create(member=paul)

@ -0,0 +1,179 @@
from django.test import TestCase
from django.utils import timezone
from django.contrib.auth.models import User
from ..models import Member, Group, Freizeit, DIVERSE, GEMEINSCHAFTS_TOUR, MemberTraining, TrainingCategory, LJPProposal
from ..rules import is_oneself, may_view, may_change, may_delete, is_own_training, is_leader_of_excursion, is_leader, statement_not_submitted, _is_leader
from finance.models import Statement
from mailer.models import EmailAddress
class RulesTestCase(TestCase):
def setUp(self):
# Create email address for groups
self.email_address = EmailAddress.objects.create(name='test@example.com')
# Create test users and members
self.user1 = User.objects.create_user(username='user1', email='user1@example.com')
self.member1 = Member.objects.create(
prename='Test',
lastname='Member1',
birth_date=timezone.now().date(),
email='member1@example.com',
gender=DIVERSE
)
self.user1.member = self.member1
self.user1.save()
self.user2 = User.objects.create_user(username='user2', email='user2@example.com')
self.member2 = Member.objects.create(
prename='Test',
lastname='Member2',
birth_date=timezone.now().date(),
email='member2@example.com',
gender=DIVERSE
)
self.user2.member = self.member2
self.user2.save()
self.user3 = User.objects.create_user(username='user3', email='user3@example.com')
self.member3 = Member.objects.create(
prename='Test',
lastname='Member3',
birth_date=timezone.now().date(),
email='member3@example.com',
gender=DIVERSE
)
self.user3.member = self.member3
self.user3.save()
# Create test group
self.group = Group.objects.create(name='Test Group')
self.group.contact_email = self.email_address
self.group.leiters.add(self.member2)
self.group.save()
# Create test excursion
self.excursion = Freizeit.objects.create(
name='Test Excursion',
tour_type=GEMEINSCHAFTS_TOUR,
kilometers_traveled=10,
difficulty=1
)
self.excursion.jugendleiter.add(self.member1)
self.excursion.groups.add(self.group)
self.excursion.save()
# Create training category and training
self.training_category = TrainingCategory.objects.create(
name='Test Training',
permission_needed=False
)
self.training = MemberTraining.objects.create(
member=self.member1,
title='Test Training',
category=self.training_category,
participated=True,
passed=True
)
# Create LJP proposal
self.ljp_proposal = LJPProposal.objects.create(
title='Test LJP',
excursion=self.excursion
)
# Create statement
self.statement_unsubmitted = Statement.objects.create(
short_description='Unsubmitted Statement',
excursion=self.excursion,
submitted=False
)
self.statement_submitted = Statement.objects.create(
short_description='Submitted Statement',
submitted=True
)
def test_is_oneself(self):
"""Test is_oneself rule - member can identify themselves."""
# Same member
self.assertTrue(is_oneself(self.user1, self.member1))
# Different members
self.assertFalse(is_oneself(self.user1, self.member2))
def test_may(self):
"""Test `may_` rules."""
self.assertTrue(may_view(self.user1, self.member1))
self.assertTrue(may_change(self.user1, self.member1))
self.assertTrue(may_delete(self.user1, self.member1))
def test_is_own_training(self):
"""Test is_own_training rule - member can access their own training."""
# Own training
self.assertTrue(is_own_training(self.user1, self.training))
# Other member's training
self.assertFalse(is_own_training(self.user2, self.training))
def test_is_leader_of_excursion(self):
"""Test is_leader_of_excursion rule for LJP proposals."""
# LJP proposal with excursion - member3 is not a leader
self.assertFalse(is_leader_of_excursion(self.user3, self.ljp_proposal))
# Directly pass an excursion
self.assertTrue(is_leader_of_excursion(self.user1, self.excursion))
def test_is_leader(self):
"""Test is_leader rule for excursions."""
# Direct excursion leader
self.assertTrue(is_leader(self.user1, self.excursion))
# Group leader (member2 is leader of group that is part of excursion)
self.assertTrue(is_leader(self.user2, self.excursion))
# member3 is unrelated
self.assertFalse(is_leader(self.user3, self.excursion))
# Test user without member attribute
user_no_member = User.objects.create_user(username='nomember', email='nomember@example.com')
self.assertFalse(is_leader(user_no_member, self.excursion))
# Test member without pk attribute
class MemberNoPk:
pass
member_no_pk = MemberNoPk()
self.assertFalse(_is_leader(member_no_pk, self.excursion))
# Test member with None pk
class MemberNonePk:
pk = None
member_none_pk = MemberNonePk()
self.assertFalse(_is_leader(member_none_pk, self.excursion))
def test_statement_not_submitted(self):
"""Test statement_not_submitted rule."""
# Unsubmitted statement with excursion
self.assertTrue(statement_not_submitted(self.user1, self.excursion))
# Submitted statement
self.excursion.statement = self.statement_submitted
self.excursion.save()
self.assertFalse(statement_not_submitted(self.user1, self.excursion))
# Excursion without statement
excursion_no_statement = Freizeit.objects.create(
name='No Statement Excursion',
tour_type=GEMEINSCHAFTS_TOUR,
kilometers_traveled=10,
difficulty=1
)
self.assertFalse(statement_not_submitted(self.user1, excursion_no_statement))
# Test the excursion.statement is None case
# Create a special test object to directly trigger
class ExcursionWithNoneStatement:
def __init__(self):
self.statement = None
# if excursion.statement is None: return False
self.assertFalse(statement_not_submitted(self.user1, ExcursionWithNoneStatement()))

@ -0,0 +1,141 @@
from unittest.mock import patch, MagicMock
from django.test import TestCase
from django.utils import timezone
from django.conf import settings
from ..models import MemberWaitingList, Freizeit, Group, DIVERSE, GEMEINSCHAFTS_TOUR
from ..tasks import ask_for_waiting_confirmation, send_crisis_intervention_list, send_notification_crisis_intervention_list
from mailer.models import EmailAddress
class TasksTestCase(TestCase):
def setUp(self):
# Create test email address
self.email_address = EmailAddress.objects.create(name='test@example.com')
# Create test group
self.group = Group.objects.create(name='Test Group')
self.group.contact_email = self.email_address
self.group.save()
# Create test waiters
now = timezone.now()
old_confirmation = now - timezone.timedelta(days=settings.WAITING_CONFIRMATION_FREQUENCY + 1)
old_reminder = now - timezone.timedelta(days=settings.CONFIRMATION_REMINDER_FREQUENCY + 1)
self.waiter1 = MemberWaitingList.objects.create(
prename='Test',
lastname='Waiter1',
birth_date=now.date(),
email='waiter1@example.com',
gender=DIVERSE,
last_wait_confirmation=old_confirmation,
last_reminder=old_reminder,
sent_reminders=0
)
self.waiter2 = MemberWaitingList.objects.create(
prename='Test',
lastname='Waiter2',
birth_date=now.date(),
email='waiter2@example.com',
gender=DIVERSE,
last_wait_confirmation=old_confirmation,
last_reminder=old_reminder,
sent_reminders=settings.MAX_REMINDER_COUNT - 1
)
# Create waiter that shouldn't receive reminder (too recent confirmation)
self.waiter3 = MemberWaitingList.objects.create(
prename='Test',
lastname='Waiter3',
birth_date=now.date(),
email='waiter3@example.com',
gender=DIVERSE,
last_wait_confirmation=now,
last_reminder=old_reminder,
sent_reminders=0
)
# Create waiter that shouldn't receive reminder (max reminders reached)
self.waiter4 = MemberWaitingList.objects.create(
prename='Test',
lastname='Waiter4',
birth_date=now.date(),
email='waiter4@example.com',
gender=DIVERSE,
last_wait_confirmation=old_confirmation,
last_reminder=old_reminder,
sent_reminders=settings.MAX_REMINDER_COUNT
)
# Create test excursions
today = timezone.now().date()
tomorrow = today + timezone.timedelta(days=1)
self.excursion_today_not_sent = Freizeit.objects.create(
name='Today Excursion 1',
date=timezone.now().replace(hour=10, minute=0, second=0, microsecond=0),
tour_type=GEMEINSCHAFTS_TOUR,
kilometers_traveled=10,
difficulty=1,
crisis_intervention_list_sent=False,
notification_crisis_intervention_list_sent=False
)
self.excursion_today_sent = Freizeit.objects.create(
name='Today Excursion 2',
date=timezone.now().replace(hour=14, minute=0, second=0, microsecond=0),
tour_type=GEMEINSCHAFTS_TOUR,
kilometers_traveled=10,
difficulty=1,
crisis_intervention_list_sent=True,
notification_crisis_intervention_list_sent=True
)
self.excursion_tomorrow_not_sent = Freizeit.objects.create(
name='Tomorrow Excursion 1',
date=(timezone.now() + timezone.timedelta(days=1)).replace(hour=10, minute=0, second=0, microsecond=0),
tour_type=GEMEINSCHAFTS_TOUR,
kilometers_traveled=10,
difficulty=1,
crisis_intervention_list_sent=False,
notification_crisis_intervention_list_sent=False
)
self.excursion_tomorrow_sent = Freizeit.objects.create(
name='Tomorrow Excursion 2',
date=(timezone.now() + timezone.timedelta(days=1)).replace(hour=14, minute=0, second=0, microsecond=0),
tour_type=GEMEINSCHAFTS_TOUR,
kilometers_traveled=10,
difficulty=1,
crisis_intervention_list_sent=True,
notification_crisis_intervention_list_sent=True
)
@patch.object(MemberWaitingList, 'ask_for_wait_confirmation')
def test_ask_for_waiting_confirmation(self, mock_ask):
"""Test ask_for_waiting_confirmation task calls correct waiters."""
result = ask_for_waiting_confirmation()
# Should call ask_for_wait_confirmation for waiter1 and waiter2 only
self.assertEqual(result, 2)
self.assertEqual(mock_ask.call_count, 2)
@patch.object(Freizeit, 'send_crisis_intervention_list')
def test_send_crisis_intervention_list(self, mock_send):
"""Test send_crisis_intervention_list task calls correct excursions."""
result = send_crisis_intervention_list()
# Should call send_crisis_intervention_list for today's excursions that haven't been sent
self.assertEqual(result, 1)
self.assertEqual(mock_send.call_count, 1)
@patch.object(Freizeit, 'notify_leaders_crisis_intervention_list')
def test_send_notification_crisis_intervention_list(self, mock_notify):
"""Test send_notification_crisis_intervention_list task calls correct excursions."""
result = send_notification_crisis_intervention_list()
# Should call notify_leaders_crisis_intervention_list for tomorrow's excursions that haven't been sent
self.assertEqual(result, 1)
self.assertEqual(mock_notify.call_count, 1)

@ -83,7 +83,8 @@ class BasicMemberTestCase(TestCase):
""" """
def setUp(self): def setUp(self):
self.jl = Group.objects.create(name="Jugendleiter", year_from=0, year_to=0) self.jl = Group.objects.create(name="Jugendleiter", year_from=0, year_to=0)
self.alp = Group.objects.create(name="Alpenfuechse", year_from=1900, year_to=2000) self.alp = Group.objects.create(name="Alpenfuechse", year_from=1900, year_to=2000,
show_website=True)
self.spiel = Group.objects.create(name="Spielkinder") self.spiel = Group.objects.create(name="Spielkinder")
self.fritz = Member.objects.create(prename="Fritz", lastname="Wulter", birth_date=timezone.now().date(), self.fritz = Member.objects.create(prename="Fritz", lastname="Wulter", birth_date=timezone.now().date(),

@ -0,0 +1,110 @@
from unittest import skip
from http import HTTPStatus
from django.test import TestCase, Client
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import gettext as _
from mailer.models import EmailAddress
from ..models import Member, Group, InvitationToGroup, MemberWaitingList, DIVERSE
class ConfirmInvitationViewTestCase(TestCase):
def setUp(self):
self.client = Client()
# Create an email address for the group
self.email_address = EmailAddress.objects.create(name='testmail')
# Create a test group
self.group = Group.objects.create(name='Test Group')
self.group.contact_email = self.email_address
self.group.save()
# Create a waiting list entry
self.waiter = MemberWaitingList.objects.create(
prename='Waiter',
lastname='User',
birth_date=timezone.now().date(),
email='waiter@example.com',
gender=DIVERSE,
wait_confirmation_key='test_wait_key',
wait_confirmation_key_expire=timezone.now() + timezone.timedelta(days=1)
)
# Create an invitation
self.invitation = InvitationToGroup.objects.create(
waiter=self.waiter,
group=self.group,
key='test_invitation_key',
date=timezone.now().date()
)
def test_confirm_invitation_get_valid_key(self):
"""Test GET request with valid key shows invitation confirmation page."""
url = reverse('members:confirm_invitation')
response = self.client.get(url, {'key': 'test_invitation_key'})
self.assertEqual(response.status_code, HTTPStatus.OK)
self.assertContains(response, _('Confirm trial group meeting invitation'))
self.assertContains(response, self.group.name)
def test_confirm_invitation_get_invalid_key(self):
"""Test GET request with invalid key shows invalid confirmation page."""
url = reverse('members:confirm_invitation')
# no key
response = self.client.get(url)
self.assertEqual(response.status_code, HTTPStatus.OK)
self.assertContains(response, _('This invitation is invalid or expired.'))
# invalid key
response = self.client.get(url, {'key': 'invalid_key'})
self.assertEqual(response.status_code, HTTPStatus.OK)
self.assertContains(response, _('This invitation is invalid or expired.'))
def test_confirm_invitation_get_rejected_invitation(self):
"""Test GET request with rejected invitation shows invalid confirmation page."""
self.invitation.rejected = True
self.invitation.save()
url = reverse('members:confirm_invitation')
response = self.client.get(url, {'key': self.invitation.key})
self.assertEqual(response.status_code, HTTPStatus.OK)
self.assertContains(response, _('This invitation is invalid or expired.'))
def test_confirm_invitation_get_expired_invitation(self):
"""Test GET request with expired invitation shows invalid confirmation page."""
# Set invitation date to more than 30 days ago to make it expired
self.invitation.date = timezone.now().date() - timezone.timedelta(days=31)
self.invitation.save()
url = reverse('members:confirm_invitation')
response = self.client.get(url, {'key': self.invitation.key})
self.assertEqual(response.status_code, HTTPStatus.OK)
self.assertContains(response, _('This invitation is invalid or expired.'))
def test_confirm_invitation_post_invalid_key(self):
"""Test POST request with invalid key shows invalid confirmation page."""
url = reverse('members:confirm_invitation')
# no key
response = self.client.post(url)
self.assertEqual(response.status_code, HTTPStatus.OK)
self.assertContains(response, _('This invitation is invalid or expired.'))
# invalid key
response = self.client.post(url, {'key': 'invalid_key'})
self.assertEqual(response.status_code, HTTPStatus.OK)
self.assertContains(response, _('This invitation is invalid or expired.'))
def test_confirm_invitation_post_valid_key(self):
"""Test POST request with valid key confirms invitation and shows success page."""
url = reverse('members:confirm_invitation')
response = self.client.post(url, {'key': self.invitation.key})
self.assertEqual(response.status_code, HTTPStatus.OK)
self.assertContains(response, _('Invitation confirmed'))
self.assertContains(response, self.group.name)
# Verify invitation was not marked as rejected (confirm() sets rejected=False)
self.invitation.refresh_from_db()
self.assertFalse(self.invitation.rejected)

@ -9,6 +9,7 @@ from members.models import Member, RegistrationPassword, MemberUnconfirmedProxy,
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.conf import settings from django.conf import settings
from django.views.decorators.cache import never_cache
from .pdf import render_tex, media_path from .pdf import render_tex, media_path
@ -505,6 +506,7 @@ def render_confirm_success(request, invitation):
'timeinfo': invitation.group.get_time_info()}) 'timeinfo': invitation.group.get_time_info()})
@never_cache
def confirm_invitation(request): def confirm_invitation(request):
if request.method == 'GET' and 'key' in request.GET: if request.method == 'GET' and 'key' in request.GET:
key = request.GET['key'] key = request.GET['key']

Loading…
Cancel
Save