impreza-cli 0.6.0__tar.gz → 0.7.0__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.
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/PKG-INFO +21 -11
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/README.md +19 -9
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/commands/context.py +18 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/config.py +6 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/main.py +10 -1
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/sdk.py +6 -3
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/state.py +5 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli.egg-info/PKG-INFO +21 -11
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli.egg-info/requires.txt +1 -1
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/pyproject.toml +1 -1
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_context_commands.py +116 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/__init__.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/commands/__init__.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/commands/_helpers.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/commands/account.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/commands/catalog.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/commands/config.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/commands/dedicated.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/commands/doctor.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/commands/domain.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/commands/invoice.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/commands/key.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/commands/orders.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/commands/services.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/commands/vps.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/commands/vps_cloud.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/commands/vps_proxmox.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/commands/webhooks.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli/output.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli.egg-info/SOURCES.txt +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli.egg-info/dependency_links.txt +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli.egg-info/entry_points.txt +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/impreza_cli.egg-info/top_level.txt +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/setup.cfg +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/.gitkeep +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/__init__.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/conftest.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_account_commands.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_account_topup_commands.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_catalog_commands.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_config.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_config_commands.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_dedicated_commands.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_doctor_commands.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_domain_commands.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_invoice_commands.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_key_commands.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_orders_commands.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_phase_2_2_smoke.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_phase_2_3_smoke.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_phase_2_4_smoke.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_phase_2_5_smoke.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_phase_2_6_smoke.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_phase_3_1_smoke.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_phase_3_2_smoke.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_phase_3_3_smoke.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_phase_3_4_smoke.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_phase_3_5_smoke.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_phase_3_6_smoke.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_phase_3_7_smoke.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_services_commands.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_vps_cloud_commands.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_vps_commands.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_vps_proxmox_commands.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_webhooks_commands.py +0 -0
- {impreza_cli-0.6.0 → impreza_cli-0.7.0}/tests/test_yaml_output.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: impreza-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Official command-line interface for the Impreza Host public REST API
|
|
5
5
|
Author-email: Impreza Host <support@imprezahost.com>
|
|
6
6
|
License: MIT
|
|
@@ -23,7 +23,7 @@ Classifier: Topic :: Internet :: WWW/HTTP
|
|
|
23
23
|
Classifier: Topic :: Utilities
|
|
24
24
|
Requires-Python: >=3.10
|
|
25
25
|
Description-Content-Type: text/markdown
|
|
26
|
-
Requires-Dist: impreza-sdk<0.
|
|
26
|
+
Requires-Dist: impreza-sdk<0.8,>=0.7.0
|
|
27
27
|
Requires-Dist: typer>=0.12
|
|
28
28
|
Requires-Dist: rich>=13.7
|
|
29
29
|
Requires-Dist: click>=8.0
|
|
@@ -227,23 +227,33 @@ equivalent) and `impreza <TAB>` should suggest resource groups,
|
|
|
227
227
|
|
|
228
228
|
## Tor
|
|
229
229
|
|
|
230
|
-
|
|
230
|
+
Four knobs, from the most persistent to the most one-off:
|
|
231
231
|
|
|
232
232
|
```bash
|
|
233
|
-
#
|
|
234
|
-
impreza context create offshore
|
|
235
|
-
|
|
236
|
-
|
|
233
|
+
# Persisted at context creation (writes [settings] use_tor=true)
|
|
234
|
+
impreza context create offshore --key imp_... --secret ... --via-tor
|
|
235
|
+
|
|
236
|
+
# Explicit SOCKS5 proxy instead (wins over use_tor; works for any
|
|
237
|
+
# SOCKS5, not just Tor)
|
|
238
|
+
impreza context create offshore --key imp_... --secret ... --proxy socks5://127.0.0.1:9050
|
|
239
|
+
|
|
240
|
+
# One-shot global flag on any command
|
|
241
|
+
impreza --via-tor account info
|
|
237
242
|
|
|
238
243
|
# Env var, picked up by the SDK transparently
|
|
239
244
|
IMPREZA_USE_TOR=1 impreza account info
|
|
240
|
-
|
|
241
|
-
# Programmatic via the SDK (Python users skip the CLI for this)
|
|
242
245
|
```
|
|
243
246
|
|
|
247
|
+
All four route through the local Tor daemon's SOCKS5 port with remote
|
|
248
|
+
DNS (no local lookup of the API hostname). Because exit nodes rotate,
|
|
249
|
+
set the API key's IP factor to `tofu` or `keyonly` in the client area —
|
|
250
|
+
`whitelist` cannot work without a stable egress IP. When the account
|
|
251
|
+
uses the API's `.onion` mirror, the IP factor sees a stable loopback
|
|
252
|
+
instead.
|
|
253
|
+
|
|
244
254
|
The SDK's `auto_tor=True` path (probe Tor, fall back to clearnet)
|
|
245
|
-
isn't surfaced through the CLI yet —
|
|
246
|
-
|
|
255
|
+
isn't surfaced through the CLI yet — it is a fail-open convenience,
|
|
256
|
+
which is the wrong default here.
|
|
247
257
|
|
|
248
258
|
## Error handling
|
|
249
259
|
|
|
@@ -185,23 +185,33 @@ equivalent) and `impreza <TAB>` should suggest resource groups,
|
|
|
185
185
|
|
|
186
186
|
## Tor
|
|
187
187
|
|
|
188
|
-
|
|
188
|
+
Four knobs, from the most persistent to the most one-off:
|
|
189
189
|
|
|
190
190
|
```bash
|
|
191
|
-
#
|
|
192
|
-
impreza context create offshore
|
|
193
|
-
|
|
194
|
-
|
|
191
|
+
# Persisted at context creation (writes [settings] use_tor=true)
|
|
192
|
+
impreza context create offshore --key imp_... --secret ... --via-tor
|
|
193
|
+
|
|
194
|
+
# Explicit SOCKS5 proxy instead (wins over use_tor; works for any
|
|
195
|
+
# SOCKS5, not just Tor)
|
|
196
|
+
impreza context create offshore --key imp_... --secret ... --proxy socks5://127.0.0.1:9050
|
|
197
|
+
|
|
198
|
+
# One-shot global flag on any command
|
|
199
|
+
impreza --via-tor account info
|
|
195
200
|
|
|
196
201
|
# Env var, picked up by the SDK transparently
|
|
197
202
|
IMPREZA_USE_TOR=1 impreza account info
|
|
198
|
-
|
|
199
|
-
# Programmatic via the SDK (Python users skip the CLI for this)
|
|
200
203
|
```
|
|
201
204
|
|
|
205
|
+
All four route through the local Tor daemon's SOCKS5 port with remote
|
|
206
|
+
DNS (no local lookup of the API hostname). Because exit nodes rotate,
|
|
207
|
+
set the API key's IP factor to `tofu` or `keyonly` in the client area —
|
|
208
|
+
`whitelist` cannot work without a stable egress IP. When the account
|
|
209
|
+
uses the API's `.onion` mirror, the IP factor sees a stable loopback
|
|
210
|
+
instead.
|
|
211
|
+
|
|
202
212
|
The SDK's `auto_tor=True` path (probe Tor, fall back to clearnet)
|
|
203
|
-
isn't surfaced through the CLI yet —
|
|
204
|
-
|
|
213
|
+
isn't surfaced through the CLI yet — it is a fail-open convenience,
|
|
214
|
+
which is the wrong default here.
|
|
205
215
|
|
|
206
216
|
## Error handling
|
|
207
217
|
|
|
@@ -88,6 +88,18 @@ def create(
|
|
|
88
88
|
help="Optional default output format for this context.",
|
|
89
89
|
case_sensitive=False,
|
|
90
90
|
),
|
|
91
|
+
via_tor: bool = typer.Option(
|
|
92
|
+
False,
|
|
93
|
+
"--via-tor",
|
|
94
|
+
help="Route API calls through the local Tor daemon "
|
|
95
|
+
"(sets [settings] use_tor=true, CLI-wide).",
|
|
96
|
+
),
|
|
97
|
+
proxy: str | None = typer.Option(
|
|
98
|
+
None,
|
|
99
|
+
"--proxy",
|
|
100
|
+
help="Explicit SOCKS5 proxy, e.g. socks5://127.0.0.1:9050 "
|
|
101
|
+
"(sets [settings] proxy, CLI-wide; wins over use_tor).",
|
|
102
|
+
),
|
|
91
103
|
overwrite: bool = typer.Option(
|
|
92
104
|
False,
|
|
93
105
|
"--overwrite",
|
|
@@ -108,6 +120,12 @@ def create(
|
|
|
108
120
|
)
|
|
109
121
|
except (ContextAlreadyExists, InvalidContextName) as exc:
|
|
110
122
|
_exit_on_config_error(exc)
|
|
123
|
+
|
|
124
|
+
if via_tor or proxy:
|
|
125
|
+
if via_tor:
|
|
126
|
+
cfg.settings.use_tor = True
|
|
127
|
+
if proxy:
|
|
128
|
+
cfg.settings.proxy = proxy
|
|
111
129
|
cfg.save()
|
|
112
130
|
|
|
113
131
|
is_default = cfg.default_context == name
|
|
@@ -167,6 +167,7 @@ class _Settings:
|
|
|
167
167
|
|
|
168
168
|
poll_interval: float | None = None
|
|
169
169
|
use_tor: bool | None = None
|
|
170
|
+
proxy: str | None = None
|
|
170
171
|
|
|
171
172
|
def to_toml_dict(self) -> dict[str, Any]:
|
|
172
173
|
body: dict[str, Any] = {}
|
|
@@ -174,6 +175,8 @@ class _Settings:
|
|
|
174
175
|
body["poll_interval"] = self.poll_interval
|
|
175
176
|
if self.use_tor is not None:
|
|
176
177
|
body["use_tor"] = self.use_tor
|
|
178
|
+
if self.proxy is not None:
|
|
179
|
+
body["proxy"] = self.proxy
|
|
177
180
|
return body
|
|
178
181
|
|
|
179
182
|
|
|
@@ -267,6 +270,9 @@ class Config:
|
|
|
267
270
|
tor = settings_raw.get("use_tor")
|
|
268
271
|
if isinstance(tor, bool):
|
|
269
272
|
settings.use_tor = tor
|
|
273
|
+
proxy = settings_raw.get("proxy")
|
|
274
|
+
if isinstance(proxy, str) and proxy:
|
|
275
|
+
settings.proxy = proxy
|
|
270
276
|
|
|
271
277
|
default_context = raw.get("default_context")
|
|
272
278
|
if not isinstance(default_context, str):
|
|
@@ -62,6 +62,15 @@ def _root(
|
|
|
62
62
|
"-c",
|
|
63
63
|
help="Override the default context for this invocation.",
|
|
64
64
|
),
|
|
65
|
+
via_tor: bool = typer.Option(
|
|
66
|
+
False,
|
|
67
|
+
"--via-tor",
|
|
68
|
+
help=(
|
|
69
|
+
"Route this invocation through the local Tor daemon "
|
|
70
|
+
"(SOCKS5 127.0.0.1:9050). Overrides [settings] use_tor once; "
|
|
71
|
+
"persist it with `impreza context create --via-tor`."
|
|
72
|
+
),
|
|
73
|
+
),
|
|
65
74
|
output: OutputFormat | None = typer.Option(
|
|
66
75
|
None,
|
|
67
76
|
"--output",
|
|
@@ -78,7 +87,7 @@ def _root(
|
|
|
78
87
|
Global flags ``--context`` and ``--output`` apply to every
|
|
79
88
|
subcommand and are inherited via Typer's context object.
|
|
80
89
|
"""
|
|
81
|
-
ctx.obj = GlobalState(context_override=context, output=output)
|
|
90
|
+
ctx.obj = GlobalState(context_override=context, output=output, via_tor=via_tor)
|
|
82
91
|
|
|
83
92
|
|
|
84
93
|
# ── Subcommand mounts ────────────────────────────────────────────────
|
|
@@ -69,10 +69,13 @@ def make_client(
|
|
|
69
69
|
if ctx.base_url:
|
|
70
70
|
kwargs["base_url"] = ctx.base_url
|
|
71
71
|
|
|
72
|
-
# Tor preference
|
|
73
|
-
#
|
|
72
|
+
# Tor/proxy preference: an explicit [settings] proxy wins; then the
|
|
73
|
+
# one-shot global --via-tor flag; then the CLI-wide use_tor setting.
|
|
74
|
+
# A future per-context override (one VPN context, one clearnet, etc.)
|
|
74
75
|
# would slot in here without touching callers.
|
|
75
|
-
if cfg.settings.
|
|
76
|
+
if cfg.settings.proxy:
|
|
77
|
+
kwargs["proxy"] = cfg.settings.proxy
|
|
78
|
+
elif state.via_tor or cfg.settings.use_tor:
|
|
76
79
|
kwargs["use_tor"] = True
|
|
77
80
|
|
|
78
81
|
return Client(**kwargs)
|
|
@@ -31,6 +31,11 @@ class GlobalState:
|
|
|
31
31
|
#: ``None`` means "use the config file's ``default_context``".
|
|
32
32
|
context_override: str | None = None
|
|
33
33
|
|
|
34
|
+
#: One-shot Tor routing selected via the global ``--via-tor`` flag.
|
|
35
|
+
#: Overrides the config file's ``[settings] use_tor`` for this
|
|
36
|
+
#: invocation only; ``None``/False means "follow the config".
|
|
37
|
+
via_tor: bool = False
|
|
38
|
+
|
|
34
39
|
#: Default output format selected via the global ``--output`` flag.
|
|
35
40
|
#: Per-command ``--output`` flags can override this on a single
|
|
36
41
|
#: invocation; ``None`` here means "no preference, fall back to
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: impreza-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Official command-line interface for the Impreza Host public REST API
|
|
5
5
|
Author-email: Impreza Host <support@imprezahost.com>
|
|
6
6
|
License: MIT
|
|
@@ -23,7 +23,7 @@ Classifier: Topic :: Internet :: WWW/HTTP
|
|
|
23
23
|
Classifier: Topic :: Utilities
|
|
24
24
|
Requires-Python: >=3.10
|
|
25
25
|
Description-Content-Type: text/markdown
|
|
26
|
-
Requires-Dist: impreza-sdk<0.
|
|
26
|
+
Requires-Dist: impreza-sdk<0.8,>=0.7.0
|
|
27
27
|
Requires-Dist: typer>=0.12
|
|
28
28
|
Requires-Dist: rich>=13.7
|
|
29
29
|
Requires-Dist: click>=8.0
|
|
@@ -227,23 +227,33 @@ equivalent) and `impreza <TAB>` should suggest resource groups,
|
|
|
227
227
|
|
|
228
228
|
## Tor
|
|
229
229
|
|
|
230
|
-
|
|
230
|
+
Four knobs, from the most persistent to the most one-off:
|
|
231
231
|
|
|
232
232
|
```bash
|
|
233
|
-
#
|
|
234
|
-
impreza context create offshore
|
|
235
|
-
|
|
236
|
-
|
|
233
|
+
# Persisted at context creation (writes [settings] use_tor=true)
|
|
234
|
+
impreza context create offshore --key imp_... --secret ... --via-tor
|
|
235
|
+
|
|
236
|
+
# Explicit SOCKS5 proxy instead (wins over use_tor; works for any
|
|
237
|
+
# SOCKS5, not just Tor)
|
|
238
|
+
impreza context create offshore --key imp_... --secret ... --proxy socks5://127.0.0.1:9050
|
|
239
|
+
|
|
240
|
+
# One-shot global flag on any command
|
|
241
|
+
impreza --via-tor account info
|
|
237
242
|
|
|
238
243
|
# Env var, picked up by the SDK transparently
|
|
239
244
|
IMPREZA_USE_TOR=1 impreza account info
|
|
240
|
-
|
|
241
|
-
# Programmatic via the SDK (Python users skip the CLI for this)
|
|
242
245
|
```
|
|
243
246
|
|
|
247
|
+
All four route through the local Tor daemon's SOCKS5 port with remote
|
|
248
|
+
DNS (no local lookup of the API hostname). Because exit nodes rotate,
|
|
249
|
+
set the API key's IP factor to `tofu` or `keyonly` in the client area —
|
|
250
|
+
`whitelist` cannot work without a stable egress IP. When the account
|
|
251
|
+
uses the API's `.onion` mirror, the IP factor sees a stable loopback
|
|
252
|
+
instead.
|
|
253
|
+
|
|
244
254
|
The SDK's `auto_tor=True` path (probe Tor, fall back to clearnet)
|
|
245
|
-
isn't surfaced through the CLI yet —
|
|
246
|
-
|
|
255
|
+
isn't surfaced through the CLI yet — it is a fail-open convenience,
|
|
256
|
+
which is the wrong default here.
|
|
247
257
|
|
|
248
258
|
## Error handling
|
|
249
259
|
|
|
@@ -41,7 +41,7 @@ dependencies = [
|
|
|
41
41
|
# breaking (see CHANGELOG). Raise BOTH bounds in the same commit that
|
|
42
42
|
# makes the CLI depend on a newer SDK feature, or this stops
|
|
43
43
|
# protecting anything.
|
|
44
|
-
"impreza-sdk>=0.
|
|
44
|
+
"impreza-sdk>=0.7.0,<0.8",
|
|
45
45
|
"typer>=0.12",
|
|
46
46
|
"rich>=13.7",
|
|
47
47
|
# Declared explicitly because `config.py` imports click directly, for
|
|
@@ -16,6 +16,8 @@ from typer.testing import CliRunner
|
|
|
16
16
|
|
|
17
17
|
from impreza_cli.config import Config
|
|
18
18
|
from impreza_cli.main import app
|
|
19
|
+
from impreza_cli.sdk import make_client
|
|
20
|
+
from impreza_cli.state import GlobalState
|
|
19
21
|
|
|
20
22
|
# Click 8.2+ removed `mix_stderr` from CliRunner — stderr is always
|
|
21
23
|
# captured separately now via result.stderr.
|
|
@@ -322,3 +324,117 @@ def test_full_lifecycle(isolated_config: Path, output_fmt: str) -> None:
|
|
|
322
324
|
assert result.exit_code == 0
|
|
323
325
|
assert "work" in result.stdout
|
|
324
326
|
assert "personal" not in result.stdout
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
# ── Tor / proxy flags ─────────────────────────────────
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def test_create_via_tor_sets_use_tor_setting(isolated_config: Path) -> None:
|
|
333
|
+
result = runner.invoke(
|
|
334
|
+
app,
|
|
335
|
+
[
|
|
336
|
+
"context", "create", "tor",
|
|
337
|
+
"--key", _FAKE_KEY, "--secret", _FAKE_SECRET, "--via-tor",
|
|
338
|
+
],
|
|
339
|
+
)
|
|
340
|
+
assert result.exit_code == 0
|
|
341
|
+
cfg = Config.load(isolated_config)
|
|
342
|
+
assert cfg.settings.use_tor is True
|
|
343
|
+
assert cfg.settings.proxy is None
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def test_create_with_proxy_sets_proxy_setting(isolated_config: Path) -> None:
|
|
347
|
+
result = runner.invoke(
|
|
348
|
+
app,
|
|
349
|
+
[
|
|
350
|
+
"context", "create", "tor",
|
|
351
|
+
"--key", _FAKE_KEY, "--secret", _FAKE_SECRET,
|
|
352
|
+
"--proxy", "socks5://127.0.0.1:9050",
|
|
353
|
+
],
|
|
354
|
+
)
|
|
355
|
+
assert result.exit_code == 0
|
|
356
|
+
cfg = Config.load(isolated_config)
|
|
357
|
+
assert cfg.settings.proxy == "socks5://127.0.0.1:9050"
|
|
358
|
+
assert cfg.settings.use_tor is None # proxy wins; use_tor untouched
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def test_create_without_tor_flags_leaves_settings_empty(
|
|
362
|
+
isolated_config: Path,
|
|
363
|
+
) -> None:
|
|
364
|
+
result = runner.invoke(
|
|
365
|
+
app,
|
|
366
|
+
["context", "create", "plain", "--key", _FAKE_KEY, "--secret", _FAKE_SECRET],
|
|
367
|
+
)
|
|
368
|
+
assert result.exit_code == 0
|
|
369
|
+
cfg = Config.load(isolated_config)
|
|
370
|
+
assert cfg.settings.use_tor is None
|
|
371
|
+
assert cfg.settings.proxy is None
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def _capture_client_kwargs(monkeypatch: pytest.MonkeyPatch) -> dict[str, object]:
|
|
375
|
+
captured: dict[str, object] = {}
|
|
376
|
+
|
|
377
|
+
class _FakeClient:
|
|
378
|
+
def __init__(self, **kwargs: object) -> None:
|
|
379
|
+
captured.update(kwargs)
|
|
380
|
+
|
|
381
|
+
monkeypatch.setattr("impreza_cli.sdk.Client", _FakeClient)
|
|
382
|
+
return captured
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def test_make_client_proxy_setting_wins(
|
|
386
|
+
isolated_config: Path, monkeypatch: pytest.MonkeyPatch
|
|
387
|
+
) -> None:
|
|
388
|
+
runner.invoke(
|
|
389
|
+
app,
|
|
390
|
+
[
|
|
391
|
+
"context", "create", "tor",
|
|
392
|
+
"--key", _FAKE_KEY, "--secret", _FAKE_SECRET,
|
|
393
|
+
"--via-tor", "--proxy", "socks5://127.0.0.1:9150",
|
|
394
|
+
],
|
|
395
|
+
)
|
|
396
|
+
captured = _capture_client_kwargs(monkeypatch)
|
|
397
|
+
make_client(GlobalState())
|
|
398
|
+
assert captured.get("proxy") == "socks5://127.0.0.1:9150"
|
|
399
|
+
assert "use_tor" not in captured
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def test_make_client_use_tor_from_settings(
|
|
403
|
+
isolated_config: Path, monkeypatch: pytest.MonkeyPatch
|
|
404
|
+
) -> None:
|
|
405
|
+
runner.invoke(
|
|
406
|
+
app,
|
|
407
|
+
[
|
|
408
|
+
"context", "create", "tor",
|
|
409
|
+
"--key", _FAKE_KEY, "--secret", _FAKE_SECRET, "--via-tor",
|
|
410
|
+
],
|
|
411
|
+
)
|
|
412
|
+
captured = _capture_client_kwargs(monkeypatch)
|
|
413
|
+
make_client(GlobalState())
|
|
414
|
+
assert captured.get("use_tor") is True
|
|
415
|
+
assert "proxy" not in captured
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def test_make_client_global_via_tor_flag(
|
|
419
|
+
isolated_config: Path, monkeypatch: pytest.MonkeyPatch
|
|
420
|
+
) -> None:
|
|
421
|
+
runner.invoke(
|
|
422
|
+
app,
|
|
423
|
+
["context", "create", "plain", "--key", _FAKE_KEY, "--secret", _FAKE_SECRET],
|
|
424
|
+
)
|
|
425
|
+
captured = _capture_client_kwargs(monkeypatch)
|
|
426
|
+
make_client(GlobalState(via_tor=True))
|
|
427
|
+
assert captured.get("use_tor") is True
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def test_make_client_clearnet_default(
|
|
431
|
+
isolated_config: Path, monkeypatch: pytest.MonkeyPatch
|
|
432
|
+
) -> None:
|
|
433
|
+
runner.invoke(
|
|
434
|
+
app,
|
|
435
|
+
["context", "create", "plain", "--key", _FAKE_KEY, "--secret", _FAKE_SECRET],
|
|
436
|
+
)
|
|
437
|
+
captured = _capture_client_kwargs(monkeypatch)
|
|
438
|
+
make_client(GlobalState())
|
|
439
|
+
assert "use_tor" not in captured
|
|
440
|
+
assert "proxy" not in captured
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|