246 lines
11 KiB
Python
246 lines
11 KiB
Python
from django.test import TestCase, override_settings
|
|
from django.utils import timezone
|
|
from django.core.exceptions import ValidationError
|
|
|
|
from common.models import Thematic
|
|
from contracts.models import Company, Contract
|
|
from assets.models import AssetCategory
|
|
from interventions.models import Intervention, Symptom
|
|
from interventions.admin import SymptomAdminForm
|
|
from interventions.views.helpers import get_contract_recipients, get_contract_cc_recipients
|
|
|
|
|
|
@override_settings(EMAIL_OVERRIDE=None)
|
|
class SymptomNotificationRecipientsTests(TestCase):
|
|
def setUp(self):
|
|
self.thematic, _ = Thematic.objects.get_or_create(code='trafficlights', defaults={'name': 'Feux de signalisation'})
|
|
self.category, _ = AssetCategory.objects.get_or_create(name_fr='Test Category', thematic=self.thematic)
|
|
|
|
self.company = Company.objects.create(name='Prestataire Feux')
|
|
today = timezone.now().date()
|
|
|
|
self.contract = Contract.objects.create(
|
|
contract_number='CTR-FEUX-001',
|
|
company=self.company,
|
|
start_date=today,
|
|
end_date=today + timezone.timedelta(days=365),
|
|
is_active=True,
|
|
is_blocked=False,
|
|
notification_emails='contract_to1@example.com; contract_to2@example.com',
|
|
cc_notification_emails='contract_cc1@example.com, contract_cc2@example.com',
|
|
send_email_notifications=True,
|
|
)
|
|
self.contract.thematics.add(self.thematic)
|
|
|
|
self.contract_blocked = Contract.objects.create(
|
|
contract_number='CTR-FEUX-BLOCKED',
|
|
company=self.company,
|
|
start_date=today,
|
|
end_date=today + timezone.timedelta(days=365),
|
|
is_active=True,
|
|
is_blocked=True,
|
|
blocked_reason='Gel budget',
|
|
notification_emails='blocked_contract@example.com',
|
|
cc_notification_emails='blocked_cc@example.com',
|
|
send_email_notifications=True,
|
|
)
|
|
self.contract_blocked.thematics.add(self.thematic)
|
|
|
|
def test_default_symptom_notifies_contract_recipients(self):
|
|
"""Par défaut (notify_contract_recipients=True, sans emails supplémentaires), seuls les emails du contrat sont retournés."""
|
|
symptom = Symptom.objects.create(
|
|
name_fr='Ampoule grillée',
|
|
thematic=self.thematic,
|
|
contract=self.contract,
|
|
notify_contract_recipients=True,
|
|
)
|
|
intervention = Intervention.objects.create(
|
|
title='Remplacement ampoule',
|
|
thematic=self.thematic,
|
|
contract=self.contract,
|
|
symptom=symptom,
|
|
)
|
|
|
|
recipients = get_contract_recipients(self.contract, intervention)
|
|
self.assertIn('contract_to1@example.com', recipients)
|
|
self.assertIn('contract_to2@example.com', recipients)
|
|
self.assertEqual(len(recipients), 2)
|
|
|
|
cc_recipients = get_contract_cc_recipients(self.contract, intervention)
|
|
self.assertIn('contract_cc1@example.com', cc_recipients)
|
|
self.assertIn('contract_cc2@example.com', cc_recipients)
|
|
self.assertIn('bm.perm.tech@sprb.brussels', cc_recipients)
|
|
|
|
def test_symptom_with_additional_recipients_and_cc(self):
|
|
"""Avec notify_contract_recipients=True et des emails supplémentaires, les emails du contrat et du symptôme sont cumulés."""
|
|
symptom = Symptom.objects.create(
|
|
name_fr='Panne contrôleur critique',
|
|
thematic=self.thematic,
|
|
contract=self.contract,
|
|
notify_contract_recipients=True,
|
|
notification_emails='extra_expert@sprb.brussels; contract_to1@example.com', # Doublon volontaire avec contrat
|
|
cc_notification_emails='extra_supervision@sprb.brussels',
|
|
)
|
|
intervention = Intervention.objects.create(
|
|
title='Contrôleur HS',
|
|
thematic=self.thematic,
|
|
contract=self.contract,
|
|
symptom=symptom,
|
|
)
|
|
|
|
recipients = get_contract_recipients(self.contract, intervention)
|
|
self.assertEqual(
|
|
set(recipients),
|
|
{'contract_to1@example.com', 'contract_to2@example.com', 'extra_expert@sprb.brussels'}
|
|
)
|
|
|
|
cc_recipients = get_contract_cc_recipients(self.contract, intervention)
|
|
self.assertEqual(
|
|
set(cc_recipients),
|
|
{'contract_cc1@example.com', 'contract_cc2@example.com', 'extra_supervision@sprb.brussels', 'bm.perm.tech@sprb.brussels'}
|
|
)
|
|
|
|
def test_symptom_disabling_contract_recipients(self):
|
|
"""Avec notify_contract_recipients=False, les emails du contrat sont exclus, seuls les emails du symptôme sont prévenus."""
|
|
symptom = Symptom.objects.create(
|
|
name_fr='Vandalisme interne',
|
|
thematic=self.thematic,
|
|
contract=self.contract,
|
|
notify_contract_recipients=False,
|
|
notification_emails='police_bruxelles@sprb.brussels, securite@sprb.brussels',
|
|
cc_notification_emails='responsable_securite@sprb.brussels',
|
|
)
|
|
intervention = Intervention.objects.create(
|
|
title='Dégradation armoire',
|
|
thematic=self.thematic,
|
|
contract=self.contract,
|
|
symptom=symptom,
|
|
)
|
|
|
|
recipients = get_contract_recipients(self.contract, intervention)
|
|
self.assertEqual(
|
|
set(recipients),
|
|
{'police_bruxelles@sprb.brussels', 'securite@sprb.brussels'}
|
|
)
|
|
self.assertNotIn('contract_to1@example.com', recipients)
|
|
self.assertNotIn('contract_to2@example.com', recipients)
|
|
|
|
cc_recipients = get_contract_cc_recipients(self.contract, intervention)
|
|
self.assertIn('responsable_securite@sprb.brussels', cc_recipients)
|
|
self.assertIn('bm.perm.tech@sprb.brussels', cc_recipients)
|
|
self.assertNotIn('contract_cc1@example.com', cc_recipients)
|
|
self.assertNotIn('contract_cc2@example.com', cc_recipients)
|
|
|
|
def test_symptom_disabling_contract_recipients_without_extra_emails(self):
|
|
"""Avec notify_contract_recipients=False et sans emails supplémentaires, aucun destinataire principal n'est retourné."""
|
|
symptom = Symptom.objects.create(
|
|
name_fr='Test sans notification',
|
|
thematic=self.thematic,
|
|
contract=self.contract,
|
|
notify_contract_recipients=False,
|
|
)
|
|
intervention = Intervention.objects.create(
|
|
title='Intervention silencieuse',
|
|
thematic=self.thematic,
|
|
contract=self.contract,
|
|
symptom=symptom,
|
|
)
|
|
|
|
recipients = get_contract_recipients(self.contract, intervention)
|
|
self.assertEqual(recipients, [])
|
|
|
|
def test_blocked_contract_returns_empty_even_with_symptom_emails(self):
|
|
"""Un marché bloqué ne doit envoyer aucun email, même si le symptôme configure des destinataires."""
|
|
symptom = Symptom.objects.create(
|
|
name_fr='Panne sur marché bloqué',
|
|
thematic=self.thematic,
|
|
contract=self.contract_blocked,
|
|
notify_contract_recipients=True,
|
|
notification_emails='extra@sprb.brussels',
|
|
cc_notification_emails='extra_cc@sprb.brussels',
|
|
)
|
|
intervention = Intervention.objects.create(
|
|
title='Intervention bloquée',
|
|
thematic=self.thematic,
|
|
contract=self.contract_blocked,
|
|
symptom=symptom,
|
|
)
|
|
|
|
recipients = get_contract_recipients(self.contract_blocked, intervention)
|
|
self.assertEqual(recipients, [])
|
|
|
|
cc_recipients = get_contract_cc_recipients(self.contract_blocked, intervention)
|
|
self.assertEqual(cc_recipients, [])
|
|
|
|
def test_intervention_without_contract_uses_symptom_recipients(self):
|
|
"""Une intervention sans contrat mais avec un symptôme configuré peut quand même notifier ses destinataires spécifiques."""
|
|
symptom = Symptom.objects.create(
|
|
name_fr='Alerte voirie spéciale',
|
|
thematic=self.thematic,
|
|
contract=None,
|
|
notify_contract_recipients=True,
|
|
notification_emails='service_voirie@sprb.brussels',
|
|
cc_notification_emails='cc_voirie@sprb.brussels',
|
|
)
|
|
intervention = Intervention.objects.create(
|
|
title='Intervention sans contrat',
|
|
thematic=self.thematic,
|
|
contract=None,
|
|
symptom=symptom,
|
|
)
|
|
|
|
recipients = get_contract_recipients(None, intervention)
|
|
self.assertEqual(recipients, ['service_voirie@sprb.brussels'])
|
|
|
|
cc_recipients = get_contract_cc_recipients(None, intervention)
|
|
self.assertIn('cc_voirie@sprb.brussels', cc_recipients)
|
|
self.assertIn('bm.perm.tech@sprb.brussels', cc_recipients)
|
|
|
|
def test_backward_compatibility_without_intervention(self):
|
|
"""L'appel sans intervention (intervention=None) conserve le comportement historique du contrat."""
|
|
recipients = get_contract_recipients(self.contract)
|
|
self.assertEqual(
|
|
set(recipients),
|
|
{'contract_to1@example.com', 'contract_to2@example.com'}
|
|
)
|
|
|
|
cc_recipients = get_contract_cc_recipients(self.contract)
|
|
self.assertIn('contract_cc1@example.com', cc_recipients)
|
|
self.assertIn('contract_cc2@example.com', cc_recipients)
|
|
self.assertIn('bm.perm.tech@sprb.brussels', cc_recipients)
|
|
|
|
def test_symptom_admin_form_email_validation(self):
|
|
"""Le formulaire d'administration valide et normalise les listes d'adresses email."""
|
|
# Emails valides avec mélange de virgules et points-virgules
|
|
form = SymptomAdminForm(data={
|
|
'name_fr': 'Test Form',
|
|
'name_nl': 'Test Form NL',
|
|
'thematic': self.thematic.pk,
|
|
'priority': '3',
|
|
'is_active': True,
|
|
'notify_contract_recipients': True,
|
|
'notification_emails': 'alpha@example.com; beta@example.com, gamma@example.com',
|
|
'cc_notification_emails': 'delta@example.com; epsilon@example.com',
|
|
})
|
|
self.assertTrue(form.is_valid(), form.errors)
|
|
self.assertEqual(
|
|
form.cleaned_data['notification_emails'],
|
|
'alpha@example.com, beta@example.com, gamma@example.com'
|
|
)
|
|
self.assertEqual(
|
|
form.cleaned_data['cc_notification_emails'],
|
|
'delta@example.com, epsilon@example.com'
|
|
)
|
|
|
|
# Email invalide
|
|
form_invalid = SymptomAdminForm(data={
|
|
'name_fr': 'Test Form Invalid',
|
|
'name_nl': 'Test Form Invalid NL',
|
|
'thematic': self.thematic.pk,
|
|
'priority': '3',
|
|
'is_active': True,
|
|
'notify_contract_recipients': True,
|
|
'notification_emails': 'adresse_pas_valide',
|
|
})
|
|
self.assertFalse(form_invalid.is_valid())
|
|
self.assertIn('notification_emails', form_invalid.errors)
|