Skip to content

Commit 83b5457

Browse files
Merge pull request #103 from Bandwidth/DX-2728
DX-2728 Improved Logging
2 parents 81f21a0 + 98e96e6 commit 83b5457

File tree

9 files changed

+133
-39
lines changed

9 files changed

+133
-39
lines changed

.github/workflows/test.yaml

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ on:
55
- cron: "0 4 * * *"
66
pull_request:
77
workflow_dispatch:
8+
inputs:
9+
logLevel:
10+
description: Log level
11+
required: false
12+
default: WARNING
13+
type: choice
14+
options:
15+
- WARNING
16+
- DEBUG
817

918
jobs:
1019
deploy:
@@ -14,6 +23,28 @@ jobs:
1423
matrix:
1524
os: [windows-2022, windows-2019, ubuntu-18.04, ubuntu-20.04]
1625
python-version: [3.7, 3.8, 3.9, "3.10"]
26+
env:
27+
BW_ACCOUNT_ID: ${{ secrets.BW_ACCOUNT_ID }}
28+
BW_USERNAME: ${{ secrets.BW_USERNAME }}
29+
BW_PASSWORD: ${{ secrets.BW_PASSWORD }}
30+
BW_USERNAME_FORBIDDEN: ${{ secrets.BW_USERNAME_FORBIDDEN }}
31+
BW_PASSWORD_FORBIDDEN: ${{ secrets.BW_PASSWORD_FORBIDDEN }}
32+
BW_VOICE_APPLICATION_ID: ${{ secrets.BW_VOICE_APPLICATION_ID }}
33+
BW_MESSAGING_APPLICATION_ID: ${{ secrets.BW_MESSAGING_APPLICATION_ID }}
34+
BW_NUMBER: ${{ secrets.BW_NUMBER }}
35+
USER_NUMBER: ${{ secrets.USER_NUMBER }}
36+
VZW_NUMBER: ${{ secrets.VZW_NUMBER }}
37+
ATT_NUMBER: ${{ secrets.ATT_NUMBER }}
38+
T_MOBILE_NUMBER: ${{ secrets.T_MOBILE_NUMBER }}
39+
BASE_CALLBACK_URL: ${{ secrets.BASE_CALLBACK_URL }}
40+
PYTHON_VERSION: ${{ matrix.python-version }}
41+
OPERATING_SYSTEM: ${{ matrix.os }}
42+
MANTECA_ACTIVE_NUMBER: ${{ secrets.MANTECA_ACTIVE_NUMBER }}
43+
MANTECA_IDLE_NUMBER: ${{ secrets.MANTECA_IDLE_NUMBER }}
44+
MANTECA_BASE_URL: ${{ secrets.MANTECA_BASE_URL }}
45+
MANTECA_APPLICATION_ID: ${{ secrets.MANTECA_APPLICATION_ID }}
46+
BW_NUMBER_PROVIDER: ${{ secrets.BW_NUMBER_PROVIDER }}
47+
1748
steps:
1849
- name: Checkout
1950
uses: actions/checkout@v2
@@ -28,28 +59,17 @@ jobs:
2859
pip install -r requirements.txt
2960
pip install -r test-requirements.txt
3061
31-
- name: Test
32-
env:
33-
BW_ACCOUNT_ID: ${{ secrets.BW_ACCOUNT_ID }}
34-
BW_USERNAME: ${{ secrets.BW_USERNAME }}
35-
BW_PASSWORD: ${{ secrets.BW_PASSWORD }}
36-
BW_USERNAME_FORBIDDEN: ${{ secrets.BW_USERNAME_FORBIDDEN }}
37-
BW_PASSWORD_FORBIDDEN: ${{ secrets.BW_PASSWORD_FORBIDDEN }}
38-
BW_VOICE_APPLICATION_ID: ${{ secrets.BW_VOICE_APPLICATION_ID }}
39-
BW_MESSAGING_APPLICATION_ID: ${{ secrets.BW_MESSAGING_APPLICATION_ID }}
40-
BW_NUMBER: ${{ secrets.BW_NUMBER }}
41-
USER_NUMBER: ${{ secrets.USER_NUMBER }}
42-
VZW_NUMBER: ${{ secrets.VZW_NUMBER }}
43-
ATT_NUMBER: ${{ secrets.ATT_NUMBER }}
44-
T_MOBILE_NUMBER: ${{ secrets.T_MOBILE_NUMBER }}
45-
BASE_CALLBACK_URL: ${{ secrets.BASE_CALLBACK_URL }}
46-
PYTHON_VERSION: ${{ matrix.python-version }}
47-
OPERATING_SYSTEM: ${{ matrix.os }}
48-
MANTECA_ACTIVE_NUMBER: ${{ secrets.MANTECA_ACTIVE_NUMBER }}
49-
MANTECA_IDLE_NUMBER: ${{ secrets.MANTECA_IDLE_NUMBER }}
50-
MANTECA_BASE_URL: ${{ secrets.MANTECA_BASE_URL }}
51-
MANTECA_APPLICATION_ID: ${{ secrets.MANTECA_APPLICATION_ID }}
52-
run: pytest
62+
- name: Test at Debug Level
63+
if: ${{ inputs.logLevel == 'DEBUG' }}
64+
run: |
65+
echo "Log level: DEBUG"
66+
pytest -v --log-cli-level=DEBUG
67+
68+
- name: Test at Warning Level
69+
if: ${{( inputs.logLevel == null) || ( inputs.logLevel == 'WARNING') }}
70+
run: |
71+
echo "Log level: WARNING"
72+
pytest -v --log-cli-level=WARNING
5373
5474
- name: Notify Slack of Failures
5575
uses: Bandwidth/[email protected]

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ __pycache__/
1313
.Python
1414
env/
1515
build/
16+
logs/
1617
develop-eggs/
1718
dist/
1819
downloads/

