1
+ import inspect
1
2
from datetime import timezone , datetime
2
3
from behave import given , when , then
3
4
from decimal import Decimal
4
- from sinch import SinchClient
5
5
from sinch .domains .numbers .exceptions import NumberNotFoundException
6
6
from sinch .domains .numbers .models .available .activate_number_response import ActivateNumberResponse
7
7
from sinch .domains .numbers .models .available .rent_any_number_response import RentAnyNumberResponse
8
8
from sinch .domains .numbers .models .numbers import NotFoundError
9
9
10
+ def execute_sync_or_async (context ,call ):
11
+ """
12
+ Ensures proper execution of both synchronous and asynchronous calls.
13
+ - If the call is synchronous, it executes directly.
14
+ - If the call is a coroutine (async), it runs using asyncio
15
+ This abstracts away execution differences, allowing test steps to be written uniformly.
16
+ """
17
+ if call is None :
18
+ return None
19
+ if inspect .iscoroutine (call ):
20
+ # Reuse the single loop created in before_all
21
+ return context .loop .run_until_complete (call )
22
+ else :
23
+ return call
10
24
11
25
@given ('the Numbers service is available' )
12
26
def step_service_is_available (context ):
13
- sinch = SinchClient (
14
- project_id = 'tinyfrog-jump-high-over-lilypadbasin' ,
15
- key_id = 'keyId' ,
16
- key_secret = 'keySecret' ,
17
- )
18
- sinch .configuration .auth_origin = 'http://localhost:3011'
19
- sinch .configuration .numbers_origin = 'http://localhost:3013'
20
- context .sinch = sinch
27
+ """Ensures the Sinch client is initialized"""
28
+ assert hasattr (context , 'sinch' ) and context .sinch , 'Sinch client was not initialized'
21
29
22
30
@when ('I send a request to search for available phone numbers' )
23
31
def step_search_available_numbers (context ):
24
32
response = context .sinch .numbers .available .list (
25
33
region_code = 'US' ,
26
34
number_type = 'LOCAL'
27
35
)
28
- context .response = response
36
+ context .response = execute_sync_or_async ( context , response )
29
37
30
38
@then ('the response contains "{count}" available phone numbers' )
31
39
def step_check_available_numbers_count (context , count ):
@@ -50,11 +58,10 @@ def step_check_number_properties(context):
50
58
def step_check_number_availability (context , phone_number ):
51
59
try :
52
60
response = context .sinch .numbers .available .check_availability (phone_number )
53
- context .response = response
61
+ context .response = execute_sync_or_async ( context , response )
54
62
except NumberNotFoundException as e :
55
63
context .error = e
56
64
57
-
58
65
@then ('the response displays the phone number "{phone_number}" details' )
59
66
def step_validate_number_details (context , phone_number ):
60
67
data = context .response
@@ -69,8 +76,7 @@ def step_check_unavailable_number(context, phone_number):
69
76
70
77
@when ('I send a request to rent a number with some criteria' )
71
78
def step_rent_any_number (context ):
72
- sinch_client : SinchClient = context .sinch
73
- response = sinch_client .numbers .available .rent_any (
79
+ response = context .sinch .numbers .available .rent_any (
74
80
region_code = 'US' ,
75
81
type_ = 'LOCAL' ,
76
82
capabilities = ['SMS' , 'VOICE' ],
@@ -86,7 +92,7 @@ def step_rent_any_number(context):
86
92
'search_pattern' : 'END'
87
93
},
88
94
)
89
- context .response = response
95
+ context .response = execute_sync_or_async ( context , response )
90
96
91
97
@then ('the response contains a rented phone number' )
92
98
def step_validate_rented_number (context ):
@@ -100,15 +106,19 @@ def step_validate_rented_number(context):
100
106
assert data .money .currency_code == 'EUR'
101
107
assert data .money .amount == Decimal ('0.80' )
102
108
assert data .payment_interval_months == 1
103
- assert data .next_charge_date == datetime .fromisoformat ('2024-06-06T14:42:42.022227+00:00' ).astimezone (tz = timezone .utc )
109
+ assert data .next_charge_date == datetime .fromisoformat (
110
+ '2024-06-06T14:42:42.022227+00:00'
111
+ ).astimezone (tz = timezone .utc )
104
112
assert data .expire_at == None
105
113
assert data .callback_url == ''
106
114
assert data .sms_configuration .service_plan_id == ''
107
115
assert data .sms_configuration .campaign_id == ''
108
116
assert data .sms_configuration .scheduled_provisioning .service_plan_id == 'SpaceMonkeySquadron'
109
117
assert data .sms_configuration .scheduled_provisioning .campaign_id == ''
110
118
assert data .sms_configuration .scheduled_provisioning .status == 'WAITING'
111
- assert data .sms_configuration .scheduled_provisioning .last_updated_time == datetime .fromisoformat ('2024-06-06T14:42:42.596223+00:00' ).astimezone (tz = timezone .utc )
119
+ assert data .sms_configuration .scheduled_provisioning .last_updated_time == datetime .fromisoformat (
120
+ '2024-06-06T14:42:42.596223+00:00'
121
+ ).astimezone (tz = timezone .utc )
112
122
assert data .sms_configuration .scheduled_provisioning .error_codes == []
113
123
assert data .voice_configuration .type == 'RTC'
114
124
assert data .voice_configuration .app_id == ''
@@ -119,12 +129,13 @@ def step_validate_rented_number(context):
119
129
assert data .voice_configuration .scheduled_voice_provisioning .trunk_id == ''
120
130
assert data .voice_configuration .scheduled_voice_provisioning .service_id == ''
121
131
assert data .voice_configuration .scheduled_voice_provisioning .status == 'WAITING'
122
- assert data .voice_configuration .scheduled_voice_provisioning .last_updated_time == datetime .fromisoformat ('2024-06-06T14:42:42.604092+00:00' ).astimezone (tz = timezone .utc )
132
+ assert data .voice_configuration .scheduled_voice_provisioning .last_updated_time == datetime .fromisoformat (
133
+ '2024-06-06T14:42:42.604092+00:00'
134
+ ).astimezone (tz = timezone .utc )
123
135
124
136
@when ('I send a request to rent the phone number "{phone_number}"' )
125
137
def step_rent_specific_number (context , phone_number ):
126
- sinch_client : SinchClient = context .sinch
127
- response = sinch_client .numbers .available .activate (
138
+ response = context .sinch .numbers .available .activate (
128
139
phone_number = phone_number ,
129
140
sms_configuration = {
130
141
'service_plan_id' : 'SpaceMonkeySquadron' ,
@@ -133,7 +144,7 @@ def step_rent_specific_number(context, phone_number):
133
144
'app_id' : 'sunshine-rain-drop-very-beautifulday'
134
145
}
135
146
)
136
- context .response = response
147
+ context .response = execute_sync_or_async ( context , response )
137
148
138
149
@then ('the response contains this rented phone number "{phone_number}"' )
139
150
def step_validate_rented_specific_number (context , phone_number ):
@@ -142,9 +153,8 @@ def step_validate_rented_specific_number(context, phone_number):
142
153
143
154
@when ('I send a request to rent the unavailable phone number "{phone_number}"' )
144
155
def step_rent_unavailable_number (context , phone_number ):
145
- sinch_client : SinchClient = context .sinch
146
156
try :
147
- response = sinch_client .numbers .available .activate (
157
+ response = context . sinch .numbers .available .activate (
148
158
phone_number = phone_number ,
149
159
sms_configuration = {
150
160
'service_plan_id' : 'SpaceMonkeySquadron' ,
@@ -153,18 +163,18 @@ def step_rent_unavailable_number(context, phone_number):
153
163
'app_id' : 'sunshine-rain-drop-very-beautifulday'
154
164
}
155
165
)
156
- context .response = response
166
+ context .response = execute_sync_or_async ( context , response )
157
167
except NumberNotFoundException as e :
158
168
context .error = e
159
169
160
170
@when ("I send a request to list the phone numbers" )
161
171
def step_when_list_phone_numbers (context ):
162
- sinch_client : SinchClient = context .sinch
163
- response = sinch_client .numbers .active .list (
172
+ response = context .sinch .numbers .active .list (
164
173
region_code = 'US' ,
165
174
number_type = 'LOCAL'
166
175
)
167
176
# Get the first page
177
+ response = execute_sync_or_async (context , response )
168
178
context .response = response .content ()
169
179
170
180
@@ -175,15 +185,23 @@ def step_then_response_contains_x_phone_numbers(context, count):
175
185
176
186
@when ("I send a request to list all the phone numbers" )
177
187
def step_when_list_all_phone_numbers (context ):
178
- sinch_client : SinchClient = context .sinch
179
- response = sinch_client .numbers .active .list (
188
+ response = context .sinch .numbers .active .list (
180
189
region_code = 'US' ,
181
190
number_type = 'LOCAL'
182
191
)
183
192
active_numbers_list = []
184
193
185
- for number in response .iterator ():
186
- active_numbers_list .append (number )
194
+ response = execute_sync_or_async (context , response )
195
+ if inspect .isasyncgen (response .iterator ()):
196
+ async def collect_async_numbers ():
197
+ async for number in response .iterator ():
198
+ active_numbers_list .append (number )
199
+
200
+ execute_sync_or_async (context , collect_async_numbers ())
201
+ else :
202
+ for number in response .iterator ():
203
+ active_numbers_list .append (number )
204
+
187
205
context .active_numbers_list = active_numbers_list
188
206
189
207
@then ('the phone numbers list contains "{count}" phone numbers' )
0 commit comments