diff --git a/bda/tests/test_views.py b/bda/tests/test_views.py index 7204a320..1d4494cb 100644 --- a/bda/tests/test_views.py +++ b/bda/tests/test_views.py @@ -1,221 +1,357 @@ 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 django.test import TestCase +from django.utils import formats, timezone -from ..models import CategorieSpectacle, Salle, Spectacle, Tirage +from ..models import CategorieSpectacle, Participant, Salle +from .testcases import BdATestHelpers, BdAViewTestCaseMixin +from .utils import create_spectacle -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 +class InscriptionViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase): + url_name = "bda-tirage-inscription" + http_methods = ["GET", "POST"] -def user_is_cof(user): - return (user is not None) and user.profile.is_cof + auth_user = "bda_member" + auth_forbidden = [None, "bda_other"] + bda_testdata = True -def user_is_staff(user): - return (user is not None) and user.profile.is_buro + @property + def url_kwargs(self): + return {"tirage_id": self.tirage.id} + @property + def url_expected(self): + return "/bda/inscription/{}".format(self.tirage.id) -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) + def test_get_opened(self): + self.tirage.ouverture = timezone.now() - timedelta(days=1) + self.tirage.fermeture = timezone.now() + timedelta(days=1) + self.tirage.save() - member = create_user(username="bda_member", is_cof=True) - member_c = Client() - member_c.force_login(member) + resp = self.client.get(self.url) - other = create_user(username="bda_other") - other_c = Client() - other_c.force_login(other) + self.assertEqual(resp.status_code, 200) + self.assertFalse(resp.context["messages"]) - self.client_matrix = [ - (staff, staff_c), - (member, member_c), - (other, other_c), - (None, Client()), - ] + def test_get_closed_future(self): + self.tirage.ouverture = timezone.now() + timedelta(days=1) + self.tirage.fermeture = timezone.now() + timedelta(days=2) + self.tirage.save() - def require_custommails(self): - data_file = os.path.join( - settings.BASE_DIR, "gestioncof", "management", "data", "custommail.json" - ) - call_command("syncmails", data_file, verbosity=0) + resp = self.client.get(self.url) - 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, - ), - ] + self.assertEqual(resp.status_code, 200) + self.assertIn( + "Le tirage n'est pas encore ouvert : ouverture le {}".format( + formats.localize(timezone.template_localtime(self.tirage.ouverture)) + ), + [str(msg) for msg in resp.context["messages"]], ) - def test_bda_inscriptions(self): - # TODO: test the form - url = "/bda/inscription/{}".format(self.tirage.id) - self.check_restricted_access(url) + def test_get_closed_past(self): + self.tirage.ouverture = timezone.now() - timedelta(days=2) + self.tirage.fermeture = timezone.now() - timedelta(days=1) + self.tirage.save() - def test_bda_places(self): - url = "/bda/places/{}".format(self.tirage.id) - self.check_restricted_access(url) + resp = self.client.get(self.url) - def test_etat_places(self): - url = "/bda/etat-places/{}".format(self.tirage.id) - self.check_restricted_access(url) + self.assertEqual(resp.status_code, 200) + self.assertIn( + " C'est fini : tirage au sort dans la journée !", + [str(msg) for msg in resp.context["messages"]], + ) - 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) + def get_base_post_data(self): + return { + "choixspectacle_set-TOTAL_FORMS": "3", + "choixspectacle_set-INITIAL_FORMS": "0", + "choixspectacle_set-MIN_NUM_FORMS": "0", + "choixspectacle_set-MAX_NUM_FORMS": "1000", + } - _, staff_c = self.client_matrix[0] + base_post_data = property(get_base_post_data) + + def test_post(self): + self.tirage.ouverture = timezone.now() - timedelta(days=1) + self.tirage.fermeture = timezone.now() + timedelta(days=1) + self.tirage.save() + + data = dict( + self.base_post_data, + **{ + "choixspectacle_set-TOTAL_FORMS": "2", + "choixspectacle_set-0-id": "", + "choixspectacle_set-0-participant": "", + "choixspectacle_set-0-spectacle": str(self.show1.pk), + "choixspectacle_set-0-double_choice": "1", + "choixspectacle_set-0-priority": "2", + "choixspectacle_set-1-id": "", + "choixspectacle_set-1-participant": "", + "choixspectacle_set-1-spectacle": str(self.show2.pk), + "choixspectacle_set-1-double_choice": "autoquit", + "choixspectacle_set-1-priority": "1", + } + ) + resp = self.client.post(self.url, data) + + self.assertEqual(resp.status_code, 200) + self.assertIn( + "Votre inscription a été mise à jour avec succès !", + [str(msg) for msg in resp.context["messages"]], + ) + participant = Participant.objects.get( + user=self.users["bda_member"], tirage=self.tirage + ) + self.assertSetEqual( + set( + participant.choixspectacle_set.values_list( + "priority", "spectacle_id", "double_choice" + ) + ), + {(1, self.show2.pk, "autoquit"), (2, self.show1.pk, "1")}, + ) + + def test_post_state_changed(self): + self.tirage.ouverture = timezone.now() - timedelta(days=1) + self.tirage.fermeture = timezone.now() + timedelta(days=1) + self.tirage.save() + + data = {"dbstate": "different"} + resp = self.client.post(self.url, data) + + self.assertEqual(resp.status_code, 200) + self.assertIn( + "Impossible d'enregistrer vos modifications : vous avez apporté d'autres " + "modifications entre temps.", + [str(msg) for msg in resp.context["messages"]], + ) + + +class PlacesViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase): + url_name = "bda-places-attribuees" + + auth_user = "bda_member" + auth_forbidden = [None, "bda_other"] + + bda_testdata = True + + @property + def url_kwargs(self): + return {"tirage_id": self.tirage.id} + + @property + def url_expected(self): + return "/bda/places/{}".format(self.tirage.id) + + +class EtatPlacesViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase): + url_name = "bda-etat-places" + + auth_user = "bda_member" + auth_forbidden = [None, "bda_other"] + + bda_testdata = True + + @property + def url_kwargs(self): + return {"tirage_id": self.tirage.id} + + @property + def url_expected(self): + return "/bda/etat-places/{}".format(self.tirage.id) + + +class TirageViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase): + url_name = "bda-tirage" + + http_methods = ["GET", "POST"] + + auth_user = "bda_staff" + auth_forbidden = [None, "bda_other", "bda_member"] + + bda_testdata = True + + @property + def url_kwargs(self): + return {"tirage_id": self.tirage.id} + + @property + def url_expected(self): + return "/bda/tirage/{}".format(self.tirage.id) + + def test_perform_tirage_disabled(self): # Cannot be performed if disabled self.tirage.enable_do_tirage = False self.tirage.save() - resp = staff_c.get(url) + resp = self.client.get(self.url) self.assertTemplateUsed(resp, "tirage-failed.html") + + def test_perform_tirage_opened_registrations(self): # 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) + resp = self.client.get(self.url) self.assertTemplateUsed(resp, "tirage-failed.html") + + def test_perform_tirage(self): # Otherwise, perform the tirage + self.tirage.enable_do_tirage = True self.tirage.fermeture = timezone.now() self.tirage.save() - resp = staff_c.get(url) + resp = self.client.get(self.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) +class SpectacleListViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase): + url_name = "bda-liste-spectacles" - def test_tirage_unpaid(self): - url = "/bda/spectacles/unpaid/{}".format(self.tirage.id) - self.check_restricted_access(url, validate_user=user_is_staff) + auth_user = "bda_staff" + auth_forbidden = [None, "bda_other", "bda_member"] - def test_send_reminders(self): + bda_testdata = True + + @property + def url_kwargs(self): + return {"tirage_id": self.tirage.id} + + @property + def url_expected(self): + return "/bda/spectacles/{}".format(self.tirage.id) + + +class SpectacleViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase): + url_name = "bda-spectacle" + + auth_user = "bda_staff" + auth_forbidden = [None, "bda_other", "bda_member"] + + bda_testdata = True + + @property + def url_kwargs(self): + return {"tirage_id": self.tirage.id, "spectacle_id": self.show1.id} + + @property + def url_expected(self): + return "/bda/spectacles/{}/{}".format(self.tirage.id, self.show1.id) + + +class UnpaidViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase): + url_name = "bda-unpaid" + + auth_user = "bda_staff" + auth_forbidden = [None, "bda_other", "bda_member"] + + bda_testdata = True + + @property + def url_kwargs(self): + return {"tirage_id": self.tirage.id} + + @property + def url_expected(self): + return "/bda/spectacles/unpaid/{}".format(self.tirage.id) + + +class SendRemindersViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase): + url_name = "bda-rappels" + + auth_user = "bda_staff" + auth_forbidden = [None, "bda_other", "bda_member"] + + bda_testdata = True + + @property + def url_kwargs(self): + return {"spectacle_id": self.show1.id} + + @property + def url_expected(self): + return "/bda/mails-rappel/{}".format(self.show1.id) + + def test_post(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) + resp = self.client.post(self.url) self.assertEqual(200, resp.status_code) # TODO: check that emails are sent - def test_catalogue_api(self): + +class DescriptionsSpectaclesViewTestCase( + BdATestHelpers, BdAViewTestCaseMixin, TestCase +): + url_name = "bda-descriptions" + + auth_user = None + auth_forbidden = [] + + bda_testdata = True + + @property + def url_kwargs(self): + return {"tirage_id": self.tirage.pk} + + @property + def url_expected(self): + return "/bda/descriptions/{}".format(self.tirage.pk) + + def test_get(self): + resp = self.client.get(self.url) + self.assertEqual(resp.status_code, 200) + self.assertListEqual( + list(resp.context["shows"]), [self.show1, self.show2, self.show3] + ) + + def test_get_filter_category(self): + category1 = CategorieSpectacle.objects.create(name="Category 1") + category2 = CategorieSpectacle.objects.create(name="Category 2") + show1 = create_spectacle(category=category1, tirage=self.tirage) + show2 = create_spectacle(category=category2, tirage=self.tirage) + + resp = self.client.get(self.url, {"category": "Category 1"}) + self.assertEqual(resp.status_code, 200) + self.assertListEqual(list(resp.context["shows"]), [show1]) + + resp = self.client.get(self.url, {"category": "Category 2"}) + self.assertEqual(resp.status_code, 200) + self.assertListEqual(list(resp.context["shows"]), [show2]) + + def test_get_filter_location(self): + location1 = Salle.objects.create(name="Location 1") + location2 = Salle.objects.create(name="Location 2") + show1 = create_spectacle(location=location1, tirage=self.tirage) + show2 = create_spectacle(location=location2, tirage=self.tirage) + + resp = self.client.get(self.url, {"location": str(location1.pk)}) + self.assertEqual(resp.status_code, 200) + self.assertListEqual(list(resp.context["shows"]), [show1]) + + resp = self.client.get(self.url, {"location": str(location2.pk)}) + self.assertEqual(resp.status_code, 200) + self.assertListEqual(list(resp.context["shows"]), [show2]) + + +class CatalogueViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase): + auth_user = None + auth_forbidden = [] + + bda_testdata = True + + def test_api_list(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) + resp = self.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) + def test_api_details(self): + url_details = "/bda/catalogue/details?id={}".format(self.tirage.id) + resp = self.client.get(url_details) self.assertJSONEqual( resp.content.decode("utf-8"), { @@ -224,8 +360,9 @@ class TestBdAViews(BdATestHelpers, TestCase): }, ) - # Descriptions - resp = client.get(url_descriptions) + def test_api_descriptions(self): + url_descriptions = "/bda/catalogue/descriptions?id={}".format(self.tirage.id) + resp = self.client.get(url_descriptions) raw = resp.content.decode("utf-8") try: results = json.loads(raw) diff --git a/bda/tests/testcases.py b/bda/tests/testcases.py new file mode 100644 index 00000000..f5ac7f83 --- /dev/null +++ b/bda/tests/testcases.py @@ -0,0 +1,75 @@ +import os + +from django.conf import settings +from django.core.management import call_command +from django.utils import timezone + +from shared.tests.testcases import ViewTestCaseMixin + +from ..models import CategorieSpectacle, Salle, Spectacle, Tirage +from .utils import create_user + + +class BdAViewTestCaseMixin(ViewTestCaseMixin): + def get_users_base(self): + return { + "bda_other": create_user(username="bda_other"), + "bda_member": create_user(username="bda_member", is_cof=True), + "bda_staff": create_user(username="bda_staff", is_cof=True, is_buro=True), + } + + +class BdATestHelpers: + bda_testdata = False + + def setUp(self): + super().setUp() + + if self.bda_testdata: + self.load_bda_testdata() + + 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 load_bda_testdata(self): + 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") + self.show1 = Spectacle.objects.create( + title="foo", + date=timezone.now(), + location=self.location, + price=0, + slots=42, + tirage=self.tirage, + listing=False, + category=self.category, + ) + self.show2 = Spectacle.objects.create( + title="bar", + date=timezone.now(), + location=self.location, + price=1, + slots=142, + tirage=self.tirage, + listing=False, + category=self.category, + ) + self.show3 = Spectacle.objects.create( + title="baz", + date=timezone.now(), + location=self.location, + price=2, + slots=242, + tirage=self.tirage, + listing=False, + category=self.category, + ) diff --git a/bda/tests/utils.py b/bda/tests/utils.py new file mode 100644 index 00000000..68f51fb6 --- /dev/null +++ b/bda/tests/utils.py @@ -0,0 +1,36 @@ +from datetime import timedelta + +from django.contrib.auth.models import User +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 + + +def create_spectacle(**kwargs): + defaults = { + "title": "Title", + "category": CategorieSpectacle.objects.first(), + "date": (timezone.now() + timedelta(days=7)).date(), + "location": Salle.objects.first(), + "price": 10.0, + "slots": 20, + "tirage": Tirage.objects.first(), + "listing": False, + } + return Spectacle.objects.create(**dict(defaults, **kwargs)) diff --git a/bda/urls.py b/bda/urls.py index 7ceccfe0..7ac21648 100644 --- a/bda/urls.py +++ b/bda/urls.py @@ -12,7 +12,7 @@ urlpatterns = [ ), url(r"^places/(?P\d+)$", views.places, name="bda-places-attribuees"), url(r"^etat-places/(?P\d+)$", views.etat_places, name="bda-etat-places"), - url(r"^tirage/(?P\d+)$", views.tirage), + url(r"^tirage/(?P\d+)$", views.tirage, name="bda-tirage"), url( r"^spectacles/(?P\d+)$", buro_required(SpectacleListView.as_view()), diff --git a/shared/tests/testcases.py b/shared/tests/testcases.py index 19122322..11de06fb 100644 --- a/shared/tests/testcases.py +++ b/shared/tests/testcases.py @@ -330,6 +330,7 @@ class ViewTestCaseMixin(TestCaseMixin): kwargs=url_conf.get("kwargs", {}), ) for url_conf in self.urls_conf + if url_conf["name"] is not None ] @property