forked from DGNum/gestioCOF
Sépare un gros fourre-tout en plus petits mixins
This commit is contained in:
parent
f642b218d0
commit
bbe831a226
3 changed files with 112 additions and 67 deletions
|
@ -17,6 +17,7 @@ from django.urls import reverse
|
||||||
from bda.models import Salle, Tirage
|
from bda.models import Salle, Tirage
|
||||||
from gestioncof.models import CalendarSubscription, Club, Event, Survey, SurveyAnswer
|
from gestioncof.models import CalendarSubscription, Club, Event, Survey, SurveyAnswer
|
||||||
from gestioncof.tests.testcases import ViewTestCaseMixin
|
from gestioncof.tests.testcases import ViewTestCaseMixin
|
||||||
|
from shared.tests.testcases import ICalMixin, MockLDAPMixin
|
||||||
from shared.views.autocomplete import Clipper
|
from shared.views.autocomplete import Clipper
|
||||||
|
|
||||||
from .utils import create_member, create_root, create_user
|
from .utils import create_member, create_root, create_user
|
||||||
|
@ -267,7 +268,7 @@ class RegistrationFormViewTests(ViewTestCaseMixin, TestCase):
|
||||||
|
|
||||||
|
|
||||||
@override_settings(LDAP_SERVER_URL="ldap_url")
|
@override_settings(LDAP_SERVER_URL="ldap_url")
|
||||||
class RegistrationAutocompleteViewTests(ViewTestCaseMixin, TestCase):
|
class RegistrationAutocompleteViewTests(MockLDAPMixin, ViewTestCaseMixin, TestCase):
|
||||||
url_name = "cof.registration.autocomplete"
|
url_name = "cof.registration.autocomplete"
|
||||||
url_expected = "/autocomplete/registration"
|
url_expected = "/autocomplete/registration"
|
||||||
|
|
||||||
|
@ -815,7 +816,7 @@ class CalendarViewTests(ViewTestCaseMixin, TestCase):
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
class CalendarICSViewTests(ViewTestCaseMixin, TestCase):
|
class CalendarICSViewTests(ICalMixin, ViewTestCaseMixin, TestCase):
|
||||||
url_name = "calendar.ics"
|
url_name = "calendar.ics"
|
||||||
|
|
||||||
auth_user = None
|
auth_user = None
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
from shared.tests.testcases import ViewTestCaseMixin as BaseViewTestCaseMixin
|
from shared.tests.testcases import (
|
||||||
|
CSVResponseMixin,
|
||||||
|
ViewTestCaseMixin as BaseViewTestCaseMixin,
|
||||||
|
)
|
||||||
|
|
||||||
from .utils import create_member, create_staff, create_user
|
from .utils import create_member, create_staff, create_user
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,111 @@ from django.utils.functional import cached_property
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class MockLDAPMixin:
|
||||||
|
"""
|
||||||
|
Mixin pour simuler un appel à un serveur LDAP (e.g., celui de l'ENS) dans des
|
||||||
|
tests unitaires. La réponse est une instance d'une classe Entry, qui simule
|
||||||
|
grossièrement l'interface de ldap3.
|
||||||
|
Cette classe patche la méthode magique `__enter__`, le code correspondant doit donc
|
||||||
|
appeler `with Connection(*args, **kwargs) as foo` pour que le test fonctionne.
|
||||||
|
"""
|
||||||
|
|
||||||
|
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(
|
||||||
|
"shared.views.autocomplete.Connection", new=mock_context_manager
|
||||||
|
)
|
||||||
|
patcher.start()
|
||||||
|
self.addCleanup(patcher.stop)
|
||||||
|
|
||||||
|
return mock_connection
|
||||||
|
|
||||||
|
|
||||||
|
class CSVResponseMixin:
|
||||||
|
"""
|
||||||
|
Mixin pour manipuler des réponses données via CSV. Deux choix sont possibles:
|
||||||
|
- si `as_dict=False`, convertit le CSV en une liste de listes (une liste par ligne)
|
||||||
|
- si `as_dict=True`, convertit le CSV en une liste de dicts, avec les champs donnés
|
||||||
|
par la première ligne du CSV.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def load_from_csv_response(self, r, as_dict=False, **reader_kwargs):
|
||||||
|
content = r.content.decode("utf-8")
|
||||||
|
|
||||||
|
# la dernière ligne du fichier CSV est toujours vide
|
||||||
|
content = content.split("\n")[:-1]
|
||||||
|
if as_dict:
|
||||||
|
reader_class = csv.DictReader
|
||||||
|
else:
|
||||||
|
reader_class = csv.reader
|
||||||
|
|
||||||
|
return list(reader_class(content, **reader_kwargs))
|
||||||
|
|
||||||
|
|
||||||
|
class ICalMixin:
|
||||||
|
"""
|
||||||
|
Mixin pour manipuler des iCalendars. Permet de tester l'égalité entre
|
||||||
|
in iCal d'une part, et une liste d'évènements (représentés par des dicts)
|
||||||
|
d'autre part.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _test_event_equal(self, event, exp):
|
||||||
|
"""
|
||||||
|
Les éléments du dict peuvent être de deux types:
|
||||||
|
- un tuple `(getter, expected_value)`, auquel cas on teste l'égalité
|
||||||
|
`getter(event[key]) == value)`;
|
||||||
|
- une variable `value` de n'importe quel autre type, auquel cas on teste
|
||||||
|
`event[key] == value`.
|
||||||
|
"""
|
||||||
|
for key, value in exp.items():
|
||||||
|
if isinstance(value, tuple):
|
||||||
|
getter = value[0]
|
||||||
|
v = value[1]
|
||||||
|
else:
|
||||||
|
getter = lambda v: v
|
||||||
|
v = value
|
||||||
|
# dans un iCal, les fields sont en majuscules
|
||||||
|
if getter(event[key.upper()]) != v:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _find_event(self, ev, l):
|
||||||
|
for i, elt in enumerate(l):
|
||||||
|
if self._test_event_equal(ev, elt):
|
||||||
|
return elt, i
|
||||||
|
return False, -1
|
||||||
|
|
||||||
|
def assertCalEqual(self, ical_content, expected):
|
||||||
|
remaining = expected.copy()
|
||||||
|
unexpected = []
|
||||||
|
|
||||||
|
cal = icalendar.Calendar.from_ical(ical_content)
|
||||||
|
|
||||||
|
for ev in cal.walk("vevent"):
|
||||||
|
found, i_found = self._find_event(ev, remaining)
|
||||||
|
if found:
|
||||||
|
remaining.pop(i_found)
|
||||||
|
else:
|
||||||
|
unexpected.append(ev)
|
||||||
|
|
||||||
|
|
||||||
class TestCaseMixin:
|
class TestCaseMixin:
|
||||||
def assertForbidden(self, response):
|
def assertForbidden(self, response):
|
||||||
"""
|
"""
|
||||||
|
@ -91,70 +196,6 @@ class TestCaseMixin:
|
||||||
else:
|
else:
|
||||||
self.assertEqual(actual, expected)
|
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(
|
|
||||||
"shared.views.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]))
|
|
||||||
|
|
||||||
def _test_event_equal(self, event, exp):
|
|
||||||
for k, v_desc in exp.items():
|
|
||||||
if isinstance(v_desc, tuple):
|
|
||||||
v_getter = v_desc[0]
|
|
||||||
v = v_desc[1]
|
|
||||||
else:
|
|
||||||
v_getter = lambda v: v
|
|
||||||
v = v_desc
|
|
||||||
if v_getter(event[k.upper()]) != v:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _find_event(self, ev, l):
|
|
||||||
for i, elt in enumerate(l):
|
|
||||||
if self._test_event_equal(ev, elt):
|
|
||||||
return elt, i
|
|
||||||
return False, -1
|
|
||||||
|
|
||||||
def assertCalEqual(self, ical_content, expected):
|
|
||||||
remaining = expected.copy()
|
|
||||||
unexpected = []
|
|
||||||
|
|
||||||
cal = icalendar.Calendar.from_ical(ical_content)
|
|
||||||
|
|
||||||
for ev in cal.walk("vevent"):
|
|
||||||
found, i_found = self._find_event(ev, remaining)
|
|
||||||
if found:
|
|
||||||
remaining.pop(i_found)
|
|
||||||
else:
|
|
||||||
unexpected.append(ev)
|
|
||||||
|
|
||||||
self.assertListEqual(unexpected, [])
|
|
||||||
self.assertListEqual(remaining, [])
|
|
||||||
|
|
||||||
|
|
||||||
class ViewTestCaseMixin(TestCaseMixin):
|
class ViewTestCaseMixin(TestCaseMixin):
|
||||||
|
|
Loading…
Reference in a new issue