tests.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. import os
  4. import logging
  5. import ldapdb
  6. from datetime import date
  7. from cStringIO import StringIO
  8. from dateutil.relativedelta import relativedelta
  9. from django import db
  10. from django.test import TestCase, Client, override_settings
  11. from django.contrib.auth.models import User
  12. from django.core import mail, management
  13. from coin.members.models import Member, MembershipFee, LdapUser
  14. class MemberTests(TestCase):
  15. def test_when_creating_member_a_ldapuser_is_also_created_with_same_data(self):
  16. """
  17. Test que lors de la création d'un nouveau membre, une entrée
  18. correspondante est bien créée dans le LDAP et qu'elle contient
  19. les mêmes données.
  20. Cela concerne le nom et le prénom
  21. """
  22. #~ Créé un membre
  23. first_name = 'Gérard'
  24. last_name = 'Majax'
  25. username = MemberTestsUtils.get_random_username()
  26. member = Member(first_name=first_name,
  27. last_name=last_name,
  28. username=username)
  29. member.save()
  30. #~ Récupère l'utilisateur LDAP et fait les tests
  31. ldap_user = LdapUser.objects.get(pk=username)
  32. self.assertEqual(ldap_user.first_name, first_name)
  33. self.assertEqual(ldap_user.last_name, last_name)
  34. self.assertEqual(ldap_user.pk, username)
  35. member.delete()
  36. def test_when_modifiying_member_corresponding_ldap_user_is_also_modified_with_same_data(self):
  37. """
  38. Test que lorsque l'on modifie un membre, l'utilisateur LDAP
  39. correspondant est bien modifié
  40. Cela concerne le no met le prénom
  41. """
  42. #~ Créé un membre
  43. first_name = 'Ronald'
  44. last_name = 'Mac Donald'
  45. username = MemberTestsUtils.get_random_username()
  46. member = Member(first_name=first_name,
  47. last_name=last_name, username=username)
  48. member.save()
  49. #~ Le modifie
  50. new_first_name = 'José'
  51. new_last_name = 'Bové'
  52. member.first_name = new_first_name
  53. member.last_name = new_last_name
  54. member.save()
  55. #~ Récupère l'utilisateur LDAP et fait les tests
  56. ldap_user = LdapUser.objects.get(pk=username)
  57. self.assertEqual(ldap_user.first_name, new_first_name)
  58. self.assertEqual(ldap_user.last_name, new_last_name)
  59. member.delete()
  60. # def test_when_creating_member_corresponding_ldap_user_is_in_coin_ldap_group(self):
  61. # """
  62. # Test que l'utilisateur Ldap fraichement créé est bien dans le group "coin"
  63. # Et que lors de la supression d'un membre, l'utilisateur LDAP correspondant
  64. # est bien retiré du groupe.
  65. # """
  66. # ~ Créé un membre
  67. # username = MemberTestsUtils.get_random_username()
  68. # member = Member(first_name='Canard',
  69. # last_name='WC', username=username)
  70. # member.save()
  71. # ~ Récupère le group "coin" et test que l'utilisateur y est présent
  72. # ldap_group = LdapGroup.objects.get(pk="coin")
  73. # self.assertEqual(username in ldap_group.members, True)
  74. # ~ Supprime l'utilisateur
  75. # member.delete()
  76. # ~ Récupère le group "coin" et test que l'utilisateur n'y est plus
  77. # ldap_group = LdapGroup.objects.get(pk="coin")
  78. # self.assertEqual(username in ldap_group.members, False)
  79. # LdapUser.objects.get(pk=username).delete()
  80. def test_change_password_and_auth(self):
  81. """
  82. Test que la fonction change_password de member fonctionne et permet
  83. l'authentification avec le nouveau mot de passe
  84. """
  85. username = MemberTestsUtils.get_random_username()
  86. password = "1234"
  87. #~ Créé un nouveau membre
  88. member = Member(first_name='Passe-partout',
  89. last_name='Du fort Boyard', username=username)
  90. member.save()
  91. #~ Récupère l'utilisateur LDAP
  92. ldap_user = LdapUser.objects.get(pk=username)
  93. #~ Change son mot de passe
  94. member.set_password(password)
  95. member.save()
  96. #~ Test l'authentification django
  97. c = Client()
  98. self.assertEqual(c.login(username=username, password=password), True)
  99. # Test l'authentification ldap
  100. import ldap
  101. ldap_conn_settings = db.connections['ldap'].settings_dict
  102. l = ldap.initialize(ldap_conn_settings['NAME'])
  103. options = ldap_conn_settings.get('CONNECTION_OPTIONS', {})
  104. for opt, value in options.items():
  105. l.set_option(opt, value)
  106. if ldap_conn_settings.get('TLS', False):
  107. l.start_tls_s()
  108. # Raise "Invalid credentials" exception if auth fail
  109. l.simple_bind_s(ldap_conn_settings['USER'],
  110. ldap_conn_settings['PASSWORD'])
  111. l.unbind_s()
  112. member.delete()
  113. def test_when_creating_member_ldap_display_name_is_well_defined(self):
  114. """
  115. Lors de la création d'un membre, le champ "display_name" du LDAP est
  116. prenom + nom
  117. """
  118. first_name = 'Gérard'
  119. last_name = 'Majax'
  120. username = MemberTestsUtils.get_random_username()
  121. member = Member(first_name=first_name,
  122. last_name=last_name, username=username)
  123. member.save()
  124. #~ Récupère l'utilisateur LDAP
  125. ldap_user = LdapUser.objects.get(pk=username)
  126. self.assertEqual(ldap_user.display_name, '%s %s' %
  127. (first_name, last_name))
  128. member.delete()
  129. def test_when_creating_member_username_is_well_defined(self):
  130. """
  131. Lors de la création d'un membre, le champ "username", s'il n'est pas
  132. définit doit être généré avec les contraintes suivantes :
  133. premières lettres du prénom + nom le tout en minuscule,
  134. sans caractères accentués et sans espaces.
  135. """
  136. random = os.urandom(4).encode('hex')
  137. first_name = 'Gérard-Étienne'
  138. last_name = 'Majax de la Boétie!B' + random
  139. control = 'gemajaxdelaboetieb' + random
  140. control = control[:30]
  141. member = Member(first_name=first_name, last_name=last_name)
  142. member.save()
  143. self.assertEqual(member.username, control)
  144. member.delete()
  145. def test_when_creating_member_with_username_already_exists_username_is_incr(self):
  146. """
  147. Lors de la création d'un membre, test si le username existe déjà,
  148. renvoi avec un incrément à la fin
  149. """
  150. random = os.urandom(4).encode('hex')
  151. member1 = Member(first_name='Hervé', last_name='DUPOND' + random, email='hdupond@coin.org')
  152. member1.save()
  153. self.assertEqual(member1.username, 'hdupond' + random)
  154. member2 = Member(first_name='Henri', last_name='DUPOND' + random, email='hdupond2@coin.org')
  155. member2.save()
  156. self.assertEqual(member2.username, 'hdupond' + random + '2')
  157. member3 = Member(first_name='Hector', last_name='DUPOND' + random, email='hdupond3@coin.org')
  158. member3.save()
  159. self.assertEqual(member3.username, 'hdupond' + random + '3')
  160. member1.delete()
  161. member2.delete()
  162. member3.delete()
  163. def test_when_creating_legal_entity_organization_name_is_used_for_username(self):
  164. """
  165. Lors de la créatio d'une entreprise, son nom doit être utilisée lors de
  166. la détermination automatique du username
  167. """
  168. random = os.urandom(4).encode('hex')
  169. member = Member(type='legal_entity', organization_name='ILLYSE' + random, email='illyse@coin.org')
  170. member.save()
  171. self.assertEqual(member.username, 'illyse' + random)
  172. member.delete()
  173. def test_when_creating_member_with_nickname_it_is_used_for_username(self):
  174. """
  175. Lors de la création d'une personne, qui a un pseudo, celui-ci est utilisé en priorité
  176. """
  177. random = os.urandom(4).encode('hex')
  178. member = Member(first_name='Richard', last_name='Stallman', nickname='rms' + random, email='illyse@coin.org')
  179. member.save()
  180. self.assertEqual(member.username, 'rms' + random)
  181. member.delete()
  182. def test_when_saving_member_and_ldap_fail_dont_save(self):
  183. """
  184. Test que lors de la sauvegarde d'un membre et que la sauvegarde en LDAP
  185. échoue (ici mauvais mot de passe), rien n'est sauvegardé en base
  186. """
  187. # Fait échouer le LDAP en définissant un mauvais mot de passe
  188. for dbconnection in db.connections.all():
  189. if (type(dbconnection) is
  190. ldapdb.backends.ldap.base.DatabaseWrapper):
  191. dbconnection.settings_dict[
  192. 'PREVIOUSPASSWORD'] = dbconnection.settings_dict['PASSWORD']
  193. dbconnection.settings_dict['PASSWORD'] = 'wrong password test'
  194. # Créé un membre
  195. first_name = 'Du'
  196. last_name = 'Pont'
  197. username = MemberTestsUtils.get_random_username()
  198. member = Member(first_name=first_name,
  199. last_name=last_name, username=username)
  200. # Le sauvegarde en base de donnée
  201. # Le save devrait renvoyer une exception parceque le LDAP échoue
  202. self.assertRaises(Exception, member.save)
  203. # On s'assure, malgré l'exception, que le membre n'est pas en base
  204. with self.assertRaises(Member.DoesNotExist):
  205. Member.objects.get(username=username)
  206. # Restaure le mot de passe pour les tests suivants
  207. for dbconnection in db.connections.all():
  208. if (type(dbconnection) is
  209. ldapdb.backends.ldap.base.DatabaseWrapper):
  210. dbconnection.settings_dict[
  211. 'PASSWORD'] = dbconnection.settings_dict['PREVIOUSPASSWORD']
  212. # def test_when_user_login_member_user_field_is_updated(self):
  213. # """
  214. # Test que lorqu'un utilisateur se connect, le champ user du membre
  215. # correspondant est mis à jour convenablement
  216. # """
  217. # Créé un membre
  218. # first_name = 'Du'
  219. # last_name = 'Pond'
  220. # password = '1234'
  221. # username = MemberTestsUtils.get_random_username()
  222. # member = Member(first_name=first_name,
  223. # last_name=last_name, username=username)
  224. # member.save()
  225. # member.change_password(password)
  226. # Vérifie que user non définit
  227. # self.assertIsNone(member.user)
  228. # Connection
  229. # c = Client()
  230. # self.assertEqual(c.login(username=username, password=password), True)
  231. # Vérifie que user définit
  232. # member = Member.objects.get(username=username)
  233. # self.assertIsNotNone(member.user)
  234. # LdapUser.objects.get(pk=member.username).delete()
  235. def test_member_end_date_of_memberhip(self):
  236. """
  237. Test que end_date_of_membership d'un membre envoi bien la date de fin d'adhésion
  238. """
  239. # Créer un membre
  240. first_name = 'Tin'
  241. last_name = 'Tin'
  242. username = MemberTestsUtils.get_random_username()
  243. member = Member(first_name=first_name,
  244. last_name=last_name, username=username)
  245. member.save()
  246. start_date = date.today()
  247. end_date = start_date + relativedelta(years=+1)
  248. # Créé une cotisation
  249. membershipfee = MembershipFee(member=member, amount=20,
  250. start_date=start_date,
  251. end_date=end_date)
  252. membershipfee.save()
  253. self.assertEqual(member.end_date_of_membership(), end_date)
  254. def test_member_is_paid_up(self):
  255. """
  256. Test l'état "a jour de cotisation" d'un adhérent.
  257. """
  258. # Créé un membre
  259. first_name = 'Capitain'
  260. last_name = 'Haddock'
  261. username = MemberTestsUtils.get_random_username()
  262. member = Member(first_name=first_name,
  263. last_name=last_name, username=username)
  264. member.save()
  265. start_date = date.today()
  266. end_date = start_date + relativedelta(years=+1)
  267. # Test qu'un membre sans cotisation n'est pas à jour
  268. self.assertEqual(member.is_paid_up(), False)
  269. # Créé une cotisation passée
  270. membershipfee = MembershipFee(member=member, amount=20,
  271. start_date=date.today() +
  272. relativedelta(years=-1),
  273. end_date=date.today() + relativedelta(days=-10))
  274. membershipfee.save()
  275. # La cotisation s'étant terminée il y a 10 jours, il ne devrait pas
  276. # être à jour de cotistion
  277. self.assertEqual(member.is_paid_up(), False)
  278. # Créé une cotisation actuelle
  279. membershipfee = MembershipFee(member=member, amount=20,
  280. start_date=date.today() +
  281. relativedelta(days=-10),
  282. end_date=date.today() + relativedelta(days=+10))
  283. membershipfee.save()
  284. # La cotisation se terminant dans 10 jour, il devrait être à jour
  285. # de cotisation
  286. self.assertEqual(member.is_paid_up(), True)
  287. def test_member_cant_be_created_without_names(self):
  288. """
  289. Test qu'un membre ne peut pas être créé sans "noms"
  290. (prenom, nom) ou pseudo ou nom d'organization
  291. """
  292. member = Member(username='blop')
  293. self.assertRaises(Exception, member.save)
  294. member = Member()
  295. self.assertRaises(Exception, member.save)
  296. class MemberAdminTests(TestCase):
  297. def setUp(self):
  298. #~ Client web
  299. self.client = Client()
  300. #~ Créé un superuser
  301. self.admin_user_password = '1234'
  302. self.admin_user = Member.objects.create_superuser(
  303. username='test_admin_user',
  304. first_name='test',
  305. last_name='Admin user',
  306. email='i@mail.com',
  307. password=self.admin_user_password)
  308. #~ Connection
  309. self.assertEqual(self.client.login(
  310. username=self.admin_user.username, password=self.admin_user_password), True)
  311. def tearDown(self):
  312. # Supprime le superuser
  313. self.admin_user.delete()
  314. def test_cant_change_username_when_editing(self):
  315. """
  316. Vérifie que dans l'admin Django, le champ username n'est pad modifiable
  317. sur une fiche existante
  318. """
  319. #~ Créé un membre
  320. first_name = 'Gérard'
  321. last_name = 'Majax'
  322. username = MemberTestsUtils.get_random_username()
  323. member = Member(first_name=first_name,
  324. last_name=last_name, username=username)
  325. member.save()
  326. edit_page = self.client.get('/admin/members/member/%i/' % member.id)
  327. self.assertNotContains(edit_page,
  328. '''<input id="id_username" />''',
  329. html=True)
  330. member.delete()
  331. class MemberTestCallForMembershipCommand(TestCase):
  332. def setUp(self):
  333. # Créé un membre
  334. self.username = MemberTestsUtils.get_random_username()
  335. self.member = Member(first_name='Richard', last_name='Stallman',
  336. username=self.username)
  337. self.member.save()
  338. def tearDown(self):
  339. # Supprime le membre
  340. self.member.delete()
  341. MembershipFee.objects.all().delete()
  342. def create_membership_fee(self, end_date):
  343. # Créé une cotisation passée se terminant dans un mois
  344. membershipfee = MembershipFee(member=self.member, amount=20,
  345. start_date=end_date + relativedelta(years=-1),
  346. end_date=end_date)
  347. membershipfee.save()
  348. def create_membership_fee(self, end_date):
  349. # Créé une cotisation se terminant à la date indiquée
  350. membershipfee = MembershipFee(member=self.member, amount=20,
  351. start_date=end_date + relativedelta(years=-1),
  352. end_date=end_date)
  353. membershipfee.save()
  354. return membershipfee
  355. def do_test_email_sent(self, expected_emails = 1, reset_date_last_call = True):
  356. # Vide la outbox
  357. mail.outbox = []
  358. # Call command
  359. management.call_command('call_for_membership_fees', stdout=StringIO())
  360. # Test
  361. self.assertEqual(len(mail.outbox), expected_emails)
  362. # Comme on utilise le même membre, on reset la date de dernier envoi
  363. if reset_date_last_call:
  364. self.member.date_last_call_for_membership_fees_email = None
  365. self.member.save()
  366. def do_test_for_a_end_date(self, end_date, expected_emails=1, reset_date_last_call = True):
  367. # Supprimer toutes les cotisations (au cas ou)
  368. MembershipFee.objects.all().delete()
  369. # Créé la cotisation
  370. membershipfee = self.create_membership_fee(end_date)
  371. self.do_test_email_sent(expected_emails, reset_date_last_call)
  372. membershipfee.delete()
  373. def test_call_email_sent_at_expected_dates(self):
  374. # 1 mois avant la fin, à la fin et chaque mois après la fin pendant 3 mois
  375. self.do_test_for_a_end_date(date.today() + relativedelta(months=+1))
  376. self.do_test_for_a_end_date(date.today())
  377. self.do_test_for_a_end_date(date.today() + relativedelta(months=-1))
  378. self.do_test_for_a_end_date(date.today() + relativedelta(months=-2))
  379. self.do_test_for_a_end_date(date.today() + relativedelta(months=-3))
  380. def test_call_email_not_sent_if_active_membership_fee(self):
  381. # Créé une cotisation se terminant dans un mois
  382. membershipfee = self.create_membership_fee(date.today() + relativedelta(months=+1))
  383. # Un mail devrait être envoyé (ne pas vider date_last_call_for_membership_fees_email)
  384. self.do_test_email_sent(1, False)
  385. # Créé une cotisation enchainant et se terminant dans un an
  386. membershipfee = self.create_membership_fee(date.today() + relativedelta(months=+1, years=+1))
  387. # Pas de mail envoyé
  388. self.do_test_email_sent(0)
  389. def test_date_last_call_for_membership_fees_email(self):
  390. # Créé une cotisation se terminant dans un mois
  391. membershipfee = self.create_membership_fee(date.today() + relativedelta(months=+1))
  392. # Un mail envoyé (ne pas vider date_last_call_for_membership_fees_email)
  393. self.do_test_email_sent(1, False)
  394. # Tente un deuxième envoi, qui devrait être à 0
  395. self.do_test_email_sent(0)
  396. class MemberTestsUtils(object):
  397. @staticmethod
  398. def get_random_username():
  399. """
  400. Renvoi une clé aléatoire pour un utilisateur LDAP
  401. """
  402. return 'coin_test_' + os.urandom(8).encode('hex')