import json from datetime import timedelta from unittest import mock from urllib.parse import urlencode from django.contrib.auth.models import User 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): from django.core.management import call_command call_command("syncmails", verbosity=0) def check_restricted_access( self, url, validate_user=user_is_cof, redirect_url=None ): def craft_redirect_url(user): if redirect_url: return redirect_url elif user is None: # client is not logged in login_url = "/login" if url: login_url += "?{}".format(urlencode({"next": url}, safe="/")) return login_url else: return "/" for (user, client) in self.client_matrix: resp = client.get(url, follow=True) if validate_user(user): self.assertEqual(200, resp.status_code) else: self.assertRedirects(resp, craft_redirect_url(user)) 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