setup.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
[flake8]
22
max-line-length=99
3+
[tool:pytest]
4+
log_cli = True
5+
junit_logging = system-out
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from typing import Any
2+
3+
from hamcrest.core.base_matcher import BaseMatcher
4+
from hamcrest.core.description import Description
5+
from hamcrest.core.matcher import Matcher
6+
7+
8+
class IsOneOfString(BaseMatcher[Any]):
9+
def __init__(self, equalsAnyString: Any) -> None:
10+
self.object = equalsAnyString
11+
12+
def _matches(self, item: Any) -> bool:
13+
if isinstance(self.object, list):
14+
return item in self.object
15+
return False
16+
17+
def describe_to(self, description: Description) -> None:
18+
nested_matcher = isinstance(self.object, Matcher)
19+
description.append_description_of("one of ")
20+
if nested_matcher:
21+
description.append_text("<")
22+
description.append_description_of(self.object)
23+
if nested_matcher:
24+
description.append_text(">")
25+
26+
27+
def is_one_of_string(obj: Any) -> Matcher[Any]:
28+
"""Matches expected string is in a given list.
29+
30+
:param obj: The object to compare against as the expected value.
31+
32+
This matcher compares the evaluated object to ``obj`` for a match."""
33+
return IsOneOfString(obj)

test/integration/test_calls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import time
88
import unittest
99
import datetime
10+
1011
from hamcrest import assert_that, has_properties, not_none, instance_of
1112

1213
import bandwidth

