diff --git a/gestioncof/autocomplete.py b/gestioncof/autocomplete.py
index 1d60cd78..d6483869 100644
--- a/gestioncof/autocomplete.py
+++ b/gestioncof/autocomplete.py
@@ -19,6 +19,13 @@ class Clipper(object):
self.clipper = clipper
self.fullname = fullname
+ def __str__(self):
+ return '{} ({})'.format(self.clipper, self.fullname)
+
+ def __eq__(self, other):
+ return (
+ self.clipper == other.clipper and self.fullname == other.fullname)
+
@buro_required
def autocomplete(request):
diff --git a/gestioncof/tests/test_views.py b/gestioncof/tests/test_views.py
index f6dd7eb9..1ab6a5f4 100644
--- a/gestioncof/tests/test_views.py
+++ b/gestioncof/tests/test_views.py
@@ -3,19 +3,344 @@ import uuid
from datetime import timedelta
from django.contrib import messages
+from django.contrib.auth import get_user_model
from django.contrib.messages.api import get_messages
from django.contrib.messages.storage.base import Message
-from django.test import Client, TestCase
+from django.core import mail
+from django.core.management import call_command
+from django.test import Client, TestCase, override_settings
from django.urls import reverse
from bda.models import Salle, Tirage
+from gestioncof.autocomplete import Clipper
from gestioncof.models import (
CalendarSubscription, Club, Event, Survey, SurveyAnswer
)
from gestioncof.tests.testcases import ViewTestCaseMixin
+from custommail.models import CustomMail
+
from .utils import create_member, create_root, create_user
+User = get_user_model()
+
+
+class RegistrationViewTests(ViewTestCaseMixin, TestCase):
+ url_name = 'registration'
+ url_expected = '/registration'
+
+ http_methods = ['GET', 'POST']
+
+ auth_user = 'staff'
+ auth_forbidden = [None, 'user', 'member']
+
+ def requires_mails(self):
+ call_command('syncmails', verbosity=0)
+
+ def test_get(self):
+ r = self.client.get(self.url)
+ self.assertEqual(r.status_code, 200)
+
+ @property
+ def _minimal_data(self):
+ return {
+ 'first_name': '',
+ 'last_name': '',
+ 'email': '',
+
+ # 'is_cof': '1',
+ 'login_clipper': '',
+ 'phone': '',
+ 'occupation': '1A',
+ 'departement': '',
+ 'type_cotiz': 'normalien',
+ 'comments': '',
+
+ # 'user_exists': '1',
+
+ 'events-TOTAL_FORMS': '0',
+ 'events-INITIAL_FORMS': '0',
+ 'events-MIN_NUM_FORMS': '0',
+ 'events-MAX_NUM_FORMS': '1000',
+ }
+
+ def test_post_new(self):
+ self.requires_mails()
+
+ r = self.client.post(self.url, dict(self._minimal_data, **{
+ 'username': 'username',
+ 'first_name': 'first',
+ 'last_name': 'last',
+ 'email': 'username@mail.net',
+ 'is_cof': '1',
+ }))
+
+ self.assertEqual(r.status_code, 200)
+ u = User.objects.get(username='username')
+ expected_message = Message(messages.SUCCESS, (
+ "L'inscription de first last (username@mail.net) a été "
+ "enregistrée avec succès.\n"
+ "Il est désormais membre du COF n°{} !"
+ .format(u.pk)
+ ))
+ self.assertIn(expected_message, get_messages(r.wsgi_request))
+
+ self.assertEqual(u.first_name, 'first')
+ self.assertEqual(u.last_name, 'last')
+ self.assertEqual(u.email, 'username@mail.net')
+
+ def test_post_edit(self):
+ self.requires_mails()
+ u = self.users['user']
+
+ r = self.client.post(self.url, dict(self._minimal_data, **{
+ 'username': 'user',
+ 'first_name': 'first',
+ 'last_name': 'last',
+ 'email': 'user@mail.net',
+ 'is_cof': '1',
+ 'user_exists': '1',
+ }))
+
+ self.assertEqual(r.status_code, 200)
+ u.refresh_from_db()
+ expected_message = Message(messages.SUCCESS, (
+ "L'inscription de first last (user@mail.net) a été "
+ "enregistrée avec succès.\n"
+ "Il est désormais membre du COF n°{} !"
+ .format(u.pk)
+ ))
+ self.assertIn(expected_message, get_messages(r.wsgi_request))
+
+ self.assertEqual(u.first_name, 'first')
+ self.assertEqual(u.last_name, 'last')
+ self.assertEqual(u.email, 'user@mail.net')
+
+ def _test_mail_welcome(self, was_cof, is_cof, expect_mail):
+ self.requires_mails()
+ u = self.users['member'] if was_cof else self.users['user']
+
+ data = dict(self._minimal_data, **{
+ 'username': u.username,
+ 'email': 'user@mail.net',
+ 'user_exists': '1',
+ })
+ if is_cof:
+ data['is_cof'] = '1'
+ self.client.post(self.url, data)
+
+ u.refresh_from_db()
+
+ def _is_sent():
+ cm = CustomMail.objects.get(shortname='welcome')
+ welcome_msg = cm.get_message({'member': u})
+ for m in mail.outbox:
+ if m.subject == welcome_msg.subject:
+ return True
+ return False
+
+ self.assertEqual(_is_sent(), expect_mail)
+
+ def test_mail_welcome_0(self):
+ self._test_mail_welcome(was_cof=False, is_cof=False, expect_mail=False)
+
+ def test_mail_welcome_1(self):
+ self._test_mail_welcome(was_cof=False, is_cof=True, expect_mail=True)
+
+ def test_mail_welcome_2(self):
+ self._test_mail_welcome(was_cof=True, is_cof=False, expect_mail=False)
+
+ def test_mail_welcome_3(self):
+ self._test_mail_welcome(was_cof=True, is_cof=True, expect_mail=False)
+
+ def test_events(self):
+ e = Event.objects.create()
+
+ cf1 = e.commentfields.create(name='Comment Field 1')
+ cf2 = e.commentfields.create(
+ name='Comment Field 2', fieldtype='char',
+ )
+
+ o1 = e.options.create(name='Option 1')
+ o2 = e.options.create(name='Option 2', multi_choices=True)
+
+ oc1 = o1.choices.create(value='O1 - Choice 1')
+ o1.choices.create(value='O1 - Choice 2')
+ oc3 = o2.choices.create(value='O2 - Choice 1')
+ o2.choices.create(value='O2 - Choice 2')
+
+ self.client.post(self.url, dict(self._minimal_data, **{
+ 'username': 'user',
+ 'user_exists': '1',
+ 'events-TOTAL_FORMS': '1',
+ 'events-INITIAL_FORMS': '0',
+ 'events-MIN_NUM_FORMS': '0',
+ 'events-MAX_NUM_FORMS': '1000',
+ 'events-0-status': 'paid',
+ 'events-0-option_{}'.format(o1.pk): [str(oc1.pk)],
+ 'events-0-option_{}'.format(o2.pk): [str(oc3.pk)],
+ 'events-0-comment_{}'.format(cf1.pk): 'comment 1',
+ 'events-0-comment_{}'.format(cf2.pk): '',
+ }))
+
+ er = e.eventregistration_set.get(user=self.users['user'])
+ self.assertQuerysetEqual(
+ er.options.all(), map(repr, [oc1, oc3]),
+ ordered=False,
+ )
+ self.assertCountEqual(er.comments.values_list('content', flat=True), [
+ 'comment 1',
+ ])
+
+
+class RegistrationFormViewTests(ViewTestCaseMixin, TestCase):
+ urls_conf = [
+ {
+ 'name': 'empty-registration',
+ 'expected': '/registration/empty',
+ },
+ {
+ 'name': 'user-registration',
+ 'kwargs': {'username': 'user'},
+ 'expected': '/registration/user/user',
+ },
+ {
+ 'name': 'clipper-registration',
+ 'kwargs': {
+ 'login_clipper': 'uid',
+ 'fullname': 'First Last1 Last2',
+ },
+ 'expected': '/registration/clipper/uid/First%20Last1%20Last2',
+ },
+ ]
+
+ auth_user = 'staff'
+ auth_forbidden = [None, 'user', 'member']
+
+ def test_empty(self):
+ r = self.client.get(self.t_urls[0])
+
+ self.assertIn('user_form', r.context)
+ self.assertIn('profile_form', r.context)
+ self.assertIn('event_formset', r.context)
+ self.assertIn('clubs_form', r.context)
+
+ def test_username(self):
+ u = self.users['user']
+ u.first_name = 'first'
+ u.last_name = 'last'
+ u.save()
+
+ r = self.client.get(self.t_urls[1])
+
+ self.assertIn('user_form', r.context)
+ self.assertIn('profile_form', r.context)
+ self.assertIn('event_formset', r.context)
+ self.assertIn('clubs_form', r.context)
+ user_form = r.context['user_form']
+ self.assertEqual(user_form['username'].initial, 'user')
+ self.assertEqual(user_form['first_name'].initial, 'first')
+ self.assertEqual(user_form['last_name'].initial, 'last')
+
+ def test_clipper(self):
+ r = self.client.get(self.t_urls[2])
+
+ self.assertIn('user_form', r.context)
+ self.assertIn('profile_form', r.context)
+ self.assertIn('event_formset', r.context)
+ self.assertIn('clubs_form', r.context)
+ user_form = r.context['user_form']
+ profile_form = r.context['profile_form']
+ self.assertEqual(user_form['first_name'].initial, 'First')
+ self.assertEqual(user_form['last_name'].initial, 'Last1 Last2')
+ self.assertEqual(user_form['email'].initial, 'uid@clipper.ens.fr')
+ self.assertEqual(profile_form['login_clipper'].initial, 'uid')
+
+
+@override_settings(LDAP_SERVER_URL='ldap_url')
+class RegistrationAutocompleteViewTests(ViewTestCaseMixin, TestCase):
+ url_name = 'cof.registration.autocomplete'
+ url_expected = '/autocomplete/registration'
+
+ auth_user = 'staff'
+ auth_forbidden = [None, 'user', 'member']
+
+ def setUp(self):
+ super().setUp()
+
+ self.u1 = create_user('uu_u1', attrs={
+ 'first_name': 'abc', 'last_name': 'xyz',
+ })
+ self.u2 = create_user('uu_u2', attrs={
+ 'first_name': 'wyz', 'last_name': 'abd',
+ })
+ self.m1 = create_member('uu_m1', attrs={
+ 'first_name': 'ebd', 'last_name': 'wyv',
+ })
+
+ self.mockLDAP([])
+
+ def _test(
+ self, query, expected_users, expected_members, expected_clippers,
+ ):
+ r = self.client.get(self.url, {'q': query})
+
+ self.assertEqual(r.status_code, 200)
+
+ self.assertQuerysetEqual(
+ r.context['users'], map(repr, expected_users),
+ ordered=False,
+ )
+ self.assertQuerysetEqual(
+ r.context['members'],
+ map(lambda u: repr(u.profile), expected_members),
+ ordered=False,
+ )
+ self.assertCountEqual(
+ map(str, r.context.get('clippers', [])),
+ map(str, expected_clippers),
+ )
+
+ def test_username(self):
+ self._test('uu', [self.u1, self.u2], [self.m1], [])
+
+ def test_firstname(self):
+ self._test('ab', [self.u1, self.u2], [], [])
+
+ def test_lastname(self):
+ self._test('wy', [self.u2], [self.m1], [])
+
+ def test_multi_query(self):
+ self._test('wy bd', [self.u2], [self.m1], [])
+
+ def test_clipper(self):
+ mock_ldap = self.mockLDAP([('uid', 'first last')])
+
+ self._test('aa bb', [], [], [Clipper('uid', 'first last')])
+
+ mock_ldap.search.assert_called_once_with(
+ 'dc=spi,dc=ens,dc=fr',
+ '(&(|(cn=*aa*)(uid=*aa*))(|(cn=*bb*)(uid=*bb*)))',
+ attributes=['uid', 'cn'],
+ )
+
+ def test_clipper_escaped(self):
+ mock_ldap = self.mockLDAP([])
+
+ self._test('; & | (', [], [], [])
+
+ mock_ldap.search.assert_not_called()
+
+ def test_clipper_no_duplicate(self):
+ self.mockLDAP([('uid', 'uu_u1')])
+
+ self._test('uu u1', [self.u1], [], [Clipper('uid', 'uu_u1')])
+
+ self.u1.profile.login_clipper = 'uid'
+ self.u1.profile.save()
+
+ self._test('uu u1', [self.u1], [], [])
+
class HomeViewTests(ViewTestCaseMixin, TestCase):
url_name = 'home'
@@ -458,9 +783,10 @@ class CalendarViewTests(ViewTestCaseMixin, TestCase):
fermeture=self.now,
active=True,
)
- l = Salle.objects.create()
+ location = Salle.objects.create()
s = t.spectacle_set.create(
- date=self.now, price=3.5, slots=20, location=l, listing=True)
+ date=self.now, price=3.5, slots=20, location=location,
+ listing=True)
r = self.client.post(self.url, {'other_shows': [str(s.pk)]})
@@ -491,17 +817,17 @@ class CalendarICSViewTests(ViewTestCaseMixin, TestCase):
fermeture=self.now,
active=True,
)
- l = Salle.objects.create(name='Location')
+ location = Salle.objects.create(name='Location')
self.s1 = self.t.spectacle_set.create(
- price=1, slots=10, location=l, listing=True,
+ price=1, slots=10, location=location, listing=True,
title='Spectacle 1', date=self.now + timedelta(days=1),
)
self.s2 = self.t.spectacle_set.create(
- price=2, slots=20, location=l, listing=True,
+ price=2, slots=20, location=location, listing=True,
title='Spectacle 2', date=self.now + timedelta(days=2),
)
self.s3 = self.t.spectacle_set.create(
- price=3, slots=30, location=l, listing=True,
+ price=3, slots=30, location=location, listing=True,
title='Spectacle 3', date=self.now + timedelta(days=3),
)
diff --git a/shared/tests/testcases.py b/shared/tests/testcases.py
index 6967d9b7..91bd9d38 100644
--- a/shared/tests/testcases.py
+++ b/shared/tests/testcases.py
@@ -95,6 +95,38 @@ class TestCaseMixin:
else:
self.assertEqual(actual, expected)
+ def mockLDAP(self, results):
+ class Elt:
+ def __init__(self, value):
+ self.value = value
+
+ class Entry:
+ def __init__(self, **kwargs):
+ for k, v in kwargs.items():
+ setattr(self, k, Elt(v))
+
+ results_as_ldap = [
+ Entry(uid=uid, cn=name) for uid, name in results
+ ]
+
+ mock_connection = mock.MagicMock()
+ mock_connection.entries = results_as_ldap
+
+ # Connection is used as a context manager.
+ mock_context_manager = mock.MagicMock()
+ mock_context_manager.return_value.__enter__.return_value = (
+ mock_connection
+ )
+
+ patcher = mock.patch(
+ 'gestioncof.autocomplete.Connection',
+ new=mock_context_manager,
+ )
+ patcher.start()
+ self.addCleanup(patcher.stop)
+
+ return mock_connection
+
def load_from_csv_response(self, r):
decoded = r.content.decode('utf-8')
return list(csv.reader(decoded.split('\n')[:-1]))