edq-utils 0.1.9__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.
Files changed (83) hide show
  1. edq/__init__.py +5 -0
  2. edq/cli/__init__.py +0 -0
  3. edq/cli/config/__init__.py +3 -0
  4. edq/cli/config/list.py +69 -0
  5. edq/cli/http/__init__.py +3 -0
  6. edq/cli/http/exchange-server.py +71 -0
  7. edq/cli/http/send-exchange.py +45 -0
  8. edq/cli/http/verify-exchanges.py +38 -0
  9. edq/cli/testing/__init__.py +3 -0
  10. edq/cli/testing/cli-test.py +49 -0
  11. edq/cli/version.py +28 -0
  12. edq/core/__init__.py +0 -0
  13. edq/core/argparser.py +137 -0
  14. edq/core/argparser_test.py +124 -0
  15. edq/core/config.py +268 -0
  16. edq/core/config_test.py +1038 -0
  17. edq/core/log.py +101 -0
  18. edq/core/version.py +6 -0
  19. edq/procedure/__init__.py +0 -0
  20. edq/procedure/verify_exchanges.py +85 -0
  21. edq/py.typed +0 -0
  22. edq/testing/__init__.py +3 -0
  23. edq/testing/asserts.py +65 -0
  24. edq/testing/cli.py +360 -0
  25. edq/testing/cli_test.py +15 -0
  26. edq/testing/httpserver.py +578 -0
  27. edq/testing/httpserver_test.py +424 -0
  28. edq/testing/run.py +142 -0
  29. edq/testing/testdata/cli/data/configs/empty/edq-config.json +1 -0
  30. edq/testing/testdata/cli/data/configs/simple-1/edq-config.json +4 -0
  31. edq/testing/testdata/cli/data/configs/simple-2/edq-config.json +3 -0
  32. edq/testing/testdata/cli/data/configs/value-number/edq-config.json +3 -0
  33. edq/testing/testdata/cli/tests/config/list/config_list_base.txt +16 -0
  34. edq/testing/testdata/cli/tests/config/list/config_list_config_value_number.txt +10 -0
  35. edq/testing/testdata/cli/tests/config/list/config_list_ignore_config.txt +14 -0
  36. edq/testing/testdata/cli/tests/config/list/config_list_no_config.txt +8 -0
  37. edq/testing/testdata/cli/tests/config/list/config_list_show_origin.txt +13 -0
  38. edq/testing/testdata/cli/tests/config/list/config_list_skip_header.txt +10 -0
  39. edq/testing/testdata/cli/tests/help_base.txt +9 -0
  40. edq/testing/testdata/cli/tests/platform_skip.txt +5 -0
  41. edq/testing/testdata/cli/tests/version_base.txt +6 -0
  42. edq/testing/testdata/http/exchanges/simple.httpex.json +5 -0
  43. edq/testing/testdata/http/exchanges/simple_anchor.httpex.json +5 -0
  44. edq/testing/testdata/http/exchanges/simple_file.httpex.json +10 -0
  45. edq/testing/testdata/http/exchanges/simple_file_binary.httpex.json +10 -0
  46. edq/testing/testdata/http/exchanges/simple_file_get_params.httpex.json +14 -0
  47. edq/testing/testdata/http/exchanges/simple_file_multiple.httpex.json +13 -0
  48. edq/testing/testdata/http/exchanges/simple_file_name.httpex.json +11 -0
  49. edq/testing/testdata/http/exchanges/simple_file_post_multiple.httpex.json +13 -0
  50. edq/testing/testdata/http/exchanges/simple_file_post_params.httpex.json +14 -0
  51. edq/testing/testdata/http/exchanges/simple_headers.httpex.json +8 -0
  52. edq/testing/testdata/http/exchanges/simple_jsonresponse_dict.httpex.json +7 -0
  53. edq/testing/testdata/http/exchanges/simple_jsonresponse_list.httpex.json +9 -0
  54. edq/testing/testdata/http/exchanges/simple_params.httpex.json +9 -0
  55. edq/testing/testdata/http/exchanges/simple_post.httpex.json +5 -0
  56. edq/testing/testdata/http/exchanges/simple_post_params.httpex.json +9 -0
  57. edq/testing/testdata/http/exchanges/simple_post_urlparams.httpex.json +5 -0
  58. edq/testing/testdata/http/exchanges/simple_urlparams.httpex.json +5 -0
  59. edq/testing/testdata/http/exchanges/specialcase_listparams_explicit.httpex.json +8 -0
  60. edq/testing/testdata/http/exchanges/specialcase_listparams_url.httpex.json +5 -0
  61. edq/testing/testdata/http/files/a.txt +1 -0
  62. edq/testing/testdata/http/files/tiny.png +0 -0
  63. edq/testing/unittest.py +88 -0
  64. edq/util/__init__.py +3 -0
  65. edq/util/dirent.py +340 -0
  66. edq/util/dirent_test.py +979 -0
  67. edq/util/encoding.py +18 -0
  68. edq/util/hash.py +41 -0
  69. edq/util/hash_test.py +89 -0
  70. edq/util/json.py +180 -0
  71. edq/util/json_test.py +228 -0
  72. edq/util/net.py +1008 -0
  73. edq/util/parse.py +33 -0
  74. edq/util/pyimport.py +94 -0
  75. edq/util/pyimport_test.py +119 -0
  76. edq/util/reflection.py +32 -0
  77. edq/util/time.py +75 -0
  78. edq/util/time_test.py +107 -0
  79. edq_utils-0.1.9.dist-info/METADATA +164 -0
  80. edq_utils-0.1.9.dist-info/RECORD +83 -0
  81. edq_utils-0.1.9.dist-info/WHEEL +5 -0
  82. edq_utils-0.1.9.dist-info/licenses/LICENSE +21 -0
  83. edq_utils-0.1.9.dist-info/top_level.txt +1 -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 ADDED
