etlplus 0.4.8__tar.gz → 0.4.9__tar.gz
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.
- {etlplus-0.4.8/etlplus.egg-info → etlplus-0.4.9}/PKG-INFO +1 -1
- {etlplus-0.4.8 → etlplus-0.4.9/etlplus.egg-info}/PKG-INFO +1 -1
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/cli/test_u_cli_app.py +207 -186
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/cli/test_u_cli_handlers.py +58 -35
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/cli/test_u_cli_main.py +88 -68
- {etlplus-0.4.8 → etlplus-0.4.9}/.coveragerc +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/.editorconfig +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/.gitattributes +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/.github/actions/python-bootstrap/action.yml +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/.github/workflows/ci.yml +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/.gitignore +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/.pre-commit-config.yaml +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/.ruff.toml +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/CODE_OF_CONDUCT.md +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/CONTRIBUTING.md +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/DEMO.md +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/LICENSE +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/Makefile +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/README.md +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/REFERENCES.md +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/docs/pipeline-guide.md +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/docs/snippets/installation_version.md +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/__init__.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/__main__.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/__version__.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/api/README.md +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/api/__init__.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/api/auth.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/api/config.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/api/endpoint_client.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/api/errors.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/api/pagination/__init__.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/api/pagination/client.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/api/pagination/config.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/api/pagination/paginator.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/api/rate_limiting/__init__.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/api/rate_limiting/config.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/api/rate_limiting/rate_limiter.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/api/request_manager.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/api/retry_manager.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/api/transport.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/api/types.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/cli/__init__.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/cli/app.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/cli/handlers.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/cli/main.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/config/__init__.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/config/connector.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/config/jobs.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/config/pipeline.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/config/profile.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/config/types.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/config/utils.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/enums.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/extract.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/file.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/load.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/mixins.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/py.typed +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/run.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/run_helpers.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/transform.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/types.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/utils.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/validate.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/validation/__init__.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus/validation/utils.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus.egg-info/SOURCES.txt +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus.egg-info/dependency_links.txt +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus.egg-info/entry_points.txt +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus.egg-info/requires.txt +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/etlplus.egg-info/top_level.txt +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/examples/README.md +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/examples/configs/pipeline.yml +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/examples/data/sample.csv +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/examples/data/sample.json +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/examples/data/sample.xml +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/examples/data/sample.xsd +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/examples/data/sample.yaml +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/examples/quickstart_python.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/pyproject.toml +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/pytest.ini +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/setup.cfg +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/setup.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/__init__.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/conftest.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/integration/conftest.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/integration/test_i_cli.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/integration/test_i_examples_data_parity.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/integration/test_i_pagination_strategy.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/integration/test_i_pipeline_smoke.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/integration/test_i_pipeline_yaml_load.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/integration/test_i_run.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/integration/test_i_run_profile_pagination_defaults.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/integration/test_i_run_profile_rate_limit_defaults.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/api/conftest.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/api/test_u_auth.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/api/test_u_config.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/api/test_u_endpoint_client.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/api/test_u_mocks.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/api/test_u_pagination_client.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/api/test_u_pagination_config.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/api/test_u_paginator.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/api/test_u_rate_limit_config.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/api/test_u_rate_limiter.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/api/test_u_request_manager.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/api/test_u_retry_manager.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/api/test_u_transport.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/api/test_u_types.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/cli/conftest.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/config/test_u_config_utils.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/config/test_u_connector.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/config/test_u_jobs.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/config/test_u_pipeline.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/conftest.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/test_u_enums.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/test_u_extract.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/test_u_file.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/test_u_load.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/test_u_main.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/test_u_mixins.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/test_u_run.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/test_u_run_helpers.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/test_u_transform.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/test_u_utils.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/test_u_validate.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/test_u_version.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tests/unit/validation/test_u_validation_utils.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tools/run_pipeline.py +0 -0
- {etlplus-0.4.8 → etlplus-0.4.9}/tools/update_demo_snippets.py +0 -0
|
@@ -14,6 +14,7 @@ from unittest.mock import Mock
|
|
|
14
14
|
import pytest
|
|
15
15
|
import typer
|
|
16
16
|
from typer.testing import CliRunner
|
|
17
|
+
from typer.testing import Result
|
|
17
18
|
|
|
18
19
|
import etlplus
|
|
19
20
|
import etlplus.cli.app as cli_app_module
|
|
@@ -24,7 +25,8 @@ from etlplus.cli.app import app as cli_app
|
|
|
24
25
|
|
|
25
26
|
pytestmark = pytest.mark.unit
|
|
26
27
|
|
|
27
|
-
CaptureHelper = Callable[[str], tuple[dict[str,
|
|
28
|
+
CaptureHelper = Callable[[str], tuple[dict[str, argparse.Namespace], Mock]]
|
|
29
|
+
InvokeCli = Callable[..., tuple[Result, argparse.Namespace, Mock]]
|
|
28
30
|
|
|
29
31
|
|
|
30
32
|
# SECTION: FIXTURES ========================================================= #
|
|
@@ -48,8 +50,8 @@ def capture_cmd_fixture(
|
|
|
48
50
|
passed to the handler.
|
|
49
51
|
"""
|
|
50
52
|
|
|
51
|
-
def _capture(name: str) -> tuple[dict[str,
|
|
52
|
-
captured: dict[str,
|
|
53
|
+
def _capture(name: str) -> tuple[dict[str, argparse.Namespace], Mock]:
|
|
54
|
+
captured: dict[str, argparse.Namespace] = {}
|
|
53
55
|
|
|
54
56
|
def _fake(ns: argparse.Namespace) -> int:
|
|
55
57
|
captured['ns'] = ns
|
|
@@ -62,27 +64,71 @@ def capture_cmd_fixture(
|
|
|
62
64
|
return _capture
|
|
63
65
|
|
|
64
66
|
|
|
67
|
+
@pytest.fixture(name='invoke_cli')
|
|
68
|
+
def invoke_cli_fixture(
|
|
69
|
+
runner: CliRunner,
|
|
70
|
+
capture_cmd: CaptureHelper,
|
|
71
|
+
) -> InvokeCli:
|
|
72
|
+
"""Invoke the Typer CLI and capture the patched handler call.
|
|
73
|
+
|
|
74
|
+
Parameters
|
|
75
|
+
----------
|
|
76
|
+
runner : CliRunner
|
|
77
|
+
Typer CLI runner fixture.
|
|
78
|
+
capture_cmd : CaptureHelper
|
|
79
|
+
Helper that patches handler bindings and records the namespace.
|
|
80
|
+
|
|
81
|
+
Returns
|
|
82
|
+
-------
|
|
83
|
+
InvokeCli
|
|
84
|
+
Callable that invokes the CLI, returning the ``Result``, handler
|
|
85
|
+
namespace, and mock used for assertion.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
def _invoke(
|
|
89
|
+
handler: str,
|
|
90
|
+
*args: str,
|
|
91
|
+
) -> tuple[Result, argparse.Namespace, Mock]:
|
|
92
|
+
captured, cmd = capture_cmd(handler)
|
|
93
|
+
result = runner.invoke(cli_app, list(args))
|
|
94
|
+
return result, captured['ns'], cmd
|
|
95
|
+
|
|
96
|
+
return _invoke
|
|
97
|
+
|
|
98
|
+
|
|
65
99
|
# SECTION: TESTS ============================================================ #
|
|
66
100
|
|
|
67
101
|
|
|
68
102
|
class TestCliAppInternalHelpers:
|
|
69
103
|
"""Unit tests for private helper utilities."""
|
|
70
104
|
|
|
71
|
-
|
|
72
|
-
|
|
105
|
+
@pytest.mark.parametrize(
|
|
106
|
+
('raw', 'expected'),
|
|
107
|
+
(
|
|
108
|
+
('-', 'file'),
|
|
109
|
+
('https://example.com/data.json', 'api'),
|
|
110
|
+
('postgres://user@host/db', 'database'),
|
|
111
|
+
),
|
|
112
|
+
)
|
|
113
|
+
def test_infer_resource_type_variants(
|
|
114
|
+
self,
|
|
115
|
+
raw: str,
|
|
116
|
+
expected: str,
|
|
117
|
+
) -> None:
|
|
118
|
+
"""
|
|
119
|
+
Test that :func:`_infer_resource_type` classifies common resource
|
|
120
|
+
inputs.
|
|
121
|
+
"""
|
|
73
122
|
# pylint: disable=protected-access
|
|
74
123
|
|
|
75
|
-
assert cli_app_module._infer_resource_type(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
cli_app_module._infer_resource_type('postgres://user@host/db')
|
|
84
|
-
== 'database'
|
|
85
|
-
)
|
|
124
|
+
assert cli_app_module._infer_resource_type(raw) == expected
|
|
125
|
+
|
|
126
|
+
def test_infer_resource_type_file_path(self, tmp_path: Path) -> None:
|
|
127
|
+
"""
|
|
128
|
+
Test that :func:`_infer_resource_type` detects local files via
|
|
129
|
+
extension parsing.
|
|
130
|
+
"""
|
|
131
|
+
# pylint: disable=protected-access
|
|
86
132
|
|
|
87
133
|
path = tmp_path / 'payload.csv'
|
|
88
134
|
path.write_text('a,b\n1,2\n', encoding='utf-8')
|
|
@@ -97,33 +143,40 @@ class TestCliAppInternalHelpers:
|
|
|
97
143
|
with pytest.raises(ValueError):
|
|
98
144
|
cli_app_module._infer_resource_type('unknown-resource')
|
|
99
145
|
|
|
100
|
-
|
|
101
|
-
|
|
146
|
+
@pytest.mark.parametrize(
|
|
147
|
+
('choice', 'expected'),
|
|
148
|
+
((None, None), ('json', 'json')),
|
|
149
|
+
)
|
|
150
|
+
def test_optional_choice_passthrough_and_validation(
|
|
151
|
+
self,
|
|
152
|
+
choice: str | None,
|
|
153
|
+
expected: str | None,
|
|
154
|
+
) -> None:
|
|
155
|
+
"""
|
|
156
|
+
Test that :func:`_optional_choice` preserves ``None`` and normalizes
|
|
157
|
+
valid values.
|
|
158
|
+
"""
|
|
102
159
|
# pylint: disable=protected-access
|
|
103
160
|
|
|
104
161
|
assert (
|
|
105
162
|
cli_app_module._optional_choice(
|
|
106
|
-
|
|
163
|
+
choice,
|
|
107
164
|
{'json', 'csv'},
|
|
108
165
|
label='format',
|
|
109
166
|
)
|
|
110
|
-
|
|
167
|
+
== expected
|
|
111
168
|
)
|
|
112
169
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
label='format',
|
|
118
|
-
)
|
|
119
|
-
== 'json'
|
|
120
|
-
)
|
|
170
|
+
@pytest.mark.parametrize('invalid', ('yaml', 'parquet'))
|
|
171
|
+
def test_optional_choice_rejects_invalid(self, invalid: str) -> None:
|
|
172
|
+
"""Test that invalid choices raise :class:`typer.BadParameter`."""
|
|
173
|
+
# pylint: disable=protected-access
|
|
121
174
|
|
|
122
175
|
with pytest.raises(typer.BadParameter):
|
|
123
|
-
cli_app_module._optional_choice(
|
|
176
|
+
cli_app_module._optional_choice(invalid, {'json'}, label='format')
|
|
124
177
|
|
|
125
178
|
def test_stateful_namespace_includes_cli_flags(self) -> None:
|
|
126
|
-
"""
|
|
179
|
+
"""Test that state flags propagate into handler namespaces."""
|
|
127
180
|
# pylint: disable=protected-access
|
|
128
181
|
|
|
129
182
|
state = cli_app_module.CliState(pretty=False, quiet=True, verbose=True)
|
|
@@ -144,25 +197,23 @@ class TestTyperCliAppWiring:
|
|
|
144
197
|
|
|
145
198
|
def test_extract_default_format_maps_namespace(
|
|
146
199
|
self,
|
|
147
|
-
|
|
148
|
-
capture_cmd: CaptureHelper,
|
|
200
|
+
invoke_cli: InvokeCli,
|
|
149
201
|
) -> None:
|
|
150
202
|
"""
|
|
151
|
-
Test that
|
|
152
|
-
|
|
203
|
+
Test that ``extract`` defaults to JSON and marks the data format as
|
|
204
|
+
implicit.
|
|
153
205
|
"""
|
|
154
206
|
# pylint: disable=protected-access
|
|
155
207
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
208
|
+
result, ns, cmd = invoke_cli(
|
|
209
|
+
'cmd_extract',
|
|
210
|
+
'extract',
|
|
211
|
+
'/path/to/file.json',
|
|
160
212
|
)
|
|
161
213
|
|
|
162
214
|
assert result.exit_code == 0
|
|
163
215
|
cmd.assert_called_once()
|
|
164
216
|
|
|
165
|
-
ns = captured['ns']
|
|
166
217
|
assert isinstance(ns, argparse.Namespace)
|
|
167
218
|
assert ns.command == 'extract'
|
|
168
219
|
assert ns.source_type == 'file'
|
|
@@ -172,57 +223,51 @@ class TestTyperCliAppWiring:
|
|
|
172
223
|
|
|
173
224
|
def test_extract_explicit_format_maps_namespace(
|
|
174
225
|
self,
|
|
175
|
-
|
|
176
|
-
capture_cmd: CaptureHelper,
|
|
226
|
+
invoke_cli: InvokeCli,
|
|
177
227
|
) -> None:
|
|
178
228
|
"""
|
|
179
|
-
Test that
|
|
180
|
-
when provided.
|
|
229
|
+
Test that ``extract`` marks the data format as explicit when provided.
|
|
181
230
|
"""
|
|
182
231
|
# pylint: disable=protected-access
|
|
183
232
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
233
|
+
result, ns, cmd = invoke_cli(
|
|
234
|
+
'cmd_extract',
|
|
235
|
+
'extract',
|
|
236
|
+
'/path/to/file.csv',
|
|
237
|
+
'--source-format',
|
|
238
|
+
'csv',
|
|
188
239
|
)
|
|
189
240
|
|
|
190
241
|
assert result.exit_code == 0
|
|
191
242
|
cmd.assert_called_once()
|
|
192
243
|
|
|
193
|
-
ns = captured['ns']
|
|
194
244
|
assert isinstance(ns, argparse.Namespace)
|
|
195
245
|
assert ns.format == 'csv'
|
|
196
246
|
assert ns._format_explicit is True
|
|
197
247
|
|
|
198
248
|
def test_extract_from_option_sets_source_type_and_state_flags(
|
|
199
249
|
self,
|
|
200
|
-
|
|
201
|
-
capture_cmd: CaptureHelper,
|
|
250
|
+
invoke_cli: InvokeCli,
|
|
202
251
|
) -> None:
|
|
203
252
|
"""
|
|
204
|
-
Test that
|
|
205
|
-
|
|
253
|
+
Test that root flags propagate into the handler namespace for
|
|
254
|
+
``extract``.
|
|
206
255
|
"""
|
|
207
256
|
# pylint: disable=protected-access
|
|
208
257
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
'api',
|
|
218
|
-
'https://example.com/data.json',
|
|
219
|
-
],
|
|
258
|
+
result, ns, cmd = invoke_cli(
|
|
259
|
+
'cmd_extract',
|
|
260
|
+
'--no-pretty',
|
|
261
|
+
'--quiet',
|
|
262
|
+
'extract',
|
|
263
|
+
'--source-type',
|
|
264
|
+
'api',
|
|
265
|
+
'https://example.com/data.json',
|
|
220
266
|
)
|
|
221
267
|
|
|
222
268
|
assert result.exit_code == 0
|
|
223
269
|
cmd.assert_called_once()
|
|
224
270
|
|
|
225
|
-
ns = captured['ns']
|
|
226
271
|
assert isinstance(ns, argparse.Namespace)
|
|
227
272
|
assert ns.source_type == 'api'
|
|
228
273
|
assert ns.source == 'https://example.com/data.json'
|
|
@@ -232,22 +277,22 @@ class TestTyperCliAppWiring:
|
|
|
232
277
|
|
|
233
278
|
def test_list_maps_flags(
|
|
234
279
|
self,
|
|
235
|
-
|
|
236
|
-
capture_cmd: CaptureHelper,
|
|
280
|
+
invoke_cli: InvokeCli,
|
|
237
281
|
) -> None:
|
|
238
282
|
"""
|
|
239
|
-
Test that
|
|
240
|
-
namespace.
|
|
283
|
+
Test that ``list`` maps section flags into the handler namespace.
|
|
241
284
|
"""
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
285
|
+
result, ns, cmd = invoke_cli(
|
|
286
|
+
'cmd_list',
|
|
287
|
+
'list',
|
|
288
|
+
'--config',
|
|
289
|
+
'p.yml',
|
|
290
|
+
'--pipelines',
|
|
291
|
+
'--sources',
|
|
246
292
|
)
|
|
247
293
|
assert result.exit_code == 0
|
|
248
294
|
cmd.assert_called_once()
|
|
249
295
|
|
|
250
|
-
ns = captured['ns']
|
|
251
296
|
assert isinstance(ns, argparse.Namespace)
|
|
252
297
|
assert ns.command == 'list'
|
|
253
298
|
assert ns.config == 'p.yml'
|
|
@@ -256,25 +301,25 @@ class TestTyperCliAppWiring:
|
|
|
256
301
|
|
|
257
302
|
def test_load_default_format_maps_namespace(
|
|
258
303
|
self,
|
|
259
|
-
|
|
260
|
-
capture_cmd: CaptureHelper,
|
|
304
|
+
invoke_cli: InvokeCli,
|
|
261
305
|
) -> None:
|
|
262
306
|
"""
|
|
263
|
-
Test that
|
|
264
|
-
|
|
307
|
+
Test that ``load`` defaults to JSON and marks the data format as
|
|
308
|
+
implicit.
|
|
265
309
|
"""
|
|
266
310
|
# pylint: disable=protected-access
|
|
267
311
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
312
|
+
result, ns, cmd = invoke_cli(
|
|
313
|
+
'cmd_load',
|
|
314
|
+
'load',
|
|
315
|
+
'--target-type',
|
|
316
|
+
'file',
|
|
317
|
+
'/path/to/out.json',
|
|
272
318
|
)
|
|
273
319
|
|
|
274
320
|
assert result.exit_code == 0
|
|
275
321
|
cmd.assert_called_once()
|
|
276
322
|
|
|
277
|
-
ns = captured['ns']
|
|
278
323
|
assert isinstance(ns, argparse.Namespace)
|
|
279
324
|
assert ns.command == 'load'
|
|
280
325
|
assert ns.source == '-'
|
|
@@ -285,30 +330,25 @@ class TestTyperCliAppWiring:
|
|
|
285
330
|
|
|
286
331
|
def test_load_explicit_format_maps_namespace(
|
|
287
332
|
self,
|
|
288
|
-
|
|
289
|
-
capture_cmd: CaptureHelper,
|
|
333
|
+
invoke_cli: InvokeCli,
|
|
290
334
|
) -> None:
|
|
291
335
|
"""
|
|
292
|
-
Test that
|
|
336
|
+
Test that ``load`` marks the target data format as explicit when
|
|
293
337
|
provided.
|
|
294
338
|
"""
|
|
295
339
|
# pylint: disable=protected-access
|
|
296
340
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
'csv',
|
|
304
|
-
'/path/to/out.csv',
|
|
305
|
-
],
|
|
341
|
+
result, ns, cmd = invoke_cli(
|
|
342
|
+
'cmd_load',
|
|
343
|
+
'load',
|
|
344
|
+
'--target-format',
|
|
345
|
+
'csv',
|
|
346
|
+
'/path/to/out.csv',
|
|
306
347
|
)
|
|
307
348
|
|
|
308
349
|
assert result.exit_code == 0
|
|
309
350
|
cmd.assert_called_once()
|
|
310
351
|
|
|
311
|
-
ns = captured['ns']
|
|
312
352
|
assert isinstance(ns, argparse.Namespace)
|
|
313
353
|
assert ns.source == '-'
|
|
314
354
|
assert ns.target_type == 'file'
|
|
@@ -317,29 +357,24 @@ class TestTyperCliAppWiring:
|
|
|
317
357
|
|
|
318
358
|
def test_load_to_option_defaults_source_to_stdin(
|
|
319
359
|
self,
|
|
320
|
-
|
|
321
|
-
capture_cmd: CaptureHelper,
|
|
360
|
+
invoke_cli: InvokeCli,
|
|
322
361
|
) -> None:
|
|
323
362
|
"""
|
|
324
|
-
Test that ``
|
|
325
|
-
|
|
363
|
+
Test that ``load`` defaults to stdin when only target options are
|
|
364
|
+
provided.
|
|
326
365
|
"""
|
|
327
366
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
'database',
|
|
335
|
-
'postgres://db.example.org/app',
|
|
336
|
-
],
|
|
367
|
+
result, ns, cmd = invoke_cli(
|
|
368
|
+
'cmd_load',
|
|
369
|
+
'load',
|
|
370
|
+
'--target-type',
|
|
371
|
+
'database',
|
|
372
|
+
'postgres://db.example.org/app',
|
|
337
373
|
)
|
|
338
374
|
|
|
339
375
|
assert result.exit_code == 0
|
|
340
376
|
cmd.assert_called_once()
|
|
341
377
|
|
|
342
|
-
ns = captured['ns']
|
|
343
378
|
assert isinstance(ns, argparse.Namespace)
|
|
344
379
|
assert ns.source == '-'
|
|
345
380
|
assert ns.target == 'postgres://db.example.org/app'
|
|
@@ -353,23 +388,21 @@ class TestTyperCliAppWiring:
|
|
|
353
388
|
|
|
354
389
|
def test_pipeline_maps_flags(
|
|
355
390
|
self,
|
|
356
|
-
|
|
357
|
-
capture_cmd: CaptureHelper,
|
|
391
|
+
invoke_cli: InvokeCli,
|
|
358
392
|
) -> None:
|
|
359
393
|
"""
|
|
360
|
-
Test that
|
|
361
|
-
argument and the ``--run`` command-line argument into the expected
|
|
362
|
-
namespace.
|
|
394
|
+
Test that ``pipeline`` maps list flags into the handler namespace.
|
|
363
395
|
"""
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
396
|
+
result, ns, cmd = invoke_cli(
|
|
397
|
+
'cmd_pipeline',
|
|
398
|
+
'pipeline',
|
|
399
|
+
'--config',
|
|
400
|
+
'p.yml',
|
|
401
|
+
'--jobs',
|
|
368
402
|
)
|
|
369
403
|
assert result.exit_code == 0
|
|
370
404
|
cmd.assert_called_once()
|
|
371
405
|
|
|
372
|
-
ns = captured['ns']
|
|
373
406
|
assert isinstance(ns, argparse.Namespace)
|
|
374
407
|
assert ns.command == 'pipeline'
|
|
375
408
|
assert ns.config == 'p.yml'
|
|
@@ -378,42 +411,44 @@ class TestTyperCliAppWiring:
|
|
|
378
411
|
|
|
379
412
|
def test_pipeline_run_sets_run_option(
|
|
380
413
|
self,
|
|
381
|
-
|
|
382
|
-
capture_cmd: CaptureHelper,
|
|
414
|
+
invoke_cli: InvokeCli,
|
|
383
415
|
) -> None:
|
|
384
|
-
"""
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
result =
|
|
388
|
-
|
|
389
|
-
|
|
416
|
+
"""
|
|
417
|
+
Test that ``pipeline --job`` wires run metadata into the namespace.
|
|
418
|
+
"""
|
|
419
|
+
result, ns, cmd = invoke_cli(
|
|
420
|
+
'cmd_pipeline',
|
|
421
|
+
'pipeline',
|
|
422
|
+
'--config',
|
|
423
|
+
'p.yml',
|
|
424
|
+
'--job',
|
|
425
|
+
'job-2',
|
|
390
426
|
)
|
|
391
427
|
|
|
392
428
|
assert result.exit_code == 0
|
|
393
429
|
cmd.assert_called_once()
|
|
394
|
-
ns = captured['ns']
|
|
395
430
|
assert isinstance(ns, argparse.Namespace)
|
|
396
431
|
assert ns.run == 'job-2'
|
|
397
432
|
assert ns.list is False
|
|
398
433
|
|
|
399
434
|
def test_run_maps_flags(
|
|
400
435
|
self,
|
|
401
|
-
|
|
402
|
-
capture_cmd: CaptureHelper,
|
|
436
|
+
invoke_cli: InvokeCli,
|
|
403
437
|
) -> None:
|
|
404
438
|
"""
|
|
405
|
-
Test that
|
|
406
|
-
command-line argument into the expected namespace.
|
|
439
|
+
Test that ``run`` maps job flags into the handler namespace.
|
|
407
440
|
"""
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
441
|
+
result, ns, cmd = invoke_cli(
|
|
442
|
+
'cmd_run',
|
|
443
|
+
'run',
|
|
444
|
+
'--config',
|
|
445
|
+
'p.yml',
|
|
446
|
+
'--job',
|
|
447
|
+
'j1',
|
|
412
448
|
)
|
|
413
449
|
assert result.exit_code == 0
|
|
414
450
|
cmd.assert_called_once()
|
|
415
451
|
|
|
416
|
-
ns = captured['ns']
|
|
417
452
|
assert isinstance(ns, argparse.Namespace)
|
|
418
453
|
assert ns.command == 'run'
|
|
419
454
|
assert ns.config == 'p.yml'
|
|
@@ -421,28 +456,23 @@ class TestTyperCliAppWiring:
|
|
|
421
456
|
|
|
422
457
|
def test_transform_parses_operations_json(
|
|
423
458
|
self,
|
|
424
|
-
|
|
425
|
-
capture_cmd: CaptureHelper,
|
|
459
|
+
invoke_cli: InvokeCli,
|
|
426
460
|
) -> None:
|
|
427
461
|
"""
|
|
428
|
-
Test that
|
|
429
|
-
|
|
430
|
-
"""
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
'--operations',
|
|
438
|
-
'{"select": ["id"]}',
|
|
439
|
-
],
|
|
462
|
+
Test that ``transform`` parses JSON operations passed via
|
|
463
|
+
``--operations``.
|
|
464
|
+
"""
|
|
465
|
+
result, ns, cmd = invoke_cli(
|
|
466
|
+
'cmd_transform',
|
|
467
|
+
'transform',
|
|
468
|
+
'/path/to/file.json',
|
|
469
|
+
'--operations',
|
|
470
|
+
'{"select": ["id"]}',
|
|
440
471
|
)
|
|
441
472
|
|
|
442
473
|
assert result.exit_code == 0
|
|
443
474
|
cmd.assert_called_once()
|
|
444
475
|
|
|
445
|
-
ns = captured['ns']
|
|
446
476
|
assert isinstance(ns, argparse.Namespace)
|
|
447
477
|
assert ns.command == 'transform'
|
|
448
478
|
assert ns.source == '/path/to/file.json'
|
|
@@ -451,49 +481,42 @@ class TestTyperCliAppWiring:
|
|
|
451
481
|
|
|
452
482
|
def test_transform_respects_source_format(
|
|
453
483
|
self,
|
|
454
|
-
|
|
455
|
-
capture_cmd: CaptureHelper,
|
|
484
|
+
invoke_cli: InvokeCli,
|
|
456
485
|
) -> None:
|
|
457
486
|
"""
|
|
458
|
-
Test that
|
|
459
|
-
|
|
487
|
+
Test that ``transform`` propagates ``--source-format`` into the
|
|
488
|
+
namespace.
|
|
460
489
|
"""
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
490
|
+
result, ns, cmd = invoke_cli(
|
|
491
|
+
'cmd_transform',
|
|
492
|
+
'transform',
|
|
493
|
+
'--source-format',
|
|
494
|
+
'csv',
|
|
465
495
|
)
|
|
466
496
|
|
|
467
497
|
assert result.exit_code == 0
|
|
468
498
|
cmd.assert_called_once()
|
|
469
|
-
ns = captured['ns']
|
|
470
499
|
assert isinstance(ns, argparse.Namespace)
|
|
471
500
|
assert ns.source_format == 'csv'
|
|
472
501
|
|
|
473
502
|
def test_validate_parses_rules_json(
|
|
474
503
|
self,
|
|
475
|
-
|
|
476
|
-
capture_cmd: CaptureHelper,
|
|
504
|
+
invoke_cli: InvokeCli,
|
|
477
505
|
) -> None:
|
|
478
506
|
"""
|
|
479
|
-
Test that
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
'/path/to/file.json',
|
|
488
|
-
'--rules',
|
|
489
|
-
'{"required": ["id"]}',
|
|
490
|
-
],
|
|
507
|
+
Test that ``validate`` parses JSON rules passed via ``--rules``.
|
|
508
|
+
"""
|
|
509
|
+
result, ns, cmd = invoke_cli(
|
|
510
|
+
'cmd_validate',
|
|
511
|
+
'validate',
|
|
512
|
+
'/path/to/file.json',
|
|
513
|
+
'--rules',
|
|
514
|
+
'{"required": ["id"]}',
|
|
491
515
|
)
|
|
492
516
|
|
|
493
517
|
assert result.exit_code == 0
|
|
494
518
|
cmd.assert_called_once()
|
|
495
519
|
|
|
496
|
-
ns = captured['ns']
|
|
497
520
|
assert isinstance(ns, argparse.Namespace)
|
|
498
521
|
assert ns.command == 'validate'
|
|
499
522
|
assert ns.source == '/path/to/file.json'
|
|
@@ -502,24 +525,22 @@ class TestTyperCliAppWiring:
|
|
|
502
525
|
|
|
503
526
|
def test_validate_respects_source_format(
|
|
504
527
|
self,
|
|
505
|
-
|
|
506
|
-
capture_cmd: CaptureHelper,
|
|
528
|
+
invoke_cli: InvokeCli,
|
|
507
529
|
) -> None:
|
|
508
530
|
"""
|
|
509
|
-
Test that
|
|
510
|
-
|
|
531
|
+
Test that ``validate`` propagates ``--source-format`` into the
|
|
532
|
+
namespace.
|
|
511
533
|
"""
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
534
|
+
result, ns, cmd = invoke_cli(
|
|
535
|
+
'cmd_validate',
|
|
536
|
+
'validate',
|
|
537
|
+
'--source-format',
|
|
538
|
+
'csv',
|
|
517
539
|
)
|
|
518
540
|
|
|
519
541
|
assert result.exit_code == 0
|
|
520
542
|
cmd.assert_called_once()
|
|
521
543
|
|
|
522
|
-
ns = captured['ns']
|
|
523
544
|
assert isinstance(ns, argparse.Namespace)
|
|
524
545
|
assert ns.source_format == 'csv'
|
|
525
546
|
|