test_transcription.py
1 """ 2 Tests pour les fonctionnalités de transcription 3 ---------------------------------------------- 4 Ce module teste les fonctionnalités de transcription audio de l'API, 5 y compris les transcriptions de monologue et multi-locuteurs. 6 """ 7 8 import pytest 9 import requests 10 import json 11 import os 12 import time 13 import io 14 from typing import Dict, Any, Optional 15 from pathlib import Path 16 17 # URL de base de l'API (ajuster selon l'environnement) 18 BASE_URL = os.environ.get("API_BASE_URL", "http://localhost:8000") 19 20 # Variable pour stocker les informations de test 21 test_data = { 22 "api_key": None, 23 "token": None, 24 "audio_path": None, 25 "monologue_task_id": None, 26 "multispeaker_task_id": None, 27 "tasks": [] # Liste des IDs de tâches créées durant les tests 28 } 29 30 # Fichier audio de test (si disponible) 31 SAMPLE_AUDIO_PATH = os.environ.get("SAMPLE_AUDIO_PATH", None) 32 33 def setup_module(): 34 """Configuration initiale pour les tests de transcription.""" 35 # Récupérer une clé API valide depuis les tests d'authentification ou utiliser une variable d'environnement 36 try: 37 # Essayer d'utiliser une variable d'environnement pour la clé API 38 api_key = os.environ.get("TEST_API_KEY") 39 token = os.environ.get("TEST_TOKEN") 40 41 if not api_key: 42 # Importer et exécuter les tests d'authentification si nécessaire 43 from test_auth import test_register_user, test_login, test_create_api_key 44 45 # Créer un utilisateur et une clé API si nécessaire 46 test_register_user() 47 test_login() 48 test_create_api_key() 49 50 from test_auth import test_data as auth_test_data 51 api_key = auth_test_data["api_key"] 52 token = auth_test_data["token"] 53 except Exception as e: 54 # En cas d'erreur, une clé de test doit être fournie en variable d'environnement 55 api_key = os.environ.get("TEST_API_KEY") 56 if not api_key: 57 raise Exception("Aucune clé API disponible pour les tests. Définissez TEST_API_KEY ou exécutez test_auth.py") 58 59 test_data["api_key"] = api_key 60 test_data["token"] = token 61 62 def get_auth_headers(): 63 """Retourne les en-têtes d'authentification avec la clé API.""" 64 return {"X-API-Key": test_data["api_key"]} 65 66 def get_token_headers(): 67 """Retourne les en-têtes d'authentification avec le token.""" 68 return {"Authorization": f"Bearer {test_data['token']}"} 69 70 def get_sample_audio(): 71 """ 72 Retourne un fichier audio de test. 73 Si SAMPLE_AUDIO_PATH est défini, utilise ce fichier. 74 Sinon, crée un fichier audio minimal pour les tests. 75 76 Returns: 77 Fichier ouvert en mode binaire ou BytesIO 78 """ 79 if SAMPLE_AUDIO_PATH and os.path.exists(SAMPLE_AUDIO_PATH): 80 return open(SAMPLE_AUDIO_PATH, "rb") 81 else: 82 # Créer un fichier MP3 minimal pour les tests 83 fake_mp3 = io.BytesIO() 84 # MP3 header (ID3v2) 85 fake_mp3.write(b'ID3\x03\x00\x00\x00\x00\x00\x00') 86 # MP3 frame header 87 fake_mp3.write(b'\xFF\xFB\x90\x44\x00\x00\x00\x00') 88 # Additional data 89 fake_mp3.write(b'\x00' * 1024) 90 fake_mp3.seek(0) 91 return fake_mp3 92 93 def wait_for_task_completion(task_id: str, max_retries: int = 30, delay: int = 10) -> Optional[Dict[str, Any]]: 94 """ 95 Attend qu'une tâche de transcription soit terminée et retourne son résultat. 96 97 Args: 98 task_id: ID de la tâche à attendre 99 max_retries: Nombre maximal de tentatives 100 delay: Délai entre les tentatives en secondes 101 102 Returns: 103 Dict contenant les données de la tâche ou None en cas d'échec 104 """ 105 headers = get_auth_headers() 106 107 for i in range(max_retries): 108 response = requests.get(f"{BASE_URL}/api/transcription/tasks/{task_id}", headers=headers) 109 110 # Si le premier endpoint ne fonctionne pas, essayer l'endpoint centralisé 111 if response.status_code == 404: 112 response = requests.get(f"{BASE_URL}/api/tasks/{task_id}", headers=headers) 113 114 if response.status_code != 200: 115 print(f"Erreur lors de la récupération de la tâche: {response.status_code}") 116 time.sleep(delay) 117 continue 118 119 data = response.json() 120 121 # Si la tâche a échoué, retourner None 122 if data["status"] == "failed": 123 print(f"La tâche a échoué: {data.get('error', 'Erreur inconnue')}") 124 return None 125 126 # Si la tâche est terminée, retourner les résultats 127 if data["status"] == "completed": 128 print(f"Tâche {task_id} terminée avec succès.") 129 return data 130 131 # Afficher la progression 132 print(f"Attente de la fin de la tâche... Progression: {data.get('progress', 0):.0f}% (tentative {i+1}/{max_retries})") 133 134 # Attendre avant de réessayer 135 time.sleep(delay) 136 137 print(f"Délai dépassé en attendant la tâche {task_id}") 138 return None 139 140 def test_upload_audio_file(): 141 """Teste le téléchargement d'un fichier audio.""" 142 # S'assurer qu'une clé API est disponible 143 assert test_data["api_key"] is not None, "Aucune clé API disponible pour le test" 144 145 # Configurer les headers avec la clé API 146 headers = get_auth_headers() 147 148 # Télécharger un fichier audio 149 try: 150 audio_file = get_sample_audio() 151 152 files = { 153 "file": ("test_audio.mp3", audio_file, "audio/mpeg") 154 } 155 156 response = requests.post( 157 f"{BASE_URL}/api/transcription/upload", 158 headers=headers, 159 files=files 160 ) 161 162 # Fermer le fichier après utilisation si c'est un fichier réel 163 if hasattr(audio_file, 'close'): 164 audio_file.close() 165 166 # Si l'endpoint n'existe pas, essayer un autre endpoint 167 if response.status_code == 404: 168 audio_file = get_sample_audio() 169 files = { 170 "audio": ("test_audio.mp3", audio_file, "audio/mpeg") 171 } 172 response = requests.post( 173 f"{BASE_URL}/api/upload/audio", 174 headers=headers, 175 files=files 176 ) 177 # Fermer le fichier après utilisation si c'est un fichier réel 178 if hasattr(audio_file, 'close'): 179 audio_file.close() 180 181 # Si cet endpoint n'existe pas non plus, ignorer ce test 182 if response.status_code == 404: 183 pytest.skip("Aucun endpoint d'upload audio disponible") 184 185 assert response.status_code == 200, f"Code de statut inattendu: {response.status_code}, {response.text}" 186 187 # Vérifier la réponse 188 data = response.json() 189 assert "file_path" in data or "audio_path" in data, "Chemin du fichier manquant dans la réponse" 190 191 # Sauvegarder le chemin de l'audio pour les tests suivants 192 test_data["audio_path"] = data.get("file_path") or data.get("audio_path") 193 194 except Exception as e: 195 pytest.skip(f"Erreur lors du téléchargement de l'audio: {e}") 196 197 def test_start_transcription_monologue(): 198 """Teste le démarrage d'une transcription monologue.""" 199 # S'assurer qu'une clé API et un chemin audio sont disponibles 200 assert test_data["api_key"] is not None, "Aucune clé API disponible pour le test" 201 202 # Configurer les headers avec la clé API 203 headers = get_auth_headers() 204 205 # Si nous n'avons pas de chemin audio, essayer d'uploader un fichier 206 if not test_data.get("audio_path"): 207 try: 208 test_upload_audio_file() 209 except Exception: 210 pass 211 212 # Vérifier si nous avons maintenant un chemin audio 213 if not test_data.get("audio_path"): 214 # Si nous n'avons toujours pas de chemin audio, tester l'API avec un upload direct 215 audio_file = get_sample_audio() 216 files = { 217 "audio": ("test_audio.mp3", audio_file, "audio/mpeg") 218 } 219 data = { 220 "language": "fr", 221 "model": "base" # Utiliser le modèle le plus petit pour des tests plus rapides 222 } 223 response = requests.post( 224 f"{BASE_URL}/api/transcription/monologue", 225 headers=headers, 226 files=files, 227 data=data 228 ) 229 230 # Fermer le fichier après utilisation si c'est un fichier réel 231 if hasattr(audio_file, 'close'): 232 audio_file.close() 233 else: 234 # Utiliser le chemin audio que nous avons 235 transcription_data = { 236 "file_path": test_data["audio_path"], 237 "language": "fr", 238 "model": "base", # Utiliser le modèle le plus petit pour des tests plus rapides 239 "diarize": False 240 } 241 242 # Envoyer la requête de transcription 243 response = requests.post( 244 f"{BASE_URL}/api/transcription/monologue", 245 json=transcription_data, 246 headers=headers 247 ) 248 249 # Si l'endpoint n'existe pas, ignorer ce test 250 if response.status_code == 404: 251 # Essayer l'endpoint alternatif 252 if test_data.get("audio_path"): 253 transcription_data = { 254 "file_path": test_data["audio_path"], 255 "language": "fr", 256 "model_size": "base", 257 "diarize": False 258 } 259 response = requests.post( 260 f"{BASE_URL}/api/transcription/start", 261 json=transcription_data, 262 headers=headers 263 ) 264 else: 265 pytest.skip("Aucun endpoint de transcription monologue disponible") 266 267 # Vérifier que la réponse est acceptée, peu importe la version de l'API 268 assert response.status_code in [200, 202], f"Code de statut inattendu: {response.status_code}, {response.text}" 269 270 # Vérifier la réponse 271 data = response.json() 272 assert "task_id" in data, "ID de tâche manquant dans la réponse" 273 274 # Sauvegarder l'ID de tâche pour les tests suivants 275 test_data["monologue_task_id"] = data["task_id"] 276 test_data["tasks"].append(data["task_id"]) 277 print(f"Tâche de transcription monologue créée: {test_data['monologue_task_id']}") 278 279 def test_start_transcription_multispeaker(): 280 """Teste le démarrage d'une transcription multi-locuteurs.""" 281 # S'assurer qu'une clé API est disponible 282 assert test_data["api_key"] is not None, "Aucune clé API disponible pour le test" 283 284 # Configurer les headers avec la clé API 285 headers = get_auth_headers() 286 287 # Si nous n'avons pas de chemin audio, ignorer ce test 288 if not test_data.get("audio_path"): 289 try: 290 test_upload_audio_file() 291 except Exception: 292 pass 293 294 # Vérifier si nous avons maintenant un chemin audio 295 if not test_data.get("audio_path"): 296 # Si nous n'avons toujours pas de chemin audio, tester l'API avec un upload direct 297 audio_file = get_sample_audio() 298 files = { 299 "audio": ("test_audio.mp3", audio_file, "audio/mpeg") 300 } 301 data = { 302 "language": "fr", 303 "model": "base", # Utiliser le modèle le plus petit pour des tests plus rapides 304 "min_speakers": 2, 305 "max_speakers": 5 306 } 307 response = requests.post( 308 f"{BASE_URL}/api/transcription/multispeaker", 309 headers=headers, 310 files=files, 311 data=data 312 ) 313 314 # Fermer le fichier après utilisation si c'est un fichier réel 315 if hasattr(audio_file, 'close'): 316 audio_file.close() 317 else: 318 # Utiliser le chemin audio que nous avons 319 transcription_data = { 320 "file_path": test_data["audio_path"], 321 "language": "fr", 322 "model": "base", # Utiliser le modèle le plus petit pour des tests plus rapides 323 "diarize": True, 324 "min_speakers": 2, 325 "max_speakers": 5 326 } 327 328 # Envoyer la requête de transcription 329 response = requests.post( 330 f"{BASE_URL}/api/transcription/multispeaker", 331 json=transcription_data, 332 headers=headers 333 ) 334 335 # Si l'endpoint n'existe pas, ignorer ce test 336 if response.status_code == 404: 337 # Essayer l'endpoint alternatif 338 if test_data.get("audio_path"): 339 transcription_data = { 340 "file_path": test_data["audio_path"], 341 "language": "fr", 342 "model_size": "base", 343 "diarize": True, 344 "min_speakers": 2, 345 "max_speakers": 5 346 } 347 response = requests.post( 348 f"{BASE_URL}/api/transcription/start", 349 json=transcription_data, 350 headers=headers 351 ) 352 else: 353 pytest.skip("Aucun endpoint de transcription multi-locuteurs disponible") 354 355 # Si le service n'est pas disponible, ignorer ce test 356 if response.status_code == 503: 357 pytest.skip("Service de diarisation non disponible") 358 359 assert response.status_code in [200, 202], f"Code de statut inattendu: {response.status_code}, {response.text}" 360 361 # Vérifier la réponse 362 data = response.json() 363 assert "task_id" in data, "ID de tâche manquant dans la réponse" 364 365 # Sauvegarder l'ID de tâche pour les tests suivants 366 test_data["multispeaker_task_id"] = data["task_id"] 367 test_data["tasks"].append(data["task_id"]) 368 print(f"Tâche de transcription multi-locuteurs créée: {test_data['multispeaker_task_id']}") 369 370 def test_get_transcription_status(): 371 """Teste la récupération de l'état d'une tâche de transcription.""" 372 # S'assurer qu'une clé API et un ID de tâche sont disponibles 373 assert test_data["api_key"] is not None, "Aucune clé API disponible pour le test" 374 375 task_id = test_data.get("monologue_task_id") or test_data.get("multispeaker_task_id") 376 if task_id is None: 377 pytest.skip("Aucun ID de tâche de transcription disponible pour le test") 378 379 # Configurer les headers avec la clé API 380 headers = get_auth_headers() 381 382 # Envoyer la requête pour récupérer l'état de la tâche 383 response = requests.get(f"{BASE_URL}/api/transcription/tasks/{task_id}", headers=headers) 384 385 # Si l'endpoint n'existe pas, essayer l'endpoint centralisé 386 if response.status_code == 404: 387 response = requests.get(f"{BASE_URL}/api/tasks/{task_id}", headers=headers) 388 389 # Si cet endpoint n'existe pas non plus, ignorer ce test 390 if response.status_code == 404: 391 pytest.skip("Aucun endpoint de statut de tâche disponible") 392 393 assert response.status_code == 200, f"Code de statut inattendu: {response.status_code}, {response.text}" 394 395 # Vérifier la réponse 396 data = response.json() 397 assert "status" in data, "Statut manquant dans la réponse" 398 assert "progress" in data, "Progression manquante dans la réponse" 399 400 # La tâche peut être en attente, en cours d'exécution ou terminée 401 assert data["status"] in ["pending", "running", "completed", "failed"], f"Statut inattendu: {data['status']}" 402 403 def test_wait_for_transcription_completion(): 404 """Teste l'attente de la fin d'une tâche de transcription.""" 405 # S'assurer qu'une clé API et un ID de tâche sont disponibles 406 assert test_data["api_key"] is not None, "Aucune clé API disponible pour le test" 407 408 task_id = test_data.get("monologue_task_id") or test_data.get("multispeaker_task_id") 409 if task_id is None: 410 pytest.skip("Aucun ID de tâche de transcription disponible pour le test") 411 412 # Attendre que la tâche soit terminée (avec un délai d'attente plus court pour les tests) 413 result = wait_for_task_completion(task_id, max_retries=10, delay=5) 414 415 # Si la tâche ne s'est pas terminée dans le délai imparti, ignorer le test 416 if result is None: 417 pytest.skip(f"La tâche {task_id} n'a pas été terminée dans le délai imparti") 418 419 # Vérifier que la tâche est terminée avec succès 420 assert result["status"] == "completed", f"La tâche a terminé avec le statut {result['status']}" 421 422 # Vérifier que les résultats contiennent une transcription 423 assert "results" in result, "Résultats manquants dans la réponse" 424 results = result["results"] 425 426 # Les résultats peuvent être structurés différemment selon l'endpoint 427 if isinstance(results, dict): 428 assert "transcription" in results, "Transcription manquante dans les résultats" 429 430 # La transcription peut être une chaîne ou une liste pour la diarisation 431 transcription = results["transcription"] 432 assert isinstance(transcription, (str, list)), f"Type de transcription inattendu: {type(transcription)}" 433 434 def test_list_transcription_tasks(): 435 """Teste la récupération de la liste des tâches de transcription.""" 436 # S'assurer qu'une clé API est disponible 437 assert test_data["api_key"] is not None, "Aucune clé API disponible pour le test" 438 439 # Configurer les headers avec la clé API 440 headers = get_auth_headers() 441 442 # Envoyer la requête pour récupérer la liste des tâches 443 response = requests.get(f"{BASE_URL}/api/transcription/tasks", headers=headers) 444 445 # Si l'endpoint n'existe pas, essayer l'endpoint centralisé 446 if response.status_code == 404: 447 # Utilisez l'endpoint centralisé avec un filtre sur le type 448 response = requests.get(f"{BASE_URL}/api/tasks?task_type=transcription", headers=headers) 449 450 # Si cet endpoint n'existe pas non plus, ignorer ce test 451 if response.status_code == 404: 452 pytest.skip("Aucun endpoint de liste de tâches disponible") 453 454 assert response.status_code == 200, f"Code de statut inattendu: {response.status_code}, {response.text}" 455 456 # Vérifier la réponse 457 data = response.json() 458 459 # La réponse peut être une liste directe ou un objet contenant une liste 460 tasks = data if isinstance(data, list) else data.get("tasks", []) 461 462 # Vérifier que c'est une liste 463 assert isinstance(tasks, list), "Les tâches ne sont pas retournées sous forme de liste" 464 465 # Si nous avons créé des tâches, au moins certaines d'entre elles devraient être présentes 466 if test_data["tasks"]: 467 # Récupérer tous les IDs de tâches 468 task_ids = [task.get("task_id") or task.get("id") for task in tasks] 469 470 # Vérifier que certaines de nos tâches sont présentes 471 found_tasks = [task_id for task_id in test_data["tasks"] if task_id in task_ids] 472 assert found_tasks, "Aucune des tâches créées n'a été trouvée dans la liste" 473 474 def test_invalid_audio_format(): 475 """Teste la validation du format audio.""" 476 # S'assurer qu'une clé API est disponible 477 assert test_data["api_key"] is not None, "Aucune clé API disponible pour le test" 478 479 # Configurer les headers avec la clé API 480 headers = get_auth_headers() 481 482 # Créer un fichier texte au lieu d'un fichier audio 483 text_file = io.BytesIO(b"Ceci n'est pas un fichier audio") 484 485 files = { 486 "audio": ("not_audio.txt", text_file, "text/plain") 487 } 488 489 data = { 490 "language": "fr", 491 "model": "base" 492 } 493 494 # Envoyer la requête avec un format audio invalide 495 response = requests.post( 496 f"{BASE_URL}/api/transcription/monologue", 497 headers=headers, 498 files=files, 499 data=data 500 ) 501 502 # Si l'endpoint n'existe pas, ignorer ce test 503 if response.status_code == 404: 504 pytest.skip("Endpoint de transcription monologue non disponible") 505 506 # La réponse devrait être une erreur 400 Bad Request 507 assert response.status_code == 400, f"Code de statut inattendu: {response.status_code}, {response.text}" 508 509 # Vérifier que l'erreur est liée au format du fichier 510 error_data = response.json() 511 assert "detail" in error_data or "error" in error_data, "Message d'erreur manquant dans la réponse" 512 error_message = error_data.get("detail") or error_data.get("error") 513 assert "format" in error_message.lower() or "fichier" in error_message.lower(), "L'erreur ne mentionne pas le format du fichier" 514 515 def test_cancel_transcription_task(): 516 """Teste l'annulation d'une tâche de transcription.""" 517 # S'assurer qu'une clé API et un ID de tâche sont disponibles 518 assert test_data["api_key"] is not None, "Aucune clé API disponible pour le test" 519 520 # Utiliser la dernière tâche créée pour l'annulation 521 if not test_data["tasks"]: 522 pytest.skip("Aucune tâche disponible pour l'annulation") 523 524 task_id = test_data["tasks"][-1] 525 526 # Configurer les headers avec la clé API 527 headers = get_auth_headers() 528 529 # Obtenir d'abord l'état actuel de la tâche 530 response = requests.get(f"{BASE_URL}/api/transcription/tasks/{task_id}", headers=headers) 531 532 # Si l'endpoint spécifique n'existe pas, essayer l'endpoint centralisé 533 if response.status_code == 404: 534 response = requests.get(f"{BASE_URL}/api/tasks/{task_id}", headers=headers) 535 536 # Si nous ne pouvons pas obtenir l'état, ignorer ce test 537 if response.status_code != 200: 538 pytest.skip(f"Impossible d'obtenir l'état de la tâche {task_id}") 539 540 task_data = response.json() 541 542 # Si la tâche est déjà terminée, ignorer ce test 543 if task_data["status"] not in ["pending", "running"]: 544 pytest.skip(f"La tâche {task_id} est déjà dans l'état {task_data['status']} et ne peut pas être annulée") 545 546 # Annuler la tâche 547 # Essayer d'abord l'endpoint spécifique 548 response = requests.delete(f"{BASE_URL}/api/transcription/tasks/{task_id}", headers=headers) 549 550 # Si l'endpoint spécifique n'existe pas, essayer l'endpoint centralisé 551 if response.status_code == 404: 552 # Certaines API utilisent DELETE, d'autres POST avec /cancel 553 response = requests.post(f"{BASE_URL}/api/tasks/{task_id}/cancel", headers=headers) 554 555 # Si cela ne fonctionne pas, essayer DELETE sur l'endpoint centralisé 556 if response.status_code == 404: 557 response = requests.delete(f"{BASE_URL}/api/tasks/{task_id}", headers=headers) 558 559 # Si nous ne pouvons pas annuler la tâche, ignorer ce test 560 if response.status_code not in [200, 202]: 561 pytest.skip(f"Impossible d'annuler la tâche {task_id}: {response.status_code}, {response.text}") 562 563 # Vérifier la réponse 564 data = response.json() 565 success = data.get("success") or (data.get("status") == "cancelled") 566 assert success, f"La tâche n'a pas été correctement annulée: {data}" 567 568 # Vérifier que la tâche a bien été annulée 569 response = requests.get(f"{BASE_URL}/api/tasks/{task_id}", headers=headers) 570 571 # Si la tâche a été complètement supprimée, c'est aussi acceptable 572 if response.status_code == 404: 573 return 574 575 assert response.status_code == 200, f"Impossible de vérifier l'annulation: {response.status_code}, {response.text}" 576 577 task_data = response.json() 578 assert task_data["status"] in ["cancelled", "deleted"], f"La tâche n'a pas été annulée correctement: {task_data['status']}" 579 580 def test_cleanup(): 581 """Nettoie toutes les tâches créées pendant les tests.""" 582 # S'assurer qu'une clé API est disponible 583 assert test_data["api_key"] is not None, "Aucune clé API disponible pour le test" 584 585 # Si aucune tâche n'a été créée, rien à nettoyer 586 if not test_data["tasks"]: 587 return 588 589 # Configurer les headers avec la clé API 590 headers = get_auth_headers() 591 592 # Supprimer toutes les tâches de test 593 for task_id in test_data["tasks"]: 594 try: 595 # Essayer d'abord l'endpoint spécifique 596 response = requests.delete(f"{BASE_URL}/api/transcription/tasks/{task_id}", headers=headers) 597 598 # Si l'endpoint spécifique n'existe pas, essayer l'endpoint centralisé 599 if response.status_code == 404: 600 response = requests.delete(f"{BASE_URL}/api/tasks/{task_id}", headers=headers) 601 602 # Ne pas échouer si la suppression échoue 603 if response.status_code == 200: 604 print(f"Tâche {task_id} supprimée avec succès") 605 except Exception as e: 606 # Ignorer les erreurs lors du nettoyage 607 print(f"Erreur lors du nettoyage de la tâche {task_id}: {e}") 608 609 if __name__ == "__main__": 610 # Initialiser les tests 611 setup_module() 612 613 # Exécuter les tests manuellement 614 test_upload_audio_file() 615 616 try: 617 test_start_transcription_monologue() 618 test_get_transcription_status() 619 except Exception as e: 620 print(f"Les tests de transcription monologue ont échoué: {e}") 621 622 try: 623 test_start_transcription_multispeaker() 624 except Exception as e: 625 print(f"Les tests de transcription multi-locuteurs ont échoué: {e}") 626 627 try: 628 test_wait_for_transcription_completion() 629 except Exception as e: 630 print(f"Le test d'attente de complétion a échoué: {e}") 631 632 test_list_transcription_tasks() 633 634 try: 635 test_invalid_audio_format() 636 except Exception as e: 637 print(f"Le test de format audio invalide a échoué: {e}") 638 639 try: 640 test_cancel_transcription_task() 641 except Exception as e: 642 print(f"Le test d'annulation a échoué: {e}") 643 644 test_cleanup() 645 646 print("Tous les tests de transcription ont réussi!")