test/integration/test_media_api.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@
22
Integration test for Bandwidth's Media API
33
"""
44

5-
import os
65
import uuid
76
import filecmp
87
import unittest
8+
import logging
99

1010
import bandwidth
1111
from bandwidth.api import media_api
1212
from bandwidth.model.media import Media
1313
from bandwidth.exceptions import ApiException, NotFoundException
14+
from test.utils.env_variables import *
1415

1516

1617
class TestMedia(unittest.TestCase):
@@ -19,17 +20,16 @@ class TestMedia(unittest.TestCase):
1920

2021
def setUp(self) -> None:
2122
configuration = bandwidth.Configuration(
22-
username=os.environ['BW_USERNAME'],
23-
password=os.environ['BW_PASSWORD']
23+
username=BW_USERNAME,
24+
password=BW_PASSWORD
2425
)
25-
api_client = bandwidth.ApiClient(configuration)
26-
self.api_instance = media_api.MediaApi(api_client)
27-
self.account_id = os.environ['BW_ACCOUNT_ID']
26+
self.api_client = bandwidth.ApiClient(configuration)
27+
self.api_instance = media_api.MediaApi(self.api_client)
28+
self.account_id = BW_ACCOUNT_ID
2829
self.media_path = "./test/fixtures/"
2930
self.media_file = "python_cat.jpeg"
30-
self.media_id = os.environ['PYTHON_VERSION'] + "_" + os.environ['RUNNER_OS'] + "_" + str(uuid.uuid4()) + "_" + self.media_file
31+
self.media_id = PYTHON_VERSION + "_" + RUNNER_OS + "_" + str(uuid.uuid4()) + "_" + self.media_file
3132
self.download_file_path = "cat_download.jpeg"
32-
3333
self.original_file = open(self.media_path + self.media_file, "rb")
3434

3535
def uploadMedia(self) -> None:
@@ -48,6 +48,7 @@ def uploadMedia(self) -> None:
4848
_return_http_data_only=False
4949
)
5050

51+
logging.debug(api_response_with_http_info)
5152
self.assertEqual(api_response_with_http_info[1], 204)
5253

5354
# reopen the media file
@@ -73,6 +74,7 @@ def listMedia(self) -> None:
7374
self.assertEqual(api_response_with_http_info[1], 200)
7475

7576
api_response = self.api_instance.list_media(self.account_id)
77+
logging.debug("List Media" + str(api_response))
7678

7779
self.assertIs(type(api_response[0]), Media)
7880
pass
@@ -83,6 +85,7 @@ def getMedia(self) -> None:
8385
api_response_with_http_info = self.api_instance.get_media(
8486
self.account_id, self.media_id, _return_http_data_only=False)
8587

88+
logging.debug(api_response_with_http_info)
8689
self.assertEqual(api_response_with_http_info[1], 200)
8790

8891
api_response = self.api_instance.get_media(
@@ -100,7 +103,8 @@ def deleteMedia(self) -> None:
100103
"""
101104
api_response_with_http_info = self.api_instance.delete_media(
102105
self.account_id, self.media_id, _return_http_data_only=False)
103-
106+
107+
logging.debug(api_response_with_http_info)
104108
self.assertEqual(api_response_with_http_info[1], 204)
105109

106110
# returns void
@@ -114,6 +118,7 @@ def _steps(self) -> None:
114118
def test_steps(self) -> None:
115119
"""Test each function from _steps.call_order in specified order
116120
"""
121+
117122
for name, step in self._steps():
118123
step()
119124

test/integration/test_multi_factor_authentication.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import os
66
import time
77
import unittest
8+
import logging
89
from random import randint
910

1011
import bandwidth
@@ -16,6 +17,9 @@
1617
from bandwidth.model.voice_code_response import VoiceCodeResponse
1718
from bandwidth.exceptions import ApiException, UnauthorizedException, ForbiddenException
1819

20+
import hamcrest
21+
from hamcrest.core import *
22+
from hamcrest.library import *
1923

