From c95a37f266a3d28dfdf5f98f43032e655c207195 Mon Sep 17 00:00:00 2001 From: Alvaro Navarro Date: Fri, 27 Feb 2026 16:58:52 +0100 Subject: [PATCH] feat(voice): add shaken property to Phone endpoint --- .../vonage_voice/models/connect_endpoints.py | 2 + voice/src/vonage_voice/models/requests.py | 2 + voice/tests/test_ncco_actions.py | 2 + voice/tests/test_voice.py | 352 ++++++++++-------- 4 files changed, 193 insertions(+), 165 deletions(-) diff --git a/voice/src/vonage_voice/models/connect_endpoints.py b/voice/src/vonage_voice/models/connect_endpoints.py index d1c15715..cc7659bf 100644 --- a/voice/src/vonage_voice/models/connect_endpoints.py +++ b/voice/src/vonage_voice/models/connect_endpoints.py @@ -27,11 +27,13 @@ class PhoneEndpoint(BaseModel): number (PhoneNumber): The phone number to call. dtmfAnswer (Optional[Dtmf]): The DTMF tones to send when the call is answered. onAnswer (Optional[OnAnswer]): Settings for what to do when the call is answered. + shaken (Optional[str]): STIR/SHAKEN Identity header content to use for this call. """ number: PhoneNumber dtmfAnswer: Optional[Dtmf] = None onAnswer: Optional[OnAnswer] = None + shaken: Optional[str] = None type: ConnectEndpointType = ConnectEndpointType.PHONE diff --git a/voice/src/vonage_voice/models/requests.py b/voice/src/vonage_voice/models/requests.py index 25c006a4..d738710f 100644 --- a/voice/src/vonage_voice/models/requests.py +++ b/voice/src/vonage_voice/models/requests.py @@ -15,9 +15,11 @@ class ToPhone(Phone): Args: number (PhoneNumber): The phone number. dtmf_answer (Optional[Dtmf]): The DTMF tones to send when the call is answered. + shaken (Optional[str]): STIR/SHAKEN Identity header content to use for this call. """ dtmf_answer: Optional[Dtmf] = Field(None, serialization_alias='dtmfAnswer') + shaken: Optional[str] = None class CreateCallRequest(BaseModel): diff --git a/voice/tests/test_ncco_actions.py b/voice/tests/test_ncco_actions.py index 4e1a3c75..e066c10e 100644 --- a/voice/tests/test_ncco_actions.py +++ b/voice/tests/test_ncco_actions.py @@ -89,10 +89,12 @@ def test_create_connect_endpoints(): number='447000000000', dtmfAnswer='1234', onAnswer={'url': 'https://example.com', 'ringbackTone': 'http://example.com'}, + shaken='shaken-token', ).model_dump() == { 'number': '447000000000', 'dtmfAnswer': '1234', 'onAnswer': {'url': 'https://example.com', 'ringbackTone': 'http://example.com'}, + 'shaken': 'shaken-token', 'type': 'phone', } diff --git a/voice/tests/test_voice.py b/voice/tests/test_voice.py index cfcd459e..3dd32eba 100644 --- a/voice/tests/test_voice.py +++ b/voice/tests/test_voice.py @@ -10,6 +10,7 @@ CreateCallRequest, ListCallsFilter, Sip, + ToPhone, TtsStreamOptions, ) from vonage_voice.errors import VoiceError @@ -32,16 +33,16 @@ def test_http_client_property(): @responses.activate def test_create_call_basic_ncco(): build_response( - path, 'POST', 'https://api.nexmo.com/v1/calls', 'create_call.json', 201 + path, "POST", "https://api.nexmo.com/v1/calls", "create_call.json", 201 ) - ncco = [Talk(text='Hello world')] + ncco = [Talk(text="Hello world")] call = CreateCallRequest( ncco=ncco, to=[ Sip( - uri='sip:test@example.com', - headers={'location': 'New York City'}, - standard_headers={'User-to-User': '342342ef34;encoding=hex'}, + uri="sip:test@example.com", + headers={"location": "New York City"}, + standard_headers={"User-to-User": "342342ef34;encoding=hex"}, ) ], random_from_number=True, @@ -50,105 +51,126 @@ def test_create_call_basic_ncco(): response = voice.create_call(call) body = json.loads(voice.http_client.last_request.body) - assert body['to'][0]['headers'] == {'location': 'New York City'} - assert body['to'][0]['standard_headers'] == { - 'User-to-User': '342342ef34;encoding=hex' + assert body["to"][0]["headers"] == {"location": "New York City"} + assert body["to"][0]["standard_headers"] == { + "User-to-User": "342342ef34;encoding=hex" } assert type(response) == CreateCallResponse - assert response.uuid == '106a581a-34d0-432a-a625-220221fd434f' - assert response.status == 'started' - assert response.direction == 'outbound' - assert response.conversation_uuid == 'CON-2be039b2-d0a4-4274-afc8-d7b241c7c044' + assert response.uuid == "106a581a-34d0-432a-a625-220221fd434f" + assert response.status == "started" + assert response.direction == "outbound" + assert response.conversation_uuid == "CON-2be039b2-d0a4-4274-afc8-d7b241c7c044" @responses.activate def test_create_call_basic_ncco_from_sip(): build_response( - path, 'POST', 'https://api.nexmo.com/v1/calls', 'create_call.json', 201 + path, "POST", "https://api.nexmo.com/v1/calls", "create_call.json", 201 ) - ncco = [Talk(text='Hello world')] + ncco = [Talk(text="Hello world")] call = CreateCallRequest( ncco=ncco, - to=[Sip(uri='sip:test@example.com')], - from_='sip:from_sip_uri@example.com', + to=[Sip(uri="sip:test@example.com")], + from_="sip:from_sip_uri@example.com", ) response = voice.create_call(call) assert type(response) == CreateCallResponse - assert response.uuid == '106a581a-34d0-432a-a625-220221fd434f' - assert response.status == 'started' - assert response.direction == 'outbound' - assert response.conversation_uuid == 'CON-2be039b2-d0a4-4274-afc8-d7b241c7c044' + assert response.uuid == "106a581a-34d0-432a-a625-220221fd434f" + assert response.status == "started" + assert response.direction == "outbound" + assert response.conversation_uuid == "CON-2be039b2-d0a4-4274-afc8-d7b241c7c044" @responses.activate def test_create_call_ncco_options(): build_response( - path, 'POST', 'https://api.nexmo.com/v1/calls', 'create_call.json', 201 + path, "POST", "https://api.nexmo.com/v1/calls", "create_call.json", 201 ) - ncco = [Talk(text='Hello world')] + ncco = [Talk(text="Hello world")] call = CreateCallRequest( ncco=ncco, - to=[{'type': 'phone', 'number': '1234567890', 'dtmf_answer': '1234'}], - from_={'number': '1234567890', 'type': 'phone'}, - event_url=['https://example.com/event'], - event_method='POST', - machine_detection='hangup', + to=[{"type": "phone", "number": "1234567890", "dtmf_answer": "1234"}], + from_={"number": "1234567890", "type": "phone"}, + event_url=["https://example.com/event"], + event_method="POST", + machine_detection="hangup", length_timer=60, ringing_timer=30, ) response = voice.create_call(call) assert type(response) == CreateCallResponse - assert response.uuid == '106a581a-34d0-432a-a625-220221fd434f' - assert response.status == 'started' - assert response.direction == 'outbound' - assert response.conversation_uuid == 'CON-2be039b2-d0a4-4274-afc8-d7b241c7c044' + assert response.uuid == "106a581a-34d0-432a-a625-220221fd434f" + assert response.status == "started" + assert response.direction == "outbound" + assert response.conversation_uuid == "CON-2be039b2-d0a4-4274-afc8-d7b241c7c044" + + +@responses.activate +def test_create_call_phone_shaken(): + build_response( + path, "POST", "https://api.nexmo.com/v1/calls", "create_call.json", 201 + ) + ncco = [Talk(text="Hello world")] + to_phone = ToPhone(number="1234567890", dtmf_answer="1234", shaken="shaken-token") + call = CreateCallRequest( + ncco=ncco, + to=[to_phone], + from_={"number": "1234567890", "type": "phone"}, + ) + response = voice.create_call(call) + + body = json.loads(voice.http_client.last_request.body) + assert body["to"][0]["number"] == "1234567890" + assert body["to"][0]["dtmfAnswer"] == "1234" + assert body["to"][0]["shaken"] == "shaken-token" + assert type(response) == CreateCallResponse @responses.activate def test_create_call_basic_answer_url(): build_response( - path, 'POST', 'https://api.nexmo.com/v1/calls', 'create_call.json', 201 + path, "POST", "https://api.nexmo.com/v1/calls", "create_call.json", 201 ) call = CreateCallRequest( to=[ { - 'type': 'websocket', - 'uri': 'wss://example.com/websocket', - 'content_type': 'audio/l16;rate=8000', - 'headers': {'key': 'value'}, + "type": "websocket", + "uri": "wss://example.com/websocket", + "content_type": "audio/l16;rate=8000", + "headers": {"key": "value"}, } ], - answer_url=['https://example.com/answer'], + answer_url=["https://example.com/answer"], random_from_number=True, ) response = voice.create_call(call) assert type(response) == CreateCallResponse - assert response.uuid == '106a581a-34d0-432a-a625-220221fd434f' - assert response.status == 'started' - assert response.direction == 'outbound' - assert response.conversation_uuid == 'CON-2be039b2-d0a4-4274-afc8-d7b241c7c044' + assert response.uuid == "106a581a-34d0-432a-a625-220221fd434f" + assert response.status == "started" + assert response.direction == "outbound" + assert response.conversation_uuid == "CON-2be039b2-d0a4-4274-afc8-d7b241c7c044" @responses.activate def test_create_call_answer_url_options(): build_response( - path, 'POST', 'https://api.nexmo.com/v1/calls', 'create_call.json', 201 + path, "POST", "https://api.nexmo.com/v1/calls", "create_call.json", 201 ) call = CreateCallRequest( - to=[{'type': 'vbc', 'extension': '1234'}], - answer_url=['https://example.com/answer'], - answer_method='GET', + to=[{"type": "vbc", "extension": "1234"}], + answer_url=["https://example.com/answer"], + answer_method="GET", random_from_number=True, - event_url=['https://example.com/event'], - event_method='POST', + event_url=["https://example.com/event"], + event_method="POST", advanced_machine_detection={ - 'behavior': 'hangup', - 'mode': 'detect_beep', - 'beep_timeout': 50, + "behavior": "hangup", + "mode": "detect_beep", + "beep_timeout": 50, }, length_timer=60, ringing_timer=30, @@ -156,89 +178,89 @@ def test_create_call_answer_url_options(): response = voice.create_call(call) assert type(response) == CreateCallResponse - assert response.uuid == '106a581a-34d0-432a-a625-220221fd434f' - assert response.status == 'started' - assert response.direction == 'outbound' - assert response.conversation_uuid == 'CON-2be039b2-d0a4-4274-afc8-d7b241c7c044' + assert response.uuid == "106a581a-34d0-432a-a625-220221fd434f" + assert response.status == "started" + assert response.direction == "outbound" + assert response.conversation_uuid == "CON-2be039b2-d0a4-4274-afc8-d7b241c7c044" def test_create_call_ncco_and_answer_url_error(): with raises(VoiceError) as e: CreateCallRequest( - to=[{'type': 'phone', 'number': '1234567890'}], + to=[{"type": "phone", "number": "1234567890"}], random_from_number=True, ) - assert e.match('Either `ncco` or `answer_url` must be set') + assert e.match("Either `ncco` or `answer_url` must be set") with raises(VoiceError) as e: CreateCallRequest( - ncco=[Talk(text='Hello world')], - answer_url=['https://example.com/answer'], - to=[{'type': 'phone', 'number': '1234567890'}], + ncco=[Talk(text="Hello world")], + answer_url=["https://example.com/answer"], + to=[{"type": "phone", "number": "1234567890"}], random_from_number=True, ) - assert e.match('`ncco` and `answer_url` cannot be used together') + assert e.match("`ncco` and `answer_url` cannot be used together") def test_create_call_from_and_random_from_number_error(): with raises(VoiceError) as e: CreateCallRequest( - ncco=[Talk(text='Hello world')], - to=[{'type': 'phone', 'number': '1234567890'}], + ncco=[Talk(text="Hello world")], + to=[{"type": "phone", "number": "1234567890"}], ) - assert e.match('Either `from_` or `random_from_number` must be set') + assert e.match("Either `from_` or `random_from_number` must be set") with raises(VoiceError) as e: CreateCallRequest( - ncco=[Talk(text='Hello world')], - to=[{'type': 'phone', 'number': '1234567890'}], - from_={'number': '9876543210', 'type': 'phone'}, + ncco=[Talk(text="Hello world")], + to=[{"type": "phone", "number": "1234567890"}], + from_={"number": "9876543210", "type": "phone"}, random_from_number=True, ) - assert e.match('`from_` and `random_from_number` cannot be used together') + assert e.match("`from_` and `random_from_number` cannot be used together") @responses.activate def test_list_calls(): - build_response(path, 'GET', 'https://api.nexmo.com/v1/calls', 'list_calls.json', 200) + build_response(path, "GET", "https://api.nexmo.com/v1/calls", "list_calls.json", 200) calls, _ = voice.list_calls() assert len(calls) == 3 - assert calls[0].to.number == '1234567890' - assert calls[0].from_.number == '9876543210' - assert calls[0].uuid == 'e154eb57-2962-41e7-baf4-90f63e25e439' - assert calls[1].direction == 'outbound' - assert calls[1].status == 'completed' - assert calls[2].conversation_uuid == 'CON-2be039b2-d0a4-4274-afc8-d7b241c7c044' + assert calls[0].to.number == "1234567890" + assert calls[0].from_.number == "9876543210" + assert calls[0].uuid == "e154eb57-2962-41e7-baf4-90f63e25e439" + assert calls[1].direction == "outbound" + assert calls[1].status == "completed" + assert calls[2].conversation_uuid == "CON-2be039b2-d0a4-4274-afc8-d7b241c7c044" @responses.activate def test_list_calls_filter(): build_response( - path, 'GET', 'https://api.nexmo.com/v1/calls', 'list_calls_filter.json', 200 + path, "GET", "https://api.nexmo.com/v1/calls", "list_calls_filter.json", 200 ) filter = ListCallsFilter( - status='completed', - date_start='2024-03-14T07:45:14Z', - date_end='2024-04-19T08:45:14Z', + status="completed", + date_start="2024-03-14T07:45:14Z", + date_end="2024-04-19T08:45:14Z", page_size=10, record_index=0, - order='asc', - conversation_uuid='CON-2be039b2-d0a4-4274-afc8-d7b241c7c044', + order="asc", + conversation_uuid="CON-2be039b2-d0a4-4274-afc8-d7b241c7c044", ) filter_dict = { - 'status': 'completed', - 'date_start': '2024-03-14T07:45:14Z', - 'date_end': '2024-04-19T08:45:14Z', - 'page_size': 10, - 'record_index': 0, - 'order': 'asc', - 'conversation_uuid': 'CON-2be039b2-d0a4-4274-afc8-d7b241c7c044', + "status": "completed", + "date_start": "2024-03-14T07:45:14Z", + "date_end": "2024-04-19T08:45:14Z", + "page_size": 10, + "record_index": 0, + "order": "asc", + "conversation_uuid": "CON-2be039b2-d0a4-4274-afc8-d7b241c7c044", } assert filter.model_dump(by_alias=True, exclude_none=True) == filter_dict calls, next_record_index = voice.list_calls(filter) assert len(calls) == 1 - assert calls[0].to.number == '1234567890' + assert calls[0].to.number == "1234567890" assert next_record_index == 2 @@ -246,51 +268,51 @@ def test_list_calls_filter(): def test_get_call(): build_response( path, - 'GET', - 'https://api.nexmo.com/v1/calls/e154eb57-2962-41e7-baf4-90f63e25e439', - 'get_call.json', + "GET", + "https://api.nexmo.com/v1/calls/e154eb57-2962-41e7-baf4-90f63e25e439", + "get_call.json", 200, ) - call = voice.get_call('e154eb57-2962-41e7-baf4-90f63e25e439') - assert call.to.number == '1234567890' - assert call.from_.number == '9876543210' - assert call.uuid == 'e154eb57-2962-41e7-baf4-90f63e25e439' - assert call.link == '/v1/calls/e154eb57-2962-41e7-baf4-90f63e25e439' + call = voice.get_call("e154eb57-2962-41e7-baf4-90f63e25e439") + assert call.to.number == "1234567890" + assert call.from_.number == "9876543210" + assert call.uuid == "e154eb57-2962-41e7-baf4-90f63e25e439" + assert call.link == "/v1/calls/e154eb57-2962-41e7-baf4-90f63e25e439" @responses.activate def test_transfer_call_ncco(): build_response( path, - 'PUT', - 'https://api.nexmo.com/v1/calls/e154eb57-2962-41e7-baf4-90f63e25e439', + "PUT", + "https://api.nexmo.com/v1/calls/e154eb57-2962-41e7-baf4-90f63e25e439", status_code=204, ) - ncco = [Talk(text='Hello world')] - voice.transfer_call_ncco('e154eb57-2962-41e7-baf4-90f63e25e439', ncco) + ncco = [Talk(text="Hello world")] + voice.transfer_call_ncco("e154eb57-2962-41e7-baf4-90f63e25e439", ncco) assert voice._http_client.last_response.status_code == 204 @responses.activate def test_transfer_call_answer_url(): - answer_url = 'https://example.com/answer' + answer_url = "https://example.com/answer" build_response( path, - 'PUT', - 'https://api.nexmo.com/v1/calls/e154eb57-2962-41e7-baf4-90f63e25e439', + "PUT", + "https://api.nexmo.com/v1/calls/e154eb57-2962-41e7-baf4-90f63e25e439", status_code=204, match=[ json_params_matcher( { - 'action': 'transfer', - 'destination': {'type': 'ncco', 'url': [answer_url]}, + "action": "transfer", + "destination": {"type": "ncco", "url": [answer_url]}, }, ), ], ) - voice.transfer_call_answer_url('e154eb57-2962-41e7-baf4-90f63e25e439', answer_url) + voice.transfer_call_answer_url("e154eb57-2962-41e7-baf4-90f63e25e439", answer_url) assert voice._http_client.last_response.status_code == 204 @@ -298,13 +320,13 @@ def test_transfer_call_answer_url(): def test_hangup(): build_response( path, - 'PUT', - 'https://api.nexmo.com/v1/calls/e154eb57-2962-41e7-baf4-90f63e25e439', + "PUT", + "https://api.nexmo.com/v1/calls/e154eb57-2962-41e7-baf4-90f63e25e439", status_code=204, - match=[json_params_matcher({'action': 'hangup'})], + match=[json_params_matcher({"action": "hangup"})], ) - voice.hangup('e154eb57-2962-41e7-baf4-90f63e25e439') + voice.hangup("e154eb57-2962-41e7-baf4-90f63e25e439") assert voice._http_client.last_response.status_code == 204 @@ -312,13 +334,13 @@ def test_hangup(): def test_mute(): build_response( path, - 'PUT', - 'https://api.nexmo.com/v1/calls/e154eb57-2962-41e7-baf4-90f63e25e439', + "PUT", + "https://api.nexmo.com/v1/calls/e154eb57-2962-41e7-baf4-90f63e25e439", status_code=204, - match=[json_params_matcher({'action': 'mute'})], + match=[json_params_matcher({"action": "mute"})], ) - voice.mute('e154eb57-2962-41e7-baf4-90f63e25e439') + voice.mute("e154eb57-2962-41e7-baf4-90f63e25e439") assert voice._http_client.last_response.status_code == 204 @@ -326,13 +348,13 @@ def test_mute(): def test_unmute(): build_response( path, - 'PUT', - 'https://api.nexmo.com/v1/calls/e154eb57-2962-41e7-baf4-90f63e25e439', + "PUT", + "https://api.nexmo.com/v1/calls/e154eb57-2962-41e7-baf4-90f63e25e439", status_code=204, - match=[json_params_matcher({'action': 'unmute'})], + match=[json_params_matcher({"action": "unmute"})], ) - voice.unmute('e154eb57-2962-41e7-baf4-90f63e25e439') + voice.unmute("e154eb57-2962-41e7-baf4-90f63e25e439") assert voice._http_client.last_response.status_code == 204 @@ -340,13 +362,13 @@ def test_unmute(): def test_earmuff(): build_response( path, - 'PUT', - 'https://api.nexmo.com/v1/calls/e154eb57-2962-41e7-baf4-90f63e25e439', + "PUT", + "https://api.nexmo.com/v1/calls/e154eb57-2962-41e7-baf4-90f63e25e439", status_code=204, - match=[json_params_matcher({'action': 'earmuff'})], + match=[json_params_matcher({"action": "earmuff"})], ) - voice.earmuff('e154eb57-2962-41e7-baf4-90f63e25e439') + voice.earmuff("e154eb57-2962-41e7-baf4-90f63e25e439") assert voice._http_client.last_response.status_code == 204 @@ -354,94 +376,94 @@ def test_earmuff(): def test_unearmuff(): build_response( path, - 'PUT', - 'https://api.nexmo.com/v1/calls/e154eb57-2962-41e7-baf4-90f63e25e439', + "PUT", + "https://api.nexmo.com/v1/calls/e154eb57-2962-41e7-baf4-90f63e25e439", status_code=204, - match=[json_params_matcher({'action': 'unearmuff'})], + match=[json_params_matcher({"action": "unearmuff"})], ) - voice.unearmuff('e154eb57-2962-41e7-baf4-90f63e25e439') + voice.unearmuff("e154eb57-2962-41e7-baf4-90f63e25e439") assert voice._http_client.last_response.status_code == 204 @responses.activate def test_play_audio_into_call(): - uuid = 'e154eb57-2962-41e7-baf4-90f63e25e439' + uuid = "e154eb57-2962-41e7-baf4-90f63e25e439" build_response( path, - 'PUT', - f'https://api.nexmo.com/v1/calls/{uuid}/stream', - 'play_audio_into_call.json', + "PUT", + f"https://api.nexmo.com/v1/calls/{uuid}/stream", + "play_audio_into_call.json", ) options = AudioStreamOptions( - stream_url=['https://example.com/audio'], loop=2, level=0.5 + stream_url=["https://example.com/audio"], loop=2, level=0.5 ) response = voice.play_audio_into_call(uuid, options) - assert response.message == 'Stream started' + assert response.message == "Stream started" assert response.uuid == uuid @responses.activate def test_stop_audio_stream(): - uuid = 'e154eb57-2962-41e7-baf4-90f63e25e439' + uuid = "e154eb57-2962-41e7-baf4-90f63e25e439" build_response( path, - 'DELETE', - f'https://api.nexmo.com/v1/calls/{uuid}/stream', - 'stop_audio_stream.json', + "DELETE", + f"https://api.nexmo.com/v1/calls/{uuid}/stream", + "stop_audio_stream.json", ) response = voice.stop_audio_stream(uuid) - assert response.message == 'Stream stopped' + assert response.message == "Stream stopped" assert response.uuid == uuid @responses.activate def test_play_tts_into_call(): - uuid = 'e154eb57-2962-41e7-baf4-90f63e25e439' + uuid = "e154eb57-2962-41e7-baf4-90f63e25e439" build_response( path, - 'PUT', - f'https://api.nexmo.com/v1/calls/{uuid}/talk', - 'play_tts_into_call.json', + "PUT", + f"https://api.nexmo.com/v1/calls/{uuid}/talk", + "play_tts_into_call.json", ) options = TtsStreamOptions( - text='Hello world', language='en-ZA', style=1, premium=False, loop=2, level=0.5 + text="Hello world", language="en-ZA", style=1, premium=False, loop=2, level=0.5 ) response = voice.play_tts_into_call(uuid, options) - assert response.message == 'Talk started' + assert response.message == "Talk started" assert response.uuid == uuid @responses.activate def test_stop_tts(): - uuid = 'e154eb57-2962-41e7-baf4-90f63e25e439' + uuid = "e154eb57-2962-41e7-baf4-90f63e25e439" build_response( path, - 'DELETE', - f'https://api.nexmo.com/v1/calls/{uuid}/talk', - 'stop_tts.json', + "DELETE", + f"https://api.nexmo.com/v1/calls/{uuid}/talk", + "stop_tts.json", ) response = voice.stop_tts(uuid) - assert response.message == 'Talk stopped' + assert response.message == "Talk stopped" assert response.uuid == uuid @responses.activate def test_play_dtmf_into_call(): - uuid = 'e154eb57-2962-41e7-baf4-90f63e25e439' + uuid = "e154eb57-2962-41e7-baf4-90f63e25e439" build_response( path, - 'PUT', - f'https://api.nexmo.com/v1/calls/{uuid}/dtmf', - 'play_dtmf_into_call.json', + "PUT", + f"https://api.nexmo.com/v1/calls/{uuid}/dtmf", + "play_dtmf_into_call.json", ) - response = voice.play_dtmf_into_call(uuid, dtmf='1234*#') - assert response.message == 'DTMF sent' + response = voice.play_dtmf_into_call(uuid, dtmf="1234*#") + assert response.message == "DTMF sent" assert response.uuid == uuid @@ -449,32 +471,32 @@ def test_play_dtmf_into_call(): def test_download_recording(): build_response( path, - 'GET', - 'https://api.nexmo.com/v1/files/aaaaaaaa-bbbb-cccc-dddd-0123456789ab', - 'file_stream.mp3', + "GET", + "https://api.nexmo.com/v1/files/aaaaaaaa-bbbb-cccc-dddd-0123456789ab", + "file_stream.mp3", ) voice.download_recording( - url='https://api.nexmo.com/v1/files/aaaaaaaa-bbbb-cccc-dddd-0123456789ab', - file_path='voice/tests/data/file_stream.mp3', + url="https://api.nexmo.com/v1/files/aaaaaaaa-bbbb-cccc-dddd-0123456789ab", + file_path="voice/tests/data/file_stream.mp3", ) - with open('voice/tests/data/file_stream.mp3', 'rb') as file: + with open("voice/tests/data/file_stream.mp3", "rb") as file: file_content = file.read() - assert file_content.startswith(b'ID3') + assert file_content.startswith(b"ID3") def test_download_recording_invalid_url(): with raises(VoiceError) as e: voice.download_recording( - url='https://invalid.com/v1/files/aaaaaaaa-bbbb-cccc-dddd-0123456789ab', - file_path='voice/tests/data/file_stream.mp3', + url="https://invalid.com/v1/files/aaaaaaaa-bbbb-cccc-dddd-0123456789ab", + file_path="voice/tests/data/file_stream.mp3", ) - assert e.match('The recording URL must be from a Vonage or Nexmo hostname.') + assert e.match("The recording URL must be from a Vonage or Nexmo hostname.") def test_verify_signature(): - token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2OTc2MzQ2ODAsImV4cCI6MzMyNTQ1NDA4MjgsImF1ZCI6IiIsInN1YiI6IiJ9.88vJc3I2HhuqEDixHXVhc9R30tA6U_HQHZTC29y6CGM' + token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2OTc2MzQ2ODAsImV4cCI6MzMyNTQ1NDA4MjgsImF1ZCI6IiIsInN1YiI6IiJ9.88vJc3I2HhuqEDixHXVhc9R30tA6U_HQHZTC29y6CGM" valid_signature = "qwertyuiopasdfghjklzxcvbnm123456" assert voice.verify_signature(token, valid_signature) is True