oed-cli 0.2.0__tar.gz → 0.2.1__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.
- {oed_cli-0.2.0/src/oed_cli.egg-info → oed_cli-0.2.1}/PKG-INFO +1 -1
- {oed_cli-0.2.0 → oed_cli-0.2.1}/pyproject.toml +1 -1
- {oed_cli-0.2.0 → oed_cli-0.2.1}/src/oed_cli/http.py +42 -5
- {oed_cli-0.2.0 → oed_cli-0.2.1}/src/oed_cli/invoke.py +15 -3
- {oed_cli-0.2.0 → oed_cli-0.2.1}/src/oed_cli/main.py +3 -1
- {oed_cli-0.2.0 → oed_cli-0.2.1/src/oed_cli.egg-info}/PKG-INFO +1 -1
- {oed_cli-0.2.0 → oed_cli-0.2.1}/tests/test_dynamic.py +203 -309
- {oed_cli-0.2.0 → oed_cli-0.2.1}/LICENSE +0 -0
- {oed_cli-0.2.0 → oed_cli-0.2.1}/README.md +0 -0
- {oed_cli-0.2.0 → oed_cli-0.2.1}/setup.cfg +0 -0
- {oed_cli-0.2.0 → oed_cli-0.2.1}/src/oed_cli/__init__.py +0 -0
- {oed_cli-0.2.0 → oed_cli-0.2.1}/src/oed_cli/__main__.py +0 -0
- {oed_cli-0.2.0 → oed_cli-0.2.1}/src/oed_cli/cli.py +0 -0
- {oed_cli-0.2.0 → oed_cli-0.2.1}/src/oed_cli/discovery.py +0 -0
- {oed_cli-0.2.0 → oed_cli-0.2.1}/src/oed_cli/dynamic.py +0 -0
- {oed_cli-0.2.0 → oed_cli-0.2.1}/src/oed_cli/errors.py +0 -0
- {oed_cli-0.2.0 → oed_cli-0.2.1}/src/oed_cli/py.typed +0 -0
- {oed_cli-0.2.0 → oed_cli-0.2.1}/src/oed_cli.egg-info/SOURCES.txt +0 -0
- {oed_cli-0.2.0 → oed_cli-0.2.1}/src/oed_cli.egg-info/dependency_links.txt +0 -0
- {oed_cli-0.2.0 → oed_cli-0.2.1}/src/oed_cli.egg-info/entry_points.txt +0 -0
- {oed_cli-0.2.0 → oed_cli-0.2.1}/src/oed_cli.egg-info/requires.txt +0 -0
- {oed_cli-0.2.0 → oed_cli-0.2.1}/src/oed_cli.egg-info/top_level.txt +0 -0
- {oed_cli-0.2.0 → oed_cli-0.2.1}/tests/test_cli.py +0 -0
|
@@ -4,10 +4,20 @@ The gateway sits behind a CloudWAF that returns a Chinese-language HTML block
|
|
|
4
4
|
page when the request lacks browser-style headers (see ``context/discoverAPI.md``
|
|
5
5
|
section "注意事项 & 已知限制"). This module bakes those headers in so every call
|
|
6
6
|
made by ``oed`` succeeds without users tweaking curl flags.
|
|
7
|
+
|
|
8
|
+
Note on ``Referer``: openEuler APIG rejects requests that carry a
|
|
9
|
+
``Referer: https://api-gateway.osinfra.cn/`` header with HTTP 401 on at
|
|
10
|
+
least one production path (``easysearch`` / ``sigsearch/docs``). The
|
|
11
|
+
``/discovery/apis`` feed does not need Referer to pass the CloudWAF
|
|
12
|
+
either — empirically verified 2026-07-28. So ``_headers()`` does NOT
|
|
13
|
+
emit a default Referer. Callers can still pass one explicitly via the
|
|
14
|
+
``headers=`` argument to :func:`get_request` if a future endpoint
|
|
15
|
+
demands it.
|
|
7
16
|
"""
|
|
8
17
|
|
|
9
18
|
from __future__ import annotations
|
|
10
19
|
|
|
20
|
+
import os
|
|
11
21
|
from typing import Any
|
|
12
22
|
|
|
13
23
|
import httpx
|
|
@@ -17,14 +27,31 @@ from .errors import NetworkError, NotFoundError, UpstreamError
|
|
|
17
27
|
|
|
18
28
|
DEFAULT_GATEWAY = "https://api-gateway.osinfra.cn"
|
|
19
29
|
_TIMEOUT_SECONDS = 30.0
|
|
30
|
+
DEFAULT_USER_AGENT = f"oed/{__version__} (+https://gitee.com/openeuler/oed-cli)"
|
|
31
|
+
|
|
20
32
|
|
|
33
|
+
def _resolve_user_agent(user_agent: str | None) -> str:
|
|
34
|
+
"""Resolve the User-Agent: explicit arg > ``OED_USER_AGENT`` env > default.
|
|
35
|
+
|
|
36
|
+
An empty string falls through to the next source — passing ``--user-agent ""``
|
|
37
|
+
is treated the same as not passing it.
|
|
38
|
+
"""
|
|
21
39
|
|
|
22
|
-
|
|
40
|
+
if user_agent:
|
|
41
|
+
return user_agent
|
|
42
|
+
env = os.environ.get("OED_USER_AGENT")
|
|
43
|
+
if env:
|
|
44
|
+
return env
|
|
45
|
+
return DEFAULT_USER_AGENT
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _headers(
|
|
49
|
+
extra: dict[str, str] | None = None, *, user_agent: str | None = None
|
|
50
|
+
) -> dict[str, str]:
|
|
23
51
|
h = {
|
|
24
|
-
"User-Agent":
|
|
52
|
+
"User-Agent": _resolve_user_agent(user_agent),
|
|
25
53
|
"Accept": "application/json, text/plain, */*",
|
|
26
54
|
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
|
|
27
|
-
"Referer": f"{DEFAULT_GATEWAY}/",
|
|
28
55
|
}
|
|
29
56
|
if extra:
|
|
30
57
|
h.update(extra)
|
|
@@ -60,6 +87,7 @@ def get_request(
|
|
|
60
87
|
body: Any = None,
|
|
61
88
|
headers: dict | None = None,
|
|
62
89
|
timeout: float = _TIMEOUT_SECONDS,
|
|
90
|
+
user_agent: str | None = None,
|
|
63
91
|
) -> httpx.Response:
|
|
64
92
|
"""Run an arbitrary HTTP request and return the raw :class:`httpx.Response`.
|
|
65
93
|
|
|
@@ -67,6 +95,11 @@ def get_request(
|
|
|
67
95
|
when ``body`` is set and the caller has not overridden it. Surfaces
|
|
68
96
|
connectivity failures as :class:`NetworkError`; WAF blocks as
|
|
69
97
|
:class:`NetworkError` (kind ``waf_block``); 5xx as :class:`UpstreamError`.
|
|
98
|
+
|
|
99
|
+
``user_agent`` overrides the default User-Agent header — useful when a
|
|
100
|
+
specific APIG backend's WAF rejects the default ``oed/x.y.z`` UA with a
|
|
101
|
+
misleading 401 (e.g. ``easysearch``). Falls back to ``OED_USER_AGENT`` env,
|
|
102
|
+
then the bundled default.
|
|
70
103
|
"""
|
|
71
104
|
|
|
72
105
|
method = method.upper()
|
|
@@ -81,7 +114,7 @@ def get_request(
|
|
|
81
114
|
url,
|
|
82
115
|
params=params if params else None,
|
|
83
116
|
json=body if body is not None else None,
|
|
84
|
-
headers=_headers(extra),
|
|
117
|
+
headers=_headers(extra, user_agent=user_agent),
|
|
85
118
|
)
|
|
86
119
|
except httpx.HTTPError as exc:
|
|
87
120
|
raise NetworkError(f"{method} {url} failed: {exc}", kind="network_error") from exc
|
|
@@ -110,7 +143,11 @@ def _decode(
|
|
|
110
143
|
raise NetworkError(
|
|
111
144
|
f"Gateway WAF blocked the request to {resp.url}",
|
|
112
145
|
kind="waf_block",
|
|
113
|
-
hint=
|
|
146
|
+
hint=(
|
|
147
|
+
"Some APIG backends reject the bundled oed/x.y.z User-Agent. "
|
|
148
|
+
"Retry with `--user-agent 'Mozilla/5.0 ...'` or set "
|
|
149
|
+
"OED_USER_AGENT in the environment."
|
|
150
|
+
),
|
|
114
151
|
)
|
|
115
152
|
|
|
116
153
|
if not resp.content:
|
|
@@ -24,7 +24,7 @@ import httpx
|
|
|
24
24
|
from . import http as http_mod
|
|
25
25
|
from .dynamic import RUNTIME_GATEWAY, Operation, coerce_param_types, to_flag
|
|
26
26
|
from .errors import NetworkError, UserError
|
|
27
|
-
from .http import _is_waf_block
|
|
27
|
+
from .http import _is_waf_block, _resolve_user_agent
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
def _fill_path(template: str, params: dict[str, Any]) -> tuple[str, list[str]]:
|
|
@@ -95,12 +95,16 @@ def call_operation(
|
|
|
95
95
|
dry_run: bool = False,
|
|
96
96
|
timeout: float = 30.0,
|
|
97
97
|
include_request: bool = False,
|
|
98
|
+
user_agent: str | None = None,
|
|
98
99
|
) -> dict[str, Any]:
|
|
99
100
|
"""Invoke ``op`` and return a structured JSON dict suitable for stdout.
|
|
100
101
|
|
|
101
102
|
``ok`` is ``True`` for any 2xx/3xx response. Non-success still surfaces
|
|
102
103
|
the body and status under ``response`` / ``status``; the caller is
|
|
103
104
|
responsible for the exit code.
|
|
105
|
+
|
|
106
|
+
``user_agent`` overrides the default User-Agent header; falls back to
|
|
107
|
+
``OED_USER_AGENT`` env, then the bundled default.
|
|
104
108
|
"""
|
|
105
109
|
|
|
106
110
|
path_params, query_params, unused = _select_params(op, params)
|
|
@@ -117,11 +121,14 @@ def call_operation(
|
|
|
117
121
|
query_params = {k: v for k, v in query_params.items() if v is not None}
|
|
118
122
|
url = f"{RUNTIME_GATEWAY}{filled_path}"
|
|
119
123
|
|
|
124
|
+
request_headers: dict[str, str] = {"User-Agent": _resolve_user_agent(user_agent)}
|
|
125
|
+
if body is not None:
|
|
126
|
+
request_headers["Content-Type"] = "application/json"
|
|
120
127
|
request_view: dict[str, Any] = {
|
|
121
128
|
"method": op.backend.method,
|
|
122
129
|
"url": url,
|
|
123
130
|
"query": query_params,
|
|
124
|
-
"headers":
|
|
131
|
+
"headers": request_headers,
|
|
125
132
|
"body": body,
|
|
126
133
|
}
|
|
127
134
|
|
|
@@ -146,13 +153,18 @@ def call_operation(
|
|
|
146
153
|
params=query_params if query_params else None,
|
|
147
154
|
body=body,
|
|
148
155
|
timeout=timeout,
|
|
156
|
+
user_agent=user_agent,
|
|
149
157
|
)
|
|
150
158
|
|
|
151
159
|
if _is_waf_block(resp.text):
|
|
152
160
|
raise NetworkError(
|
|
153
161
|
f"Backend WAF blocked {op.backend.method} {url}",
|
|
154
162
|
kind="waf_block",
|
|
155
|
-
hint=
|
|
163
|
+
hint=(
|
|
164
|
+
"Some APIG backends reject the bundled oed/x.y.z User-Agent. "
|
|
165
|
+
"Retry with `--user-agent 'Mozilla/5.0 ...'` or set "
|
|
166
|
+
"OED_USER_AGENT in the environment."
|
|
167
|
+
),
|
|
156
168
|
)
|
|
157
169
|
|
|
158
170
|
out = {
|
|
@@ -74,7 +74,7 @@ LEADING_FLAGS: frozenset[str] = frozenset({"-h", "--help", "-V", "--version"})
|
|
|
74
74
|
# Built-in control flags handled by the dispatcher itself. Everything
|
|
75
75
|
# else is treated as a candidate per-parameter flag, validated later
|
|
76
76
|
# against the resolved operation's declared parameters.
|
|
77
|
-
_VALUE_FLAGS: frozenset[str] = frozenset({"params", "json", "path"})
|
|
77
|
+
_VALUE_FLAGS: frozenset[str] = frozenset({"params", "json", "path", "user-agent"})
|
|
78
78
|
_BOOL_FLAGS: frozenset[str] = frozenset({"dry-run"})
|
|
79
79
|
|
|
80
80
|
|
|
@@ -215,6 +215,7 @@ def _dispatch_dynamic(argv: Sequence[str]) -> int:
|
|
|
215
215
|
click.echo(json.dumps(exc.to_dict(), ensure_ascii=False), err=True)
|
|
216
216
|
return exc.code
|
|
217
217
|
|
|
218
|
+
user_agent = raw_flags.pop("user-agent", None)
|
|
218
219
|
method = positional[0] if positional else None
|
|
219
220
|
|
|
220
221
|
try:
|
|
@@ -290,6 +291,7 @@ def _dispatch_dynamic(argv: Sequence[str]) -> int:
|
|
|
290
291
|
body=body,
|
|
291
292
|
dry_run=dry_run,
|
|
292
293
|
include_request=True,
|
|
294
|
+
user_agent=user_agent,
|
|
293
295
|
)
|
|
294
296
|
except OedError as exc:
|
|
295
297
|
click.echo(json.dumps(exc.to_dict(), ensure_ascii=False), err=True)
|
|
@@ -158,7 +158,7 @@ def patched(monkeypatch):
|
|
|
158
158
|
|
|
159
159
|
captured = {}
|
|
160
160
|
|
|
161
|
-
def _fake_do_call(method, url, params, body, timeout):
|
|
161
|
+
def _fake_do_call(method, url, params=None, body=None, timeout=30.0, headers=None, user_agent=None):
|
|
162
162
|
class _R:
|
|
163
163
|
status_code = 200
|
|
164
164
|
content = b'{"code":"","msg":"","data":{"ok":true}}'
|
|
@@ -172,6 +172,7 @@ def patched(monkeypatch):
|
|
|
172
172
|
captured["url"] = url
|
|
173
173
|
captured["params"] = params
|
|
174
174
|
captured["body"] = body
|
|
175
|
+
captured["user_agent"] = user_agent
|
|
175
176
|
return _R()
|
|
176
177
|
|
|
177
178
|
# Replace get_request since invoke calls it
|
|
@@ -245,16 +246,6 @@ def test_backend_extraction():
|
|
|
245
246
|
assert op.body_required is True
|
|
246
247
|
|
|
247
248
|
|
|
248
|
-
def test_operations_table_supports_id_and_path_aliases():
|
|
249
|
-
from oed_cli.dynamic import operations_table
|
|
250
|
-
|
|
251
|
-
table = operations_table(SAMPLE_SPEC, "x")
|
|
252
|
-
# primary key
|
|
253
|
-
assert "API_listSoftwarePackages" in table
|
|
254
|
-
# alias
|
|
255
|
-
assert "GET /v1/softwarepkg" in table
|
|
256
|
-
|
|
257
|
-
|
|
258
249
|
def test_parse_json_arg_rejects_garbage():
|
|
259
250
|
from oed_cli.dynamic import parse_json_arg
|
|
260
251
|
from oed_cli.errors import UserError
|
|
@@ -263,22 +254,6 @@ def test_parse_json_arg_rejects_garbage():
|
|
|
263
254
|
parse_json_arg("not-json", flag="params")
|
|
264
255
|
|
|
265
256
|
|
|
266
|
-
def test_path_fill_reports_missing():
|
|
267
|
-
|
|
268
|
-
ops = {o.operation_id: o for o in []}
|
|
269
|
-
assert ops == {}
|
|
270
|
-
# exercise _fill_path via the direct path
|
|
271
|
-
from oed_cli.invoke import _fill_path
|
|
272
|
-
|
|
273
|
-
rendered, missing = _fill_path("/api/v1/softwarepkg/{id}/review", {"id": "12345"})
|
|
274
|
-
assert rendered == "/api/v1/softwarepkg/12345/review"
|
|
275
|
-
assert missing == []
|
|
276
|
-
|
|
277
|
-
rendered, missing = _fill_path("/api/v1/softwarepkg/{id}", {})
|
|
278
|
-
assert rendered == "/api/v1/softwarepkg/{id}" # placeholder kept verbatim
|
|
279
|
-
assert missing == ["id"]
|
|
280
|
-
|
|
281
|
-
|
|
282
257
|
def test_coerce_param_types_handles_string_ints():
|
|
283
258
|
from oed_cli.dynamic import coerce_param_types, collect_operations
|
|
284
259
|
|
|
@@ -320,18 +295,6 @@ def test_call_operation_post_sends_body(patched):
|
|
|
320
295
|
assert patched["captured"]["body"] == body
|
|
321
296
|
|
|
322
297
|
|
|
323
|
-
def test_call_operation_dry_run_does_not_call(patched):
|
|
324
|
-
from oed_cli.dynamic import operations_table
|
|
325
|
-
from oed_cli.invoke import call_operation
|
|
326
|
-
|
|
327
|
-
table = operations_table(SAMPLE_SPEC, "software-package-server")
|
|
328
|
-
op = table["API_verifyCla"]
|
|
329
|
-
|
|
330
|
-
payload = call_operation(op, dry_run=True)
|
|
331
|
-
assert payload["dry_run"] is True
|
|
332
|
-
assert "method" not in patched["captured"] # no real call
|
|
333
|
-
|
|
334
|
-
|
|
335
298
|
def test_call_operation_missing_path_param_raises_user_error(patched):
|
|
336
299
|
from oed_cli.dynamic import operations_table
|
|
337
300
|
from oed_cli.errors import UserError
|
|
@@ -346,31 +309,6 @@ def test_call_operation_missing_path_param_raises_user_error(patched):
|
|
|
346
309
|
# ---------- main.py dispatch ----------
|
|
347
310
|
|
|
348
311
|
|
|
349
|
-
def test_dispatch_invokes_via_dynamic_path(patched, runner, monkeypatch):
|
|
350
|
-
from oed_cli import main as oed_main
|
|
351
|
-
|
|
352
|
-
monkeypatch.setenv("OED_COMMUNITY", "openeuler")
|
|
353
|
-
code = oed_main.main(
|
|
354
|
-
[
|
|
355
|
-
"software-package-server",
|
|
356
|
-
"API_getSoftwarePackage",
|
|
357
|
-
"--params",
|
|
358
|
-
'{"id":"12345"}',
|
|
359
|
-
]
|
|
360
|
-
)
|
|
361
|
-
assert code == 0
|
|
362
|
-
assert patched["captured"]["url"] == "https://apig.osinfra.cn/v1/softwarepkg/12345"
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
def test_dispatch_lists_operations_when_only_service(patched, runner, monkeypatch):
|
|
366
|
-
from oed_cli import main as oed_main
|
|
367
|
-
|
|
368
|
-
monkeypatch.setenv("OED_COMMUNITY", "openeuler")
|
|
369
|
-
code = oed_main.main(["software-package-server"])
|
|
370
|
-
assert code == 0
|
|
371
|
-
# Just verify exit code; output already verified manually against live.
|
|
372
|
-
|
|
373
|
-
|
|
374
312
|
def test_dispatch_unknown_service(patched, runner, monkeypatch):
|
|
375
313
|
from oed_cli import dynamic as dyn
|
|
376
314
|
from oed_cli import main as oed_main
|
|
@@ -403,47 +341,6 @@ def test_dispatch_invalid_json_flag(patched, runner, monkeypatch):
|
|
|
403
341
|
assert code == 1
|
|
404
342
|
|
|
405
343
|
|
|
406
|
-
def test_reserved_routes_go_to_click(patched, runner, monkeypatch):
|
|
407
|
-
"""`oed info` is a click sub-command, even though `info` could in principle
|
|
408
|
-
look like a dynamic service name."""
|
|
409
|
-
|
|
410
|
-
from oed_cli import main as oed_main
|
|
411
|
-
|
|
412
|
-
code = oed_main.main(["--version"])
|
|
413
|
-
assert code == 0
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
def test_dispatch_dry_run(patched, runner, monkeypatch):
|
|
417
|
-
from oed_cli import main as oed_main
|
|
418
|
-
|
|
419
|
-
monkeypatch.setenv("OED_COMMUNITY", "openeuler")
|
|
420
|
-
code = oed_main.main(
|
|
421
|
-
[
|
|
422
|
-
"software-package-server",
|
|
423
|
-
"API_listSoftwarePackages",
|
|
424
|
-
"--dry-run",
|
|
425
|
-
]
|
|
426
|
-
)
|
|
427
|
-
assert code == 0
|
|
428
|
-
# no HTTP capture → confirms dry-run short-circuited
|
|
429
|
-
assert "method" not in patched["captured"]
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
def test_service_level_help_lists_operations(patched, monkeypatch, capsys):
|
|
433
|
-
"""`oed <service> --help` should list every operation for that service."""
|
|
434
|
-
|
|
435
|
-
from oed_cli import main as oed_main
|
|
436
|
-
|
|
437
|
-
monkeypatch.setenv("OED_COMMUNITY", "openeuler")
|
|
438
|
-
code = oed_main.main(["software-package-server", "--help"])
|
|
439
|
-
captured = capsys.readouterr()
|
|
440
|
-
assert code == 0
|
|
441
|
-
text = captured.out + captured.err
|
|
442
|
-
assert "API_listSoftwarePackages" in text
|
|
443
|
-
assert "API_applyNewSoftwarePackage" in text
|
|
444
|
-
assert text.lstrip().startswith("{")
|
|
445
|
-
|
|
446
|
-
|
|
447
344
|
def test_service_level_help_with_method_rejected(patched, monkeypatch, capsys):
|
|
448
345
|
"""`oed <service> <method> --help` now resolves the operation first and
|
|
449
346
|
shows its per-parameter flag cheatsheet. With an unknown method it
|
|
@@ -459,7 +356,7 @@ def test_service_level_help_with_method_rejected(patched, monkeypatch, capsys):
|
|
|
459
356
|
assert "method_not_found" in text
|
|
460
357
|
|
|
461
358
|
|
|
462
|
-
def test_spec_missing_returns_exit_4(patched, runner, monkeypatch):
|
|
359
|
+
def test_spec_missing_returns_exit_4(patched, runner, monkeypatch, capsys):
|
|
463
360
|
from oed_cli import dynamic as dyn
|
|
464
361
|
from oed_cli import main as oed_main
|
|
465
362
|
from oed_cli.errors import NotFoundError
|
|
@@ -474,264 +371,261 @@ def test_spec_missing_returns_exit_4(patched, runner, monkeypatch):
|
|
|
474
371
|
),
|
|
475
372
|
)
|
|
476
373
|
|
|
477
|
-
monkeypatch.setattr(
|
|
374
|
+
monkeypatch.setattr(oed_main, "fetch_service_spec", _raise)
|
|
478
375
|
code = oed_main.main(["software-package-server", "API_x"])
|
|
479
|
-
|
|
376
|
+
captured = capsys.readouterr()
|
|
377
|
+
assert code == 4 and "spec_missing" in (captured.out + captured.err)
|
|
480
378
|
|
|
481
379
|
|
|
482
|
-
|
|
483
|
-
"""Spec-declared ``x-apigateway-backend.httpEndpoints.address`` is
|
|
484
|
-
parsed for diagnostics but never trusted as the runtime host — every
|
|
485
|
-
call must go through ``https://apig.osinfra.cn`` so a stale spec
|
|
486
|
-
pointing at a staging host (e.g. ``cvesa.test.osinfra.cn``) cannot
|
|
487
|
-
break the call.
|
|
488
|
-
"""
|
|
380
|
+
# ---------- describe_* helpers + main dispatch sweep ----------
|
|
489
381
|
|
|
490
|
-
from oed_cli.dynamic import operations_table
|
|
491
|
-
from oed_cli.invoke import RUNTIME_GATEWAY, call_operation
|
|
492
382
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
383
|
+
def test_describe_helpers_and_main_dispatch(patched, monkeypatch, capsys):
|
|
384
|
+
"""Compact sweep covering describe_*, main per-param flag dispatch,
|
|
385
|
+
and parse_json_arg / coerce_flag_value edge cases."""
|
|
386
|
+
from oed_cli.discovery import ServiceMeta
|
|
387
|
+
from oed_cli.dynamic import (
|
|
388
|
+
collect_operations, operations_table, parse_json_arg,
|
|
389
|
+
coerce_flag_value, resolve_operation,
|
|
390
|
+
)
|
|
391
|
+
from oed_cli.invoke import describe_service, describe_operation_help, call_operation
|
|
392
|
+
from oed_cli import main as oed_main
|
|
393
|
+
from oed_cli.errors import UserError
|
|
394
|
+
|
|
395
|
+
svc = ServiceMeta.from_raw(SAMPLE_FEED["communities"]["openeuler"][0])
|
|
396
|
+
ops = collect_operations(SAMPLE_SPEC, svc.service_name)
|
|
397
|
+
doc = describe_service(svc, ops)
|
|
398
|
+
assert {op["operation_id"] for op in doc["operations"]} >= {"listSoftwarePackages", "verifyCla"}
|
|
399
|
+
help_doc = describe_operation_help(
|
|
400
|
+
operations_table(SAMPLE_SPEC, svc.service_name)["API_listSoftwarePackages"], svc
|
|
401
|
+
)
|
|
402
|
+
flags = {p["flag"] for p in help_doc["parameters"]}
|
|
403
|
+
assert {"--phase", "--page-num", "--count-per-page"} <= flags
|
|
404
|
+
assert help_doc["usage"] and help_doc["examples"]
|
|
405
|
+
table = operations_table(SAMPLE_SPEC, svc.service_name)
|
|
406
|
+
assert call_operation(table["API_verifyCla"], dry_run=True)["dry_run"] is True
|
|
407
|
+
assert call_operation(table["API_verifyCla"], params={"nope": 1})["unused_params"] == ["nope"]
|
|
408
|
+
|
|
409
|
+
monkeypatch.setenv("OED_COMMUNITY", "openeuler")
|
|
410
|
+
assert oed_main.main([
|
|
411
|
+
"software-package-server", "API_getSoftwarePackage",
|
|
412
|
+
"--id", "42", "--language", "zh_CN",
|
|
413
|
+
]) == 0
|
|
414
|
+
assert patched["captured"]["url"].endswith("/v1/softwarepkg/42")
|
|
415
|
+
assert patched["captured"]["params"] == {"language": "zh_CN"}
|
|
416
|
+
assert oed_main.main([
|
|
417
|
+
"software-package-server", "api_getsoftwarepackage", "--id", "1",
|
|
418
|
+
]) == 0
|
|
419
|
+
assert resolve_operation(
|
|
420
|
+
operations_table(SAMPLE_SPEC, "x"), "api_listsoftwarepackages"
|
|
421
|
+
).operation_id == "API_listSoftwarePackages"
|
|
422
|
+
|
|
423
|
+
code = oed_main.main([
|
|
424
|
+
"software-package-server", "API_listSoftwarePackages", "--bogus", "x",
|
|
425
|
+
])
|
|
426
|
+
captured = capsys.readouterr()
|
|
427
|
+
assert code == 1 and "unknown_flag" in (captured.out + captured.err)
|
|
428
|
+
code = oed_main.main(["software-package-server", "API_getSoftwarePackage", "--help"])
|
|
429
|
+
text = capsys.readouterr().out + capsys.readouterr().err
|
|
430
|
+
assert code == 0 and "--id" in text and "language" in text
|
|
499
431
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
assert
|
|
432
|
+
assert parse_json_arg("", flag="p") is None
|
|
433
|
+
assert parse_json_arg(None, flag="p") is None
|
|
434
|
+
with pytest.raises(UserError):
|
|
435
|
+
parse_json_arg("[1]", flag="p")
|
|
436
|
+
assert coerce_flag_value({"schema": {"type": "integer"}}, "5") == 5
|
|
437
|
+
assert coerce_flag_value({"schema": {"type": "number"}}, "1.5") == 1.5
|
|
438
|
+
assert coerce_flag_value({"schema": {"type": "boolean"}}, "yes") is True
|
|
439
|
+
assert coerce_flag_value({"schema": {"type": "boolean"}}, "false") is False
|
|
440
|
+
assert parse_json_arg('{"k":1}', flag="p") == {"k": 1}
|
|
441
|
+
assert coerce_flag_value({"schema": {"type": "integer"}}, "abc") == "abc"
|
|
442
|
+
assert coerce_flag_value({"schema": {"type": "number"}}, "abc") == "abc"
|
|
506
443
|
|
|
507
444
|
|
|
508
|
-
# ----------
|
|
445
|
+
# ---------- main() dispatch edges ----------
|
|
509
446
|
|
|
510
447
|
|
|
511
|
-
def
|
|
512
|
-
|
|
448
|
+
def test_main_dispatch_comprehensive(patched, monkeypatch, capsys):
|
|
449
|
+
"""Sweep main dispatch surfaces: reserved routes, --key=value, parse-error,
|
|
450
|
+
dry-run short-circuit, service-only listing, and call_operation errors."""
|
|
451
|
+
from oed_cli import main as oed_main
|
|
452
|
+
from oed_cli import http as http_mod
|
|
453
|
+
from oed_cli.errors import NetworkError
|
|
513
454
|
|
|
514
|
-
|
|
515
|
-
assert
|
|
516
|
-
assert
|
|
517
|
-
|
|
518
|
-
assert
|
|
455
|
+
monkeypatch.setenv("OED_COMMUNITY", "openeuler")
|
|
456
|
+
assert oed_main.main(["--version"]) == 0
|
|
457
|
+
assert oed_main.main(["info"]) == 0
|
|
458
|
+
oed_main.main(["software-package-server", "API_getSoftwarePackage", "--id=42"])
|
|
459
|
+
assert patched["captured"]["url"].endswith("/v1/softwarepkg/42")
|
|
460
|
+
code = oed_main.main(["software-package-server", "API_listSoftwarePackages", "-x"])
|
|
461
|
+
assert code == 1 and "unknown short flag" in (capsys.readouterr().err)
|
|
462
|
+
code = oed_main.main(["software-package-server", "API_listSoftwarePackages",
|
|
463
|
+
"--params", "bad"])
|
|
464
|
+
out = capsys.readouterr()
|
|
465
|
+
assert code == 1 and "invalid_json" in (out.out + out.err)
|
|
466
|
+
code = oed_main.main(["software-package-server", "API_listSoftwarePackages",
|
|
467
|
+
"--params", "[]"])
|
|
468
|
+
out = capsys.readouterr()
|
|
469
|
+
assert code == 1 and "invalid_json" in (out.out + out.err)
|
|
470
|
+
code = oed_main.main(
|
|
471
|
+
["software-package-server", "API_applyNewSoftwarePackage", "--json", "bad"]
|
|
472
|
+
)
|
|
473
|
+
out = capsys.readouterr()
|
|
474
|
+
assert code == 1 and "invalid_json" in (out.out + out.err)
|
|
475
|
+
oed_main.main(["software-package-server", "API_getSoftwarePackage",
|
|
476
|
+
"--params", '{"id":"42"}', "--language", "zh_CN"])
|
|
477
|
+
assert patched["captured"]["url"].endswith("/v1/softwarepkg/42")
|
|
478
|
+
patched["captured"].clear()
|
|
479
|
+
code = oed_main.main(["software-package-server", "API_getSoftwarePackage",
|
|
480
|
+
"--id", "1", "--dry-run"])
|
|
481
|
+
assert code == 0 and "method" not in patched["captured"]
|
|
482
|
+
code = oed_main.main(["software-package-server"])
|
|
483
|
+
out = capsys.readouterr()
|
|
484
|
+
assert code == 0 and "listSoftwarePackages" in (out.out + out.err)
|
|
485
|
+
code = oed_main.main(["software-package-server", "--help"])
|
|
486
|
+
out = capsys.readouterr()
|
|
487
|
+
assert code == 0 and "API_listSoftwarePackages" in (out.out + out.err)
|
|
488
|
+
# -- separator (covers _split_dispatch_argv -- passthrough)
|
|
489
|
+
code = oed_main.main(["software-package-server", "API_getSoftwarePackage",
|
|
490
|
+
"--", "ignored"])
|
|
491
|
+
out = capsys.readouterr()
|
|
492
|
+
assert code == 1 and "missing_path_param" in (out.out + out.err)
|
|
493
|
+
monkeypatch.setattr(http_mod, "get_request",
|
|
494
|
+
lambda *a, **k: (_ for _ in ()).throw(NetworkError("x", kind="network_error")))
|
|
495
|
+
code = oed_main.main(["software-package-server", "API_getSoftwarePackage", "--id", "1"])
|
|
496
|
+
out = capsys.readouterr()
|
|
497
|
+
assert code == 2 and "network_error" in (out.out + out.err)
|
|
498
|
+
monkeypatch.setattr(http_mod, "get_request",
|
|
499
|
+
lambda *a, **k: type("R", (), {"status_code": 503, "text": "", "content": b"", "headers": {}})())
|
|
500
|
+
code = oed_main.main(["software-package-server", "API_getSoftwarePackage", "--id", "1"])
|
|
501
|
+
assert code == 3
|
|
519
502
|
|
|
520
503
|
|
|
521
|
-
|
|
522
|
-
from oed_cli.dynamic import operations_table, param_flag_index
|
|
504
|
+
# ---------- --user-agent / OED_USER_AGENT override ----------
|
|
523
505
|
|
|
524
|
-
ops = operations_table(SAMPLE_SPEC, "x")
|
|
525
|
-
op = ops["API_getSoftwarePackage"]
|
|
526
|
-
index = param_flag_index(op)
|
|
527
|
-
# kebab-case stem
|
|
528
|
-
assert "id" in index and index["id"]["name"] == "id"
|
|
529
|
-
# raw camelCase is also indexed
|
|
530
|
-
assert index["id"]["in"] == "path"
|
|
531
506
|
|
|
507
|
+
def test_resolve_user_agent_precedence(monkeypatch):
|
|
508
|
+
"""Explicit arg > ``OED_USER_AGENT`` env > bundled default; empty string falls through."""
|
|
532
509
|
|
|
533
|
-
|
|
534
|
-
"""``oed <svc> <op> --id 42`` (kebab-case from camelCase/snake) should
|
|
535
|
-
fill the path placeholder exactly like ``--params '{"id":"42"}'``."""
|
|
510
|
+
from oed_cli.http import _resolve_user_agent, DEFAULT_USER_AGENT
|
|
536
511
|
|
|
537
|
-
|
|
512
|
+
monkeypatch.delenv("OED_USER_AGENT", raising=False)
|
|
513
|
+
assert _resolve_user_agent(None) == DEFAULT_USER_AGENT
|
|
514
|
+
assert _resolve_user_agent("") == DEFAULT_USER_AGENT
|
|
515
|
+
monkeypatch.setenv("OED_USER_AGENT", "from-env/1.0")
|
|
516
|
+
assert _resolve_user_agent(None) == "from-env/1.0"
|
|
517
|
+
assert _resolve_user_agent("") == "from-env/1.0"
|
|
518
|
+
assert _resolve_user_agent("from-arg/2.0") == "from-arg/2.0"
|
|
538
519
|
|
|
539
|
-
monkeypatch.setenv("OED_COMMUNITY", "openeuler")
|
|
540
|
-
code = oed_main.main(
|
|
541
|
-
[
|
|
542
|
-
"software-package-server",
|
|
543
|
-
"API_getSoftwarePackage",
|
|
544
|
-
"--id",
|
|
545
|
-
"42",
|
|
546
|
-
"--language",
|
|
547
|
-
"zh_CN",
|
|
548
|
-
]
|
|
549
|
-
)
|
|
550
|
-
assert code == 0
|
|
551
|
-
assert patched["captured"]["url"] == "https://apig.osinfra.cn/v1/softwarepkg/42"
|
|
552
|
-
assert patched["captured"]["params"] == {"language": "zh_CN"}
|
|
553
520
|
|
|
521
|
+
def test_headers_function_uses_env(monkeypatch):
|
|
522
|
+
"""``_headers()`` resolves User-Agent through the same precedence as ``_resolve_user_agent``."""
|
|
554
523
|
|
|
555
|
-
|
|
556
|
-
"""Declared ``integer`` query params get string→int coercion from flags."""
|
|
524
|
+
from oed_cli.http import _headers
|
|
557
525
|
|
|
558
|
-
|
|
526
|
+
monkeypatch.setenv("OED_USER_AGENT", "browser-like/1.0")
|
|
527
|
+
h = _headers()
|
|
528
|
+
assert h["User-Agent"] == "browser-like/1.0"
|
|
529
|
+
h2 = _headers(user_agent="from-arg/2.0")
|
|
530
|
+
assert h2["User-Agent"] == "from-arg/2.0"
|
|
559
531
|
|
|
560
|
-
monkeypatch.setenv("OED_COMMUNITY", "openeuler")
|
|
561
|
-
code = oed_main.main(
|
|
562
|
-
[
|
|
563
|
-
"software-package-server",
|
|
564
|
-
"API_listSoftwarePackages",
|
|
565
|
-
"--page-num",
|
|
566
|
-
"3",
|
|
567
|
-
"--count-per-page",
|
|
568
|
-
"5",
|
|
569
|
-
]
|
|
570
|
-
)
|
|
571
|
-
assert code == 0
|
|
572
|
-
# ints, not strings
|
|
573
|
-
assert patched["captured"]["params"] == {"page_num": 3, "count_per_page": 5}
|
|
574
532
|
|
|
533
|
+
def test_default_headers_omit_referer():
|
|
534
|
+
"""No default ``Referer`` — openEuler APIG rejects ``easysearch`` calls with 401
|
|
535
|
+
when this header is present (verified 2026-07-28). Callers can still pass
|
|
536
|
+
one explicitly via ``extra=`` if a future endpoint needs it."""
|
|
575
537
|
|
|
576
|
-
|
|
577
|
-
"""Per-param flag values win when ``--params`` also defines the same key."""
|
|
538
|
+
from oed_cli.http import _headers
|
|
578
539
|
|
|
579
|
-
|
|
540
|
+
h = _headers()
|
|
541
|
+
assert "Referer" not in h
|
|
542
|
+
assert h["User-Agent"].startswith("oed/")
|
|
543
|
+
assert h["Accept"].startswith("application/json")
|
|
544
|
+
assert "zh-CN" in h["Accept-Language"]
|
|
580
545
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
[
|
|
584
|
-
"software-package-server",
|
|
585
|
-
"API_listSoftwarePackages",
|
|
586
|
-
"--params",
|
|
587
|
-
'{"page_num":1,"count_per_page":10,"phase":"ignored"}',
|
|
588
|
-
"--page-num",
|
|
589
|
-
"7",
|
|
590
|
-
]
|
|
591
|
-
)
|
|
592
|
-
assert code == 0
|
|
593
|
-
captured = patched["captured"]["params"]
|
|
594
|
-
assert captured["page_num"] == 7 # flag wins
|
|
595
|
-
assert captured["count_per_page"] == 10 # params still applies
|
|
546
|
+
h_with_extra = _headers(extra={"Referer": "https://example.com/"})
|
|
547
|
+
assert h_with_extra["Referer"] == "https://example.com/"
|
|
596
548
|
|
|
597
549
|
|
|
598
|
-
def
|
|
599
|
-
"""
|
|
600
|
-
hint listing what *is* valid for this operation."""
|
|
550
|
+
def test_call_operation_forwards_user_agent(patched):
|
|
551
|
+
"""``call_operation(user_agent=...)`` propagates to the HTTP layer."""
|
|
601
552
|
|
|
602
|
-
from oed_cli import
|
|
553
|
+
from oed_cli.dynamic import operations_table
|
|
554
|
+
from oed_cli.invoke import call_operation
|
|
603
555
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
"API_getSoftwarePackage",
|
|
609
|
-
"--totally-unknown",
|
|
610
|
-
"1",
|
|
611
|
-
]
|
|
612
|
-
)
|
|
613
|
-
captured = capsys.readouterr()
|
|
614
|
-
assert code == 1
|
|
615
|
-
text = captured.out + captured.err
|
|
616
|
-
assert "unknown_flag" in text
|
|
617
|
-
# The hint should list this op's declared params so the user knows what to use.
|
|
618
|
-
assert "id" in text
|
|
619
|
-
assert "language" in text
|
|
556
|
+
table = operations_table(SAMPLE_SPEC, "software-package-server")
|
|
557
|
+
op = table["API_getSoftwarePackage"]
|
|
558
|
+
call_operation(op, params={"id": "1"}, user_agent="override/1.0")
|
|
559
|
+
assert patched["captured"]["user_agent"] == "override/1.0"
|
|
620
560
|
|
|
621
561
|
|
|
622
|
-
def
|
|
623
|
-
"""
|
|
624
|
-
APIG-generated ``API_`` prefix stripped from ``help_for`` / ``usage``."""
|
|
562
|
+
def test_dispatch_user_agent_flag_flows_to_http(patched, monkeypatch, capsys):
|
|
563
|
+
"""End-to-end: ``--user-agent foo`` reaches ``get_request(user_agent=...)``."""
|
|
625
564
|
|
|
626
565
|
from oed_cli import main as oed_main
|
|
627
566
|
|
|
628
567
|
monkeypatch.setenv("OED_COMMUNITY", "openeuler")
|
|
629
|
-
code = oed_main.main(
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
568
|
+
code = oed_main.main([
|
|
569
|
+
"software-package-server", "API_getSoftwarePackage",
|
|
570
|
+
"--id", "1", "--user-agent", "browser-mock/9.9",
|
|
571
|
+
])
|
|
633
572
|
assert code == 0
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
# Raw spec form still surfaced for diagnostics.
|
|
640
|
-
assert payload["operation_id_raw"] == "API_getSoftwarePackage"
|
|
641
|
-
assert payload["method"] == "GET"
|
|
642
|
-
flags = {p["flag"] for p in payload["parameters"]}
|
|
643
|
-
assert "--id" in flags
|
|
644
|
-
assert "--language" in flags
|
|
645
|
-
# Required marker carried over.
|
|
646
|
-
id_entry = next(p for p in payload["parameters"] if p["name"] == "id")
|
|
647
|
-
assert id_entry["required"] is True
|
|
648
|
-
# Usage line lists the flags in copy-paste form (also stripped).
|
|
649
|
-
assert "--id" in payload["usage"]
|
|
650
|
-
assert "getSoftwarePackage" in payload["usage"]
|
|
651
|
-
assert "API_getSoftwarePackage" not in payload["usage"]
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
def test_api_prefix_alias_resolves_same_op(patched, runner, monkeypatch):
|
|
655
|
-
"""Both ``API_xxx`` and ``xxx`` (and case variants) must resolve to the
|
|
656
|
-
same :class:`Operation` so users never have to memorize the prefix."""
|
|
573
|
+
assert patched["captured"]["user_agent"] == "browser-mock/9.9"
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
def test_dispatch_user_agent_env_only(patched, monkeypatch):
|
|
577
|
+
"""No flag, only env → env value is forwarded."""
|
|
657
578
|
|
|
658
579
|
from oed_cli import main as oed_main
|
|
659
580
|
|
|
660
581
|
monkeypatch.setenv("OED_COMMUNITY", "openeuler")
|
|
661
|
-
|
|
662
|
-
code = oed_main.main(
|
|
663
|
-
[
|
|
664
|
-
"software-package-server",
|
|
665
|
-
"getSoftwarePackage",
|
|
666
|
-
"--id",
|
|
667
|
-
"42",
|
|
668
|
-
]
|
|
669
|
-
)
|
|
582
|
+
monkeypatch.setenv("OED_USER_AGENT", "env-only/3.0")
|
|
583
|
+
code = oed_main.main(["software-package-server", "API_getSoftwarePackage", "--id", "1"])
|
|
670
584
|
assert code == 0
|
|
671
|
-
|
|
585
|
+
assert patched["captured"]["user_agent"] == "env-only/3.0"
|
|
672
586
|
|
|
673
|
-
# 2) Raw API_ form (back-compat)
|
|
674
|
-
patched["captured"].clear()
|
|
675
|
-
code = oed_main.main(
|
|
676
|
-
[
|
|
677
|
-
"software-package-server",
|
|
678
|
-
"API_getSoftwarePackage",
|
|
679
|
-
"--id",
|
|
680
|
-
"42",
|
|
681
|
-
]
|
|
682
|
-
)
|
|
683
|
-
assert code == 0
|
|
684
|
-
raw_url = patched["captured"]["url"]
|
|
685
587
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
"software-package-server",
|
|
691
|
-
"GETSOFTWAREPACKAGE",
|
|
692
|
-
"--id",
|
|
693
|
-
"42",
|
|
694
|
-
]
|
|
695
|
-
)
|
|
696
|
-
assert code == 0
|
|
697
|
-
upper_url = patched["captured"]["url"]
|
|
588
|
+
def test_dispatch_user_agent_flag_overrides_env(patched, monkeypatch):
|
|
589
|
+
"""Flag wins over env when both are set."""
|
|
590
|
+
|
|
591
|
+
from oed_cli import main as oed_main
|
|
698
592
|
|
|
699
|
-
|
|
593
|
+
monkeypatch.setenv("OED_COMMUNITY", "openeuler")
|
|
594
|
+
monkeypatch.setenv("OED_USER_AGENT", "env/1.0")
|
|
595
|
+
code = oed_main.main([
|
|
596
|
+
"software-package-server", "API_getSoftwarePackage",
|
|
597
|
+
"--id", "1", "--user-agent=arg/2.0",
|
|
598
|
+
])
|
|
599
|
+
assert code == 0
|
|
600
|
+
assert patched["captured"]["user_agent"] == "arg/2.0"
|
|
700
601
|
|
|
701
602
|
|
|
702
|
-
def
|
|
703
|
-
"""
|
|
704
|
-
map so callers can see the original spec form when it's non-trivial."""
|
|
603
|
+
def test_dispatch_user_agent_in_dry_run(patched, monkeypatch, capsys):
|
|
604
|
+
"""Dry-run surfaces the resolved User-Agent under ``request.headers``."""
|
|
705
605
|
|
|
706
606
|
from oed_cli import main as oed_main
|
|
707
607
|
|
|
708
608
|
monkeypatch.setenv("OED_COMMUNITY", "openeuler")
|
|
709
|
-
code = oed_main.main([
|
|
710
|
-
|
|
609
|
+
code = oed_main.main([
|
|
610
|
+
"software-package-server", "API_getSoftwarePackage",
|
|
611
|
+
"--id", "1", "--user-agent", "preview-ua/1.0", "--dry-run",
|
|
612
|
+
])
|
|
711
613
|
assert code == 0
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
assert
|
|
715
|
-
assert "
|
|
716
|
-
# Aliases map the display form back to the raw spec form
|
|
717
|
-
assert payload["operation_aliases"]["listSoftwarePackages"] == "API_listSoftwarePackages"
|
|
614
|
+
import json as _json
|
|
615
|
+
payload = _json.loads(capsys.readouterr().out)
|
|
616
|
+
assert payload["dry_run"] is True
|
|
617
|
+
assert payload["request"]["headers"]["User-Agent"] == "preview-ua/1.0"
|
|
718
618
|
|
|
719
619
|
|
|
720
|
-
def
|
|
721
|
-
"""``--
|
|
722
|
-
``--help`` should still show the help, not error on the flag."""
|
|
620
|
+
def test_dispatch_user_agent_missing_value_errors(patched, monkeypatch, capsys):
|
|
621
|
+
"""``--user-agent`` with no following value fails fast with exit 1."""
|
|
723
622
|
|
|
724
623
|
from oed_cli import main as oed_main
|
|
725
624
|
|
|
726
625
|
monkeypatch.setenv("OED_COMMUNITY", "openeuler")
|
|
727
|
-
code = oed_main.main(
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
"--help",
|
|
734
|
-
]
|
|
735
|
-
)
|
|
736
|
-
assert code == 0
|
|
737
|
-
assert "method" not in patched["captured"] # no network call
|
|
626
|
+
code = oed_main.main([
|
|
627
|
+
"software-package-server", "API_getSoftwarePackage",
|
|
628
|
+
"--id", "1", "--user-agent",
|
|
629
|
+
])
|
|
630
|
+
out = capsys.readouterr()
|
|
631
|
+
assert code == 1 and "missing_flag_value" in (out.out + out.err)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|