forked from DGNum/gestioCOF
Ce patch rend les décorateurs `cof_required` et `buro_required` plus agréables pour les utilisateurs; en particulier, ils ne font plus une redirection sur la page de connexion si la condition n'est pas remplie. Dans les deux cas : - Si l'utilisateur n'est pas connecté, il est renvoyé sur la page de connexion - Si l'utilisateur est connecté mais pas membre du COF/du Burô, une page d'erreur "403 Forbidden" est affichée. Dans le cas de `cof_required` cette page demande à l'utilisateur de s'inscrire au COF; dans le cas `buro_required` elle indique simplement que la page est réservée au Burô. gestioncof/ * gestioncof/templates/buro-denied.html: Ajouté. * decorators.py: bda/ * tests/test_views.py: Modifié pour correctement gérer le nouveau fonctionnement des décorateurs.
244 lines
8.2 KiB
Python
244 lines
8.2 KiB
Python
import json
|
|
import os
|
|
from datetime import timedelta
|
|
from unittest import mock
|
|
from urllib.parse import urlencode
|
|
|
|
from django.conf import settings
|
|
from django.contrib.auth.models import User
|
|
from django.core.management import call_command
|
|
from django.test import Client, TestCase
|
|
from django.utils import timezone
|
|
|
|
from ..models import CategorieSpectacle, Salle, Spectacle, Tirage
|
|
|
|
|
|
def create_user(username, is_cof=False, is_buro=False):
|
|
user = User.objects.create_user(username=username, password=username)
|
|
user.profile.is_cof = is_cof
|
|
user.profile.is_buro = is_buro
|
|
user.profile.save()
|
|
return user
|
|
|
|
|
|
def user_is_cof(user):
|
|
return (user is not None) and user.profile.is_cof
|
|
|
|
|
|
def user_is_staff(user):
|
|
return (user is not None) and user.profile.is_buro
|
|
|
|
|
|
class BdATestHelpers:
|
|
def setUp(self):
|
|
# Some user with different access privileges
|
|
staff = create_user(username="bda_staff", is_cof=True, is_buro=True)
|
|
staff_c = Client()
|
|
staff_c.force_login(staff)
|
|
|
|
member = create_user(username="bda_member", is_cof=True)
|
|
member_c = Client()
|
|
member_c.force_login(member)
|
|
|
|
other = create_user(username="bda_other")
|
|
other_c = Client()
|
|
other_c.force_login(other)
|
|
|
|
self.client_matrix = [
|
|
(staff, staff_c),
|
|
(member, member_c),
|
|
(other, other_c),
|
|
(None, Client()),
|
|
]
|
|
|
|
def require_custommails(self):
|
|
data_file = os.path.join(
|
|
settings.BASE_DIR, "gestioncof", "management", "data", "custommail.json"
|
|
)
|
|
call_command("syncmails", data_file, verbosity=0)
|
|
|
|
def check_restricted_access(
|
|
self, url, validate_user=user_is_cof, redirect_url=None
|
|
):
|
|
for (user, client) in self.client_matrix:
|
|
resp = client.get(url, follow=True)
|
|
if validate_user(user):
|
|
self.assertEqual(200, resp.status_code)
|
|
elif redirect_url:
|
|
self.assertRedirects(resp, redirect_url)
|
|
elif user is None:
|
|
# client is not logged in
|
|
login_url = "/login"
|
|
if url:
|
|
login_url += "?{}".format(urlencode({"next": url}, safe="/"))
|
|
self.assertRedirects(resp, login_url)
|
|
else:
|
|
self.assertEqual(403, resp.status_code)
|
|
|
|
|
|
class TestBdAViews(BdATestHelpers, TestCase):
|
|
def setUp(self):
|
|
# Signals handlers on login/logout send messages.
|
|
# Due to the way the Django' test Client performs login, this raise an
|
|
# error. As workaround, we mock the Django' messages module.
|
|
patcher_messages = mock.patch("gestioncof.signals.messages")
|
|
patcher_messages.start()
|
|
self.addCleanup(patcher_messages.stop)
|
|
# Set up the helpers
|
|
super().setUp()
|
|
# Some BdA stuff
|
|
self.tirage = Tirage.objects.create(
|
|
title="Test tirage",
|
|
appear_catalogue=True,
|
|
ouverture=timezone.now(),
|
|
fermeture=timezone.now(),
|
|
)
|
|
self.category = CategorieSpectacle.objects.create(name="Category")
|
|
self.location = Salle.objects.create(name="here")
|
|
Spectacle.objects.bulk_create(
|
|
[
|
|
Spectacle(
|
|
title="foo",
|
|
date=timezone.now(),
|
|
location=self.location,
|
|
price=0,
|
|
slots=42,
|
|
tirage=self.tirage,
|
|
listing=False,
|
|
category=self.category,
|
|
),
|
|
Spectacle(
|
|
title="bar",
|
|
date=timezone.now(),
|
|
location=self.location,
|
|
price=1,
|
|
slots=142,
|
|
tirage=self.tirage,
|
|
listing=False,
|
|
category=self.category,
|
|
),
|
|
Spectacle(
|
|
title="baz",
|
|
date=timezone.now(),
|
|
location=self.location,
|
|
price=2,
|
|
slots=242,
|
|
tirage=self.tirage,
|
|
listing=False,
|
|
category=self.category,
|
|
),
|
|
]
|
|
)
|
|
|
|
def test_bda_inscriptions(self):
|
|
# TODO: test the form
|
|
url = "/bda/inscription/{}".format(self.tirage.id)
|
|
self.check_restricted_access(url)
|
|
|
|
def test_bda_places(self):
|
|
url = "/bda/places/{}".format(self.tirage.id)
|
|
self.check_restricted_access(url)
|
|
|
|
def test_etat_places(self):
|
|
url = "/bda/etat-places/{}".format(self.tirage.id)
|
|
self.check_restricted_access(url)
|
|
|
|
def test_perform_tirage(self):
|
|
# Only staff member can perform a tirage
|
|
url = "/bda/tirage/{}".format(self.tirage.id)
|
|
self.check_restricted_access(url, validate_user=user_is_staff)
|
|
|
|
_, staff_c = self.client_matrix[0]
|
|
# Cannot be performed if disabled
|
|
self.tirage.enable_do_tirage = False
|
|
self.tirage.save()
|
|
resp = staff_c.get(url)
|
|
self.assertTemplateUsed(resp, "tirage-failed.html")
|
|
# Cannot be performed if registrations are still open
|
|
self.tirage.enable_do_tirage = True
|
|
self.tirage.fermeture = timezone.now() + timedelta(seconds=3600)
|
|
self.tirage.save()
|
|
resp = staff_c.get(url)
|
|
self.assertTemplateUsed(resp, "tirage-failed.html")
|
|
# Otherwise, perform the tirage
|
|
self.tirage.fermeture = timezone.now()
|
|
self.tirage.save()
|
|
resp = staff_c.get(url)
|
|
self.assertTemplateNotUsed(resp, "tirage-failed.html")
|
|
|
|
def test_spectacles_list(self):
|
|
url = "/bda/spectacles/{}".format(self.tirage.id)
|
|
self.check_restricted_access(url, validate_user=user_is_staff)
|
|
|
|
def test_spectacle_detail(self):
|
|
show = self.tirage.spectacle_set.first()
|
|
url = "/bda/spectacles/{}/{}".format(self.tirage.id, show.id)
|
|
self.check_restricted_access(url, validate_user=user_is_staff)
|
|
|
|
def test_tirage_unpaid(self):
|
|
url = "/bda/spectacles/unpaid/{}".format(self.tirage.id)
|
|
self.check_restricted_access(url, validate_user=user_is_staff)
|
|
|
|
def test_send_reminders(self):
|
|
self.require_custommails()
|
|
# Just get the page
|
|
show = self.tirage.spectacle_set.first()
|
|
url = "/bda/mails-rappel/{}".format(show.id)
|
|
self.check_restricted_access(url, validate_user=user_is_staff)
|
|
# Actually send the reminder emails
|
|
_, staff_c = self.client_matrix[0]
|
|
resp = staff_c.post(url)
|
|
self.assertEqual(200, resp.status_code)
|
|
# TODO: check that emails are sent
|
|
|
|
def test_catalogue_api(self):
|
|
url_list = "/bda/catalogue/list"
|
|
url_details = "/bda/catalogue/details?id={}".format(self.tirage.id)
|
|
url_descriptions = "/bda/catalogue/descriptions?id={}".format(self.tirage.id)
|
|
|
|
# Anyone can get
|
|
def anyone_can_get(url):
|
|
self.check_restricted_access(url, validate_user=lambda user: True)
|
|
|
|
anyone_can_get(url_list)
|
|
anyone_can_get(url_details)
|
|
anyone_can_get(url_descriptions)
|
|
|
|
# The resulting JSON contains the information
|
|
_, client = self.client_matrix[0]
|
|
|
|
# List
|
|
resp = client.get(url_list)
|
|
self.assertJSONEqual(
|
|
resp.content.decode("utf-8"),
|
|
[{"id": self.tirage.id, "title": self.tirage.title}],
|
|
)
|
|
|
|
# Details
|
|
resp = client.get(url_details)
|
|
self.assertJSONEqual(
|
|
resp.content.decode("utf-8"),
|
|
{
|
|
"categories": [{"id": self.category.id, "name": self.category.name}],
|
|
"locations": [{"id": self.location.id, "name": self.location.name}],
|
|
},
|
|
)
|
|
|
|
# Descriptions
|
|
resp = client.get(url_descriptions)
|
|
raw = resp.content.decode("utf-8")
|
|
try:
|
|
results = json.loads(raw)
|
|
except ValueError:
|
|
self.fail("Not valid JSON: {}".format(raw))
|
|
self.assertEqual(len(results), 3)
|
|
self.assertEqual(
|
|
{(s["title"], s["price"], s["slots"]) for s in results},
|
|
{("foo", 0, 42), ("bar", 1, 142), ("baz", 2, 242)},
|
|
)
|
|
|
|
|
|
class TestBdaRevente:
|
|
pass
|
|
# TODO
|