Repository: zestedesavoir/zds-site

Issue:
Erreur 500 lors de l'inscription en cas d'indisponibilité d'un serveur de mail
Lorsqu'on essaye de s'inscrire sur le site ET que le serveur de mail n'est pas disponible, on se prend une erreur 500 (ça arrive sur la beta actuellement qui n'a pas de serveur mail).

Un try/catch oublié dans le module membre ?



Discussion:
Vu que justement, la bêta n'as pas de serveur mail, j'ai du mal à séparer cette cause là et un oubli qui serait légitime. Je laisse investiguer qui de droit avec les accès qui vont bien.

On ne devrait jamais avoir une erreur 500 mais c'est pas lié à la release actuelle, vous avez d'autres choses à faire plus urgentes. :)

Alors c'est une 500 qui doit faire planter complètement l'application puisque c'est l'erreur 500 avec Clem en gros et non celle avec l'interface du site qui apparait. De plus, ça n’apparait donc pas dans Sentry.


Resolving patch:
diff --git a/zds/member/api/views.py b/zds/member/api/views.py
--- a/zds/member/api/views.py
+++ b/zds/member/api/views.py
@@ -1,4 +1,7 @@
 import datetime
+import logging
+
+from django.utils.translation import ugettext_lazy as _
 from django.core.cache import cache
 from django.db.models.signals import post_save, post_delete
 from dry_rest_permissions.generics import DRYPermissions
@@ -108,7 +111,11 @@ def post(self, request, *args, **kwargs):
         serializer.is_valid(raise_exception=True)
         profile = serializer.save()
         token = self.generate_token(profile.user)
-        self.send_email(token, profile.user)
+        try:
+            self.send_email(token, profile.user)
+        except Exception as e:
+            logging.getLogger(__name__).warning('Mail not sent', exc_info=e)
+            return Response({'error': _("Impossible d'envoyer l'email")}, status=status.HTTP_400_BAD_REQUEST)
         headers = self.get_success_headers(serializer.data)
         return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
 
diff --git a/zds/member/commons.py b/zds/member/commons.py
--- a/zds/member/commons.py
+++ b/zds/member/commons.py
@@ -88,10 +88,7 @@ def send_email(self, token, user):
 
         msg = EmailMultiAlternatives(subject, message_txt, from_email, [user.email])
         msg.attach_alternative(message_html, 'text/html')
-        try:
-            msg.send()
-        except Exception:
-            pass
+        msg.send()
 
 
 class MemberSanctionState(object):
diff --git a/zds/member/views.py b/zds/member/views.py
--- a/zds/member/views.py
+++ b/zds/member/views.py
@@ -445,8 +445,13 @@ def form_valid(self, form):
         profile.last_ip_address = get_client_ip(self.request)
         self.save_profile(profile)
         token = self.generate_token(profile.user)
-        self.send_email(token, profile.user)
-
+        try:
+            self.send_email(token, profile.user)
+        except Exception as e:
+            logging.getLogger(__name__).warning('Mail not sent', exc_info=e)
+            messages.warning(self.request, _("Impossible d'envoyer l'email."))
+            self.object = None
+            return self.form_invalid(form)
         return render(self.request, self.get_success_template())
 
     def get_success_template(self):
@@ -498,7 +503,12 @@ def form_valid(self, form):
 
         # Generate new token and send email
         token = self.generate_token(self.usr)
-        self.send_email(token, self.usr)
+        try:
+            self.send_email(token, self.usr)
+        except Exception as e:
+            logging.getLogger(__name__).warning('Mail not sent', exc_info=e)
+            messages.warning(_("Impossible d'envoyer l'email."))
+            return self.form_invalid(form)
 
         return render(self.request, self.get_success_template())
 


Test patch:
diff --git a/zds/member/tests/tests_views.py b/zds/member/tests/tests_views.py
--- a/zds/member/tests/tests_views.py
+++ b/zds/member/tests/tests_views.py
@@ -1,12 +1,16 @@
 import os
 from datetime import datetime
+from smtplib import SMTPException
+
+from django.core.mail.backends.base import BaseEmailBackend
+from mock import Mock
 from oauth2_provider.models import AccessToken, Application
 
 from django.conf import settings
 from django.contrib.auth.models import User, Group
 from django.core import mail
 from django.urls import reverse
-from django.test import TestCase
+from django.test import TestCase, override_settings
 from django.utils.translation import ugettext_lazy as _
 
 from zds.notification.models import TopicAnswerSubscription
@@ -1762,3 +1766,31 @@ def test_profile_report(self):
         alert = Alert.objects.get(pk=alert.pk)  # refresh
         self.assertTrue(alert.solved)
         self.assertEqual(pm_count + 1, PrivateTopic.objects.count())
+
+
+mail_backend = Mock()
+
+
+class FakeBackend(BaseEmailBackend):
+    def send_messages(self, email_messages):
+        return mail_backend.send_messages(email_messages)
+
+
+@override_settings(EMAIL_BACKEND='zds.member.tests.tests_views.FakeBackend')
+class RegisterTest(TestCase):
+    def test_exception_on_mail(self):
+        def message(l):
+            print('message sent')
+            raise SMTPException(l)
+        mail_backend.send_messages = message
+
+        result = self.client.post(
+            reverse('register-member'),
+            {
+                'username': 'firm1',
+                'password': 'flavour',
+                'password_confirm': 'flavour',
+                'email': 'firm1@zestedesavoir.com'},
+            follow=False)
+        self.assertEqual(result.status_code, 200)
+        self.assertIn('Impossible d&#39;envoyer l&#39;email.', result.content.decode('utf-8'))