6
6
import traceback
7
7
from io import BytesIO
8
8
from pathlib import Path
9
- from typing import List , Dict
9
+ from typing import List , Dict , Optional
10
10
from urllib .parse import urljoin
11
11
12
12
import pycurl
18
18
)
19
19
from py3resttest .contenthandling import ContentHandler
20
20
from py3resttest .exception import HttpMethodError , BindError , ValidatorError
21
+ from py3resttest .generators import parse_generator
21
22
from py3resttest .utils import read_testcase_file , ChangeDir , Parser
22
23
from py3resttest .validators import parse_extractor , parse_validator , Failure
23
24
@@ -28,18 +29,41 @@ class TestCaseConfig:
28
29
29
30
def __init__ (self ):
30
31
self .__variable_binds_dict = {}
32
+ self .timeout = 60
33
+ self .print_bodies = False
34
+ self .retries = 0
35
+ self .generators = {}
31
36
32
37
@property
33
- def variable_binds_dict (self ):
38
+ def variable_binds (self ):
34
39
return self .__variable_binds_dict
35
40
36
- @variable_binds_dict .setter
37
- def variable_binds_dict (self , variable_dict ):
41
+ @variable_binds .setter
42
+ def variable_binds (self , variable_dict ):
38
43
if isinstance (variable_dict , dict ):
39
- self .__variable_binds_dict .update (variable_dict )
44
+ self .__variable_binds_dict .update (Parser . flatten_dictionaries ( variable_dict ) )
40
45
41
46
def parse (self , config_node ):
42
- return
47
+ node = Parser .flatten_lowercase_keys_dict (config_node )
48
+
49
+ for key , value in node .items ():
50
+ if key == 'timeout' :
51
+ self .timeout = int (value )
52
+ elif key == u'print_bodies' :
53
+ self .print_bodies = Parser .safe_to_bool (value )
54
+ elif key == 'retries' :
55
+ self .retries = int (value )
56
+ elif key == 'variable_binds' :
57
+ self .variable_binds = value
58
+ elif key == u'generators' :
59
+ if not isinstance (value , list ):
60
+ raise TypeError ("generators in config should defined as list(array)." )
61
+ flat = Parser .flatten_dictionaries (value )
62
+ gen_dict = {}
63
+ for generator_name , generator_config in flat .items ():
64
+ gen = parse_generator (generator_config )
65
+ gen_dict [str (generator_name )] = gen
66
+ self .generators = gen_dict
43
67
44
68
def __str__ (self ):
45
69
return json .dumps (self , default = Parser .safe_to_json )
@@ -53,20 +77,20 @@ def __init__(self):
53
77
self .__context = Context ()
54
78
self .__extract_binds = {}
55
79
self .__variable_binds = {}
80
+ self .config = TestCaseConfig ()
56
81
57
82
def parse (self , base_url : str , testcase_list : List , test_file = None , working_directory = None , variable_dict = None ):
58
83
59
- testcase_config = TestCaseConfig ()
60
-
61
84
if working_directory is None :
62
85
working_directory = os .path .abspath (os .getcwd ())
63
86
else :
64
87
working_directory = Path (working_directory )
65
88
if variable_dict is None :
66
- testcase_config .variable_binds = variable_dict
89
+ self . config .variable_binds = variable_dict
67
90
if test_file :
68
91
self .__testcase_file .add (test_file )
69
92
93
+ testcase_config_object = TestCaseConfig ()
70
94
for testcase_node in testcase_list :
71
95
if not isinstance (testcase_node , dict ):
72
96
logger .warning ("Skipping the configuration %s" % testcase_node )
@@ -99,11 +123,12 @@ def parse(self, base_url: str, testcase_list: List, test_file=None, working_dire
99
123
try :
100
124
group_object = TestSet .test_group_list_dict [__group_name ]
101
125
except KeyError :
102
- group_object = TestCaseGroup (TestCaseGroup .DEFAULT_GROUP )
126
+ group_object = TestCaseGroup (TestCaseGroup .DEFAULT_GROUP , config = testcase_config_object )
103
127
TestSet .test_group_list_dict [__group_name ] = group_object
104
128
testcase_object = TestCase (
105
129
base_url = base_url , extract_binds = group_object .extract_binds ,
106
- variable_binds = group_object .variable_binds , context = group_object .context
130
+ variable_binds = group_object .variable_binds , context = group_object .context ,
131
+ config = group_object .config
107
132
)
108
133
testcase_object .url = testcase_node [key ]
109
134
group_object .testcase_list = testcase_object
@@ -112,45 +137,49 @@ def parse(self, base_url: str, testcase_list: List, test_file=None, working_dire
112
137
with ChangeDir (working_directory ):
113
138
__group_name = None
114
139
for node_dict in sub_testcase_node :
115
- __group_name = node_dict . get ( TestCaseKeywords . group )
116
- if __group_name :
140
+ if __group_name is None :
141
+ __group_name = node_dict . get ( TestCaseKeywords . group )
117
142
break
143
+
118
144
__group_name = __group_name if __group_name else TestCaseGroup .DEFAULT_GROUP
119
145
try :
120
146
group_object = TestSet .test_group_list_dict [__group_name ]
121
147
except KeyError :
122
- group_object = TestCaseGroup (TestCaseGroup .DEFAULT_GROUP )
148
+ group_object = TestCaseGroup (TestCaseGroup .DEFAULT_GROUP , config = testcase_config_object )
123
149
TestSet .test_group_list_dict [__group_name ] = group_object
124
150
125
151
testcase_object = TestCase (
126
152
base_url = base_url , extract_binds = group_object .extract_binds ,
127
- variable_binds = group_object .variable_binds , context = group_object .context
153
+ variable_binds = group_object .variable_binds , context = group_object .context ,
154
+ config = group_object .config
128
155
)
129
156
testcase_object .parse (sub_testcase_node )
130
157
group_object .testcase_list = testcase_object
131
158
132
- elif key == YamlKeyWords .BENCHMARK :
133
- ...
134
-
135
159
elif key == YamlKeyWords .CONFIG :
136
- testcase_config_object = TestCaseConfig ()
137
160
testcase_config_object .parse (sub_testcase_node )
138
161
162
+ self .config = testcase_config_object
163
+
139
164
return
140
165
141
166
142
167
class TestCaseGroup :
143
168
DEFAULT_GROUP = "NO GROUP"
144
169
145
- def __init__ (self , name , context = None , extract_binds = None , variable_binds = None ):
170
+ def __init__ (self , name , context = None , extract_binds = None , variable_binds = None , config = None ):
146
171
self .__testcase_list = []
147
172
self .__benchmark_list = []
148
173
self .__config = None
174
+
149
175
self .__name = name
150
176
self .__testcase_file = set ()
151
177
self .__context = context if context else Context ()
152
178
self .__extract_binds = extract_binds if extract_binds else {}
153
179
self .__variable_binds = variable_binds if variable_binds else {}
180
+ self .__is_global = None
181
+
182
+ self .config = config
154
183
155
184
@property
156
185
def testcase_list (self ):
@@ -160,6 +189,15 @@ def testcase_list(self):
160
189
def testcase_list (self , testcase_object ):
161
190
self .__testcase_list .append (testcase_object )
162
191
192
+ @property
193
+ def is_global (self ):
194
+ return self .__is_global
195
+
196
+ @is_global .setter
197
+ def is_global (self , val ):
198
+ if self .__is_global is None :
199
+ self .__is_global = val
200
+
163
201
@property
164
202
def benchmark_list (self ):
165
203
return self .__benchmark_list
@@ -172,15 +210,34 @@ def benchmark_list(self, benchmark_objet):
172
210
def extract_binds (self ):
173
211
return self .__extract_binds
174
212
213
+ @extract_binds .setter
214
+ def extract_binds (self , extract_dict ):
215
+ self .__extract_binds .update (extract_dict )
216
+
175
217
@property
176
218
def variable_binds (self ):
177
219
return self .__variable_binds
178
220
221
+ @variable_binds .setter
222
+ def variable_binds (self , var_dict ):
223
+ if isinstance (var_dict , dict ):
224
+ self .__variable_binds .update (var_dict )
225
+
226
+ @property
227
+ def config (self ):
228
+ return self .__config
229
+
230
+ @config .setter
231
+ def config (self , config_obj : TestCaseConfig ):
232
+ self .__config = config_obj
233
+ self .variable_binds = config_obj .variable_binds
234
+
179
235
@property
180
236
def context (self ):
181
237
return self .__context
182
238
183
239
240
+
184
241
class TestResult :
185
242
186
243
def __init__ (self , body , status_code ):
@@ -213,10 +270,11 @@ class TestCase:
213
270
214
271
KEYWORD_DICT = {k : v for k , v in TestCaseKeywords .__dict__ .items () if not k .startswith ('__' )}
215
272
216
- def __init__ (self , base_url , extract_binds , variable_binds , context = None ):
273
+ def __init__ (self , base_url , extract_binds , variable_binds , context = None , config = None ):
217
274
self .__base_url = base_url
218
275
self .__url = None
219
276
self .__body = None
277
+ self .__config = config if config else TestCaseConfig ()
220
278
self .__auth_username = None
221
279
self .__auth_password = None
222
280
self .__delay = 0
@@ -245,10 +303,21 @@ def __init__(self, base_url, extract_binds, variable_binds, context=None):
245
303
246
304
self .templates = {}
247
305
self .result = None
306
+ self .config = config
248
307
249
308
def __str__ (self ):
250
309
return json .dumps (self , default = Parser .safe_to_json )
251
310
311
+ @property
312
+ def config (self ) -> Optional [TestCaseConfig ]:
313
+ return self .__config
314
+
315
+ @config .setter
316
+ def config (self , config_object : TestCaseConfig ):
317
+ if config_object :
318
+ self .variable_binds .update (config_object .variable_binds )
319
+ self .generator_binds .update (config_object .generators )
320
+
252
321
@property
253
322
def auth_username (self ):
254
323
return self .__auth_username
@@ -311,6 +380,10 @@ def url(self, value):
311
380
def generator_binds (self ):
312
381
return self .__generator_binds_dict
313
382
383
+ @property
384
+ def delay (self ):
385
+ return self .__delay
386
+
314
387
@generator_binds .setter
315
388
def generator_binds (self , value : Dict ):
316
389
binds_dict = Parser .flatten_dictionaries (value )
@@ -367,12 +440,17 @@ def headers(self) -> Dict:
367
440
header_dict = {}
368
441
for key , header in self .__header_dict .items ():
369
442
if isinstance (header , dict ):
443
+ if key == 'template' :
444
+ for k , v in header .items ():
445
+ templated_string = string .Template (v ).safe_substitute (context_values )
446
+ header_dict [k ] = templated_string
447
+ continue
370
448
templated_value = header .get ('template' )
371
449
if templated_value :
372
450
templated_string = string .Template (templated_value ).safe_substitute (context_values )
373
451
header_dict [key ] = templated_string
374
452
else :
375
- header_dict [ key ] = header
453
+ logger . warning ( "Skipping the header: %s. We don't support mapping as header" % header )
376
454
else :
377
455
header_dict [key ] = header
378
456
@@ -419,8 +497,7 @@ def realize_template(self, variable_name, context):
419
497
return None
420
498
if not context .get_values ():
421
499
return None
422
-
423
- val = self .templates [variable_name ].substitute (context .get_values ())
500
+ val = self .templates [variable_name ].safe_substitute (context .get_values ())
424
501
return val
425
502
426
503
def parse (self , testcase_dict ):
@@ -543,6 +620,8 @@ def run(self, context=None, timeout=None, curl_handler=None):
543
620
curl_handler .setopt (pycurl .WRITEFUNCTION , body_byte .write )
544
621
curl_handler .setopt (pycurl .HEADERFUNCTION , header_byte .write )
545
622
curl_handler .setopt (pycurl .VERBOSE , self .__verbose )
623
+ if self .config .timeout :
624
+ curl_handler .setopt (pycurl .CONNECTTIMEOUT , self .config .timeout )
546
625
547
626
if self .__ssl_insecure :
548
627
curl_handler .setopt (pycurl .SSL_VERIFYPEER , 0 )
@@ -641,3 +720,4 @@ def run(self, context=None, timeout=None, curl_handler=None):
641
720
self .__failure_list .append (
642
721
Failure (message = failure_message , details = None , failure_type = FAILURE_INVALID_RESPONSE )
643
722
)
723
+ curl_handler .close ()
0 commit comments