/ tests / test_json_simplifier.py
test_json_simplifier.py
  1  """
  2  Tests pour le post-processeur JSONSimplifier
  3  -------------------------------------------
  4  Ce module teste les fonctionnalités du post-processeur JSONSimplifier,
  5  y compris l'activation/désactivation et différents scénarios de traitement.
  6  """
  7  
  8  import pytest
  9  import json
 10  import os
 11  from unittest.mock import MagicMock, patch
 12  from typing import Dict, Any
 13  
 14  from postprocessors.json_simplifier import JSONSimplifier
 15  from postprocessors import get_postprocessor
 16  
 17  # Données de test
 18  SAMPLE_JSON_RESULT = {
 19      "result": {
 20          "analysis": {
 21              "sentiment": "positive",
 22              "tone": "formal",
 23              "key_points": [
 24                  "Point 1: Le sujet est bien expliqué",
 25                  "Point 2: Arguments clairs et cohérents",
 26                  "Point 3: Conclusion logique"
 27              ],
 28              "complexity_score": 0.75,
 29              "readability_metrics": {
 30                  "flesch_kincaid": 65.2,
 31                  "smog_index": 8.1,
 32                  "coleman_liau_index": 10.3
 33              }
 34          },
 35          "language": "fr",
 36          "processing_time": 1.23
 37      }
 38  }
 39  
 40  EXPECTED_PLAIN_TEXT = "Le texte a un sentiment positif avec un ton formel. Il comprend 3 points clés: le sujet est bien expliqué, les arguments sont clairs et cohérents, et la conclusion est logique. Le texte a un score de complexité de 0,75 et présente une bonne lisibilité avec un indice Flesch-Kincaid de 65,2."
 41  
 42  class MockModel:
 43      """Classe simulant un modèle de langage pour les tests"""
 44      
 45      def __init__(self, return_value=None):
 46          self.return_value = return_value or EXPECTED_PLAIN_TEXT
 47      
 48      def generate(self, prompt, **kwargs):
 49          return self.return_value
 50  
 51  class TestJSONSimplifier:
 52      
 53      @pytest.fixture
 54      def basic_config(self):
 55          """Configuration de base pour le post-processeur"""
 56          return {
 57              "enabled": True,
 58              "model": "test-model",
 59              "system_prompt": "Translate this json {text} in plain french",
 60              "max_tokens": 500,
 61              "temperature": 0.3,
 62              "apply_to": ["inference", "video", "transcription"]
 63          }
 64      
 65      @pytest.fixture
 66      def disabled_config(self):
 67          """Configuration avec le post-processeur désactivé"""
 68          return {
 69              "enabled": False,
 70              "model": "test-model",
 71              "system_prompt": "Translate this json {text} in plain french",
 72              "max_tokens": 500,
 73              "temperature": 0.3,
 74              "apply_to": ["inference", "video", "transcription"]
 75          }
 76      
 77      @pytest.fixture
 78      def limited_tasks_config(self):
 79          """Configuration avec un ensemble limité de types de tâches"""
 80          return {
 81              "enabled": True,
 82              "model": "test-model",
 83              "system_prompt": "Translate this json {text} in plain french",
 84              "max_tokens": 500,
 85              "temperature": 0.3,
 86              "apply_to": ["inference", "video"]  # Sans "transcription"
 87          }
 88      
 89      def test_initialization(self, basic_config):
 90          """Teste l'initialisation correcte du post-processeur"""
 91          simplifier = JSONSimplifier(basic_config)
 92          
 93          assert simplifier.enabled == basic_config["enabled"]
 94          assert simplifier.model_name == basic_config["model"]
 95          assert simplifier.system_prompt == basic_config["system_prompt"]
 96          assert simplifier.max_tokens == basic_config["max_tokens"]
 97          assert simplifier.temperature == basic_config["temperature"]
 98          assert simplifier.apply_to == basic_config["apply_to"]
 99          assert simplifier.model is None  # Le modèle n'est pas chargé à l'initialisation
