@@ -134,7 +134,8 @@ def get_session_last_assistant_message(self, session_id):
134134
135135import telegram_bot .core .bot as bot_module
136136from telegram_bot .core .bot import TelegramBot
137- from telegram_bot .utils .transcription import EmptyTranscriptionError
137+ from telegram_bot .utils .tos_uploader import TOSUploadError
138+ from telegram_bot .utils .transcription import EmptyTranscriptionError , TranscriptionError
138139
139140_NOISY_LOGGERS = ["telegram_bot.core.bot" ]
140141_ORIGINAL_LEVELS = {}
@@ -342,10 +343,13 @@ async def run_now(user_id, run_task, on_overflow):
342343 side_effect = lambda path , cleanup : path
343344 )
344345 bot ._process_user_message_text = AsyncMock ()
346+ uploaded = SimpleNamespace (
347+ signed_url = "https://tos.example.com/stage/voice.ogg?X-Tos-Signature=abc" ,
348+ object_key = "telegram-voice/11/object.ogg" ,
349+ )
345350 uploader = SimpleNamespace (
346- upload_file = MagicMock (
347- return_value = "https://tos.example.com/stage/voice.ogg?X-Tos-Signature=abc"
348- ),
351+ upload_file_with_object_key = MagicMock (return_value = uploaded ),
352+ delete_object = MagicMock (return_value = None ),
349353 redact_signed_url = lambda url : (
350354 "https://tos.example.com/stage/voice.ogg?***REDACTED***"
351355 ),
@@ -367,11 +371,12 @@ async def run_now(user_id, run_task, on_overflow):
367371 bot_module .config .transcription_provider = old_provider
368372
369373 bot ._download_voice_file .assert_awaited_once ()
370- self .assertEqual (uploader .upload_file .call_count , 1 )
374+ self .assertEqual (uploader .upload_file_with_object_key .call_count , 1 )
371375 transcriber .transcribe_audio .assert_awaited_once_with (
372376 "https://tos.example.com/stage/voice.ogg?X-Tos-Signature=abc" ,
373377 duration_seconds = 30 ,
374378 )
379+ uploader .delete_object .assert_called_once_with ("telegram-voice/11/object.ogg" )
375380 bot ._prepare_audio_for_whisper .assert_not_called ()
376381 bot ._process_user_message_text .assert_awaited_once ()
377382 called = bot ._process_user_message_text .await_args
@@ -381,6 +386,110 @@ async def run_now(user_id, run_task, on_overflow):
381386 "🎤 Voice: hello from volcengine" ,
382387 )
383388
389+ async def test_volcengine_delete_failure_does_not_break_successful_reply (self ):
390+ bot = TelegramBot ()
391+ bot ._check_access = AsyncMock (return_value = True )
392+
393+ old_provider = bot_module .config .transcription_provider
394+ config_module .config .transcription_provider = "volcengine"
395+ bot_module .config .transcription_provider = "volcengine"
396+
397+ async def run_now (user_id , run_task , on_overflow ):
398+ del user_id , on_overflow
399+ await run_task ()
400+ return True
401+
402+ bot ._enqueue_user_task = run_now
403+ bot ._download_voice_file = AsyncMock (return_value = None )
404+ bot ._prepare_audio_for_whisper = AsyncMock (
405+ side_effect = lambda path , cleanup : path
406+ )
407+ bot ._process_user_message_text = AsyncMock ()
408+ uploader = SimpleNamespace (
409+ upload_file_with_object_key = MagicMock (
410+ return_value = SimpleNamespace (
411+ signed_url = "https://tos.example.com/stage/voice.ogg?X-Tos-Signature=abc" ,
412+ object_key = "telegram-voice/11/object.ogg" ,
413+ )
414+ ),
415+ delete_object = MagicMock (side_effect = TOSUploadError ("delete failed" )),
416+ redact_signed_url = lambda url : (
417+ "https://tos.example.com/stage/voice.ogg?***REDACTED***"
418+ ),
419+ )
420+ bot ._get_volcengine_tos_uploader = lambda : uploader
421+ transcriber = SimpleNamespace (
422+ transcribe_audio = AsyncMock (return_value = "hello from volcengine" )
423+ )
424+ bot ._get_volcengine_transcriber = lambda : transcriber
425+ voice = SimpleNamespace (file_id = "v1" , duration = 30 , mime_type = "audio/ogg" )
426+ update = _build_update (11 , voice )
427+
428+ try :
429+ with TemporaryDirectory () as td :
430+ bot ._audio_dir = Path (td )
431+ await bot ._handle_voice_message (update , None )
432+ finally :
433+ config_module .config .transcription_provider = old_provider
434+ bot_module .config .transcription_provider = old_provider
435+
436+ transcriber .transcribe_audio .assert_awaited_once ()
437+ uploader .delete_object .assert_called_once_with ("telegram-voice/11/object.ogg" )
438+ bot ._process_user_message_text .assert_awaited_once ()
439+
440+ async def test_volcengine_transcription_failure_still_deletes_tos_object (self ):
441+ bot = TelegramBot ()
442+ bot ._check_access = AsyncMock (return_value = True )
443+
444+ old_provider = bot_module .config .transcription_provider
445+ config_module .config .transcription_provider = "volcengine"
446+ bot_module .config .transcription_provider = "volcengine"
447+
448+ async def run_now (user_id , run_task , on_overflow ):
449+ del user_id , on_overflow
450+ await run_task ()
451+ return True
452+
453+ bot ._enqueue_user_task = run_now
454+ bot ._download_voice_file = AsyncMock (return_value = None )
455+ uploader = SimpleNamespace (
456+ upload_file_with_object_key = MagicMock (
457+ return_value = SimpleNamespace (
458+ signed_url = "https://tos.example.com/stage/voice.ogg?X-Tos-Signature=abc" ,
459+ object_key = "telegram-voice/11/object.ogg" ,
460+ )
461+ ),
462+ delete_object = MagicMock (return_value = None ),
463+ redact_signed_url = lambda url : (
464+ "https://tos.example.com/stage/voice.ogg?***REDACTED***"
465+ ),
466+ )
467+ bot ._get_volcengine_tos_uploader = lambda : uploader
468+ transcriber = SimpleNamespace (
469+ transcribe_audio = AsyncMock (side_effect = TranscriptionError ("asr failed" ))
470+ )
471+ bot ._get_volcengine_transcriber = lambda : transcriber
472+ bot ._process_user_message_text = AsyncMock ()
473+ voice = SimpleNamespace (file_id = "v1" , duration = 30 , mime_type = "audio/ogg" )
474+ update = _build_update (11 , voice )
475+
476+ try :
477+ with TemporaryDirectory () as td :
478+ bot ._audio_dir = Path (td )
479+ await bot ._handle_voice_message (update , None )
480+ finally :
481+ config_module .config .transcription_provider = old_provider
482+ bot_module .config .transcription_provider = old_provider
483+
484+ uploader .delete_object .assert_called_once_with ("telegram-voice/11/object.ogg" )
485+ bot ._process_user_message_text .assert_not_awaited ()
486+ self .assertTrue (
487+ any (
488+ "Failed to transcribe your voice message" in msg
489+ for msg in update .message .replies
490+ )
491+ )
492+
384493 async def test_reports_missing_volcengine_configuration (self ):
385494 bot = TelegramBot ()
386495 bot ._check_access = AsyncMock (return_value = True )
0 commit comments