forked from DGNum/gestioCOF
Merge branch 'Kerl/tests' into 'master'
Quelques tests pour les vues BdA See merge request cof-geek/gestioCOF!280
This commit is contained in:
commit
44af796e73
2 changed files with 234 additions and 100 deletions
|
@ -1,14 +1,93 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
from unittest import mock
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.test import TestCase, Client
|
from django.test import TestCase, Client
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from bda.models import Tirage, Spectacle, Salle, CategorieSpectacle
|
from ..models import Tirage, Spectacle, Salle, CategorieSpectacle
|
||||||
|
|
||||||
|
|
||||||
class TestBdAViews(TestCase):
|
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):
|
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")
|
||||||
|
|
||||||
|
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(
|
self.tirage = Tirage.objects.create(
|
||||||
title="Test tirage",
|
title="Test tirage",
|
||||||
appear_catalogue=True,
|
appear_catalogue=True,
|
||||||
|
@ -35,46 +114,92 @@ class TestBdAViews(TestCase):
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
|
|
||||||
self.bda_user = User.objects.create_user(
|
def test_bda_inscriptions(self):
|
||||||
username="bda_user", password="bda4ever"
|
# TODO: test the form
|
||||||
)
|
url = "/bda/inscription/{}".format(self.tirage.id)
|
||||||
self.bda_user.profile.is_cof = True
|
self.check_restricted_access(url)
|
||||||
self.bda_user.profile.is_buro = True
|
|
||||||
self.bda_user.profile.save()
|
|
||||||
|
|
||||||
def bda_participants(self):
|
def test_bda_places(self):
|
||||||
"""The BdA participants views can be queried"""
|
url = "/bda/places/{}".format(self.tirage.id)
|
||||||
client = Client()
|
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()
|
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)
|
||||||
|
|
||||||
client.login(self.bda_user.username, "bda4ever")
|
def test_tirage_unpaid(self):
|
||||||
tirage_resp = client.get("/bda/spectacles/{}".format(self.tirage.id))
|
url = "/bda/spectacles/unpaid/{}".format(self.tirage.id)
|
||||||
show_resp = client.get(
|
self.check_restricted_access(url, validate_user=user_is_staff)
|
||||||
"/bda/spectacles/{}/{}".format(self.tirage.id, show.id)
|
|
||||||
)
|
|
||||||
reminder_url = "/bda/mails-rappel/{}".format(show.id)
|
|
||||||
reminder_get_resp = client.get(reminder_url)
|
|
||||||
reminder_post_resp = client.post(reminder_url)
|
|
||||||
self.assertEqual(200, tirage_resp.status_code)
|
|
||||||
self.assertEqual(200, show_resp.status_code)
|
|
||||||
self.assertEqual(200, reminder_get_resp.status_code)
|
|
||||||
self.assertEqual(200, reminder_post_resp.status_code)
|
|
||||||
|
|
||||||
def test_catalogue(self):
|
def test_send_reminders(self):
|
||||||
"""Test the catalogue JSON API"""
|
self.require_custommails()
|
||||||
client = Client()
|
# 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
|
||||||
|
|
||||||
# The `list` hook
|
def test_catalogue_api(self):
|
||||||
resp = client.get("/bda/catalogue/list")
|
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(
|
self.assertJSONEqual(
|
||||||
resp.content.decode("utf-8"),
|
resp.content.decode("utf-8"),
|
||||||
[{"id": self.tirage.id, "title": self.tirage.title}]
|
[{"id": self.tirage.id, "title": self.tirage.title}]
|
||||||
)
|
)
|
||||||
|
|
||||||
# The `details` hook
|
# Details
|
||||||
resp = client.get(
|
resp = client.get(url_details)
|
||||||
"/bda/catalogue/details?id={}".format(self.tirage.id)
|
|
||||||
)
|
|
||||||
self.assertJSONEqual(
|
self.assertJSONEqual(
|
||||||
resp.content.decode("utf-8"),
|
resp.content.decode("utf-8"),
|
||||||
{
|
{
|
||||||
|
@ -89,10 +214,8 @@ class TestBdAViews(TestCase):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# The `descriptions` hook
|
# Descriptions
|
||||||
resp = client.get(
|
resp = client.get(url_descriptions)
|
||||||
"/bda/catalogue/descriptions?id={}".format(self.tirage.id)
|
|
||||||
)
|
|
||||||
raw = resp.content.decode("utf-8")
|
raw = resp.content.decode("utf-8")
|
||||||
try:
|
try:
|
||||||
results = json.loads(raw)
|
results = json.loads(raw)
|
||||||
|
@ -103,3 +226,8 @@ class TestBdAViews(TestCase):
|
||||||
{(s["title"], s["price"], s["slots"]) for s in results},
|
{(s["title"], s["price"], s["slots"]) for s in results},
|
||||||
{("foo", 0, 42), ("bar", 1, 142), ("baz", 2, 242)}
|
{("foo", 0, 42), ("bar", 1, 142), ("baz", 2, 242)}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestBdaRevente:
|
||||||
|
pass
|
||||||
|
# TODO
|
||||||
|
|
|
@ -10,6 +10,75 @@ from django.core.management.base import BaseCommand
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
|
|
||||||
|
DATA_LOCATION = os.path.join(os.path.dirname(__file__), "..", "data", "custommail.json")
|
||||||
|
|
||||||
|
|
||||||
|
def dummy_log(__):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# XXX. this should probably be in the custommail package
|
||||||
|
def load_from_file(log=dummy_log, verbosity=1):
|
||||||
|
with open(DATA_LOCATION, 'r') as jsonfile:
|
||||||
|
mail_data = json.load(jsonfile)
|
||||||
|
|
||||||
|
# On se souvient à quel objet correspond quel pk du json
|
||||||
|
assoc = {'types': {}, 'mails': {}}
|
||||||
|
status = {'synced': 0, 'unchanged': 0}
|
||||||
|
|
||||||
|
for obj in mail_data:
|
||||||
|
fields = obj['fields']
|
||||||
|
|
||||||
|
# Pour les trois types d'objets :
|
||||||
|
# - On récupère les objets référencés par les clefs étrangères
|
||||||
|
# - On crée l'objet si nécessaire
|
||||||
|
# - On le stocke éventuellement dans les deux dictionnaires définis
|
||||||
|
# plus haut
|
||||||
|
|
||||||
|
# Variable types
|
||||||
|
if obj['model'] == 'custommail.variabletype':
|
||||||
|
fields['inner1'] = assoc['types'].get(fields['inner1'])
|
||||||
|
fields['inner2'] = assoc['types'].get(fields['inner2'])
|
||||||
|
if fields['kind'] == 'model':
|
||||||
|
fields['content_type'] = (
|
||||||
|
ContentType.objects
|
||||||
|
.get_by_natural_key(*fields['content_type'])
|
||||||
|
)
|
||||||
|
var_type, _ = Type.objects.get_or_create(**fields)
|
||||||
|
assoc['types'][obj['pk']] = var_type
|
||||||
|
|
||||||
|
# Custom mails
|
||||||
|
if obj['model'] == 'custommail.custommail':
|
||||||
|
mail = None
|
||||||
|
try:
|
||||||
|
mail = CustomMail.objects.get(shortname=fields['shortname'])
|
||||||
|
status['unchanged'] += 1
|
||||||
|
except CustomMail.DoesNotExist:
|
||||||
|
mail = CustomMail.objects.create(**fields)
|
||||||
|
status['synced'] += 1
|
||||||
|
if verbosity:
|
||||||
|
log('SYNCED {:s}'.format(fields['shortname']))
|
||||||
|
assoc['mails'][obj['pk']] = mail
|
||||||
|
|
||||||
|
# Variables
|
||||||
|
if obj['model'] == 'custommail.custommailvariable':
|
||||||
|
fields['custommail'] = assoc['mails'].get(fields['custommail'])
|
||||||
|
fields['type'] = assoc['types'].get(fields['type'])
|
||||||
|
try:
|
||||||
|
Variable.objects.get(
|
||||||
|
custommail=fields['custommail'],
|
||||||
|
name=fields['name']
|
||||||
|
)
|
||||||
|
except Variable.DoesNotExist:
|
||||||
|
Variable.objects.create(**fields)
|
||||||
|
|
||||||
|
if verbosity:
|
||||||
|
log(
|
||||||
|
'{synced:d} mails synchronized {unchanged:d} unchanged'
|
||||||
|
.format(**status)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = ("Va chercher les données mails de GestioCOF stocké au format json "
|
help = ("Va chercher les données mails de GestioCOF stocké au format json "
|
||||||
"dans /gestioncof/management/data/custommails.json. Le format des "
|
"dans /gestioncof/management/data/custommails.json. Le format des "
|
||||||
|
@ -21,67 +90,4 @@ class Command(BaseCommand):
|
||||||
"remplacer par le nouveau résultat de la commande précédente.")
|
"remplacer par le nouveau résultat de la commande précédente.")
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
path = os.path.join(
|
load_from_file(log=self.stdout.write)
|
||||||
os.path.dirname(os.path.dirname(__file__)),
|
|
||||||
'data', 'custommail.json')
|
|
||||||
with open(path, 'r') as jsonfile:
|
|
||||||
mail_data = json.load(jsonfile)
|
|
||||||
|
|
||||||
# On se souvient à quel objet correspond quel pk du json
|
|
||||||
assoc = {'types': {}, 'mails': {}}
|
|
||||||
status = {'synced': 0, 'unchanged': 0}
|
|
||||||
|
|
||||||
for obj in mail_data:
|
|
||||||
fields = obj['fields']
|
|
||||||
|
|
||||||
# Pour les trois types d'objets :
|
|
||||||
# - On récupère les objets référencés par les clefs étrangères
|
|
||||||
# - On crée l'objet si nécessaire
|
|
||||||
# - On le stocke éventuellement dans les deux dictionnaires définis
|
|
||||||
# plus haut
|
|
||||||
|
|
||||||
# Variable types
|
|
||||||
if obj['model'] == 'custommail.variabletype':
|
|
||||||
fields['inner1'] = assoc['types'].get(fields['inner1'])
|
|
||||||
fields['inner2'] = assoc['types'].get(fields['inner2'])
|
|
||||||
if fields['kind'] == 'model':
|
|
||||||
fields['content_type'] = (
|
|
||||||
ContentType.objects
|
|
||||||
.get_by_natural_key(*fields['content_type'])
|
|
||||||
)
|
|
||||||
var_type, _ = Type.objects.get_or_create(**fields)
|
|
||||||
assoc['types'][obj['pk']] = var_type
|
|
||||||
|
|
||||||
# Custom mails
|
|
||||||
if obj['model'] == 'custommail.custommail':
|
|
||||||
mail = None
|
|
||||||
try:
|
|
||||||
mail = CustomMail.objects.get(
|
|
||||||
shortname=fields['shortname'])
|
|
||||||
status['unchanged'] += 1
|
|
||||||
except CustomMail.DoesNotExist:
|
|
||||||
mail = CustomMail.objects.create(**fields)
|
|
||||||
status['synced'] += 1
|
|
||||||
if options['verbosity']:
|
|
||||||
self.stdout.write(
|
|
||||||
'SYNCED {:s}'.format(fields['shortname']))
|
|
||||||
assoc['mails'][obj['pk']] = mail
|
|
||||||
|
|
||||||
# Variables
|
|
||||||
if obj['model'] == 'custommail.custommailvariable':
|
|
||||||
fields['custommail'] = assoc['mails'].get(fields['custommail'])
|
|
||||||
fields['type'] = assoc['types'].get(fields['type'])
|
|
||||||
try:
|
|
||||||
Variable.objects.get(
|
|
||||||
custommail=fields['custommail'],
|
|
||||||
name=fields['name']
|
|
||||||
)
|
|
||||||
except Variable.DoesNotExist:
|
|
||||||
Variable.objects.create(**fields)
|
|
||||||
|
|
||||||
if options['verbosity']:
|
|
||||||
# C'est agréable d'avoir le résultat affiché
|
|
||||||
self.stdout.write(
|
|
||||||
'{synced:d} mails synchronized {unchanged:d} unchanged'
|
|
||||||
.format(**status)
|
|
||||||
)
|
|
||||||
|
|
Loading…
Reference in a new issue