100      
101      def test_should_process_enabled(self, basic_config):
102          """Teste la méthode should_process quand le post-processeur est activé"""
103          simplifier = JSONSimplifier(basic_config)
104          
105          # Les types de tâches listés dans apply_to devraient retourner True
106          assert simplifier.should_process("inference") is True
107          assert simplifier.should_process("video") is True
108          assert simplifier.should_process("transcription") is True
109          
110          # Les autres types de tâches devraient retourner False
111          assert simplifier.should_process("other_task") is False
112      
113      def test_should_process_disabled(self, disabled_config):
114          """Teste la méthode should_process quand le post-processeur est désactivé"""
115          simplifier = JSONSimplifier(disabled_config)
116          
117          # Tous les types de tâches devraient retourner False
118          assert simplifier.should_process("inference") is False
119          assert simplifier.should_process("video") is False
120          assert simplifier.should_process("transcription") is False
121          assert simplifier.should_process("other_task") is False
122      
123      def test_should_process_limited_tasks(self, limited_tasks_config):
124          """Teste la méthode should_process avec un ensemble limité de types de tâches"""
125          simplifier = JSONSimplifier(limited_tasks_config)
126          
127          # Les types de tâches listés dans apply_to devraient retourner True
128          assert simplifier.should_process("inference") is True
129          assert simplifier.should_process("video") is True
130          
131          # Les types de tâches non listés devraient retourner False
132          assert simplifier.should_process("transcription") is False
133          assert simplifier.should_process("other_task") is False
134      
135      @patch('postprocessors.json_simplifier.ModelManager')
136      def test_process_success(self, mock_model_manager, basic_config):
137          """Teste le traitement réussi d'un résultat JSON"""
138          # Configurer le mock pour ModelManager
139          mock_manager_instance = MagicMock()
140          mock_model_manager.get_instance.return_value = mock_manager_instance
141          
142          # Configurer le mock pour le modèle
143          mock_model = MockModel()
144          mock_manager_instance.get_model.return_value = mock_model
145          
146          # Créer l'instance du simplifier
147          simplifier = JSONSimplifier(basic_config)
148          
149          # Traiter le résultat JSON
150          result = SAMPLE_JSON_RESULT.copy()
151          processed = simplifier.process(result, "inference")
152          
153          # Vérifier les résultats
154          assert "plain_explanation" in processed
155          assert processed["plain_explanation"] == EXPECTED_PLAIN_TEXT
156          
157          # Vérifier que le résultat original a été préservé
158          assert processed["result"] == SAMPLE_JSON_RESULT["result"]
159          
160          # Vérifier que le modèle a été correctement appelé
161          mock_manager_instance.get_model.assert_called_once_with("llm", basic_config["model"])
162      
163      @patch('postprocessors.json_simplifier.ModelManager')
164      def test_process_model_error(self, mock_model_manager, basic_config):
165          """Teste le comportement quand le modèle rencontre une erreur"""
166          # Configurer le mock pour déclencher une exception lors de l'appel de generate
167          mock_manager_instance = MagicMock()
168          mock_model_manager.get_instance.return_value = mock_manager_instance
169          
170          mock_model = MagicMock()
171          mock_model.generate.side_effect = Exception("Erreur de modèle simulée")
172          mock_manager_instance.get_model.return_value = mock_model
173          
174          # Créer l'instance du simplifier
175          simplifier = JSONSimplifier(basic_config)
176          
177          # Traiter le résultat JSON
178          result = SAMPLE_JSON_RESULT.copy()
179          processed = simplifier.process(result, "inference")
180          
181          # Vérifier que les résultats originaux sont retournés sans modification
182          assert "plain_explanation" not in processed
183          assert processed == result
184      
185      @patch('postprocessors.json_simplifier.ModelManager')
186      def test_process_model_not_available(self, mock_model_manager, basic_config):
187          """Teste le comportement quand le modèle n'est pas disponible"""
188          # Configurer le mock pour retourner None (modèle non disponible)
189          mock_manager_instance = MagicMock()
190          mock_model_manager.get_instance.return_value = mock_manager_instance
191          mock_manager_instance.get_model.return_value = None
192          
193          # Créer l'instance du simplifier
194          simplifier = JSONSimplifier(basic_config)
195          
196          # Traiter le résultat JSON
197          result = SAMPLE_JSON_RESULT.copy()
198          processed = simplifier.process(result, "inference")
199          
200          # Vérifier que les résultats originaux sont retournés sans modification
201          assert "plain_explanation" not in processed
202          assert processed == result
203      
204      def test_process_disabled(self, disabled_config):
205          """Teste le traitement quand le post-processeur est désactivé"""
206          # Créer l'instance du simplifier désactivé
207          simplifier = JSONSimplifier(disabled_config)
208          
209          # Traiter le résultat JSON
210          result = SAMPLE_JSON_RESULT.copy()
211          processed = simplifier.process(result, "inference")
212          
213          # Vérifier que les résultats originaux sont retournés sans modification
214          assert "plain_explanation" not in processed
215          assert processed == result
216      
217      def test_process_task_not_in_apply_to(self, limited_tasks_config):
218          """Teste le traitement quand le type de tâche n'est pas dans apply_to"""
219          # Créer l'instance du simplifier avec types de tâches limités
220          simplifier = JSONSimplifier(limited_tasks_config)
221          
222          # Traiter le résultat JSON pour un type de tâche non inclus
223          result = SAMPLE_JSON_RESULT.copy()
224          processed = simplifier.process(result, "transcription")  # transcription n'est pas dans apply_to
225          
226          # Vérifier que les résultats originaux sont retournés sans modification
227          assert "plain_explanation" not in processed
228          assert processed == result
229      
230      @patch('postprocessors.json_simplifier.ModelManager')
231      def test_process_json_serialization(self, mock_model_manager, basic_config):
232          """Teste la sérialisation JSON lors du traitement"""
233          # Configurer le mock
234          mock_manager_instance = MagicMock()
235          mock_model_manager.get_instance.return_value = mock_manager_instance
236          mock_model = MockModel()
237          mock_manager_instance.get_model.return_value = mock_model
238          
239          # Créer l'instance du simplifier
240          simplifier = JSONSimplifier(basic_config)
241          
242          # Définir un objet JSON avec des types non-sérialisables
243          complex_result = {
244              "result": {
245                  "data": set([1, 2, 3]),  # un ensemble n'est pas JSON-sérialisable
246                  "function": lambda x: x  # une fonction n'est pas JSON-sérialisable
247              }
248          }
249          
250          # Traiter le résultat JSON
251          try:
252              processed = simplifier.process(complex_result, "inference")
253              # Le test devrait échouer ici, car la sérialisation devrait échouer
254              assert False, "La sérialisation aurait dû échouer avec des types non-sérialisables"
255          except:
256              # Vérifier que l'exception est bien gérée et que le résultat original est retourné
257              pass
258      
259      def test_get_postprocessor_function(self, basic_config):
260          """Teste la fonction get_postprocessor du module postprocessors"""
261          with patch('postprocessors.available_postprocessors', {"json_simplifier": JSONSimplifier}):
262              # Obtenir une instance du post-processeur via la fonction get_postprocessor
263              processor = get_postprocessor("json_simplifier", basic_config)
264              
265              # Vérifier que l'instance est bien du type JSONSimplifier
266              assert isinstance(processor, JSONSimplifier)
267              
268              # Vérifier qu'un nom de post-processeur invalide retourne None
269              assert get_postprocessor("invalid_processor", {}) is None
270      
271      @patch('postprocessors.json_simplifier.ModelManager')
272      def test_prompt_formatting(self, mock_model_manager, basic_config):
273          """Teste le formatage du prompt avec le JSON"""
274          # Configurer le mock
275          mock_manager_instance = MagicMock()
276          mock_model_manager.get_instance.return_value = mock_manager_instance
277          mock_model = MagicMock()
278          mock_manager_instance.get_model.return_value = mock_model
279          mock_model.generate.return_value = EXPECTED_PLAIN_TEXT
280          
281          # Créer l'instance du simplifier
282          simplifier = JSONSimplifier(basic_config)
283          
284          # Traiter le résultat JSON
285          result = SAMPLE_JSON_RESULT.copy()
286          simplifier.process(result, "inference")
287          
288          # Vérifier que le prompt formaté a été passé au modèle
289          # Récupérer les arguments de l'appel à generate
290          args, kwargs = mock_model.generate.call_args
291          prompt = args[0]
292          
293          # Vérifier que le prompt contient le JSON et respecte le template
294          assert "{text}" not in prompt  # Le placeholder doit être remplacé
295          assert "Translate this json" in prompt  # Le début du prompt doit être présent
296          assert json.dumps(result["result"]) in prompt  # Le JSON doit être sérialisé et inclus
297      
298      def test_integration_with_env_variables(self):
299          """Teste l'intégration avec les variables d'environnement"""
300          # Sauvegarder les variables d'environnement actuelles
301          original_enabled = os.environ.get("JSON_SIMPLIFIER_ENABLED")
302          original_model = os.environ.get("JSON_SIMPLIFIER_MODEL")
303          original_apply_to = os.environ.get("JSON_SIMPLIFIER_APPLY_TO")
304          
305          try:
306              # Configurer les variables d'environnement pour le test
307              os.environ["JSON_SIMPLIFIER_ENABLED"] = "true"
308              os.environ["JSON_SIMPLIFIER_MODEL"] = "test-env-model"
309              os.environ["JSON_SIMPLIFIER_APPLY_TO"] = "inference,video"
310              
311              # Charger la configuration depuis l'environnement
312              from config import load_config
313              config = load_config()
314              
315              # Vérifier que la configuration a été correctement chargée
316              assert config["postprocessing"]["json_simplifier"]["enabled"] is True
317              assert config["postprocessing"]["json_simplifier"]["model"] == "test-env-model"
318              assert config["postprocessing"]["json_simplifier"]["apply_to"] == ["inference", "video"]
319              
320          finally:
321              # Restaurer les variables d'environnement originales
322              if original_enabled is not None:
323                  os.environ["JSON_SIMPLIFIER_ENABLED"] = original_enabled
324              else:
325                  os.environ.pop("JSON_SIMPLIFIER_ENABLED", None)
326                  
327              if original_model is not None:
328                  os.environ["JSON_SIMPLIFIER_MODEL"] = original_model
329              else:
330                  os.environ.pop("JSON_SIMPLIFIER_MODEL", None)
331                  
332              if original_apply_to is not None:
333                  os.environ["JSON_SIMPLIFIER_APPLY_TO"] = original_apply_to
334              else:
335                  os.environ.pop("JSON_SIMPLIFIER_APPLY_TO", None)
336  
337  
338  if __name__ == "__main__":
339      print("Exécution des tests du post-processeur JSONSimplifier...")
340      pytest.main(["-xvs", __file__])