""" Tests pour l'application Detection Créé par Marino ATOHOUN - FireWatch AI Project """ import os import tempfile from django.test import TestCase, Client from django.urls import reverse from django.core.files.uploadedfile import SimpleUploadedFile from django.contrib.auth.models import User from PIL import Image import io from .models import Contact, DetectionSession, Detection, AIModelStatus class ContactModelTest(TestCase): """Tests pour le modèle Contact - Par Marino ATOHOUN""" def setUp(self): self.contact = Contact.objects.create( name="Test User", email="test@example.com", message="Message de test" ) def test_contact_creation(self): """Test de création d'un contact""" self.assertEqual(self.contact.name, "Test User") self.assertEqual(self.contact.email, "test@example.com") self.assertFalse(self.contact.is_read) def test_contact_str_method(self): """Test de la méthode __str__ du contact""" expected = f"Message de {self.contact.name} ({self.contact.email}) - {self.contact.created_at.strftime('%d/%m/%Y %H:%M')}" self.assertEqual(str(self.contact), expected) class DetectionSessionModelTest(TestCase): """Tests pour le modèle DetectionSession - Par Marino ATOHOUN""" def setUp(self): self.session = DetectionSession.objects.create( detection_type='image' ) def test_session_creation(self): """Test de création d'une session""" self.assertEqual(self.session.detection_type, 'image') self.assertFalse(self.session.is_processed) self.assertIsNotNone(self.session.session_id) def test_session_str_method(self): """Test de la méthode __str__ de la session""" expected = f"Session {self.session.session_id} - Image - {self.session.created_at.strftime('%d/%m/%Y %H:%M')}" self.assertEqual(str(self.session), expected) class DetectionModelTest(TestCase): """Tests pour le modèle Detection - Par Marino ATOHOUN""" def setUp(self): self.session = DetectionSession.objects.create(detection_type='image') self.detection = Detection.objects.create( session=self.session, class_name='fire', confidence=0.92, bbox_x=100, bbox_y=100, bbox_width=150, bbox_height=150 ) def test_detection_creation(self): """Test de création d'une détection""" self.assertEqual(self.detection.class_name, 'fire') self.assertEqual(self.detection.confidence, 0.92) def test_bbox_dict_property(self): """Test de la propriété bbox_dict""" expected = { 'x': 100, 'y': 100, 'width': 150, 'height': 150 } self.assertEqual(self.detection.bbox_dict, expected) class ViewsTest(TestCase): """Tests pour les vues - Par Marino ATOHOUN""" def setUp(self): self.client = Client() def test_index_view(self): """Test de la vue principale""" response = self.client.get(reverse('detection:index')) self.assertEqual(response.status_code, 200) self.assertContains(response, 'FireWatch AI') def test_contact_view_post(self): """Test de soumission du formulaire de contact""" data = { 'name': 'Test User', 'email': 'test@example.com', 'message': 'Message de test' } response = self.client.post(reverse('detection:contact'), data) self.assertEqual(response.status_code, 200) # Vérifier que le contact a été créé contact = Contact.objects.get(email='test@example.com') self.assertEqual(contact.name, 'Test User') def test_contact_view_invalid_data(self): """Test avec des données invalides""" data = { 'name': '', 'email': 'invalid-email', 'message': '' } response = self.client.post(reverse('detection:contact'), data) self.assertEqual(response.status_code, 400) def create_test_image(self): """Crée une image de test""" image = Image.new('RGB', (100, 100), color='red') image_io = io.BytesIO() image.save(image_io, format='JPEG') image_io.seek(0) return SimpleUploadedFile( "test_image.jpg", image_io.getvalue(), content_type="image/jpeg" ) def test_analyze_image_view(self): """Test d'analyse d'image""" test_image = self.create_test_image() response = self.client.post( reverse('detection:analyze_image'), {'image': test_image} ) self.assertEqual(response.status_code, 200) data = response.json() self.assertTrue(data['success']) self.assertIn('session_id', data) self.assertIn('detections', data) def test_analyze_image_no_file(self): """Test d'analyse sans fichier""" response = self.client.post(reverse('detection:analyze_image')) self.assertEqual(response.status_code, 400) data = response.json() self.assertFalse(data['success']) def test_models_status_api(self): """Test de l'API de statut des modèles""" # Créer un statut de modèle de test AIModelStatus.objects.create( model_type='fire', model_path='models/incendies.pt', is_loaded=True ) response = self.client.get(reverse('detection:models_status')) self.assertEqual(response.status_code, 200) data = response.json() self.assertTrue(data['success']) self.assertIn('models', data) class AdminTest(TestCase): """Tests pour l'interface d'administration - Par Marino ATOHOUN""" def setUp(self): self.admin_user = User.objects.create_superuser( username='admin', email='admin@test.com', password='admin123' ) self.client = Client() self.client.login(username='admin', password='admin123') def test_admin_contact_list(self): """Test de la liste des contacts dans l'admin""" Contact.objects.create( name="Test User", email="test@example.com", message="Message de test" ) response = self.client.get('/admin/detection/contact/') self.assertEqual(response.status_code, 200) self.assertContains(response, 'Test User') def test_admin_detection_session_list(self): """Test de la liste des sessions dans l'admin""" session = DetectionSession.objects.create(detection_type='image') response = self.client.get('/admin/detection/detectionsession/') self.assertEqual(response.status_code, 200) self.assertContains(response, str(session.session_id)) class UtilityTest(TestCase): """Tests pour les fonctions utilitaires - Par Marino ATOHOUN""" def test_file_upload_paths(self): """Test des chemins d'upload""" from .models import upload_to_images, upload_to_videos, upload_to_results # Mock instance class MockInstance: pass instance = MockInstance() # Test des chemins image_path = upload_to_images(instance, 'test.jpg') self.assertTrue(image_path.startswith('uploads/images/')) self.assertTrue(image_path.endswith('.jpg')) video_path = upload_to_videos(instance, 'test.mp4') self.assertTrue(video_path.startswith('uploads/videos/')) self.assertTrue(video_path.endswith('.mp4')) result_path = upload_to_results(instance, 'result.jpg') self.assertTrue(result_path.startswith('results/')) self.assertTrue(result_path.endswith('.jpg')) class IntegrationTest(TestCase): """Tests d'intégration - Par Marino ATOHOUN""" def setUp(self): self.client = Client() def test_full_image_analysis_workflow(self): """Test du workflow complet d'analyse d'image""" # 1. Créer une image de test image = Image.new('RGB', (640, 480), color='red') image_io = io.BytesIO() image.save(image_io, format='JPEG') image_io.seek(0) test_image = SimpleUploadedFile( "test_fire.jpg", image_io.getvalue(), content_type="image/jpeg" ) # 2. Analyser l'image response = self.client.post( reverse('detection:analyze_image'), {'image': test_image} ) self.assertEqual(response.status_code, 200) data = response.json() self.assertTrue(data['success']) session_id = data['session_id'] # 3. Vérifier que la session a été créée session = DetectionSession.objects.get(session_id=session_id) self.assertEqual(session.detection_type, 'image') self.assertTrue(session.is_processed) # 4. Récupérer les résultats via l'API response = self.client.get( reverse('detection:get_results', kwargs={'session_id': session_id}) ) self.assertEqual(response.status_code, 200) results_data = response.json() self.assertTrue(results_data['success']) self.assertEqual(results_data['session_id'], session_id) def test_contact_and_admin_workflow(self): """Test du workflow contact et administration""" # 1. Envoyer un message de contact contact_data = { 'name': 'John Doe', 'email': 'john@example.com', 'message': 'Question sur FireWatch AI' } response = self.client.post(reverse('detection:contact'), contact_data) self.assertEqual(response.status_code, 200) # 2. Vérifier que le contact existe contact = Contact.objects.get(email='john@example.com') self.assertEqual(contact.name, 'John Doe') self.assertFalse(contact.is_read) # 3. Marquer comme lu (simulation admin) contact.is_read = True contact.save() # 4. Vérifier la mise à jour updated_contact = Contact.objects.get(email='john@example.com') self.assertTrue(updated_contact.is_read) class PerformanceTest(TestCase): """Tests de performance - Par Marino ATOHOUN""" def test_multiple_detections_creation(self): """Test de création de multiples détections""" import time session = DetectionSession.objects.create(detection_type='video') start_time = time.time() # Créer 100 détections detections = [] for i in range(100): detection = Detection( session=session, class_name='fire', confidence=0.8 + (i % 20) * 0.01, bbox_x=i * 10, bbox_y=i * 10, bbox_width=100, bbox_height=100, frame_number=i ) detections.append(detection) Detection.objects.bulk_create(detections) end_time = time.time() creation_time = end_time - start_time # Vérifier que la création est rapide (moins de 1 seconde) self.assertLess(creation_time, 1.0) # Vérifier que toutes les détections ont été créées self.assertEqual(Detection.objects.filter(session=session).count(), 100) class SecurityTest(TestCase): """Tests de sécurité - Par Marino ATOHOUN""" def test_csrf_protection(self): """Test de protection CSRF""" # Tentative de POST sans token CSRF response = self.client.post(reverse('detection:contact'), { 'name': 'Test', 'email': 'test@example.com', 'message': 'Test' }) # Django doit rejeter la requête sans CSRF token self.assertEqual(response.status_code, 403) def test_file_type_validation(self): """Test de validation des types de fichiers""" # Créer un fichier texte déguisé en image fake_image = SimpleUploadedFile( "fake.jpg", b"This is not an image", content_type="text/plain" ) response = self.client.post( reverse('detection:analyze_image'), {'image': fake_image} ) # La requête doit être rejetée self.assertEqual(response.status_code, 400) def test_large_file_rejection(self): """Test de rejet des fichiers trop volumineux""" # Créer un fichier de 100MB (simulé) large_content = b"x" * (100 * 1024 * 1024) # 100MB large_file = SimpleUploadedFile( "large.jpg", large_content, content_type="image/jpeg" ) # Note: Ce test peut ne pas fonctionner en environnement de test # car Django limite la taille en mémoire try: response = self.client.post( reverse('detection:analyze_image'), {'image': large_file} ) # Si la requête passe, elle doit être rejetée par notre validation if response.status_code == 200: data = response.json() self.assertFalse(data.get('success', True)) except Exception: # Exception attendue pour un fichier trop volumineux pass # Par Marino ATOHOUN: Commande pour lancer tous les tests # python manage.py test detection.tests