@@ -0,0 +1,142 @@
1
+ """
2
+ Discover and run unit tests (via Python's unittest package)
3
+ that live in this project's base package
4
+ (the parent of this package).
5
+ """
6
+
7
+ import argparse
8
+ import os
9
+ import re
10
+ import sys
11
+ import typing
12
+ import unittest
13
+
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
+ """
22
+
23
+ def _collect_tests(suite: typing.Union[unittest.TestCase, unittest.suite.TestSuite]) -> typing.List[unittest.TestCase]:
24
+ """
25
+ Collect and return tests (unittest.TestCase) from the target directory.
26
+ """
27
+
28
+ if (isinstance(suite, unittest.TestCase)):
29
+ return [suite]
30
+
31
+ if (not isinstance(suite, unittest.suite.TestSuite)):
32
+ raise ValueError(f"Unknown test type: '{str(type(suite))}'.")
33
+
34
+ test_cases = []
35
+ for test_object in suite:
36
+ test_cases += _collect_tests(test_object)
37
+
38
+ return test_cases
39
+
40
+ def run(args: typing.Union[argparse.Namespace, typing.Dict[str, typing.Any], None] = None) -> int:
41
+ """
42
+ Discover and run unit tests.
43
+ This function may change your working directory.
44
+ Will raise if tests fail to load (e.g. syntax errors) and a suggested exit code otherwise.
45
+ """
46
+
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'])
55
+
56
+ if (args.get('path_additions', None) is not None):
57
+ for path in args['path_additions']:
58
+ sys.path.append(path)
59
+
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('.')
66
+
67
+ runner = unittest.TextTestRunner(verbosity = 3)
68
+ test_cases = []
69
+
70
+ for test_dir in test_dirs:
71
+ discovered_suite = unittest.TestLoader().discover(test_dir, pattern = args.get('filename_pattern', DEFAULT_TEST_FILENAME_PATTERN))
72
+ test_cases += _collect_tests(discovered_suite)
73
+
74
+ # Cleanup class functions from test classes.
75
+ # {class: function, ...}
76
+ cleanup_funcs = {}
77
+
78
+ tests = unittest.suite.TestSuite()
79
+
80
+ for test_case in test_cases:
81
+ if (isinstance(test_case, unittest.loader._FailedTest)): # type: ignore[attr-defined]
82
+ raise ValueError(f"Failed to load test: '{test_case.id()}'.") from test_case._exception
83
+
84
+ pattern = args.get('pattern', None)
85
+ if ((pattern is None) or re.search(pattern, test_case.id())):
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)
91
+ else:
92
+ print(f"Skipping {test_case.id()} because of match pattern.")
93
+
94
+ result = runner.run(tests)
95
+ faults = len(result.errors) + len(result.failures)
96
+
97
+ # Perform any cleanup.
98
+ for cleanup_func in cleanup_funcs.values():
99
+ cleanup_func()
100
+
101
+ if (not result.wasSuccessful()):
102
+ # This value will be used as an exit status, so it should not be larger than a byte.
103
+ # (Some higher values are used specially, so just keep it at a round number.)
104
+ return max(1, min(faults, 100))
105
+
106
+ return 0
107
+
108
+ def main() -> int:
109
+ """ Parse the CLI arguments and run tests. """
110
+
111
+ args = _get_parser().parse_args()
112
+ return run(args)
113
+
114
+ def _get_parser() -> argparse.ArgumentParser:
115
+ """ Build a parser for CLI arguments. """
116
+
117
+ parser = argparse.ArgumentParser(description = 'Run unit tests discovered in this project.')
118
+
119
+ parser.add_argument('--work-dir', dest = 'work_dir',
120
+ action = 'store', type = str, default = os.getcwd(),
121
+ help = 'Set the working directory when running tests, defaults to the current working directory (%(default)s).')
122
+
123
+ parser.add_argument('--tests-dir', dest = 'test_dirs',
124
+ action = 'append',
125
+ help = 'Discover tests from these directories. Defaults to the current directory.')
126
+
127
+ parser.add_argument('--add-path', dest = 'path_additions',
128
+ action = 'append',
129
+ help = 'If supplied, add this path the sys.path before running tests.')
130
+
131
+ parser.add_argument('--filename-pattern', dest = 'filename_pattern',
132
+ action = 'store', type = str, default = DEFAULT_TEST_FILENAME_PATTERN,
133
+ help = 'The pattern to use to find test files (default: %(default)s).')
134
+
135
+ parser.add_argument('pattern',
136
+ action = 'store', type = str, default = None, nargs = '?',
137
+ help = 'If supplied, only tests with names matching this pattern will be run. This pattern is used directly in re.search().')
138
+
139
+ return parser
140
+
141
+ if __name__ == '__main__':
142
+ sys.exit(main())
@@ -0,0 +1,4 @@
1
+ {
2
+ "user": "user@test.edulinq.org",
3
+ }
4
+
@@ -0,0 +1,3 @@
1
+ {
2
+ "api-key": "KEY-1234567890",
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "timeout": 60
3
+ }
@@ -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,10 @@
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/value-number/edq-config.json)",
6
+ ],
7
+ }
8
+ ---
9
+ Key Value
10
+ timeout 60
@@ -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,8 @@
1
+ {
2
+ "cli": "edq.cli.config.list",
3
+ "arguments": [
4
+ "--config-global", "__DATA_DIR__(configs/empty/edq-config.json)",
5
+ ],
6
+ }
7
+ ---
8
+ Key Value
@@ -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)
@@ -0,0 +1,10 @@
1
+ {
2
+ "cli": "edq.cli.config.list",
3
+ "arguments": [
4
+ "--config-global", "__DATA_DIR__(configs/empty/edq-config.json)",
5
+ "--config", "user=user@test.edulinq.org",
6
+ "--skip-header",
7
+ ],
8
+ }
9
+ ---
10
+ user user@test.edulinq.org
@@ -0,0 +1,9 @@
1
+ {
2
+ "cli": "edq.cli.version",
3
+ "arguments": [
4
+ '--help'
5
+ ],
6
+ "error": true
7
+ }
8
+ ---
9
+ builtins.SystemExit: 0
@@ -0,0 +1,5 @@
1
+ {
2
+ "cli": "package.that.does.not.exist.and.should.never.be.called",
3
+ "platform_skip": ".*"
4
+ }
5
+ ---
@@ -0,0 +1,6 @@
1
+ {
2
+ "cli": "edq.cli.version",
3
+ "arguments": [],
4
+ }
5
+ ---
6
+ <VERSION>
@@ -0,0 +1,5 @@
1
+ {
2
+ "method": "GET",
3
+ "url": "simple",
4
+ "response_body": "simple"
5
+ }