edq-utils 0.0.5__py3-none-any.whl → 0.0.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of edq-utils might be problematic. Click here for more details.
- edq/__init__.py +1 -1
- edq/cli/config/__init__.py +3 -0
- edq/cli/config/list.py +69 -0
- edq/cli/http/__init__.py +3 -0
- edq/cli/http/exchange-server.py +71 -0
- edq/cli/http/send-exchange.py +45 -0
- edq/cli/http/verify-exchanges.py +38 -0
- edq/cli/testing/__init__.py +3 -0
- edq/cli/testing/cli-test.py +8 -5
- edq/cli/version.py +2 -1
- edq/core/argparser.py +28 -3
- edq/core/config.py +268 -0
- edq/core/config_test.py +1038 -0
- edq/procedure/__init__.py +0 -0
- edq/procedure/verify_exchanges.py +85 -0
- edq/testing/asserts.py +0 -1
- edq/testing/cli.py +17 -1
- edq/testing/httpserver.py +553 -0
- edq/testing/httpserver_test.py +424 -0
- edq/testing/run.py +40 -10
- edq/testing/testdata/cli/data/configs/empty/edq-config.json +1 -0
- edq/testing/testdata/cli/data/configs/simple-1/edq-config.json +4 -0
- edq/testing/testdata/cli/data/configs/simple-2/edq-config.json +3 -0
- edq/testing/testdata/cli/data/configs/value-number/edq-config.json +3 -0
- edq/testing/testdata/cli/tests/config/list/config_list_base.txt +16 -0
- edq/testing/testdata/cli/tests/config/list/config_list_config_value_number.txt +10 -0
- edq/testing/testdata/cli/tests/config/list/config_list_ignore_config.txt +14 -0
- edq/testing/testdata/cli/tests/config/list/config_list_no_config.txt +8 -0
- edq/testing/testdata/cli/tests/config/list/config_list_show_origin.txt +13 -0
- edq/testing/testdata/cli/tests/config/list/config_list_skip_header.txt +10 -0
- edq/testing/testdata/http/exchanges/simple.httpex.json +5 -0
- edq/testing/testdata/http/exchanges/simple_anchor.httpex.json +5 -0
- edq/testing/testdata/http/exchanges/simple_file.httpex.json +10 -0
- edq/testing/testdata/http/exchanges/simple_file_binary.httpex.json +10 -0
- edq/testing/testdata/http/exchanges/simple_file_get_params.httpex.json +14 -0
- edq/testing/testdata/http/exchanges/simple_file_multiple.httpex.json +13 -0
- edq/testing/testdata/http/exchanges/simple_file_name.httpex.json +11 -0
- edq/testing/testdata/http/exchanges/simple_file_post_multiple.httpex.json +13 -0
- edq/testing/testdata/http/exchanges/simple_file_post_params.httpex.json +14 -0
- edq/testing/testdata/http/exchanges/simple_headers.httpex.json +8 -0
- edq/testing/testdata/http/exchanges/simple_jsonresponse_dict.httpex.json +7 -0
- edq/testing/testdata/http/exchanges/simple_jsonresponse_list.httpex.json +9 -0
- edq/testing/testdata/http/exchanges/simple_params.httpex.json +9 -0
- edq/testing/testdata/http/exchanges/simple_post.httpex.json +5 -0
- edq/testing/testdata/http/exchanges/simple_post_params.httpex.json +9 -0
- edq/testing/testdata/http/exchanges/simple_post_urlparams.httpex.json +5 -0
- edq/testing/testdata/http/exchanges/simple_urlparams.httpex.json +5 -0
- edq/testing/testdata/http/exchanges/specialcase_listparams_explicit.httpex.json +8 -0
- edq/testing/testdata/http/exchanges/specialcase_listparams_url.httpex.json +5 -0
- edq/testing/testdata/http/files/a.txt +1 -0
- edq/testing/testdata/http/files/tiny.png +0 -0
- edq/testing/unittest.py +12 -7
- edq/util/dirent.py +2 -0
- edq/util/json.py +21 -4
- edq/util/net.py +895 -0
- edq_utils-0.0.7.dist-info/METADATA +156 -0
- edq_utils-0.0.7.dist-info/RECORD +78 -0
- edq_utils-0.0.5.dist-info/METADATA +0 -63
- edq_utils-0.0.5.dist-info/RECORD +0 -34
- {edq_utils-0.0.5.dist-info → edq_utils-0.0.7.dist-info}/WHEEL +0 -0
- {edq_utils-0.0.5.dist-info → edq_utils-0.0.7.dist-info}/licenses/LICENSE +0 -0
- {edq_utils-0.0.5.dist-info → edq_utils-0.0.7.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import edq.testing.httpserver
|
|
4
|
+
import edq.util.net
|
|
5
|
+
|
|
6
|
+
THIS_DIR: str = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
|
|
7
|
+
TEST_EXCHANGES_DIR: str = os.path.join(THIS_DIR, "testdata", "http", 'exchanges')
|
|
8
|
+
|
|
9
|
+
class HTTPTestServerTest(edq.testing.httpserver.HTTPServerTest):
|
|
10
|
+
""" Test the HTTP test server. """
|
|
11
|
+
|
|
12
|
+
@classmethod
|
|
13
|
+
def setup_server(cls, server: edq.testing.httpserver.HTTPTestServer) -> None:
|
|
14
|
+
edq.testing.httpserver.HTTPServerTest.setup_server(server)
|
|
15
|
+
server.load_exchanges_dir(TEST_EXCHANGES_DIR)
|
|
16
|
+
|
|
17
|
+
def test_exchange_validation(self):
|
|
18
|
+
""" Test validation of exchanges. """
|
|
19
|
+
|
|
20
|
+
# [(kwargs, expected, error substring), ...]
|
|
21
|
+
test_cases = [
|
|
22
|
+
# Base
|
|
23
|
+
|
|
24
|
+
(
|
|
25
|
+
{
|
|
26
|
+
'method': 'GET',
|
|
27
|
+
'url': 'simple',
|
|
28
|
+
},
|
|
29
|
+
edq.util.net.HTTPExchange(
|
|
30
|
+
method = 'GET',
|
|
31
|
+
url = 'simple',
|
|
32
|
+
),
|
|
33
|
+
None,
|
|
34
|
+
),
|
|
35
|
+
|
|
36
|
+
(
|
|
37
|
+
{
|
|
38
|
+
'method': 'ZZZ',
|
|
39
|
+
'url': 'simple',
|
|
40
|
+
},
|
|
41
|
+
None,
|
|
42
|
+
'unknown/disallowed method',
|
|
43
|
+
),
|
|
44
|
+
|
|
45
|
+
(
|
|
46
|
+
{
|
|
47
|
+
},
|
|
48
|
+
None,
|
|
49
|
+
'URL path cannot be empty',
|
|
50
|
+
),
|
|
51
|
+
|
|
52
|
+
# URL Components
|
|
53
|
+
|
|
54
|
+
(
|
|
55
|
+
{
|
|
56
|
+
'url': 'foo?a=b#c',
|
|
57
|
+
},
|
|
58
|
+
edq.util.net.HTTPExchange(
|
|
59
|
+
url_path = 'foo',
|
|
60
|
+
url_anchor = 'c',
|
|
61
|
+
parameters = {
|
|
62
|
+
'a': 'b',
|
|
63
|
+
},
|
|
64
|
+
),
|
|
65
|
+
None,
|
|
66
|
+
),
|
|
67
|
+
|
|
68
|
+
(
|
|
69
|
+
{
|
|
70
|
+
'url': 'for',
|
|
71
|
+
'url_path': 'bar',
|
|
72
|
+
},
|
|
73
|
+
None,
|
|
74
|
+
'Mismatched URL paths',
|
|
75
|
+
),
|
|
76
|
+
|
|
77
|
+
(
|
|
78
|
+
{
|
|
79
|
+
'url': 'for#a',
|
|
80
|
+
'url_anchor': 'b',
|
|
81
|
+
},
|
|
82
|
+
None,
|
|
83
|
+
'Mismatched URL anchors',
|
|
84
|
+
),
|
|
85
|
+
|
|
86
|
+
# Files
|
|
87
|
+
|
|
88
|
+
(
|
|
89
|
+
{
|
|
90
|
+
'url': 'foo',
|
|
91
|
+
'files': [
|
|
92
|
+
{
|
|
93
|
+
'path': 'test.txt',
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
edq.util.net.HTTPExchange(
|
|
98
|
+
url_path = 'foo',
|
|
99
|
+
files = [
|
|
100
|
+
edq.util.net.FileInfo(name = 'test.txt', path = 'test.txt'),
|
|
101
|
+
]
|
|
102
|
+
),
|
|
103
|
+
None,
|
|
104
|
+
),
|
|
105
|
+
|
|
106
|
+
(
|
|
107
|
+
{
|
|
108
|
+
'url': 'foo',
|
|
109
|
+
'files': [
|
|
110
|
+
{
|
|
111
|
+
'content': '',
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
None,
|
|
116
|
+
'No name was provided for file',
|
|
117
|
+
),
|
|
118
|
+
|
|
119
|
+
(
|
|
120
|
+
{
|
|
121
|
+
'url': 'foo',
|
|
122
|
+
'files': [
|
|
123
|
+
{
|
|
124
|
+
'name': 'foo.txt',
|
|
125
|
+
}
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
None,
|
|
129
|
+
'File must have either path or content',
|
|
130
|
+
),
|
|
131
|
+
|
|
132
|
+
# JSON Response Body
|
|
133
|
+
|
|
134
|
+
(
|
|
135
|
+
{
|
|
136
|
+
'url': 'foo',
|
|
137
|
+
'response_body': {
|
|
138
|
+
'a': 1,
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
edq.util.net.HTTPExchange(
|
|
142
|
+
url_path = 'foo',
|
|
143
|
+
json_body = True,
|
|
144
|
+
response_body = '{"a": 1}',
|
|
145
|
+
),
|
|
146
|
+
None,
|
|
147
|
+
),
|
|
148
|
+
|
|
149
|
+
(
|
|
150
|
+
{
|
|
151
|
+
'url': 'foo',
|
|
152
|
+
'response_body': [{
|
|
153
|
+
'a': 1,
|
|
154
|
+
}]
|
|
155
|
+
},
|
|
156
|
+
edq.util.net.HTTPExchange(
|
|
157
|
+
url_path = 'foo',
|
|
158
|
+
json_body = True,
|
|
159
|
+
response_body = '[{"a": 1}]',
|
|
160
|
+
),
|
|
161
|
+
None,
|
|
162
|
+
),
|
|
163
|
+
]
|
|
164
|
+
|
|
165
|
+
for (i, test_case) in enumerate(test_cases):
|
|
166
|
+
(kwargs, expected, error_substring) = test_case
|
|
167
|
+
|
|
168
|
+
with self.subTest(msg = f"Case {i}:"):
|
|
169
|
+
try:
|
|
170
|
+
actual = edq.util.net.HTTPExchange(**kwargs)
|
|
171
|
+
except Exception as ex:
|
|
172
|
+
error_string = self.format_error_string(ex)
|
|
173
|
+
if (error_substring is None):
|
|
174
|
+
self.fail(f"Unexpected error: '{error_string}'.")
|
|
175
|
+
|
|
176
|
+
self.assertIn(error_substring, error_string, 'Error is not as expected.')
|
|
177
|
+
|
|
178
|
+
continue
|
|
179
|
+
|
|
180
|
+
if (error_substring is not None):
|
|
181
|
+
self.fail(f"Did not get expected error: '{error_substring}'.")
|
|
182
|
+
|
|
183
|
+
self.assertJSONDictEqual(expected, actual)
|
|
184
|
+
|
|
185
|
+
def test_exchange_matching_base(self):
|
|
186
|
+
""" Test matching exchanges against queries. """
|
|
187
|
+
|
|
188
|
+
# {<file basename no ext>: exchange, ...}
|
|
189
|
+
exchanges = {}
|
|
190
|
+
for exchange in self.get_server().get_exchanges():
|
|
191
|
+
key = os.path.basename(exchange.source_path).replace(edq.util.net.DEFAULT_HTTP_EXCHANGE_EXTENSION, '')
|
|
192
|
+
exchanges[key] = exchange
|
|
193
|
+
|
|
194
|
+
# [(target, query, match?, hint substring), ...]
|
|
195
|
+
test_cases = [
|
|
196
|
+
# Base
|
|
197
|
+
(
|
|
198
|
+
exchanges['simple'],
|
|
199
|
+
edq.util.net.HTTPExchange(
|
|
200
|
+
method = 'GET',
|
|
201
|
+
url = 'simple',
|
|
202
|
+
),
|
|
203
|
+
{},
|
|
204
|
+
True,
|
|
205
|
+
None,
|
|
206
|
+
),
|
|
207
|
+
|
|
208
|
+
# Params
|
|
209
|
+
(
|
|
210
|
+
exchanges['simple_params'],
|
|
211
|
+
edq.util.net.HTTPExchange(
|
|
212
|
+
method = 'GET',
|
|
213
|
+
url = 'simple',
|
|
214
|
+
parameters = {
|
|
215
|
+
'a': '1',
|
|
216
|
+
'b': '2',
|
|
217
|
+
},
|
|
218
|
+
),
|
|
219
|
+
{},
|
|
220
|
+
True,
|
|
221
|
+
None,
|
|
222
|
+
),
|
|
223
|
+
|
|
224
|
+
# Params, skip missing.
|
|
225
|
+
(
|
|
226
|
+
exchanges['simple'],
|
|
227
|
+
exchanges['simple_params'],
|
|
228
|
+
{
|
|
229
|
+
'params_to_skip': ['a', 'b'],
|
|
230
|
+
},
|
|
231
|
+
True,
|
|
232
|
+
None,
|
|
233
|
+
),
|
|
234
|
+
|
|
235
|
+
# Headers, skip missing.
|
|
236
|
+
(
|
|
237
|
+
exchanges['simple'],
|
|
238
|
+
exchanges['simple_headers'],
|
|
239
|
+
{
|
|
240
|
+
'headers_to_skip': ['a'],
|
|
241
|
+
},
|
|
242
|
+
True,
|
|
243
|
+
None,
|
|
244
|
+
),
|
|
245
|
+
|
|
246
|
+
# Param, skip unmatching.
|
|
247
|
+
(
|
|
248
|
+
exchanges['simple_params'],
|
|
249
|
+
edq.util.net.HTTPExchange(
|
|
250
|
+
method = 'GET',
|
|
251
|
+
url = 'simple',
|
|
252
|
+
parameters = {
|
|
253
|
+
'a': '1',
|
|
254
|
+
'b': 'ZZZ',
|
|
255
|
+
},
|
|
256
|
+
),
|
|
257
|
+
{
|
|
258
|
+
'params_to_skip': ['b'],
|
|
259
|
+
},
|
|
260
|
+
True,
|
|
261
|
+
None,
|
|
262
|
+
),
|
|
263
|
+
|
|
264
|
+
(
|
|
265
|
+
exchanges['simple'],
|
|
266
|
+
exchanges['simple_headers'],
|
|
267
|
+
{
|
|
268
|
+
'match_headers': False,
|
|
269
|
+
},
|
|
270
|
+
True,
|
|
271
|
+
None,
|
|
272
|
+
),
|
|
273
|
+
|
|
274
|
+
# Misses
|
|
275
|
+
|
|
276
|
+
# Missed method.
|
|
277
|
+
(
|
|
278
|
+
exchanges['simple'],
|
|
279
|
+
edq.util.net.HTTPExchange(
|
|
280
|
+
method = 'POST',
|
|
281
|
+
url = 'simple',
|
|
282
|
+
),
|
|
283
|
+
{},
|
|
284
|
+
False,
|
|
285
|
+
'method does not match',
|
|
286
|
+
),
|
|
287
|
+
(
|
|
288
|
+
exchanges['simple'],
|
|
289
|
+
exchanges['simple_post'],
|
|
290
|
+
{},
|
|
291
|
+
False,
|
|
292
|
+
'method does not match',
|
|
293
|
+
),
|
|
294
|
+
|
|
295
|
+
# Missed URL path.
|
|
296
|
+
(
|
|
297
|
+
exchanges['simple'],
|
|
298
|
+
edq.util.net.HTTPExchange(
|
|
299
|
+
method = 'GET',
|
|
300
|
+
url = 'ZZZ',
|
|
301
|
+
),
|
|
302
|
+
{},
|
|
303
|
+
False,
|
|
304
|
+
'URL path does not match',
|
|
305
|
+
),
|
|
306
|
+
|
|
307
|
+
# Missed URL anchor.
|
|
308
|
+
(
|
|
309
|
+
exchanges['simple'],
|
|
310
|
+
edq.util.net.HTTPExchange(
|
|
311
|
+
method = 'GET',
|
|
312
|
+
url = 'simple#1',
|
|
313
|
+
),
|
|
314
|
+
{},
|
|
315
|
+
False,
|
|
316
|
+
'URL anchor does not match',
|
|
317
|
+
),
|
|
318
|
+
|
|
319
|
+
# Missed number of params.
|
|
320
|
+
(
|
|
321
|
+
exchanges['simple'],
|
|
322
|
+
edq.util.net.HTTPExchange(
|
|
323
|
+
method = 'GET',
|
|
324
|
+
url = 'simple',
|
|
325
|
+
parameters = {'a': '1'},
|
|
326
|
+
),
|
|
327
|
+
{},
|
|
328
|
+
False,
|
|
329
|
+
'Parameter keys do not match',
|
|
330
|
+
),
|
|
331
|
+
(
|
|
332
|
+
exchanges['simple_post'],
|
|
333
|
+
exchanges['simple_post_params'],
|
|
334
|
+
{},
|
|
335
|
+
False,
|
|
336
|
+
'Parameter keys do not match',
|
|
337
|
+
),
|
|
338
|
+
(
|
|
339
|
+
exchanges['simple_post'],
|
|
340
|
+
exchanges['simple_post_urlparams'],
|
|
341
|
+
{},
|
|
342
|
+
False,
|
|
343
|
+
'Parameter keys do not match',
|
|
344
|
+
),
|
|
345
|
+
|
|
346
|
+
# Missed param value.
|
|
347
|
+
(
|
|
348
|
+
exchanges['simple_params'],
|
|
349
|
+
edq.util.net.HTTPExchange(
|
|
350
|
+
method = 'GET',
|
|
351
|
+
url = 'simple',
|
|
352
|
+
parameters = {
|
|
353
|
+
'a': '1',
|
|
354
|
+
'b': 'ZZZ',
|
|
355
|
+
},
|
|
356
|
+
),
|
|
357
|
+
{},
|
|
358
|
+
False,
|
|
359
|
+
"Parameter 'b' has a non-matching value",
|
|
360
|
+
),
|
|
361
|
+
|
|
362
|
+
# Missed number of headers.
|
|
363
|
+
(
|
|
364
|
+
exchanges['simple'],
|
|
365
|
+
exchanges['simple_headers'],
|
|
366
|
+
{
|
|
367
|
+
'match_headers': True,
|
|
368
|
+
},
|
|
369
|
+
False,
|
|
370
|
+
'Header keys do not match',
|
|
371
|
+
),
|
|
372
|
+
|
|
373
|
+
# Missed header value.
|
|
374
|
+
(
|
|
375
|
+
exchanges['simple_headers'],
|
|
376
|
+
edq.util.net.HTTPExchange(
|
|
377
|
+
method = 'GET',
|
|
378
|
+
url = 'simple',
|
|
379
|
+
headers = {
|
|
380
|
+
'a': 'ZZZ',
|
|
381
|
+
},
|
|
382
|
+
),
|
|
383
|
+
{
|
|
384
|
+
'match_headers': True,
|
|
385
|
+
},
|
|
386
|
+
False,
|
|
387
|
+
"Header 'a' has a non-matching value",
|
|
388
|
+
),
|
|
389
|
+
|
|
390
|
+
# Param, with skip.
|
|
391
|
+
(
|
|
392
|
+
exchanges['simple_params'],
|
|
393
|
+
edq.util.net.HTTPExchange(
|
|
394
|
+
method = 'GET',
|
|
395
|
+
url = 'simple',
|
|
396
|
+
parameters = {
|
|
397
|
+
'a': '1',
|
|
398
|
+
'b': 'ZZZ',
|
|
399
|
+
},
|
|
400
|
+
),
|
|
401
|
+
{
|
|
402
|
+
'params_to_skip': ['a'],
|
|
403
|
+
},
|
|
404
|
+
False,
|
|
405
|
+
"Parameter 'b' has a non-matching value",
|
|
406
|
+
),
|
|
407
|
+
]
|
|
408
|
+
|
|
409
|
+
for (i, test_case) in enumerate(test_cases):
|
|
410
|
+
(target, query, match_options, expected_match, hint_substring) = test_case
|
|
411
|
+
base_name = os.path.basename(target.source_path).replace(edq.util.net.DEFAULT_HTTP_EXCHANGE_EXTENSION, '')
|
|
412
|
+
|
|
413
|
+
with self.subTest(msg = f"Case {i} ('{base_name}'):"):
|
|
414
|
+
actual_match, hint = target.match(query, **match_options)
|
|
415
|
+
|
|
416
|
+
self.assertEqual(expected_match, actual_match, f"Match status does not agree (hint: '{hint}').")
|
|
417
|
+
|
|
418
|
+
if (hint is not None):
|
|
419
|
+
if (hint_substring is None):
|
|
420
|
+
self.fail(f"Unexpected hint: '{hint}'.")
|
|
421
|
+
|
|
422
|
+
self.assertIn(hint_substring, hint, 'Hint is not as expected.')
|
|
423
|
+
elif (hint_substring is not None):
|
|
424
|
+
self.fail(f"Did not get expected hint: '{hint_substring}'.")
|
edq/testing/run.py
CHANGED
|
@@ -12,6 +12,13 @@ import typing
|
|
|
12
12
|
import unittest
|
|
13
13
|
|
|
14
14
|
DEFAULT_TEST_FILENAME_PATTERN: str = '*_test.py'
|
|
15
|
+
""" The default pattern for test files. """
|
|
16
|
+
|
|
17
|
+
CLEANUP_FUNC_NAME: str = 'suite_cleanup'
|
|
18
|
+
"""
|
|
19
|
+
If a test class has a function with this name,
|
|
20
|
+
then the function will be run after the test suite finishes.
|
|
21
|
+
"""
|
|
15
22
|
|
|
16
23
|
def _collect_tests(suite: typing.Union[unittest.TestCase, unittest.suite.TestSuite]) -> typing.List[unittest.TestCase]:
|
|
17
24
|
"""
|
|
@@ -30,44 +37,67 @@ def _collect_tests(suite: typing.Union[unittest.TestCase, unittest.suite.TestSui
|
|
|
30
37
|
|
|
31
38
|
return test_cases
|
|
32
39
|
|
|
33
|
-
def run(args: argparse.Namespace) -> int:
|
|
40
|
+
def run(args: typing.Union[argparse.Namespace, typing.Dict[str, typing.Any], None] = None) -> int:
|
|
34
41
|
"""
|
|
35
42
|
Discover and run unit tests.
|
|
36
43
|
This function may change your working directory.
|
|
37
44
|
Will raise if tests fail to load (e.g. syntax errors) and a suggested exit code otherwise.
|
|
38
45
|
"""
|
|
39
46
|
|
|
40
|
-
if (args
|
|
41
|
-
|
|
47
|
+
if (args is None):
|
|
48
|
+
args = {}
|
|
49
|
+
|
|
50
|
+
if (not isinstance(args, dict)):
|
|
51
|
+
args = vars(args)
|
|
52
|
+
|
|
53
|
+
if (args.get('work_dir', None) is not None):
|
|
54
|
+
os.chdir(args['work_dir'])
|
|
42
55
|
|
|
43
|
-
if (args.path_additions is not None):
|
|
44
|
-
for path in args
|
|
56
|
+
if (args.get('path_additions', None) is not None):
|
|
57
|
+
for path in args['path_additions']:
|
|
45
58
|
sys.path.append(path)
|
|
46
59
|
|
|
47
|
-
|
|
48
|
-
|
|
60
|
+
test_dirs = args.get('test_dirs', None)
|
|
61
|
+
if (test_dirs is None):
|
|
62
|
+
test_dirs = []
|
|
63
|
+
|
|
64
|
+
if (len(test_dirs) == 0):
|
|
65
|
+
test_dirs.append('.')
|
|
49
66
|
|
|
50
67
|
runner = unittest.TextTestRunner(verbosity = 3)
|
|
51
68
|
test_cases = []
|
|
52
69
|
|
|
53
|
-
for test_dir in
|
|
54
|
-
discovered_suite = unittest.TestLoader().discover(test_dir, pattern = args.filename_pattern)
|
|
70
|
+
for test_dir in test_dirs:
|
|
71
|
+
discovered_suite = unittest.TestLoader().discover(test_dir, pattern = args.get('filename_pattern', DEFAULT_TEST_FILENAME_PATTERN))
|
|
55
72
|
test_cases += _collect_tests(discovered_suite)
|
|
56
73
|
|
|
74
|
+
# Cleanup class functions from test classes.
|
|
75
|
+
# {class: function, ...}
|
|
76
|
+
cleanup_funcs = {}
|
|
77
|
+
|
|
57
78
|
tests = unittest.suite.TestSuite()
|
|
58
79
|
|
|
59
80
|
for test_case in test_cases:
|
|
60
81
|
if (isinstance(test_case, unittest.loader._FailedTest)): # type: ignore[attr-defined]
|
|
61
82
|
raise ValueError(f"Failed to load test: '{test_case.id()}'.") from test_case._exception
|
|
62
83
|
|
|
63
|
-
|
|
84
|
+
pattern = args.get('pattern', None)
|
|
85
|
+
if ((pattern is None) or re.search(pattern, test_case.id())):
|
|
64
86
|
tests.addTest(test_case)
|
|
87
|
+
|
|
88
|
+
# Check for a cleanup function.
|
|
89
|
+
if (hasattr(test_case.__class__, CLEANUP_FUNC_NAME)):
|
|
90
|
+
cleanup_funcs[test_case.__class__] = getattr(test_case.__class__, CLEANUP_FUNC_NAME)
|
|
65
91
|
else:
|
|
66
92
|
print(f"Skipping {test_case.id()} because of match pattern.")
|
|
67
93
|
|
|
68
94
|
result = runner.run(tests)
|
|
69
95
|
faults = len(result.errors) + len(result.failures)
|
|
70
96
|
|
|
97
|
+
# Perform any cleanup.
|
|
98
|
+
for cleanup_func in cleanup_funcs.values():
|
|
99
|
+
cleanup_func()
|
|
100
|
+
|
|
71
101
|
if (not result.wasSuccessful()):
|
|
72
102
|
# This value will be used as an exit status, so it should not be larger than a byte.
|
|
73
103
|
# (Some higher values are used specially, so just keep it at a round number.)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cli": "edq.cli.config.list",
|
|
3
|
+
"arguments": [
|
|
4
|
+
"--config-global", "__DATA_DIR__(configs/empty/edq-config.json)",
|
|
5
|
+
"--config-file", "__DATA_DIR__(configs/simple-1/edq-config.json)",
|
|
6
|
+
"--config-file", "__DATA_DIR__(configs/simple-2/edq-config.json)",
|
|
7
|
+
"--config", "pass=password1234",
|
|
8
|
+
"--config", "server=http://test.edulinq.org"
|
|
9
|
+
],
|
|
10
|
+
}
|
|
11
|
+
---
|
|
12
|
+
Key Value
|
|
13
|
+
api-key KEY-1234567890
|
|
14
|
+
pass password1234
|
|
15
|
+
server http://test.edulinq.org
|
|
16
|
+
user user@test.edulinq.org
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cli": "edq.cli.config.list",
|
|
3
|
+
"arguments": [
|
|
4
|
+
"--ignore-config-option", "pass",
|
|
5
|
+
"--ignore-config-option", "api-key",
|
|
6
|
+
"--config-global", "__DATA_DIR__(configs/empty/edq-config.json)",
|
|
7
|
+
"--config-file", "__DATA_DIR__(configs/simple-1/edq-config.json)",
|
|
8
|
+
"--config-file", "__DATA_DIR__(configs/simple-2/edq-config.json)",
|
|
9
|
+
"--config", "pass=password1234",
|
|
10
|
+
],
|
|
11
|
+
}
|
|
12
|
+
---
|
|
13
|
+
Key Value
|
|
14
|
+
user user@test.edulinq.org
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cli": "edq.cli.config.list",
|
|
3
|
+
"arguments": [
|
|
4
|
+
"--config-file", "__DATA_DIR__(configs/simple-1/edq-config.json)",
|
|
5
|
+
"--config-global", "__DATA_DIR__(configs/empty/edq-config.json)",
|
|
6
|
+
"--config", "pass=password1234",
|
|
7
|
+
"--show-origin",
|
|
8
|
+
],
|
|
9
|
+
}
|
|
10
|
+
---
|
|
11
|
+
Key Value Origin
|
|
12
|
+
pass password1234 <cli argument>
|
|
13
|
+
user user@test.edulinq.org __ABS_DATA_DIR__(configs/simple-1/edq-config.json)
|