From 7e1c593c7527ca37a99f419efa53c2deb43a13f9 Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 2 Aug 2022 00:40:47 -0400 Subject: [PATCH 1/6] DX-2688 --- .github/workflows/test.yaml | 7 +- test/integration/test_conferences.py | 584 +++++++++++++++++++++++++++ 2 files changed, 590 insertions(+), 1 deletion(-) create mode 100644 test/integration/test_conferences.py diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 8cba7fdf..16116bf4 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -13,7 +13,7 @@ jobs: strategy: matrix: os: [windows-2022, windows-2019, ubuntu-18.04, ubuntu-20.04] - python-version: [3.6, 3.7, 3.8, 3.9, '3.10'] + python-version: [3.6, 3.7, 3.8, 3.9, "3.10"] steps: - name: Checkout uses: actions/checkout@v2 @@ -44,6 +44,11 @@ jobs: T_MOBILE_NUMBER: ${{ secrets.T_MOBILE_NUMBER }} BASE_CALLBACK_URL: ${{ secrets.BASE_CALLBACK_URL }} PYTHON_VERSION: ${{ matrix.python-version }} + OPERATING_SYSTEM: ${{ matrix.os }} + MANTECA_ACTIVE_NUMBER: ${{ secrets.MANTECA_ACTIVE_NUMBER }} + MANTECA_IDLE_NUMBER: ${{ secrets.MANTECA_IDLE_NUMBER }} + MANTECA_BASE_URL: ${{ secrets.MANTECA_BASE_URL }} + MANTECA_APPLICATION_ID: ${{ secrets.MANTECA_APPLICATION_ID }} run: pytest - name: Notify Slack of Failures diff --git a/test/integration/test_conferences.py b/test/integration/test_conferences.py new file mode 100644 index 00000000..622b5685 --- /dev/null +++ b/test/integration/test_conferences.py @@ -0,0 +1,584 @@ +""" +Integration tests for Bandwidth's Voice Voice Conferences API +""" + +import os +import json +import time +from typing import List, Tuple +import unittest + + +import bandwidth +from bandwidth.api import calls_api +from bandwidth.model.create_call import CreateCall +from bandwidth.model.create_call_response import CreateCallResponse +from bandwidth.model.call_state import CallState +from bandwidth.model.call_state_enum import CallStateEnum +from bandwidth.model.update_call import UpdateCall +from bandwidth.model.redirect_method_enum import RedirectMethodEnum +from bandwidth.api import conferences_api +from bandwidth.model.conference_state_enum import ConferenceStateEnum +from bandwidth.model.conference_recording_metadata import ConferenceRecordingMetadata +from bandwidth.model.update_conference import UpdateConference +from bandwidth.model.update_conference_member import UpdateConferenceMember +from bandwidth.model.file_format_enum import FileFormatEnum +from bandwidth.rest import RESTClientObject, RESTResponse +from bandwidth.exceptions import ApiException, UnauthorizedException, ForbiddenException, NotFoundException + +try: + BW_USERNAME = os.environ["BW_USERNAME"] + BW_PASSWORD = os.environ["BW_PASSWORD"] + BW_ACCOUNT_ID = os.environ["BW_ACCOUNT_ID"] + BW_VOICE_APPLICATION_ID = os.environ["BW_VOICE_APPLICATION_ID"] + BASE_CALLBACK_URL = os.environ["BASE_CALLBACK_URL"] + BW_NUMBER = os.environ["BW_NUMBER"] + USER_NUMBER = os.environ["USER_NUMBER"] + FORBIDDEN_USERNAME = os.environ['BW_USERNAME_FORBIDDEN'] + FORBIDDEN_PASSWORD = os.environ['BW_PASSWORD_FORBIDDEN'] + MANTECA_ACTIVE_NUMBER = os.environ["MANTECA_ACTIVE_NUMBER"] + MANTECA_IDLE_NUMBER = os.environ["MANTECA_IDLE_NUMBER"] + MANTECA_BASE_URL = os.environ["MANTECA_BASE_URL"] + MANTECA_STATUS_URL = MANTECA_BASE_URL + "tests/" + MANTECA_APPLICATION_ID = os.environ["MANTECA_APPLICATION_ID"] + PYTHON_VERSION = os.environ["PYTHON_VERSION"] + OPERATING_SYSTEM = os.environ["OPERATING_SYSTEM"] + +except KeyError as e: + raise Exception("Environmental variables not found") + +MAX_RETRIES = 40 +TEST_SLEEP = 3 + +global callIdArray +callIdArray = [] + +global testUpdateConf +testUpdateConf = UpdateConference( + state=ConferenceStateEnum("active"), + redirect_url=MANTECA_BASE_URL + "/bxml/pause", + redirect_method=RedirectMethodEnum("POST"), + username="mySecretUsername", + password="mySecretPassword1!", + redirect_fallback_url=MANTECA_BASE_URL + "/bxml/pause", + redirect_fallback_method=RedirectMethodEnum("POST"), + fallback_username="mySecretUsername", + fallback_password="mySecretPassword1!", + tag="My Custom Tag", + ) + +global testUpdateBxml +testUpdateBxml = 'This is test BXML.' + +global testUpdateMember +testUpdateMember = UpdateConferenceMember(mute=False) + +global testConfId +testConfId = "Conf-id" + +global testMemberId +testMemberId = "Member-Id" + +global testRecordId +testRecordId = "Recording-Id" + + +class ConferencesIntegration(unittest.TestCase): + """ + Voice Conferences API integration test + """ + + def setUp(self): + """ + Set up for our tests by creating the CallsApi and ConferencesApi instances + for testing as well as the unauthorized and forbidden credentials for the 4xx tests. + """ + configuration = bandwidth.Configuration( + username = BW_USERNAME, + password = BW_PASSWORD + ) + api_client = bandwidth.ApiClient(configuration) + + # Two Valid API Clients + self.calls_api_instance = calls_api.CallsApi(api_client) + self.conference_api_instance = conferences_api.ConferencesApi(api_client) + + # Unauthorized API Client + + unauthorizedConfiguration = bandwidth.Configuration( + username='bad_username', + password='bad_password' + ) + unauthorized_api_client = bandwidth.ApiClient(unauthorizedConfiguration) + self.unauthorized_api_instance = conferences_api.ConferencesApi(unauthorized_api_client) + + # Forbidden API Client + + forbiddenConfiguration = bandwidth.Configuration( + username=FORBIDDEN_USERNAME, + password=FORBIDDEN_PASSWORD + ) + forbidden_api_client = bandwidth.ApiClient(forbiddenConfiguration) + self.forbidden_api_instance = conferences_api.ConferencesApi(forbidden_api_client) + + # Rest client for interacting with Manteca + self.rest_client = RESTClientObject(bandwidth.Configuration.get_default_copy()) + configuration = bandwidth.Configuration( + username = BW_USERNAME, + password = BW_PASSWORD, + ) + + self.account_id = BW_ACCOUNT_ID + + def tearDown(self): + """ + Whenever we create an actual call, we'll add the call_id to the callIdArray. Then when the integration test is done, as part of tearDown we'll: + Do a get to check is the call status is still active + If so, update to completed to end the call + If not, pop that callID off the array + Once we go through the whole array, we clear the array so it's empty for the next integration test. + if the status is active, send UpdateCall to change to completed + """ + + if len(callIdArray) > 0: + for callId in callIdArray: + body = UpdateCall(state=CallStateEnum("completed")) + get_call_response: CallState = self.calls_api_instance.get_call_state(BW_ACCOUNT_ID, callId, _return_http_data_only=False) + if get_call_response[0].state == 'active': + self.calls_api_instance.update_call(BW_ACCOUNT_ID, callId, body, _return_http_data_only=False) + elif get_call_response[0].state == 'complete': + callIdArray.remove(callId) + callIdArray.clear() + pass + + def assertApiException(self, context: ApiException, expectedException: ApiException, expected_status_code: int): + """Validates that common API exceptions, (401, 403, and 404) are properly formatted + Args: + context (ApiException): Exception to validate + expectedException (ApiException): Expected exception type + expected_status_code (int): Expected status code + """ + self.assertIs(type(context.exception), expectedException) + self.assertIs(type(context.exception.status), int) + self.assertEqual(context.exception.status, expected_status_code) + self.assertIs(type(context.exception.body), str) + + # Create Conference Call with Manteca + + def create_conference(self, answer_url: str) -> Tuple[str, str]: + """ + Create and validate a call between two bandwidth numbers. Initializes the call with the Manteca + system. + + Args: + answer_url (str): The answer url for the call to create. + Return: + Tuple[str, str]: A tuple containing the test id created in Manteca to track this call, as well as + the call id for the created call. + """ + + # Initialize the call with Manteca + response = self.rest_client.request( + method='POST', + url=MANTECA_BASE_URL + 'tests', + body={ + 'os': OPERATING_SYSTEM, + 'language': 'python' + PYTHON_VERSION, + 'type': 'CONFERENCE' + } + ) + + # Get the test id from the response + test_id = json.loads(response.data) + + # Make a CreateCall body and assign the appropriate params + + call_body = CreateCall(to=MANTECA_IDLE_NUMBER, _from=MANTECA_ACTIVE_NUMBER, application_id=MANTECA_APPLICATION_ID, answer_url=answer_url, tag=test_id) + + # Make the call + create_call_response: CreateCallResponse = self.calls_api_instance.create_call(BW_ACCOUNT_ID, call_body) + + # Verify info about the call + assert len(create_call_response.call_id) == 47 # assert request created and id matches expected length (47) + assert create_call_response.account_id == BW_ACCOUNT_ID + assert create_call_response.application_id == MANTECA_APPLICATION_ID + assert create_call_response.to == MANTECA_IDLE_NUMBER + assert create_call_response._from == MANTECA_ACTIVE_NUMBER + assert create_call_response.call_url == "https://voice.bandwidth.com/api/v2/accounts/" + \ + BW_ACCOUNT_ID + "/calls/" + create_call_response.call_id + + # Getting our ConferenceId and returning the test_id from Manteca and the callId + + time.sleep(2) + list_conferences_response = self.conference_api_instance.list_conferences(BW_ACCOUNT_ID, _return_http_data_only=False) + + time.sleep(2) + self.assertEqual(list_conferences_response[1], 200) + #print(list_conferences_response[0]) + conferenceId = (list_conferences_response[0][len(list_conferences_response[0])-1].id) + + + get_conference_response = self.conference_api_instance.get_conference(BW_ACCOUNT_ID, conferenceId, _return_http_data_only=False) + self.assertEqual(get_conference_response[1], 200) + + callIdArray.append(create_call_response.call_id) + + return (test_id, create_call_response.call_id) + + def validate_recording(self, recording: ConferenceRecordingMetadata, conference_id: str) -> None: + """ + Validate the given recording metadata. + Args: + recording (ConferenceRecordingMetadata): The recording metadata to validate. + conference_id (str): The call id associated with the given recording. + """ + assert recording.status == 'complete' + assert recording.file_format == FileFormatEnum('wav') + + def test_conference_and_members(self): + """ + Tests a successful flow of creating and ending a conference. + The following endpoints are tested in this flow: + - list_conferences + - get_conference_information + - get_conference_member + - update_conference_member + - update_conference_ + - update_conference_bxml + """ + + # Create the call + answer_url = MANTECA_BASE_URL + "/bxml/joinConferencePause" + (test_id, call_id) = self.create_conference(answer_url) + + # List Conferences + list_conferences_response = self.conference_api_instance.list_conferences(BW_ACCOUNT_ID, _return_http_data_only=False) + + self.assertEqual(list_conferences_response[1], 200) + self.assertIs(type(list_conferences_response[0][0].name), str) + self.assertIs(type(list_conferences_response[0][0].id), str) + + conferenceId = (list_conferences_response[0][len(list_conferences_response[0])-1].id) + + # Get Conference Information + """Validate a Get Conference Information Request + """ + get_conference_response = self.conference_api_instance.get_conference(BW_ACCOUNT_ID, conferenceId, _return_http_data_only=False) + self.assertEqual(get_conference_response[1], 200) + self.assertEqual(get_conference_response[0].id, conferenceId) + self.assertIs(type(get_conference_response[0].name), str) + callId = (get_conference_response[0].active_members[0].call_id) + callIdArray.append(callId) + + # Get Conference Member + """Validate a GET Conference Member + """ + get_conference_member_response = self.conference_api_instance.get_conference_member(BW_ACCOUNT_ID, conferenceId, callId, _return_http_data_only=False) + self.assertEqual(get_conference_member_response[1], 200) + self.assertEqual(get_conference_member_response[0].conference_id, conferenceId) + self.assertEqual(get_conference_member_response[0].call_id, callId) + + # Update Conference member test + """Validate an Update Conference Member Request + """ + + time.sleep(2) + update_conference_member_response = self.conference_api_instance.update_conference_member(BW_ACCOUNT_ID, conferenceId, callId, testUpdateMember, _return_http_data_only=False) + self.assertEqual(update_conference_member_response[1], 204) + + # Update Conference test + """Validate an Update Conference Request + """ + + time.sleep(3) + update_conference_response = self.conference_api_instance.update_conference(BW_ACCOUNT_ID, conferenceId, testUpdateConf, _return_http_data_only=False) + self.assertEqual(update_conference_response[1], 204) + + + # update Conference Bxml Test + """Validate an UpdateConferencelBxml Request + """ + updateBxmlBody = 'This is a test bxml response' + + time.sleep(3) + update_conference_bxml_response = self.conference_api_instance.update_conference_bxml(BW_ACCOUNT_ID, conferenceId, updateBxmlBody, _return_http_data_only=False) + self.assertEqual(update_conference_bxml_response[1], 204) + + # Kill the call + update_call = UpdateCall(state=CallStateEnum('completed')) + self.calls_api_instance.update_call(BW_ACCOUNT_ID, call_id, update_call, _return_http_data_only=False) + + # Conference Recordings Test + + def test_conference_recordings(self) -> None: + """ + Tests a successful flow of creating a call with a recording. + The following endpoints are tested in this flow: + - list_conference_recordings + - get_conference_recording + - download_conference_recording + """ + + # Create a conference and have it recorded + answer_url = MANTECA_BASE_URL + "/bxml/joinConferencePause" + (test_id, call_id) = self.create_conference(answer_url) + + list_conferences_response = self.conference_api_instance.list_conferences(BW_ACCOUNT_ID, _return_http_data_only=False) + + self.assertEqual(list_conferences_response[1], 200) + conferenceId = (list_conferences_response[0][len(list_conferences_response[0])-1].id) + + # update Conference Bxml Test + """Validate an UpdateConferencelBxml Request to start and stop recording + """ + updateBxmlBody = 'This should be a conference recording.' + update_conference_bxml_response = self.conference_api_instance.update_conference_bxml(BW_ACCOUNT_ID, conferenceId, updateBxmlBody, _return_http_data_only=False) + self.assertEqual(update_conference_bxml_response[1], 204) + + time.sleep(10) + + # List Conference Recordings Endpoint + list_conference_recordings_response: List[ConferenceRecordingMetadata] = self.conference_api_instance.list_conference_recordings(BW_ACCOUNT_ID, conferenceId, _return_http_data_only=False) + assert list_conference_recordings_response[1] == 200 + + # We should get back at least 1 recording + conference_recordings = list_conference_recordings_response[0] + assert len(conference_recordings) > 0 + + # Checks on the first recording + first_recording: ConferenceRecordingMetadata = conference_recordings[0] + self.validate_recording(first_recording, conferenceId) + recording_id = first_recording.recording_id + + # Get Single Recording Endpoint + recording_response: ConferenceRecordingMetadata = self.conference_api_instance.get_conference_recording(BW_ACCOUNT_ID, conferenceId, recording_id, _return_http_data_only=False) + self.assertEqual(recording_response[1], 200) + self.assertEqual(recording_response[0].conference_id, conferenceId) + self.assertEqual(recording_response[0].recording_id, recording_id) + self.assertIs(type(recording_response[0].name), str) + + self.validate_recording(recording_response[0], conferenceId) + + # Download recording media + recording_media_response = self.conference_api_instance.download_conference_recording(BW_ACCOUNT_ID, conferenceId, recording_id, _preload_content=False) + conference_recording_media = recording_media_response.data + + # List conferences 401 + + def test_list_conferences_unauthorized(self) -> None: + """Validate an unauthorized (401) request + """ + with self.assertRaises(UnauthorizedException) as context: + self.unauthorized_api_instance.list_conferences(BW_ACCOUNT_ID, _return_http_data_only=False) + + self.assertApiException(context, UnauthorizedException, 401) + + # List conferences 403 + def test_list_conferences_forbidden(self) -> None: + """Validate a forbidden (403) request + """ + with self.assertRaises(ForbiddenException) as context: + self.forbidden_api_instance.list_conferences(BW_ACCOUNT_ID, _return_http_data_only=False) + + self.assertApiException(context, ForbiddenException, 403) + + # Get Conference Information 401 + def test_get_conferences_unauthorized(self) -> None: + """Validate an unauthorized (401) Get Conference Information + """ + with self.assertRaises(UnauthorizedException) as context: + self.unauthorized_api_instance.get_conference(BW_ACCOUNT_ID, testConfId, _return_http_data_only=False) + + self.assertApiException(context, UnauthorizedException, 401) + + # Get Conference Information 403 + def test_get_conferences_forbidden(self) -> None: + """Validate a forbidden (403) Get Conference Information request + """ + with self.assertRaises(ForbiddenException) as context: + self.forbidden_api_instance.get_conference(BW_ACCOUNT_ID, testConfId, _return_http_data_only=False) + + self.assertApiException(context, ForbiddenException, 403) + + # Get Conference Information 404 + def test_get_conferences_not_found(self) -> None: + """Validate an invalid Get Conference Information Request due to a bad conferenceId + """ + with self.assertRaises(NotFoundException) as context: + self.conference_api_instance.get_conference(BW_ACCOUNT_ID, testConfId, _return_http_data_only=False) + + self.assertApiException(context, NotFoundException, 404) + + # Get Conference member 401 + + def test_get_conference_member_unauthorized(self) -> None: + """Validate an unauthorized (401) Get Conference Member + """ + with self.assertRaises(UnauthorizedException) as context: + self.unauthorized_api_instance.get_conference_member(BW_ACCOUNT_ID, testConfId, testMemberId, _return_http_data_only=False) + + self.assertApiException(context, UnauthorizedException, 401) + + # Get Conference member 403 + + def test_get_conference_member_forbidden(self) -> None: + """Validate an forbidden (403) Get Conference Member + """ + with self.assertRaises(ForbiddenException) as context: + self.forbidden_api_instance.get_conference_member(BW_ACCOUNT_ID, testConfId, testMemberId, _return_http_data_only=False) + + self.assertApiException(context, ForbiddenException, 403) + + # Get Conference member 404 + + def test_get_conference_member_not_found(self) -> None: + """Validate an not found (404) Get Conference Member + """ + with self.assertRaises(NotFoundException) as context: + self.conference_api_instance.get_conference_member(BW_ACCOUNT_ID, testConfId, testMemberId, _return_http_data_only=False) + + self.assertApiException(context, NotFoundException, 404) + + # List Conference Recordings 401 + + def test_list_conference_recordings_unauthorized(self) -> None: + """Validate an unauthorized (401) request + """ + with self.assertRaises(UnauthorizedException) as context: + self.unauthorized_api_instance.list_conference_recordings(BW_ACCOUNT_ID, testConfId, _return_http_data_only=False) + + self.assertApiException(context, UnauthorizedException, 401) + + # List Conference Recordings 403 + + def test_list_conference_recordings_forbidden(self) -> None: + """Validate a forbidden (403) request + """ + with self.assertRaises(ForbiddenException) as context: + self.forbidden_api_instance.list_conference_recordings(BW_ACCOUNT_ID, testConfId, _return_http_data_only=False) + + self.assertApiException(context, ForbiddenException, 403) + + # GET Conference Recording Information 401 + + def test_get_recording_unauthorized(self) -> None: + """Validate an unauthorized (401) Get Conference Recording + """ + with self.assertRaises(UnauthorizedException) as context: + self.unauthorized_api_instance.get_conference_recording(BW_ACCOUNT_ID, testConfId, testRecordId, _return_http_data_only=False) + + self.assertApiException(context, UnauthorizedException, 401) + + # Get Conference Recording Information 403 + + def test_get_recording_forbidden(self) -> None: + """Validate an forbidden (403) Get Conference Recording + """ + with self.assertRaises(ForbiddenException) as context: + self.forbidden_api_instance.get_conference_recording(BW_ACCOUNT_ID, testConfId, testRecordId, _return_http_data_only=False) + + self.assertApiException(context, ForbiddenException, 403) + + # Get Conference Recording Information 404 + + def test_get_conference_recording_not_found(self) -> None: + """Validate an not found (404) Get Conference Recording + """ + with self.assertRaises(NotFoundException) as context: + self.conference_api_instance.get_conference_member(BW_ACCOUNT_ID, testConfId, testRecordId, _return_http_data_only=False) + + self.assertApiException(context, NotFoundException, 404) + + # Update Conference 401 + + def test_update_conference_unauthorized(self) -> None: + """Validate an unauthorized (401) Update Conference + """ + with self.assertRaises(UnauthorizedException) as context: + self.unauthorized_api_instance.update_conference(BW_ACCOUNT_ID, testConfId, testUpdateConf, _return_http_data_only=False) + + self.assertApiException(context, UnauthorizedException, 401) + + # Update Conference 403 + + def test_update_conference_forbidden(self) -> None: + """Validate an forbidden (403) Update Conference + """ + with self.assertRaises(ForbiddenException) as context: + self.forbidden_api_instance.update_conference(BW_ACCOUNT_ID, testConfId, testUpdateConf, _return_http_data_only=False) + + self.assertApiException(context, ForbiddenException, 403) + + # Update Conference 404 + + def test_update_conference_not_found(self) -> None: + """Validate an not found (404) Update Conference + """ + with self.assertRaises(NotFoundException) as context: + self.conference_api_instance.update_conference(BW_ACCOUNT_ID, testConfId, testUpdateConf, _return_http_data_only=False) + + self.assertApiException(context, NotFoundException, 404) + + # Update Conference BXML 401 + + def test_update_conference_bxml_unauthorized(self) -> None: + """Validate an unauthorized (401) Update Conference BXML + """ + with self.assertRaises(UnauthorizedException) as context: + self.unauthorized_api_instance.update_conference_bxml(BW_ACCOUNT_ID, testConfId, testUpdateBxml, _return_http_data_only=False) + + self.assertApiException(context, UnauthorizedException, 401) + + # Update Conference BXML 403 + + def test_update_conference_bxml_forbidden(self) -> None: + """Validate an forbidden (403) Update Conference BXML + """ + with self.assertRaises(ForbiddenException) as context: + self.forbidden_api_instance.update_conference_bxml(BW_ACCOUNT_ID, testConfId, testUpdateBxml, _return_http_data_only=False) + + self.assertApiException(context, ForbiddenException, 403) + + # Update Conference BXML 404 + + def test_update_conference_bxml_not_found(self) -> None: + """Validate an not found (404) Update Conference BXML + """ + with self.assertRaises(NotFoundException) as context: + self.conference_api_instance.update_conference_bxml(BW_ACCOUNT_ID, testConfId, testUpdateBxml, _return_http_data_only=False) + + self.assertApiException(context, NotFoundException, 404) + + # Update Conference member 401 + + def test_update_conference_member_unauthorized(self) -> None: + """Validate an unauthorized (401) Get Conference Member + """ + with self.assertRaises(UnauthorizedException) as context: + self.unauthorized_api_instance.update_conference_member(BW_ACCOUNT_ID, testConfId, testMemberId, testUpdateMember, _return_http_data_only=False) + + self.assertApiException(context, UnauthorizedException, 401) + + # Update Conference member 403 + + def test_update_conference_member_forbidden(self) -> None: + """Validate an forbidden (403) Get Conference Member + """ + with self.assertRaises(ForbiddenException) as context: + self.forbidden_api_instance.update_conference_member(BW_ACCOUNT_ID, testConfId, testMemberId, testUpdateMember, _return_http_data_only=False) + + self.assertApiException(context, ForbiddenException, 403) + + # Update Conference member 404 + + def test_update_conference_member_not_found(self) -> None: + """Validate an not found (404) Get Conference Member + """ + with self.assertRaises(NotFoundException) as context: + self.conference_api_instance.update_conference_member(BW_ACCOUNT_ID, testConfId, testMemberId, testUpdateMember, _return_http_data_only=False) + + self.assertApiException(context, NotFoundException, 404) + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From 1a4f53088714dc57053ce04b36d676c596e12867 Mon Sep 17 00:00:00 2001 From: Brian Date: Thu, 4 Aug 2022 14:16:53 -0400 Subject: [PATCH 2/6] DX-2688 Added utils and other cleanup --- test-requirements.txt | 1 + test/integration/test_conferences.py | 286 +++++++++------------------ test/utils/__init__.py | 0 test/utils/call_cleanup.py | 25 +++ test/utils/env_variables.py | 22 +++ 5 files changed, 143 insertions(+), 191 deletions(-) create mode 100644 test/utils/__init__.py create mode 100644 test/utils/call_cleanup.py create mode 100644 test/utils/env_variables.py diff --git a/test-requirements.txt b/test-requirements.txt index bb4f22bb..30838dfe 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1 +1,2 @@ pytest-cov>=2.8.1 +pyhamcrest>=2.0.3 \ No newline at end of file diff --git a/test/integration/test_conferences.py b/test/integration/test_conferences.py index 622b5685..fcd06c65 100644 --- a/test/integration/test_conferences.py +++ b/test/integration/test_conferences.py @@ -2,11 +2,13 @@ Integration tests for Bandwidth's Voice Voice Conferences API """ -import os +from test.utils.env_variables import * +from test.utils.call_cleanup import callCleanup import json import time from typing import List, Tuple import unittest +from hamcrest import assert_that, has_properties, not_none, instance_of, greater_than import bandwidth @@ -26,63 +28,6 @@ from bandwidth.rest import RESTClientObject, RESTResponse from bandwidth.exceptions import ApiException, UnauthorizedException, ForbiddenException, NotFoundException -try: - BW_USERNAME = os.environ["BW_USERNAME"] - BW_PASSWORD = os.environ["BW_PASSWORD"] - BW_ACCOUNT_ID = os.environ["BW_ACCOUNT_ID"] - BW_VOICE_APPLICATION_ID = os.environ["BW_VOICE_APPLICATION_ID"] - BASE_CALLBACK_URL = os.environ["BASE_CALLBACK_URL"] - BW_NUMBER = os.environ["BW_NUMBER"] - USER_NUMBER = os.environ["USER_NUMBER"] - FORBIDDEN_USERNAME = os.environ['BW_USERNAME_FORBIDDEN'] - FORBIDDEN_PASSWORD = os.environ['BW_PASSWORD_FORBIDDEN'] - MANTECA_ACTIVE_NUMBER = os.environ["MANTECA_ACTIVE_NUMBER"] - MANTECA_IDLE_NUMBER = os.environ["MANTECA_IDLE_NUMBER"] - MANTECA_BASE_URL = os.environ["MANTECA_BASE_URL"] - MANTECA_STATUS_URL = MANTECA_BASE_URL + "tests/" - MANTECA_APPLICATION_ID = os.environ["MANTECA_APPLICATION_ID"] - PYTHON_VERSION = os.environ["PYTHON_VERSION"] - OPERATING_SYSTEM = os.environ["OPERATING_SYSTEM"] - -except KeyError as e: - raise Exception("Environmental variables not found") - -MAX_RETRIES = 40 -TEST_SLEEP = 3 - -global callIdArray -callIdArray = [] - -global testUpdateConf -testUpdateConf = UpdateConference( - state=ConferenceStateEnum("active"), - redirect_url=MANTECA_BASE_URL + "/bxml/pause", - redirect_method=RedirectMethodEnum("POST"), - username="mySecretUsername", - password="mySecretPassword1!", - redirect_fallback_url=MANTECA_BASE_URL + "/bxml/pause", - redirect_fallback_method=RedirectMethodEnum("POST"), - fallback_username="mySecretUsername", - fallback_password="mySecretPassword1!", - tag="My Custom Tag", - ) - -global testUpdateBxml -testUpdateBxml = 'This is test BXML.' - -global testUpdateMember -testUpdateMember = UpdateConferenceMember(mute=False) - -global testConfId -testConfId = "Conf-id" - -global testMemberId -testMemberId = "Member-Id" - -global testRecordId -testRecordId = "Recording-Id" - - class ConferencesIntegration(unittest.TestCase): """ Voice Conferences API integration test @@ -129,27 +74,29 @@ def setUp(self): ) self.account_id = BW_ACCOUNT_ID - - def tearDown(self): - """ - Whenever we create an actual call, we'll add the call_id to the callIdArray. Then when the integration test is done, as part of tearDown we'll: - Do a get to check is the call status is still active - If so, update to completed to end the call - If not, pop that callID off the array - Once we go through the whole array, we clear the array so it's empty for the next integration test. - if the status is active, send UpdateCall to change to completed - """ + self.callIdArray = [] + self.testUpdateConf = UpdateConference( + state=ConferenceStateEnum("active"), + redirect_url=MANTECA_BASE_URL + "/bxml/pause", + redirect_method=RedirectMethodEnum("POST"), + username="mySecretUsername", + password="mySecretPassword1!", + redirect_fallback_url=MANTECA_BASE_URL + "/bxml/pause", + redirect_fallback_method=RedirectMethodEnum("POST"), + fallback_username="mySecretUsername", + fallback_password="mySecretPassword1!", + tag="My Custom Tag", + ) + self.testUpdateBxml = 'This is test BXML.' + self.testUpdateMember = UpdateConferenceMember(mute=False) + self.testConfId = "Conf-id" + self.testMemberId = "Member-Id" + self.testRecordId = "Recording-Id" + self.TEST_SLEEP = 3 + self.TEST_SLEEP_LONG = 10 - if len(callIdArray) > 0: - for callId in callIdArray: - body = UpdateCall(state=CallStateEnum("completed")) - get_call_response: CallState = self.calls_api_instance.get_call_state(BW_ACCOUNT_ID, callId, _return_http_data_only=False) - if get_call_response[0].state == 'active': - self.calls_api_instance.update_call(BW_ACCOUNT_ID, callId, body, _return_http_data_only=False) - elif get_call_response[0].state == 'complete': - callIdArray.remove(callId) - callIdArray.clear() - pass + def tearDown(self): + callCleanup(self) def assertApiException(self, context: ApiException, expectedException: ApiException, expected_status_code: int): """Validates that common API exceptions, (401, 403, and 404) are properly formatted @@ -158,10 +105,10 @@ def assertApiException(self, context: ApiException, expectedException: ApiExcept expectedException (ApiException): Expected exception type expected_status_code (int): Expected status code """ - self.assertIs(type(context.exception), expectedException) - self.assertIs(type(context.exception.status), int) - self.assertEqual(context.exception.status, expected_status_code) - self.assertIs(type(context.exception.body), str) + assert_that(context.exception, has_properties( + 'status', expected_status_code, + 'body', not_none() + )) # Create Conference Call with Manteca @@ -199,29 +146,27 @@ def create_conference(self, answer_url: str) -> Tuple[str, str]: create_call_response: CreateCallResponse = self.calls_api_instance.create_call(BW_ACCOUNT_ID, call_body) # Verify info about the call - assert len(create_call_response.call_id) == 47 # assert request created and id matches expected length (47) - assert create_call_response.account_id == BW_ACCOUNT_ID - assert create_call_response.application_id == MANTECA_APPLICATION_ID - assert create_call_response.to == MANTECA_IDLE_NUMBER - assert create_call_response._from == MANTECA_ACTIVE_NUMBER - assert create_call_response.call_url == "https://voice.bandwidth.com/api/v2/accounts/" + \ - BW_ACCOUNT_ID + "/calls/" + create_call_response.call_id + assert_that(create_call_response, has_properties( + "call_id", instance_of(str), + 'account_id', BW_ACCOUNT_ID, + 'application_id', MANTECA_APPLICATION_ID, + 'to', MANTECA_IDLE_NUMBER, + '_from', MANTECA_ACTIVE_NUMBER + )) # Getting our ConferenceId and returning the test_id from Manteca and the callId - time.sleep(2) list_conferences_response = self.conference_api_instance.list_conferences(BW_ACCOUNT_ID, _return_http_data_only=False) - time.sleep(2) - self.assertEqual(list_conferences_response[1], 200) - #print(list_conferences_response[0]) + time.sleep(self.TEST_SLEEP) + assert_that(list_conferences_response[1], 200) conferenceId = (list_conferences_response[0][len(list_conferences_response[0])-1].id) get_conference_response = self.conference_api_instance.get_conference(BW_ACCOUNT_ID, conferenceId, _return_http_data_only=False) - self.assertEqual(get_conference_response[1], 200) + assert_that(get_conference_response[1], 200) - callIdArray.append(create_call_response.call_id) + self.callIdArray.append(create_call_response.call_id) return (test_id, create_call_response.call_id) @@ -232,8 +177,8 @@ def validate_recording(self, recording: ConferenceRecordingMetadata, conference_ recording (ConferenceRecordingMetadata): The recording metadata to validate. conference_id (str): The call id associated with the given recording. """ - assert recording.status == 'complete' - assert recording.file_format == FileFormatEnum('wav') + assert_that(recording.status,'complete') + assert_that(recording.file_format,FileFormatEnum('wav')) def test_conference_and_members(self): """ @@ -254,9 +199,9 @@ def test_conference_and_members(self): # List Conferences list_conferences_response = self.conference_api_instance.list_conferences(BW_ACCOUNT_ID, _return_http_data_only=False) - self.assertEqual(list_conferences_response[1], 200) - self.assertIs(type(list_conferences_response[0][0].name), str) - self.assertIs(type(list_conferences_response[0][0].id), str) + assert_that(list_conferences_response[1], 200) + assert_that(list_conferences_response[0][0].name, instance_of(str)) + assert_that(list_conferences_response[0][0].id, instance_of(str)) conferenceId = (list_conferences_response[0][len(list_conferences_response[0])-1].id) @@ -264,52 +209,51 @@ def test_conference_and_members(self): """Validate a Get Conference Information Request """ get_conference_response = self.conference_api_instance.get_conference(BW_ACCOUNT_ID, conferenceId, _return_http_data_only=False) - self.assertEqual(get_conference_response[1], 200) - self.assertEqual(get_conference_response[0].id, conferenceId) - self.assertIs(type(get_conference_response[0].name), str) + assert_that(get_conference_response[1], 200) + assert_that(get_conference_response[0].id, conferenceId) + assert_that(get_conference_response[0].name, instance_of(str)) callId = (get_conference_response[0].active_members[0].call_id) - callIdArray.append(callId) + self.callIdArray.append(callId) # Get Conference Member """Validate a GET Conference Member """ get_conference_member_response = self.conference_api_instance.get_conference_member(BW_ACCOUNT_ID, conferenceId, callId, _return_http_data_only=False) - self.assertEqual(get_conference_member_response[1], 200) - self.assertEqual(get_conference_member_response[0].conference_id, conferenceId) - self.assertEqual(get_conference_member_response[0].call_id, callId) + assert_that(get_conference_member_response[1], 200) + assert_that(get_conference_member_response[0].conference_id, conferenceId) + assert_that(get_conference_member_response[0].call_id, callId) - # Update Conference member test + # Update Conference Member """Validate an Update Conference Member Request """ - time.sleep(2) - update_conference_member_response = self.conference_api_instance.update_conference_member(BW_ACCOUNT_ID, conferenceId, callId, testUpdateMember, _return_http_data_only=False) - self.assertEqual(update_conference_member_response[1], 204) + time.sleep(self.TEST_SLEEP) + update_conference_member_response = self.conference_api_instance.update_conference_member(BW_ACCOUNT_ID, conferenceId, callId, self.testUpdateMember, _return_http_data_only=False) + assert_that(update_conference_member_response[1], 204) - # Update Conference test - """Validate an Update Conference Request + # Update Conference + """Validate an Update Conference Request """ - time.sleep(3) - update_conference_response = self.conference_api_instance.update_conference(BW_ACCOUNT_ID, conferenceId, testUpdateConf, _return_http_data_only=False) - self.assertEqual(update_conference_response[1], 204) + time.sleep(self.TEST_SLEEP) + update_conference_response = self.conference_api_instance.update_conference(BW_ACCOUNT_ID, conferenceId, self.testUpdateConf, _return_http_data_only=False) + assert_that(update_conference_response[1], 204) - # update Conference Bxml Test + # Update Conference Bxml """Validate an UpdateConferencelBxml Request """ updateBxmlBody = 'This is a test bxml response' - time.sleep(3) + time.sleep(self.TEST_SLEEP) update_conference_bxml_response = self.conference_api_instance.update_conference_bxml(BW_ACCOUNT_ID, conferenceId, updateBxmlBody, _return_http_data_only=False) - self.assertEqual(update_conference_bxml_response[1], 204) + assert_that(update_conference_bxml_response[1], 204) # Kill the call update_call = UpdateCall(state=CallStateEnum('completed')) self.calls_api_instance.update_call(BW_ACCOUNT_ID, call_id, update_call, _return_http_data_only=False) - # Conference Recordings Test - + # Conference Recordings Tests def test_conference_recordings(self) -> None: """ Tests a successful flow of creating a call with a recording. @@ -325,25 +269,25 @@ def test_conference_recordings(self) -> None: list_conferences_response = self.conference_api_instance.list_conferences(BW_ACCOUNT_ID, _return_http_data_only=False) - self.assertEqual(list_conferences_response[1], 200) + assert_that(list_conferences_response[1], 200) conferenceId = (list_conferences_response[0][len(list_conferences_response[0])-1].id) - # update Conference Bxml Test + # Update Conference Bxml """Validate an UpdateConferencelBxml Request to start and stop recording """ updateBxmlBody = 'This should be a conference recording.' update_conference_bxml_response = self.conference_api_instance.update_conference_bxml(BW_ACCOUNT_ID, conferenceId, updateBxmlBody, _return_http_data_only=False) - self.assertEqual(update_conference_bxml_response[1], 204) + assert_that(update_conference_bxml_response[1], 204) - time.sleep(10) + time.sleep(self.TEST_SLEEP_LONG) # List Conference Recordings Endpoint list_conference_recordings_response: List[ConferenceRecordingMetadata] = self.conference_api_instance.list_conference_recordings(BW_ACCOUNT_ID, conferenceId, _return_http_data_only=False) - assert list_conference_recordings_response[1] == 200 + assert_that(list_conference_recordings_response[1],200) # We should get back at least 1 recording conference_recordings = list_conference_recordings_response[0] - assert len(conference_recordings) > 0 + assert_that(len(conference_recordings), greater_than(0)) # Checks on the first recording first_recording: ConferenceRecordingMetadata = conference_recordings[0] @@ -352,10 +296,10 @@ def test_conference_recordings(self) -> None: # Get Single Recording Endpoint recording_response: ConferenceRecordingMetadata = self.conference_api_instance.get_conference_recording(BW_ACCOUNT_ID, conferenceId, recording_id, _return_http_data_only=False) - self.assertEqual(recording_response[1], 200) - self.assertEqual(recording_response[0].conference_id, conferenceId) - self.assertEqual(recording_response[0].recording_id, recording_id) - self.assertIs(type(recording_response[0].name), str) + assert_that(recording_response[1], 200) + assert_that(recording_response[0].conference_id, conferenceId) + assert_that(recording_response[0].recording_id, recording_id) + assert_that(recording_response[0].name, instance_of(str)) self.validate_recording(recording_response[0], conferenceId) @@ -363,8 +307,6 @@ def test_conference_recordings(self) -> None: recording_media_response = self.conference_api_instance.download_conference_recording(BW_ACCOUNT_ID, conferenceId, recording_id, _preload_content=False) conference_recording_media = recording_media_response.data - # List conferences 401 - def test_list_conferences_unauthorized(self) -> None: """Validate an unauthorized (401) request """ @@ -373,7 +315,6 @@ def test_list_conferences_unauthorized(self) -> None: self.assertApiException(context, UnauthorizedException, 401) - # List conferences 403 def test_list_conferences_forbidden(self) -> None: """Validate a forbidden (403) request """ @@ -382,200 +323,163 @@ def test_list_conferences_forbidden(self) -> None: self.assertApiException(context, ForbiddenException, 403) - # Get Conference Information 401 def test_get_conferences_unauthorized(self) -> None: """Validate an unauthorized (401) Get Conference Information """ with self.assertRaises(UnauthorizedException) as context: - self.unauthorized_api_instance.get_conference(BW_ACCOUNT_ID, testConfId, _return_http_data_only=False) + self.unauthorized_api_instance.get_conference(BW_ACCOUNT_ID, self.testConfId, _return_http_data_only=False) self.assertApiException(context, UnauthorizedException, 401) - # Get Conference Information 403 def test_get_conferences_forbidden(self) -> None: """Validate a forbidden (403) Get Conference Information request """ with self.assertRaises(ForbiddenException) as context: - self.forbidden_api_instance.get_conference(BW_ACCOUNT_ID, testConfId, _return_http_data_only=False) + self.forbidden_api_instance.get_conference(BW_ACCOUNT_ID, self.testConfId, _return_http_data_only=False) self.assertApiException(context, ForbiddenException, 403) - # Get Conference Information 404 def test_get_conferences_not_found(self) -> None: """Validate an invalid Get Conference Information Request due to a bad conferenceId """ with self.assertRaises(NotFoundException) as context: - self.conference_api_instance.get_conference(BW_ACCOUNT_ID, testConfId, _return_http_data_only=False) + self.conference_api_instance.get_conference(BW_ACCOUNT_ID, self.testConfId, _return_http_data_only=False) self.assertApiException(context, NotFoundException, 404) - # Get Conference member 401 - def test_get_conference_member_unauthorized(self) -> None: """Validate an unauthorized (401) Get Conference Member """ with self.assertRaises(UnauthorizedException) as context: - self.unauthorized_api_instance.get_conference_member(BW_ACCOUNT_ID, testConfId, testMemberId, _return_http_data_only=False) + self.unauthorized_api_instance.get_conference_member(BW_ACCOUNT_ID, self.testConfId, self.testMemberId, _return_http_data_only=False) self.assertApiException(context, UnauthorizedException, 401) - # Get Conference member 403 - def test_get_conference_member_forbidden(self) -> None: """Validate an forbidden (403) Get Conference Member """ with self.assertRaises(ForbiddenException) as context: - self.forbidden_api_instance.get_conference_member(BW_ACCOUNT_ID, testConfId, testMemberId, _return_http_data_only=False) + self.forbidden_api_instance.get_conference_member(BW_ACCOUNT_ID, self.testConfId, self.testMemberId, _return_http_data_only=False) self.assertApiException(context, ForbiddenException, 403) - # Get Conference member 404 - def test_get_conference_member_not_found(self) -> None: """Validate an not found (404) Get Conference Member """ with self.assertRaises(NotFoundException) as context: - self.conference_api_instance.get_conference_member(BW_ACCOUNT_ID, testConfId, testMemberId, _return_http_data_only=False) + self.conference_api_instance.get_conference_member(BW_ACCOUNT_ID, self.testConfId, self.testMemberId, _return_http_data_only=False) self.assertApiException(context, NotFoundException, 404) - # List Conference Recordings 401 - def test_list_conference_recordings_unauthorized(self) -> None: """Validate an unauthorized (401) request """ with self.assertRaises(UnauthorizedException) as context: - self.unauthorized_api_instance.list_conference_recordings(BW_ACCOUNT_ID, testConfId, _return_http_data_only=False) + self.unauthorized_api_instance.list_conference_recordings(BW_ACCOUNT_ID, self.testConfId, _return_http_data_only=False) self.assertApiException(context, UnauthorizedException, 401) - # List Conference Recordings 403 - def test_list_conference_recordings_forbidden(self) -> None: """Validate a forbidden (403) request """ with self.assertRaises(ForbiddenException) as context: - self.forbidden_api_instance.list_conference_recordings(BW_ACCOUNT_ID, testConfId, _return_http_data_only=False) + self.forbidden_api_instance.list_conference_recordings(BW_ACCOUNT_ID, self.testConfId, _return_http_data_only=False) self.assertApiException(context, ForbiddenException, 403) - - # GET Conference Recording Information 401 def test_get_recording_unauthorized(self) -> None: """Validate an unauthorized (401) Get Conference Recording """ with self.assertRaises(UnauthorizedException) as context: - self.unauthorized_api_instance.get_conference_recording(BW_ACCOUNT_ID, testConfId, testRecordId, _return_http_data_only=False) + self.unauthorized_api_instance.get_conference_recording(BW_ACCOUNT_ID, self.testConfId, self.testRecordId, _return_http_data_only=False) self.assertApiException(context, UnauthorizedException, 401) - # Get Conference Recording Information 403 - def test_get_recording_forbidden(self) -> None: """Validate an forbidden (403) Get Conference Recording """ with self.assertRaises(ForbiddenException) as context: - self.forbidden_api_instance.get_conference_recording(BW_ACCOUNT_ID, testConfId, testRecordId, _return_http_data_only=False) + self.forbidden_api_instance.get_conference_recording(BW_ACCOUNT_ID, self.testConfId, self.testRecordId, _return_http_data_only=False) self.assertApiException(context, ForbiddenException, 403) - # Get Conference Recording Information 404 - def test_get_conference_recording_not_found(self) -> None: """Validate an not found (404) Get Conference Recording """ with self.assertRaises(NotFoundException) as context: - self.conference_api_instance.get_conference_member(BW_ACCOUNT_ID, testConfId, testRecordId, _return_http_data_only=False) + self.conference_api_instance.get_conference_member(BW_ACCOUNT_ID, self.testConfId, self.testRecordId, _return_http_data_only=False) self.assertApiException(context, NotFoundException, 404) - # Update Conference 401 - def test_update_conference_unauthorized(self) -> None: """Validate an unauthorized (401) Update Conference """ with self.assertRaises(UnauthorizedException) as context: - self.unauthorized_api_instance.update_conference(BW_ACCOUNT_ID, testConfId, testUpdateConf, _return_http_data_only=False) + self.unauthorized_api_instance.update_conference(BW_ACCOUNT_ID, self.testConfId, self.testUpdateConf, _return_http_data_only=False) self.assertApiException(context, UnauthorizedException, 401) - # Update Conference 403 - def test_update_conference_forbidden(self) -> None: """Validate an forbidden (403) Update Conference """ with self.assertRaises(ForbiddenException) as context: - self.forbidden_api_instance.update_conference(BW_ACCOUNT_ID, testConfId, testUpdateConf, _return_http_data_only=False) + self.forbidden_api_instance.update_conference(BW_ACCOUNT_ID, self.testConfId, self.testUpdateConf, _return_http_data_only=False) self.assertApiException(context, ForbiddenException, 403) - # Update Conference 404 - def test_update_conference_not_found(self) -> None: """Validate an not found (404) Update Conference """ with self.assertRaises(NotFoundException) as context: - self.conference_api_instance.update_conference(BW_ACCOUNT_ID, testConfId, testUpdateConf, _return_http_data_only=False) + self.conference_api_instance.update_conference(BW_ACCOUNT_ID, self.testConfId, self.testUpdateConf, _return_http_data_only=False) self.assertApiException(context, NotFoundException, 404) - # Update Conference BXML 401 - def test_update_conference_bxml_unauthorized(self) -> None: """Validate an unauthorized (401) Update Conference BXML """ with self.assertRaises(UnauthorizedException) as context: - self.unauthorized_api_instance.update_conference_bxml(BW_ACCOUNT_ID, testConfId, testUpdateBxml, _return_http_data_only=False) + self.unauthorized_api_instance.update_conference_bxml(BW_ACCOUNT_ID, self.testConfId, self.testUpdateBxml, _return_http_data_only=False) self.assertApiException(context, UnauthorizedException, 401) - # Update Conference BXML 403 - def test_update_conference_bxml_forbidden(self) -> None: """Validate an forbidden (403) Update Conference BXML """ with self.assertRaises(ForbiddenException) as context: - self.forbidden_api_instance.update_conference_bxml(BW_ACCOUNT_ID, testConfId, testUpdateBxml, _return_http_data_only=False) + self.forbidden_api_instance.update_conference_bxml(BW_ACCOUNT_ID, self.testConfId, self.testUpdateBxml, _return_http_data_only=False) self.assertApiException(context, ForbiddenException, 403) - # Update Conference BXML 404 - def test_update_conference_bxml_not_found(self) -> None: """Validate an not found (404) Update Conference BXML """ with self.assertRaises(NotFoundException) as context: - self.conference_api_instance.update_conference_bxml(BW_ACCOUNT_ID, testConfId, testUpdateBxml, _return_http_data_only=False) + self.conference_api_instance.update_conference_bxml(BW_ACCOUNT_ID, self.testConfId, self.testUpdateBxml, _return_http_data_only=False) self.assertApiException(context, NotFoundException, 404) - # Update Conference member 401 - def test_update_conference_member_unauthorized(self) -> None: """Validate an unauthorized (401) Get Conference Member """ with self.assertRaises(UnauthorizedException) as context: - self.unauthorized_api_instance.update_conference_member(BW_ACCOUNT_ID, testConfId, testMemberId, testUpdateMember, _return_http_data_only=False) + self.unauthorized_api_instance.update_conference_member(BW_ACCOUNT_ID, self.testConfId, self.testMemberId, self.testUpdateMember, _return_http_data_only=False) self.assertApiException(context, UnauthorizedException, 401) - # Update Conference member 403 - def test_update_conference_member_forbidden(self) -> None: """Validate an forbidden (403) Get Conference Member """ with self.assertRaises(ForbiddenException) as context: - self.forbidden_api_instance.update_conference_member(BW_ACCOUNT_ID, testConfId, testMemberId, testUpdateMember, _return_http_data_only=False) + self.forbidden_api_instance.update_conference_member(BW_ACCOUNT_ID, self.testConfId, self.testMemberId, self.testUpdateMember, _return_http_data_only=False) self.assertApiException(context, ForbiddenException, 403) - # Update Conference member 404 - def test_update_conference_member_not_found(self) -> None: """Validate an not found (404) Get Conference Member """ with self.assertRaises(NotFoundException) as context: - self.conference_api_instance.update_conference_member(BW_ACCOUNT_ID, testConfId, testMemberId, testUpdateMember, _return_http_data_only=False) + self.conference_api_instance.update_conference_member(BW_ACCOUNT_ID, self.testConfId, self.testMemberId, self.testUpdateMember, _return_http_data_only=False) self.assertApiException(context, NotFoundException, 404) diff --git a/test/utils/__init__.py b/test/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test/utils/call_cleanup.py b/test/utils/call_cleanup.py new file mode 100644 index 00000000..d9c0cf98 --- /dev/null +++ b/test/utils/call_cleanup.py @@ -0,0 +1,25 @@ +from test.utils.env_variables import * +from bandwidth.model.call_state import CallState +from bandwidth.model.call_state_enum import CallStateEnum +from bandwidth.model.update_call import UpdateCall + +def callCleanup(self): + """ + Whenever we create an actual call, we'll add the call_id to the callIdArray. Then when the integration test is done, as part of tearDown we'll: + Do a get to check is the call status is still active + If so, update to completed to end the call + If not, pop that callID off the array + Once we go through the whole array, we clear the array so it's empty for the next integration test. + if the status is active, send UpdateCall to change to completed + """ + + if len(self.callIdArray) > 0: + for callId in self.callIdArray: + body = UpdateCall(state=CallStateEnum("completed")) + get_call_response: CallState = self.calls_api_instance.get_call_state(BW_ACCOUNT_ID, callId, _return_http_data_only=False) + if get_call_response[0].state == 'active': + self.calls_api_instance.update_call(BW_ACCOUNT_ID, callId, body, _return_http_data_only=False) + elif get_call_response[0].state == 'complete': + self.callIdArray.remove(callId) + self.callIdArray.clear() + pass \ No newline at end of file diff --git a/test/utils/env_variables.py b/test/utils/env_variables.py new file mode 100644 index 00000000..1d9f5a19 --- /dev/null +++ b/test/utils/env_variables.py @@ -0,0 +1,22 @@ +import os + +try: + BW_USERNAME = os.environ["BW_USERNAME"] + BW_PASSWORD = os.environ["BW_PASSWORD"] + BW_ACCOUNT_ID = os.environ["BW_ACCOUNT_ID"] + BW_VOICE_APPLICATION_ID = os.environ["BW_VOICE_APPLICATION_ID"] + BASE_CALLBACK_URL = os.environ["BASE_CALLBACK_URL"] + BW_NUMBER = os.environ["BW_NUMBER"] + USER_NUMBER = os.environ["USER_NUMBER"] + FORBIDDEN_USERNAME = os.environ['BW_USERNAME_FORBIDDEN'] + FORBIDDEN_PASSWORD = os.environ['BW_PASSWORD_FORBIDDEN'] + MANTECA_ACTIVE_NUMBER = os.environ["MANTECA_ACTIVE_NUMBER"] + MANTECA_IDLE_NUMBER = os.environ["MANTECA_IDLE_NUMBER"] + MANTECA_BASE_URL = os.environ["MANTECA_BASE_URL"] + MANTECA_STATUS_URL = MANTECA_BASE_URL + "tests/" + MANTECA_APPLICATION_ID = os.environ["MANTECA_APPLICATION_ID"] + PYTHON_VERSION = os.environ["PYTHON_VERSION"] + OPERATING_SYSTEM = os.environ["OPERATING_SYSTEM"] + +except KeyError as e: + raise Exception("Environmental variables not found") \ No newline at end of file From d78f144a38f76af07e8d7ef540a5a14a94b6f1dd Mon Sep 17 00:00:00 2001 From: Brian Date: Fri, 5 Aug 2022 09:38:57 -0400 Subject: [PATCH 3/6] Update test.yaml --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e00c6db3..f7f7114b 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -13,7 +13,7 @@ jobs: strategy: matrix: os: [windows-2022, windows-2019, ubuntu-18.04, ubuntu-20.04] - python-version: [3.7, 3.8, 3.9, '3.10'] + python-version: [3.7, 3.8, 3.9, "3.10"] steps: - name: Checkout uses: actions/checkout@v2 From d45dfc5d2940f7cb88a05afef7a0e04ecde2ecc9 Mon Sep 17 00:00:00 2001 From: Brian Date: Fri, 5 Aug 2022 09:53:11 -0400 Subject: [PATCH 4/6] Update test_recordings.py --- test/integration/test_recordings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/integration/test_recordings.py b/test/integration/test_recordings.py index 28432107..2de6795c 100644 --- a/test/integration/test_recordings.py +++ b/test/integration/test_recordings.py @@ -170,6 +170,8 @@ def complete_recorded_call(self) -> Tuple[str, str]: answer_url = MANTECA_BASE_URL + '/bxml/startRecording' (test_id, call_id) = self.create_and_validate_call(answer_url) + + time.sleep(12) # Poll Manteca to make sure our call is recorded call_status = self.get_test_status(test_id) retries = 0 From e9a1b5a9c8ef1b912dcfeaa80297b840375ba841 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 8 Aug 2022 11:49:12 -0400 Subject: [PATCH 5/6] Update test_conferences.py --- test/integration/test_conferences.py | 101 +-------------------------- 1 file changed, 2 insertions(+), 99 deletions(-) diff --git a/test/integration/test_conferences.py b/test/integration/test_conferences.py index fcd06c65..769defe8 100644 --- a/test/integration/test_conferences.py +++ b/test/integration/test_conferences.py @@ -2,15 +2,11 @@ Integration tests for Bandwidth's Voice Voice Conferences API """ -from test.utils.env_variables import * -from test.utils.call_cleanup import callCleanup import json import time from typing import List, Tuple import unittest from hamcrest import assert_that, has_properties, not_none, instance_of, greater_than - - import bandwidth from bandwidth.api import calls_api from bandwidth.model.create_call import CreateCall @@ -27,6 +23,8 @@ from bandwidth.model.file_format_enum import FileFormatEnum from bandwidth.rest import RESTClientObject, RESTResponse from bandwidth.exceptions import ApiException, UnauthorizedException, ForbiddenException, NotFoundException +from test.utils.env_variables import * +from test.utils.call_cleanup import callCleanup class ConferencesIntegration(unittest.TestCase): """ @@ -44,20 +42,15 @@ def setUp(self): ) api_client = bandwidth.ApiClient(configuration) - # Two Valid API Clients self.calls_api_instance = calls_api.CallsApi(api_client) self.conference_api_instance = conferences_api.ConferencesApi(api_client) - # Unauthorized API Client - unauthorizedConfiguration = bandwidth.Configuration( username='bad_username', password='bad_password' ) unauthorized_api_client = bandwidth.ApiClient(unauthorizedConfiguration) self.unauthorized_api_instance = conferences_api.ConferencesApi(unauthorized_api_client) - - # Forbidden API Client forbiddenConfiguration = bandwidth.Configuration( username=FORBIDDEN_USERNAME, @@ -102,7 +95,6 @@ def assertApiException(self, context: ApiException, expectedException: ApiExcept """Validates that common API exceptions, (401, 403, and 404) are properly formatted Args: context (ApiException): Exception to validate - expectedException (ApiException): Expected exception type expected_status_code (int): Expected status code """ assert_that(context.exception, has_properties( @@ -110,8 +102,6 @@ def assertApiException(self, context: ApiException, expectedException: ApiExcept 'body', not_none() )) - # Create Conference Call with Manteca - def create_conference(self, answer_url: str) -> Tuple[str, str]: """ Create and validate a call between two bandwidth numbers. Initializes the call with the Manteca @@ -135,17 +125,12 @@ def create_conference(self, answer_url: str) -> Tuple[str, str]: } ) - # Get the test id from the response test_id = json.loads(response.data) - # Make a CreateCall body and assign the appropriate params - call_body = CreateCall(to=MANTECA_IDLE_NUMBER, _from=MANTECA_ACTIVE_NUMBER, application_id=MANTECA_APPLICATION_ID, answer_url=answer_url, tag=test_id) - # Make the call create_call_response: CreateCallResponse = self.calls_api_instance.create_call(BW_ACCOUNT_ID, call_body) - # Verify info about the call assert_that(create_call_response, has_properties( "call_id", instance_of(str), 'account_id', BW_ACCOUNT_ID, @@ -154,8 +139,6 @@ def create_conference(self, answer_url: str) -> Tuple[str, str]: '_from', MANTECA_ACTIVE_NUMBER )) - # Getting our ConferenceId and returning the test_id from Manteca and the callId - list_conferences_response = self.conference_api_instance.list_conferences(BW_ACCOUNT_ID, _return_http_data_only=False) time.sleep(self.TEST_SLEEP) @@ -171,12 +154,6 @@ def create_conference(self, answer_url: str) -> Tuple[str, str]: return (test_id, create_call_response.call_id) def validate_recording(self, recording: ConferenceRecordingMetadata, conference_id: str) -> None: - """ - Validate the given recording metadata. - Args: - recording (ConferenceRecordingMetadata): The recording metadata to validate. - conference_id (str): The call id associated with the given recording. - """ assert_that(recording.status,'complete') assert_that(recording.file_format,FileFormatEnum('wav')) @@ -192,11 +169,9 @@ def test_conference_and_members(self): - update_conference_bxml """ - # Create the call answer_url = MANTECA_BASE_URL + "/bxml/joinConferencePause" (test_id, call_id) = self.create_conference(answer_url) - # List Conferences list_conferences_response = self.conference_api_instance.list_conferences(BW_ACCOUNT_ID, _return_http_data_only=False) assert_that(list_conferences_response[1], 200) @@ -205,9 +180,6 @@ def test_conference_and_members(self): conferenceId = (list_conferences_response[0][len(list_conferences_response[0])-1].id) - # Get Conference Information - """Validate a Get Conference Information Request - """ get_conference_response = self.conference_api_instance.get_conference(BW_ACCOUNT_ID, conferenceId, _return_http_data_only=False) assert_that(get_conference_response[1], 200) assert_that(get_conference_response[0].id, conferenceId) @@ -215,45 +187,29 @@ def test_conference_and_members(self): callId = (get_conference_response[0].active_members[0].call_id) self.callIdArray.append(callId) - # Get Conference Member - """Validate a GET Conference Member - """ get_conference_member_response = self.conference_api_instance.get_conference_member(BW_ACCOUNT_ID, conferenceId, callId, _return_http_data_only=False) assert_that(get_conference_member_response[1], 200) assert_that(get_conference_member_response[0].conference_id, conferenceId) assert_that(get_conference_member_response[0].call_id, callId) - # Update Conference Member - """Validate an Update Conference Member Request - """ - time.sleep(self.TEST_SLEEP) update_conference_member_response = self.conference_api_instance.update_conference_member(BW_ACCOUNT_ID, conferenceId, callId, self.testUpdateMember, _return_http_data_only=False) assert_that(update_conference_member_response[1], 204) - # Update Conference - """Validate an Update Conference Request - """ - time.sleep(self.TEST_SLEEP) update_conference_response = self.conference_api_instance.update_conference(BW_ACCOUNT_ID, conferenceId, self.testUpdateConf, _return_http_data_only=False) assert_that(update_conference_response[1], 204) - # Update Conference Bxml - """Validate an UpdateConferencelBxml Request - """ updateBxmlBody = 'This is a test bxml response' time.sleep(self.TEST_SLEEP) update_conference_bxml_response = self.conference_api_instance.update_conference_bxml(BW_ACCOUNT_ID, conferenceId, updateBxmlBody, _return_http_data_only=False) assert_that(update_conference_bxml_response[1], 204) - # Kill the call update_call = UpdateCall(state=CallStateEnum('completed')) self.calls_api_instance.update_call(BW_ACCOUNT_ID, call_id, update_call, _return_http_data_only=False) - # Conference Recordings Tests def test_conference_recordings(self) -> None: """ Tests a successful flow of creating a call with a recording. @@ -263,7 +219,6 @@ def test_conference_recordings(self) -> None: - download_conference_recording """ - # Create a conference and have it recorded answer_url = MANTECA_BASE_URL + "/bxml/joinConferencePause" (test_id, call_id) = self.create_conference(answer_url) @@ -272,29 +227,22 @@ def test_conference_recordings(self) -> None: assert_that(list_conferences_response[1], 200) conferenceId = (list_conferences_response[0][len(list_conferences_response[0])-1].id) - # Update Conference Bxml - """Validate an UpdateConferencelBxml Request to start and stop recording - """ updateBxmlBody = 'This should be a conference recording.' update_conference_bxml_response = self.conference_api_instance.update_conference_bxml(BW_ACCOUNT_ID, conferenceId, updateBxmlBody, _return_http_data_only=False) assert_that(update_conference_bxml_response[1], 204) time.sleep(self.TEST_SLEEP_LONG) - # List Conference Recordings Endpoint list_conference_recordings_response: List[ConferenceRecordingMetadata] = self.conference_api_instance.list_conference_recordings(BW_ACCOUNT_ID, conferenceId, _return_http_data_only=False) assert_that(list_conference_recordings_response[1],200) - # We should get back at least 1 recording conference_recordings = list_conference_recordings_response[0] assert_that(len(conference_recordings), greater_than(0)) - # Checks on the first recording first_recording: ConferenceRecordingMetadata = conference_recordings[0] self.validate_recording(first_recording, conferenceId) recording_id = first_recording.recording_id - # Get Single Recording Endpoint recording_response: ConferenceRecordingMetadata = self.conference_api_instance.get_conference_recording(BW_ACCOUNT_ID, conferenceId, recording_id, _return_http_data_only=False) assert_that(recording_response[1], 200) assert_that(recording_response[0].conference_id, conferenceId) @@ -303,181 +251,136 @@ def test_conference_recordings(self) -> None: self.validate_recording(recording_response[0], conferenceId) - # Download recording media recording_media_response = self.conference_api_instance.download_conference_recording(BW_ACCOUNT_ID, conferenceId, recording_id, _preload_content=False) conference_recording_media = recording_media_response.data def test_list_conferences_unauthorized(self) -> None: - """Validate an unauthorized (401) request - """ with self.assertRaises(UnauthorizedException) as context: self.unauthorized_api_instance.list_conferences(BW_ACCOUNT_ID, _return_http_data_only=False) self.assertApiException(context, UnauthorizedException, 401) def test_list_conferences_forbidden(self) -> None: - """Validate a forbidden (403) request - """ with self.assertRaises(ForbiddenException) as context: self.forbidden_api_instance.list_conferences(BW_ACCOUNT_ID, _return_http_data_only=False) self.assertApiException(context, ForbiddenException, 403) def test_get_conferences_unauthorized(self) -> None: - """Validate an unauthorized (401) Get Conference Information - """ with self.assertRaises(UnauthorizedException) as context: self.unauthorized_api_instance.get_conference(BW_ACCOUNT_ID, self.testConfId, _return_http_data_only=False) self.assertApiException(context, UnauthorizedException, 401) def test_get_conferences_forbidden(self) -> None: - """Validate a forbidden (403) Get Conference Information request - """ with self.assertRaises(ForbiddenException) as context: self.forbidden_api_instance.get_conference(BW_ACCOUNT_ID, self.testConfId, _return_http_data_only=False) self.assertApiException(context, ForbiddenException, 403) def test_get_conferences_not_found(self) -> None: - """Validate an invalid Get Conference Information Request due to a bad conferenceId - """ with self.assertRaises(NotFoundException) as context: self.conference_api_instance.get_conference(BW_ACCOUNT_ID, self.testConfId, _return_http_data_only=False) self.assertApiException(context, NotFoundException, 404) def test_get_conference_member_unauthorized(self) -> None: - """Validate an unauthorized (401) Get Conference Member - """ with self.assertRaises(UnauthorizedException) as context: self.unauthorized_api_instance.get_conference_member(BW_ACCOUNT_ID, self.testConfId, self.testMemberId, _return_http_data_only=False) self.assertApiException(context, UnauthorizedException, 401) def test_get_conference_member_forbidden(self) -> None: - """Validate an forbidden (403) Get Conference Member - """ with self.assertRaises(ForbiddenException) as context: self.forbidden_api_instance.get_conference_member(BW_ACCOUNT_ID, self.testConfId, self.testMemberId, _return_http_data_only=False) self.assertApiException(context, ForbiddenException, 403) def test_get_conference_member_not_found(self) -> None: - """Validate an not found (404) Get Conference Member - """ with self.assertRaises(NotFoundException) as context: self.conference_api_instance.get_conference_member(BW_ACCOUNT_ID, self.testConfId, self.testMemberId, _return_http_data_only=False) self.assertApiException(context, NotFoundException, 404) def test_list_conference_recordings_unauthorized(self) -> None: - """Validate an unauthorized (401) request - """ with self.assertRaises(UnauthorizedException) as context: self.unauthorized_api_instance.list_conference_recordings(BW_ACCOUNT_ID, self.testConfId, _return_http_data_only=False) self.assertApiException(context, UnauthorizedException, 401) def test_list_conference_recordings_forbidden(self) -> None: - """Validate a forbidden (403) request - """ with self.assertRaises(ForbiddenException) as context: self.forbidden_api_instance.list_conference_recordings(BW_ACCOUNT_ID, self.testConfId, _return_http_data_only=False) self.assertApiException(context, ForbiddenException, 403) def test_get_recording_unauthorized(self) -> None: - """Validate an unauthorized (401) Get Conference Recording - """ with self.assertRaises(UnauthorizedException) as context: self.unauthorized_api_instance.get_conference_recording(BW_ACCOUNT_ID, self.testConfId, self.testRecordId, _return_http_data_only=False) self.assertApiException(context, UnauthorizedException, 401) def test_get_recording_forbidden(self) -> None: - """Validate an forbidden (403) Get Conference Recording - """ with self.assertRaises(ForbiddenException) as context: self.forbidden_api_instance.get_conference_recording(BW_ACCOUNT_ID, self.testConfId, self.testRecordId, _return_http_data_only=False) self.assertApiException(context, ForbiddenException, 403) def test_get_conference_recording_not_found(self) -> None: - """Validate an not found (404) Get Conference Recording - """ with self.assertRaises(NotFoundException) as context: self.conference_api_instance.get_conference_member(BW_ACCOUNT_ID, self.testConfId, self.testRecordId, _return_http_data_only=False) self.assertApiException(context, NotFoundException, 404) def test_update_conference_unauthorized(self) -> None: - """Validate an unauthorized (401) Update Conference - """ with self.assertRaises(UnauthorizedException) as context: self.unauthorized_api_instance.update_conference(BW_ACCOUNT_ID, self.testConfId, self.testUpdateConf, _return_http_data_only=False) self.assertApiException(context, UnauthorizedException, 401) def test_update_conference_forbidden(self) -> None: - """Validate an forbidden (403) Update Conference - """ with self.assertRaises(ForbiddenException) as context: self.forbidden_api_instance.update_conference(BW_ACCOUNT_ID, self.testConfId, self.testUpdateConf, _return_http_data_only=False) self.assertApiException(context, ForbiddenException, 403) def test_update_conference_not_found(self) -> None: - """Validate an not found (404) Update Conference - """ with self.assertRaises(NotFoundException) as context: self.conference_api_instance.update_conference(BW_ACCOUNT_ID, self.testConfId, self.testUpdateConf, _return_http_data_only=False) self.assertApiException(context, NotFoundException, 404) def test_update_conference_bxml_unauthorized(self) -> None: - """Validate an unauthorized (401) Update Conference BXML - """ with self.assertRaises(UnauthorizedException) as context: self.unauthorized_api_instance.update_conference_bxml(BW_ACCOUNT_ID, self.testConfId, self.testUpdateBxml, _return_http_data_only=False) self.assertApiException(context, UnauthorizedException, 401) def test_update_conference_bxml_forbidden(self) -> None: - """Validate an forbidden (403) Update Conference BXML - """ with self.assertRaises(ForbiddenException) as context: self.forbidden_api_instance.update_conference_bxml(BW_ACCOUNT_ID, self.testConfId, self.testUpdateBxml, _return_http_data_only=False) self.assertApiException(context, ForbiddenException, 403) def test_update_conference_bxml_not_found(self) -> None: - """Validate an not found (404) Update Conference BXML - """ with self.assertRaises(NotFoundException) as context: self.conference_api_instance.update_conference_bxml(BW_ACCOUNT_ID, self.testConfId, self.testUpdateBxml, _return_http_data_only=False) self.assertApiException(context, NotFoundException, 404) def test_update_conference_member_unauthorized(self) -> None: - """Validate an unauthorized (401) Get Conference Member - """ with self.assertRaises(UnauthorizedException) as context: self.unauthorized_api_instance.update_conference_member(BW_ACCOUNT_ID, self.testConfId, self.testMemberId, self.testUpdateMember, _return_http_data_only=False) self.assertApiException(context, UnauthorizedException, 401) def test_update_conference_member_forbidden(self) -> None: - """Validate an forbidden (403) Get Conference Member - """ with self.assertRaises(ForbiddenException) as context: self.forbidden_api_instance.update_conference_member(BW_ACCOUNT_ID, self.testConfId, self.testMemberId, self.testUpdateMember, _return_http_data_only=False) self.assertApiException(context, ForbiddenException, 403) def test_update_conference_member_not_found(self) -> None: - """Validate an not found (404) Get Conference Member - """ with self.assertRaises(NotFoundException) as context: self.conference_api_instance.update_conference_member(BW_ACCOUNT_ID, self.testConfId, self.testMemberId, self.testUpdateMember, _return_http_data_only=False) From 462f6b95a6bc534b9eb0dd842686937f61434417 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 8 Aug 2022 13:21:00 -0400 Subject: [PATCH 6/6] Update test/integration/test_conferences.py Co-authored-by: AJ Rice <53190766+ajrice6713@users.noreply.github.com> --- test/integration/test_conferences.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/integration/test_conferences.py b/test/integration/test_conferences.py index 769defe8..e1a02c0a 100644 --- a/test/integration/test_conferences.py +++ b/test/integration/test_conferences.py @@ -6,7 +6,9 @@ import time from typing import List, Tuple import unittest + from hamcrest import assert_that, has_properties, not_none, instance_of, greater_than + import bandwidth from bandwidth.api import calls_api from bandwidth.model.create_call import CreateCall