11import asyncio
22import json
33from unittest .mock import AsyncMock , MagicMock , patch
4- import pytest
54
5+ import pytest
66from elevenlabs .conversational_ai .conversation import (
7- AsyncConversation ,
87 AsyncAudioInterface ,
8+ AsyncConversation ,
99 ConversationInitiationData ,
1010)
1111
@@ -45,7 +45,6 @@ def create_mock_async_websocket(messages=None):
4545
4646 # Convert messages to JSON strings
4747 json_messages = [json .dumps (msg ) for msg in messages ]
48- json_messages .extend (['{"type": "keep_alive"}' ] * 10 ) # Add some keep-alive messages
4948
5049 # Create an iterator
5150 message_iter = iter (json_messages )
@@ -54,8 +53,9 @@ async def mock_recv():
5453 try :
5554 return next (message_iter )
5655 except StopIteration :
57- # Simulate connection close after messages
58- raise asyncio .TimeoutError ()
56+ # After all messages, simulate timeout by sleeping forever
57+ # This will be caught by asyncio.wait_for timeout in the conversation
58+ await asyncio .sleep (float ("inf" ))
5959
6060 mock_ws .recv = mock_recv
6161 return mock_ws
@@ -66,6 +66,7 @@ async def test_async_conversation_basic_flow():
6666 # Mock setup
6767 mock_ws = create_mock_async_websocket ()
6868 mock_client = MagicMock ()
69+ mock_client ._client_wrapper .get_base_url .return_value = "https://api.elevenlabs.io"
6970 agent_response_callback = AsyncMock ()
7071 test_user_id = "test_user_123"
7172
@@ -94,7 +95,7 @@ async def test_async_conversation_basic_flow():
9495
9596 # Assertions - check the call was made with the right structure
9697 send_calls = [call [0 ][0 ] for call in mock_ws .send .call_args_list ]
97- init_messages = [json .loads (call ) for call in send_calls if ' conversation_initiation_client_data' in call ]
98+ init_messages = [json .loads (call ) for call in send_calls if " conversation_initiation_client_data" in call ]
9899 assert len (init_messages ) == 1
99100 init_message = init_messages [0 ]
100101
@@ -148,6 +149,7 @@ async def test_async_conversation_with_dynamic_variables():
148149 # Mock setup
149150 mock_ws = create_mock_async_websocket ()
150151 mock_client = MagicMock ()
152+ mock_client ._client_wrapper .get_base_url .return_value = "https://api.elevenlabs.io"
151153 agent_response_callback = AsyncMock ()
152154
153155 dynamic_variables = {"name" : "angelo" }
@@ -177,7 +179,7 @@ async def test_async_conversation_with_dynamic_variables():
177179
178180 # Assertions - check the call was made with the right structure
179181 send_calls = [call [0 ][0 ] for call in mock_ws .send .call_args_list ]
180- init_messages = [json .loads (call ) for call in send_calls if ' conversation_initiation_client_data' in call ]
182+ init_messages = [json .loads (call ) for call in send_calls if " conversation_initiation_client_data" in call ]
181183 assert len (init_messages ) == 1
182184 init_message = init_messages [0 ]
183185
@@ -196,6 +198,7 @@ async def test_async_conversation_with_contextual_update():
196198 # Mock setup
197199 mock_ws = create_mock_async_websocket ([])
198200 mock_client = MagicMock ()
201+ mock_client ._client_wrapper .get_base_url .return_value = "https://api.elevenlabs.io"
199202
200203 # Setup the conversation
201204 conversation = AsyncConversation (
@@ -228,6 +231,7 @@ async def test_async_conversation_send_user_message():
228231 # Mock setup
229232 mock_ws = create_mock_async_websocket ([])
230233 mock_client = MagicMock ()
234+ mock_client ._client_wrapper .get_base_url .return_value = "https://api.elevenlabs.io"
231235
232236 # Setup the conversation
233237 conversation = AsyncConversation (
@@ -260,6 +264,7 @@ async def test_async_conversation_register_user_activity():
260264 # Mock setup
261265 mock_ws = create_mock_async_websocket ([])
262266 mock_client = MagicMock ()
267+ mock_client ._client_wrapper .get_base_url .return_value = "https://api.elevenlabs.io"
263268
264269 # Setup the conversation
265270 conversation = AsyncConversation (
@@ -300,29 +305,21 @@ async def test_async_conversation_callback_flows():
300305 "type" : "agent_response_correction" ,
301306 "agent_response_correction_event" : {
302307 "original_agent_response" : "Hello ther!" ,
303- "corrected_agent_response" : "Hello there!"
304- }
305- },
306- {
307- "type" : "user_transcript" ,
308- "user_transcription_event" : {"user_transcript" : "Hi, how are you?" }
309- },
310- {
311- "type" : "ping" ,
312- "ping_event" : {"event_id" : "123" , "ping_ms" : 50 }
313- },
314- {
315- "type" : "interruption" ,
316- "interruption_event" : {"event_id" : "456" }
308+ "corrected_agent_response" : "Hello there!" ,
309+ },
317310 },
311+ {"type" : "user_transcript" , "user_transcription_event" : {"user_transcript" : "Hi, how are you?" }},
312+ {"type" : "ping" , "ping_event" : {"event_id" : "123" , "ping_ms" : 50 }},
313+ {"type" : "interruption" , "interruption_event" : {"event_id" : "456" }},
318314 {
319315 "type" : "audio" ,
320- "audio_event" : {"event_id" : "789" , "audio_base_64" : "dGVzdA==" } # "test" in base64
321- }
316+ "audio_event" : {"event_id" : "789" , "audio_base_64" : "dGVzdA==" }, # "test" in base64
317+ },
322318 ]
323319
324320 mock_ws = create_mock_async_websocket (messages )
325321 mock_client = MagicMock ()
322+ mock_client ._client_wrapper .get_base_url .return_value = "https://api.elevenlabs.io"
326323
327324 # Setup callbacks
328325 agent_response_callback = AsyncMock ()
@@ -368,7 +365,6 @@ async def test_async_conversation_callback_flows():
368365
369366@pytest .mark .asyncio
370367async def test_async_conversation_wss_url_generation_without_get_environment ():
371-
372368 from elevenlabs .core .client_wrapper import SyncClientWrapper
373369
374370 # Test with various base URL formats to ensure robustness
@@ -383,24 +379,20 @@ async def test_async_conversation_wss_url_generation_without_get_environment():
383379 # Create a real SyncClientWrapper to ensure it doesn't have get_environment method
384380 mock_client = MagicMock ()
385381 mock_client ._client_wrapper = SyncClientWrapper (
386- base_url = base_url ,
387- api_key = "test_key" ,
388- httpx_client = MagicMock (),
389- timeout = 30.0
382+ base_url = base_url , api_key = "test_key" , httpx_client = MagicMock (), timeout = 30.0
390383 )
391384
392385 conversation = AsyncConversation (
393- client = mock_client ,
394- agent_id = TEST_AGENT_ID ,
395- requires_auth = False ,
396- audio_interface = MockAsyncAudioInterface ()
386+ client = mock_client , agent_id = TEST_AGENT_ID , requires_auth = False , audio_interface = MockAsyncAudioInterface ()
397387 )
398388
399389 try :
400390 wss_url = conversation ._get_wss_url ()
401391
402392 # Verify the URL is correctly generated
403- expected_url = f"{ expected_ws_base } /v1/convai/conversation?agent_id={ TEST_AGENT_ID } &source=python_sdk&version="
393+ expected_url = (
394+ f"{ expected_ws_base } /v1/convai/conversation?agent_id={ TEST_AGENT_ID } &source=python_sdk&version="
395+ )
404396 assert wss_url .startswith (expected_url ), f"URL should start with { expected_url } , got { wss_url } "
405397
406398 # Verify the URL contains version parameter
@@ -419,8 +411,8 @@ async def test_async_conversation_wss_url_generation_without_get_environment():
419411@pytest .mark .asyncio
420412async def test_async_websocket_url_construction_edge_cases ():
421413 """Test WebSocket URL construction edge cases for async conversation, specifically for trailing slash handling."""
422- from elevenlabs .core .client_wrapper import SyncClientWrapper
423414 from elevenlabs .conversational_ai .conversation import AsyncConversation
415+ from elevenlabs .core .client_wrapper import SyncClientWrapper
424416
425417 # Test cases with various base URL formats
426418 test_cases = [
@@ -438,23 +430,19 @@ async def test_async_websocket_url_construction_edge_cases():
438430 # Test async conversation WebSocket URL construction
439431 mock_client = MagicMock ()
440432 mock_client ._client_wrapper = SyncClientWrapper (
441- base_url = base_url ,
442- api_key = "test_key" ,
443- httpx_client = MagicMock (),
444- timeout = 30.0
433+ base_url = base_url , api_key = "test_key" , httpx_client = MagicMock (), timeout = 30.0
445434 )
446435
447436 conversation = AsyncConversation (
448- client = mock_client ,
449- agent_id = TEST_AGENT_ID ,
450- requires_auth = False ,
451- audio_interface = MockAsyncAudioInterface ()
437+ client = mock_client , agent_id = TEST_AGENT_ID , requires_auth = False , audio_interface = MockAsyncAudioInterface ()
452438 )
453439
454440 # Test conversation URL generation
455441 conv_url = conversation ._get_wss_url ()
456442 expected_conv_url = f"{ expected_ws_base } /v1/convai/conversation"
457- assert expected_conv_url in conv_url , f"Async conversation URL should contain { expected_conv_url } , got { conv_url } "
443+ assert (
444+ expected_conv_url in conv_url
445+ ), f"Async conversation URL should contain { expected_conv_url } , got { conv_url } "
458446
459447 # Ensure no double slashes in the path (except after the protocol)
460448 url_path = conv_url .split ("://" , 1 )[1 ] # Remove protocol
0 commit comments