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
|
File without changes
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Verify that exchanges sent to a given server have the same response.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import glob
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
import typing
|
|
9
|
+
import unittest
|
|
10
|
+
|
|
11
|
+
import edq.testing.unittest
|
|
12
|
+
import edq.util.net
|
|
13
|
+
|
|
14
|
+
class ExchangeVerification(edq.testing.unittest.BaseTest):
|
|
15
|
+
""" Verify that exchanges match their content. """
|
|
16
|
+
|
|
17
|
+
def run(paths: typing.List[str], server: str) -> int:
|
|
18
|
+
""" Run exchange verification. """
|
|
19
|
+
|
|
20
|
+
exchange_paths = _collect_exchange_paths(paths)
|
|
21
|
+
|
|
22
|
+
_attach_tests(exchange_paths, server)
|
|
23
|
+
|
|
24
|
+
runner = unittest.TextTestRunner(verbosity = 2)
|
|
25
|
+
tests = unittest.defaultTestLoader.loadTestsFromTestCase(ExchangeVerification)
|
|
26
|
+
results = runner.run(tests)
|
|
27
|
+
|
|
28
|
+
return len(results.errors) + len(results.failures)
|
|
29
|
+
|
|
30
|
+
def _attach_tests(
|
|
31
|
+
paths: typing.List[str],
|
|
32
|
+
server: str,
|
|
33
|
+
extension: str = edq.util.net.DEFAULT_HTTP_EXCHANGE_EXTENSION,
|
|
34
|
+
) -> None:
|
|
35
|
+
""" Create tests for each path and attach them to the ExchangeVerification class. """
|
|
36
|
+
|
|
37
|
+
common_prefix = os.path.commonprefix(paths)
|
|
38
|
+
|
|
39
|
+
for path in paths:
|
|
40
|
+
name = path.replace(common_prefix, '').replace(extension, '')
|
|
41
|
+
test_name = f"test_verify_exchange__{name}"
|
|
42
|
+
|
|
43
|
+
setattr(ExchangeVerification, test_name, _get_test_method(path, server))
|
|
44
|
+
|
|
45
|
+
def _get_test_method(path: str, server: str,
|
|
46
|
+
match_options: typing.Union[typing.Dict[str, typing.Any], None] = None,
|
|
47
|
+
) -> typing.Callable:
|
|
48
|
+
""" Create a test method for the given path. """
|
|
49
|
+
|
|
50
|
+
if (match_options is None):
|
|
51
|
+
match_options = {}
|
|
52
|
+
|
|
53
|
+
def __method(self: edq.testing.unittest.BaseTest) -> None:
|
|
54
|
+
exchange = edq.util.net.HTTPExchange.from_path(path)
|
|
55
|
+
response, body = exchange.make_request(server, raise_for_status = False, **match_options)
|
|
56
|
+
|
|
57
|
+
match, hint = exchange.match_response(response, override_body = body, **match_options)
|
|
58
|
+
if (not match):
|
|
59
|
+
raise AssertionError(f"Exchange does not match: '{hint}'.")
|
|
60
|
+
|
|
61
|
+
return __method
|
|
62
|
+
|
|
63
|
+
def _collect_exchange_paths(
|
|
64
|
+
paths: typing.List[str],
|
|
65
|
+
extension: str = edq.util.net.DEFAULT_HTTP_EXCHANGE_EXTENSION,
|
|
66
|
+
) -> typing.List[str]:
|
|
67
|
+
""" Collect exchange files by matching extensions and descending dirs. """
|
|
68
|
+
|
|
69
|
+
final_paths = []
|
|
70
|
+
|
|
71
|
+
for path in paths:
|
|
72
|
+
path = os.path.abspath(path)
|
|
73
|
+
|
|
74
|
+
if (os.path.isfile(path)):
|
|
75
|
+
if (path.endswith(extension)):
|
|
76
|
+
final_paths.append(path)
|
|
77
|
+
else:
|
|
78
|
+
logging.warning("Path does not look like an exchange file: '%s'.", path)
|
|
79
|
+
else:
|
|
80
|
+
dirent_paths = glob.glob(os.path.join(path, "**", f"*{extension}"), recursive = True)
|
|
81
|
+
for dirent_path in dirent_paths:
|
|
82
|
+
final_paths.append(dirent_path)
|
|
83
|
+
|
|
84
|
+
final_paths.sort()
|
|
85
|
+
return final_paths
|
edq/testing/asserts.py
CHANGED
|
@@ -37,7 +37,6 @@ class StringComparisonAssertion(typing.Protocol):
|
|
|
37
37
|
Perform an assertion between expected and actual data.
|
|
38
38
|
"""
|
|
39
39
|
|
|
40
|
-
|
|
41
40
|
def content_equals_raw(test: edq.testing.unittest.BaseTest, expected: str, actual: str, **kwargs: typing.Any) -> None:
|
|
42
41
|
""" Check for equality using a simple string comparison. """
|
|
43
42
|
|
edq/testing/cli.py
CHANGED
|
@@ -9,6 +9,12 @@ and a second part which is the expected text output (stdout).
|
|
|
9
9
|
For the keys of the JSON section, see the defaulted arguments to CLITestInfo.
|
|
10
10
|
The options JSON will be splatted into CLITestInfo's constructor.
|
|
11
11
|
|
|
12
|
+
If a test class implements a method with the signature `modify_cli_test_info(self, test_info: CLITestInfo) -> None`,
|
|
13
|
+
then this method will be called with the test info right after the test info is read from disk.
|
|
14
|
+
|
|
15
|
+
If a test class implements a class method with the signature `get_test_basename(cls, path: str) -> str`,
|
|
16
|
+
then this method will be called to create the base name for the test case at the given path.
|
|
17
|
+
|
|
12
18
|
The expected output or any argument can reference the test's current temp or data dirs with `__TEMP_DIR__()` or `__DATA_DIR__()`, respectively.
|
|
13
19
|
An optional slash-separated path can be used as an argument to reference a path within those base directories.
|
|
14
20
|
For example, `__DATA_DIR__(foo/bar.txt)` references `bar.txt` inside the `foo` directory inside the data directory.
|
|
@@ -30,6 +36,7 @@ import edq.util.pyimport
|
|
|
30
36
|
|
|
31
37
|
TEST_CASE_SEP: str = '---'
|
|
32
38
|
DATA_DIR_ID: str = '__DATA_DIR__'
|
|
39
|
+
ABS_DATA_DIR_ID: str = '__ABS_DATA_DIR__'
|
|
33
40
|
TEMP_DIR_ID: str = '__TEMP_DIR__'
|
|
34
41
|
BASE_DIR_ID: str = '__BASE_DIR__'
|
|
35
42
|
|
|
@@ -175,6 +182,7 @@ class CLITestInfo:
|
|
|
175
182
|
(DATA_DIR_ID, self.data_dir),
|
|
176
183
|
(TEMP_DIR_ID, self.temp_dir),
|
|
177
184
|
(BASE_DIR_ID, self.base_dir),
|
|
185
|
+
(ABS_DATA_DIR_ID, os.path.abspath(self.data_dir)),
|
|
178
186
|
]
|
|
179
187
|
|
|
180
188
|
for (key, target_dir) in replacements:
|
|
@@ -251,6 +259,10 @@ def _get_test_method(test_name: str, path: str, data_dir: str) -> typing.Callabl
|
|
|
251
259
|
def __method(self: edq.testing.unittest.BaseTest) -> None:
|
|
252
260
|
test_info = CLITestInfo.load_path(path, test_name, getattr(self, BASE_TEMP_DIR_ATTR), data_dir)
|
|
253
261
|
|
|
262
|
+
# Allow the test class a chance to modify the test info before the test runs.
|
|
263
|
+
if (hasattr(self, 'modify_cli_test_info')):
|
|
264
|
+
self.modify_cli_test_info(test_info)
|
|
265
|
+
|
|
254
266
|
if (test_info.should_skip()):
|
|
255
267
|
self.skipTest(test_info.skip_message())
|
|
256
268
|
|
|
@@ -301,7 +313,11 @@ def add_test_paths(target_class: type, data_dir: str, paths: typing.List[str]) -
|
|
|
301
313
|
setattr(target_class, BASE_TEMP_DIR_ATTR, edq.util.dirent.get_temp_path('edq_cli_test_'))
|
|
302
314
|
|
|
303
315
|
for path in sorted(paths):
|
|
304
|
-
|
|
316
|
+
basename = os.path.splitext(os.path.basename(path))[0]
|
|
317
|
+
if (hasattr(target_class, 'get_test_basename')):
|
|
318
|
+
basename = getattr(target_class, 'get_test_basename')(path)
|
|
319
|
+
|
|
320
|
+
test_name = 'test_cli__' + basename
|
|
305
321
|
|
|
306
322
|
try:
|
|
307
323
|
setattr(target_class, test_name, _get_test_method(test_name, path, data_dir))
|