Code source de services.api_ihm.tests.test_plot_data

"""
Tests unitaires pour la route /api/plot-data de l'API IHM.

Ce module contient les tests pour vérifier le bon fonctionnement
de la route qui fournit les données pour les graphiques.

Les tests vérifient :
1. Le format de la réponse de l'API
2. La gestion des erreurs (fichier non trouvé)
3. La cohérence des données retournées

Version: 1.0.0 (2024-03-19)
Auteur: Kahina et franck - Groupe 2
"""

import unittest
import json
import os
import sys
from unittest.mock import patch, mock_open
import pandas as pd
import numpy as np
from flask import Flask
import logging

# Configuration du logging pour les tests
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# Ajout du chemin src au PYTHONPATH pour trouver les modules à tester
current_dir = os.path.dirname(os.path.abspath(__file__))
src_path = os.path.join(current_dir, '..', 'src')
sys.path.insert(0, src_path)
logger.info(f"Ajout du chemin au PYTHONPATH: {src_path}")

# Import du module à tester
from main.routes import main_bp


[docs] class TestPlotDataRoute(unittest.TestCase): """ Classe de test pour la route /api/plot-data. Cette classe teste le comportement de l'endpoint qui fournit les données pour les graphiques de l'interface. Tests effectués: - Format de la réponse API - Gestion des erreurs - Cohérence des données """
[docs] def setUp(self): """ Configuration initiale pour chaque test. Cette méthode est exécutée avant chaque test et configure: - Une application Flask de test - Un client de test - Les données de test communes """ logger.info("Initialisation d'un nouveau test...") # Configuration de l'application Flask de test self.app = Flask(__name__) self.app.register_blueprint(main_bp) self.client = self.app.test_client() self.app.config['TESTING'] = True os.environ['PYTHON_ENV'] = 'test' logger.debug("Application Flask de test configurée") # Création des données de test communes # Ces données simulent des mesures sur 3 jours avec des valeurs croissantes self.test_data = pd.DataFrame({ 'year': [2024] * 3, # Année constante 'month': [3] * 3, # Mois constant (mars) 'day': [1, 2, 3], # Trois jours consécutifs 'pm2.5': [10.0, 20.0, 30.0], # Valeurs PM2.5 croissantes 'TEMP': [20.0, 25.0, 30.0], # Température croissante 'DEWP': [15.0, 18.0, 21.0], # Point de rosée croissant 'PRES': [1013.0, 1014.0, 1015.0], # Pression croissante 'Iws': [2.0, 3.0, 4.0], # Vitesse du vent croissante 'Is': [8.0, 7.0, 6.0], # Ensoleillement décroissant 'Ir': [0.0, 0.5, 1.0] # Précipitations croissantes }) logger.debug("Données de test générées")
[docs] def test_plot_data_response_format(self): """ Test du format de la réponse de l'API. Vérifie: - Le code de statut HTTP - La présence de tous les champs requis - Les types de données corrects - La cohérence des longueurs des listes """ logger.info("Début du test de format de réponse") # Simulation de la présence du fichier et des données with patch('pandas.read_csv', return_value=self.test_data), \ patch('os.path.exists', return_value=True): logger.debug("Envoi de la requête GET à /api/plot-data") response = self.client.get('/api/plot-data') # Vérification du code de statut self.assertEqual(response.status_code, 200) logger.debug("Code de statut 200 OK reçu") # Parsing de la réponse JSON data = json.loads(response.data) logger.debug("Réponse JSON parsée avec succès") # Vérification de la structure de base expected_keys = [ 'dates', 'pm25', 'correlated_vars', 'correlation_values' ] for key in expected_keys: self.assertIn(key, data) logger.debug(f"Clé '{key}' présente dans la réponse") # Vérification des types de données self.assertTrue(isinstance(data['dates'], list)) self.assertTrue(isinstance(data['pm25'], list)) self.assertTrue(isinstance(data['correlated_vars'], list)) self.assertTrue(isinstance(data['correlation_values'], list)) logger.debug("Types de données vérifiés") # Vérification de la longueur des données self.assertEqual(len(data['dates']), 3) self.assertEqual(len(data['pm25']), 3) self.assertTrue(len(data['correlated_vars']) > 0) logger.debug("Longueurs des listes vérifiées") logger.info("Test de format de réponse terminé avec succès")
[docs] def test_plot_data_file_not_found(self): """ Test de la gestion d'erreur quand le fichier de données n'existe pas. Vérifie: - Le code d'erreur 404 - Le format du message d'erreur - Le contenu du message d'erreur """ logger.info("Début du test de fichier non trouvé") # Simulation de l'absence du fichier with patch('os.path.exists', return_value=False): logger.debug("Simulation de fichier manquant") response = self.client.get('/api/plot-data') # Vérification du code d'erreur self.assertEqual(response.status_code, 404) logger.debug("Code 404 reçu comme attendu") # Vérification du message d'erreur data = json.loads(response.data) self.assertIn('error', data) self.assertTrue(isinstance(data['error'], str)) self.assertIn('Fichier de données non trouvé', data['error']) logger.debug(f"Message d'erreur vérifié: {data['error']}") logger.info("Test de fichier non trouvé terminé avec succès")
[docs] def test_plot_data_values_consistency(self): """ Test de la cohérence des données retournées. Vérifie: - Le format des dates - La cohérence des valeurs PM2.5 - La structure des variables corrélées - La normalisation des données """ logger.info("Début du test de cohérence des données") with patch('pandas.read_csv', return_value=self.test_data), \ patch('os.path.exists', return_value=True): response = self.client.get('/api/plot-data') self.assertEqual(response.status_code, 200) data = json.loads(response.data) logger.debug("Données reçues de l'API") # Test des dates self.assertEqual(len(data['dates']), 3) self.assertTrue(all(isinstance(d, str) for d in data['dates'])) logger.debug("Format des dates vérifié") # Test des valeurs PM2.5 self.assertEqual(len(data['pm25']), 3) self.assertTrue( all(isinstance(v, (int, float)) for v in data['pm25'])) logger.debug("Types des valeurs PM2.5 vérifiés") # Vérification de l'ordre croissant des PM2.5 self.assertTrue( data['pm25'][0] < data['pm25'][1] < data['pm25'][2]) logger.debug("Ordre croissant des PM2.5 vérifié") # Test des variables corrélées self.assertTrue(len(data['correlated_vars']) >= 5) for var in data['correlated_vars']: self.assertIn('name', var) self.assertIn('label', var) logger.debug("Structure des variables corrélées vérifiée") # Test des valeurs normalisées for var in ['TEMP', 'DEWP', 'PRES', 'Iws', 'Is']: if var in data: values = np.array(data[var]) mean_val = abs(np.mean(values)) std_val = np.std(values) # Vérification de la normalisation self.assertLess( mean_val, 0.5, f"Moyenne de {var} ({mean_val:.2f}) devrait être proche de 0" ) self.assertTrue( 0.5 <= std_val <= 1.5, f"Écart-type de {var} ({std_val:.2f}) devrait être proche de 1" ) logger.debug( f"Variable {var} - moyenne: {mean_val:.2f}, écart-type: {std_val:.2f}" ) logger.info("Test de cohérence des données terminé avec succès")
if __name__ == '__main__': unittest.main()