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 gestioncof.models import CalendarSubscription, Club, Event, Survey, SurveyAnswer
|
||||
from gestioncof.tests.testcases import ViewTestCaseMixin
|
||||
from shared.tests.testcases import ICalMixin, MockLDAPMixin
|
||||
from shared.views.autocomplete import Clipper
|
||||
|
||||
from .utils import create_member, create_root, create_user
|
||||
|
@ -267,7 +268,7 @@ class RegistrationFormViewTests(ViewTestCaseMixin, TestCase):
|
|||
|
||||
|
||||
@override_settings(LDAP_SERVER_URL="ldap_url")
|
||||
class RegistrationAutocompleteViewTests(ViewTestCaseMixin, TestCase):
|
||||
class RegistrationAutocompleteViewTests(MockLDAPMixin, ViewTestCaseMixin, TestCase):
|
||||
url_name = "cof.registration.autocomplete"
|
||||
url_expected = "/autocomplete/registration"
|
||||
|
||||
|
@ -815,7 +816,7 @@ class CalendarViewTests(ViewTestCaseMixin, TestCase):
|
|||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
|
||||
class CalendarICSViewTests(ViewTestCaseMixin, TestCase):
|
||||
class CalendarICSViewTests(ICalMixin, ViewTestCaseMixin, TestCase):
|
||||
url_name = "calendar.ics"
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -13,6 +13,111 @@ from django.utils.functional import cached_property
|
|||
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:
|
||||
def assertForbidden(self, response):
|
||||
"""
|
||||
|
@ -91,70 +196,6 @@ 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(
|
||||
"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):
|
||||
|
|
Loading…
Reference in a new issue