2024
class TestMultiFactorAuthentication(unittest.TestCase):
2125
"""Multi-Factor Authentication API integration Test
@@ -66,6 +70,14 @@ def assertAuthException(self, context: ApiException, expectedException: ApiExcep
6670
self.assertEqual(context.exception.status, expected_status_code)
6771
self.assertIs(type(context.exception.body), str)
6872

73+
# alternate option using hamcrest mathcers - reads like normal sentence, easy to read/speak & less brain overload
74+
assert_that(context.exception, is_(expectedException))
75+
76+
assert_that(context.exception, has_properties(
77+
'status', equal_to(expected_status_code),
78+
'body', not_none())
79+
)
80+
6981
def testSuccessfulMfaGenerateMessagingCodeRequest(self) -> None:
7082
"""Test a successful MFA messaging code request
7183
"""
@@ -114,6 +126,9 @@ def testSuccessfulMfaGVerifyCodeRequest(self) -> None:
114126
self.assertEqual(type(api_response.valid), bool)
115127
self.assertIs(api_response.valid, False)
116128

129+
# can be simplified
130+
assert_that(api_response, has_property('valid', False))
131+
117132
def testBadRequest(self) -> None:
118133
"""Validates a bad (400) request
119134
"""
@@ -162,13 +177,17 @@ def testRateLimit(self) -> None:
162177
expiration_time_in_minutes=3.0,
163178
code="123456",
164179
)
180+
call_count = 1
165181
while True:
166182
try:
183+
logging.debug('Testing rate limit, attempt #'+ str(call_count))
167184
api_response_with_http_info = self.api_instance.verify_code(
168185
self.account_id, verify_code_request
169186
)
187+
call_count += 1
170188
except ApiException as e:
171189
if e.status == 429:
190+
logging.debug('Got rate limit error')
172191
time.sleep(35)
173192
api_response_with_http_info = self.api_instance.verify_code(
174193
self.account_id, verify_code_request,
@@ -178,3 +197,6 @@ def testRateLimit(self) -> None:
178197
break
179198
else:
180199
raise e
200+
except:
201+
logging.error("Unexpected error while testing rate limit!")
202+
raise e

test/integration/test_phone_number_lookup.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
from bandwidth.model.tn_lookup_request_error import TnLookupRequestError
1818
from bandwidth.exceptions import ApiException, UnauthorizedException, ForbiddenException
1919

20+
from hamcrest.core import *
21+
from hamcrest.library import *
22+
23+
from .bwmatchers.one_of_string import is_one_of_string
24+
2025

2126
class TestPhoneNumberLookupIntegration(unittest.TestCase):
2227
"""Phone Number Lookup API integration test
@@ -39,19 +44,21 @@ def validateResult(self, result: LookupResult, e_164_format: str, line_provider:
3944
e_164_format (str): Phone number in e164 format ex: +19195551234
4045
line_provider (str): Line service provider ex: Verizon
4146
"""
42-
self.assertEqual(result.response_code, 0)
43-
self.assertIs(type(result.message), str)
44-
self.assertEqual(result.e_164_format, e_164_format)
45-
self.assertIs(type(result.formatted), str)
46-
self.assertTrue(result.country == "US" or result.country == "Canada")
47-
self.assertTrue(result.line_type == "Mobile" or result.line_type == "Fixed")
48-
self.assertIn(line_provider, result.line_provider)
4947

5048
# if result has 1 of these attributes it should have the other
5149
if result.get('mobile_country_code') or result.get('mobile_network_code'):
5250
self.assertIs(type(result.mobile_country_code), str)
5351
self.assertIs(type(result.mobile_network_code), str)
5452

53+
assert_that(result, has_properties(
54+
'response_code', 0,
55+
'e_164_format', e_164_format,
56+
'line_provider', contains_string(line_provider),
57+
'country', is_one_of_string(["US", "Canada"]),
58+
'line_type', is_one_of_string(["Mobile", "Fixed"])
59+
)
60+
)
61+
5562
def pollLookupStatus(self, request_id: str) -> LookupStatus:
5663
"""Poll LookupRequest for 'COMPLETE' status
5764
@@ -109,6 +116,7 @@ def testSuccessfulPhoneNumberLookup(self) -> None:
109116
# Create the lookup request and validate the response
110117
create_lookup_response: CreateLookupResponse = self.api_instance.create_lookup(
111118
self.account_id, lookup_request, _return_http_data_only=False)
119+
112120
self.assertEqual(create_lookup_response[1], 202)
113121
self.assertIs(type(create_lookup_response[0].status), LookupStatusEnum)
114122
self.assertEqual(create_lookup_response[0].status, LookupStatusEnum("IN_PROGRESS"))
@@ -135,7 +143,7 @@ def testSuccessfulPhoneNumberLookup(self) -> None:
135143

136144
# Check the information for a Bandwidth TN
137145
bw_lookup_result: LookupResult = get_lookup_status_response.result[0]
138-
self.validateResult(bw_lookup_result, os.environ['BW_NUMBER'], "Bandwidth")
146+
self.validateResult(bw_lookup_result, os.environ['BW_NUMBER'], os.environ['BW_NUMBER_PROVIDER'])
139147

140148
# Check the information for a Verizon TN
141149
vzw_lookup_result = get_lookup_status_response.result[1]

test/utils/env_variables.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
MANTECA_APPLICATION_ID = os.environ["MANTECA_APPLICATION_ID"]
1818
PYTHON_VERSION = os.environ["PYTHON_VERSION"]
1919
OPERATING_SYSTEM = os.environ["OPERATING_SYSTEM"]
20+
RUNNER_OS = os.environ['RUNNER_OS']
2021

2122
except KeyError as e:
2223
raise Exception("Environmental variables not found")

0 commit comments

Comments
 (0)