datalegion-cli 1.3.1__tar.gz → 1.5.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.
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/PKG-INFO +12 -2
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/README.md +10 -1
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/pyproject.toml +2 -1
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/src/datalegion_cli/client.py +13 -1
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/src/datalegion_cli/commands/company.py +1 -10
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/src/datalegion_cli/commands/person.py +1 -10
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/src/datalegion_cli/config.py +8 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/src/datalegion_cli/main.py +11 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/tests/test_client.py +44 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/tests/test_commands.py +28 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/.gitignore +0 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/src/datalegion_cli/__init__.py +0 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/src/datalegion_cli/async_typer.py +0 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/src/datalegion_cli/commands/__init__.py +0 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/src/datalegion_cli/commands/credits.py +0 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/src/datalegion_cli/commands/utility.py +0 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/src/datalegion_cli/logging.py +0 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/src/datalegion_cli/login.py +0 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/src/datalegion_cli/output.py +0 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/src/datalegion_cli/update.py +0 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/tests/__init__.py +0 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/tests/conftest.py +0 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/tests/test_config.py +0 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/tests/test_logging.py +0 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/tests/test_login.py +0 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/tests/test_output.py +0 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/tests/test_package_contents.py +0 -0
- {datalegion_cli-1.3.1 → datalegion_cli-1.5.0}/tests/test_update.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: datalegion-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
4
4
|
Summary: CLI for the Data Legion API — agent-friendly, fully async
|
|
5
5
|
Project-URL: Homepage, https://www.datalegion.ai
|
|
6
6
|
Project-URL: Documentation, https://www.datalegion.ai/docs
|
|
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
21
22
|
Classifier: Typing :: Typed
|
|
22
23
|
Requires-Python: >=3.11
|
|
23
24
|
Requires-Dist: cryptography>=44.0.0
|
|
@@ -164,7 +165,7 @@ datalegion-cli person enrich --name "Jane Smith" --company "Acme Corp" --city "N
|
|
|
164
165
|
datalegion-cli person enrich --social-url "https://linkedin.com/in/johndoe"
|
|
165
166
|
|
|
166
167
|
# Multiple results with minimum confidence
|
|
167
|
-
datalegion-cli person enrich --email john@example.com --
|
|
168
|
+
datalegion-cli person enrich --email john@example.com --min-confidence high
|
|
168
169
|
|
|
169
170
|
# Select specific fields
|
|
170
171
|
datalegion-cli person enrich --email john@example.com --fields "full_name,emails,phones"
|
|
@@ -270,12 +271,21 @@ datalegion-cli person enrich --email bad 2>/dev/null
|
|
|
270
271
|
# {"ok": false, "error": "bad_request", "message": "..."}
|
|
271
272
|
```
|
|
272
273
|
|
|
274
|
+
## Sandbox
|
|
275
|
+
|
|
276
|
+
Try commands without spending credits: `--sandbox` (or `LEGION_SANDBOX=1`, or `datalegion-cli config set sandbox true`) makes enrich, bulk enrich, search and discover return sample records with every field a real record has. Utilities, which are free, still call the real API. Sandbox discover answers only a few fixed example queries.
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
datalegion-cli --sandbox person enrich --email anyone@example.com
|
|
280
|
+
```
|
|
281
|
+
|
|
273
282
|
## Global flags
|
|
274
283
|
|
|
275
284
|
| Flag | Description |
|
|
276
285
|
|------|-------------|
|
|
277
286
|
| `--verbose` / `-v` | Enable debug logging to stderr |
|
|
278
287
|
| `--quiet` / `-q` | Suppress all log output |
|
|
288
|
+
| `--sandbox` | Return sample records from enrich, search and discover, using no credits |
|
|
279
289
|
| `--help` | Show help for any command |
|
|
280
290
|
|
|
281
291
|
## Development
|
|
@@ -130,7 +130,7 @@ datalegion-cli person enrich --name "Jane Smith" --company "Acme Corp" --city "N
|
|
|
130
130
|
datalegion-cli person enrich --social-url "https://linkedin.com/in/johndoe"
|
|
131
131
|
|
|
132
132
|
# Multiple results with minimum confidence
|
|
133
|
-
datalegion-cli person enrich --email john@example.com --
|
|
133
|
+
datalegion-cli person enrich --email john@example.com --min-confidence high
|
|
134
134
|
|
|
135
135
|
# Select specific fields
|
|
136
136
|
datalegion-cli person enrich --email john@example.com --fields "full_name,emails,phones"
|
|
@@ -236,12 +236,21 @@ datalegion-cli person enrich --email bad 2>/dev/null
|
|
|
236
236
|
# {"ok": false, "error": "bad_request", "message": "..."}
|
|
237
237
|
```
|
|
238
238
|
|
|
239
|
+
## Sandbox
|
|
240
|
+
|
|
241
|
+
Try commands without spending credits: `--sandbox` (or `LEGION_SANDBOX=1`, or `datalegion-cli config set sandbox true`) makes enrich, bulk enrich, search and discover return sample records with every field a real record has. Utilities, which are free, still call the real API. Sandbox discover answers only a few fixed example queries.
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
datalegion-cli --sandbox person enrich --email anyone@example.com
|
|
245
|
+
```
|
|
246
|
+
|
|
239
247
|
## Global flags
|
|
240
248
|
|
|
241
249
|
| Flag | Description |
|
|
242
250
|
|------|-------------|
|
|
243
251
|
| `--verbose` / `-v` | Enable debug logging to stderr |
|
|
244
252
|
| `--quiet` / `-q` | Suppress all log output |
|
|
253
|
+
| `--sandbox` | Return sample records from enrich, search and discover, using no credits |
|
|
245
254
|
| `--help` | Show help for any command |
|
|
246
255
|
|
|
247
256
|
## Development
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "datalegion-cli"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.5.0"
|
|
4
4
|
description = "CLI for the Data Legion API — agent-friendly, fully async"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -25,6 +25,7 @@ classifiers = [
|
|
|
25
25
|
"Programming Language :: Python :: 3.12",
|
|
26
26
|
"Programming Language :: Python :: 3.13",
|
|
27
27
|
"Programming Language :: Python :: 3.14",
|
|
28
|
+
"Programming Language :: Python :: 3.15",
|
|
28
29
|
"Environment :: Console",
|
|
29
30
|
"Typing :: Typed",
|
|
30
31
|
]
|
|
@@ -8,7 +8,7 @@ import httpx
|
|
|
8
8
|
from loguru import logger
|
|
9
9
|
|
|
10
10
|
from datalegion_cli import __version__
|
|
11
|
-
from datalegion_cli.config import get_api_key, get_base_url
|
|
11
|
+
from datalegion_cli.config import get_api_key, get_base_url, get_sandbox
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class APIError(Exception):
|
|
@@ -32,6 +32,15 @@ class APIError(Exception):
|
|
|
32
32
|
return d
|
|
33
33
|
|
|
34
34
|
|
|
35
|
+
# Endpoints the API mirrors under /sandbox/: sample records (Jane Sandbox, Sandbox Corp), no credits.
|
|
36
|
+
# Utilities, reports, credits and dynamic bulk have no sandbox and call the real API.
|
|
37
|
+
SANDBOX_PATHS = frozenset(
|
|
38
|
+
f"/{entity}/{op}"
|
|
39
|
+
for entity in ("person", "company")
|
|
40
|
+
for op in ("enrich", "enrich/bulk", "search", "discover")
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
35
44
|
class LegionClient:
|
|
36
45
|
"""Async client wrapping the Data Legion API."""
|
|
37
46
|
|
|
@@ -45,6 +54,7 @@ class LegionClient:
|
|
|
45
54
|
self._base_url = base_url or get_base_url()
|
|
46
55
|
self._api_key = (api_key or "").strip() or get_api_key()
|
|
47
56
|
self._timeout = timeout
|
|
57
|
+
self._sandbox = get_sandbox()
|
|
48
58
|
self._client: httpx.AsyncClient | None = None
|
|
49
59
|
self.last_credits_used: str | None = None
|
|
50
60
|
self.last_credits_remaining: str | None = None
|
|
@@ -77,6 +87,8 @@ class LegionClient:
|
|
|
77
87
|
params: dict[str, Any] | None = None,
|
|
78
88
|
) -> dict[str, Any] | list[Any]:
|
|
79
89
|
client = await self._get_client()
|
|
90
|
+
if self._sandbox and endpoint in SANDBOX_PATHS:
|
|
91
|
+
endpoint = f"/sandbox{endpoint}"
|
|
80
92
|
logger.debug("{} {}", method.upper(), endpoint)
|
|
81
93
|
|
|
82
94
|
if method.upper() == "GET":
|
|
@@ -31,7 +31,6 @@ MinConfidence = Annotated[
|
|
|
31
31
|
RequiredFields = Annotated[
|
|
32
32
|
str | None, typer.Option("--required", help="Comma-separated required fields.")
|
|
33
33
|
]
|
|
34
|
-
MultiOpt = Annotated[bool, typer.Option("--multiple", "-m", help="Return multiple matches.")]
|
|
35
34
|
|
|
36
35
|
|
|
37
36
|
@app.command()
|
|
@@ -52,8 +51,6 @@ async def enrich(
|
|
|
52
51
|
industry: Annotated[
|
|
53
52
|
str | None, typer.Option("--industry", help="Industry (narrows name matching).")
|
|
54
53
|
] = None,
|
|
55
|
-
multiple: MultiOpt = False,
|
|
56
|
-
limit: LimitOpt = 2,
|
|
57
54
|
min_confidence: MinConfidence = None,
|
|
58
55
|
titlecase: TitleOpt = False,
|
|
59
56
|
required_fields: RequiredFields = None,
|
|
@@ -67,7 +64,6 @@ async def enrich(
|
|
|
67
64
|
datalegion-cli company enrich --domain google.com
|
|
68
65
|
datalegion-cli company enrich --name "Acme Corp" --industry "Software"
|
|
69
66
|
datalegion-cli company enrich --ticker AAPL --fields "name,industry,revenue"
|
|
70
|
-
datalegion-cli company enrich --linkedin-id 1441 --multiple --limit 5
|
|
71
67
|
echo '{"domain":"stripe.com"}' | datalegion-cli company enrich --stdin
|
|
72
68
|
"""
|
|
73
69
|
if stdin:
|
|
@@ -81,8 +77,6 @@ async def enrich(
|
|
|
81
77
|
social_url=social_url,
|
|
82
78
|
ticker_symbol=ticker_symbol,
|
|
83
79
|
industry=industry,
|
|
84
|
-
multiple_results=multiple,
|
|
85
|
-
limit=limit,
|
|
86
80
|
min_confidence=min_confidence,
|
|
87
81
|
titlecase=titlecase,
|
|
88
82
|
required_fields=required_fields,
|
|
@@ -168,8 +162,6 @@ async def discover(
|
|
|
168
162
|
# @async_command
|
|
169
163
|
# async def bulk(
|
|
170
164
|
# file: Annotated[str, typer.Argument(help="Path to JSON file with items array, or '-' for stdin.")],
|
|
171
|
-
# multiple: MultiOpt = False,
|
|
172
|
-
# limit: LimitOpt = 2,
|
|
173
165
|
# titlecase: TitleOpt = False,
|
|
174
166
|
# ) -> None:
|
|
175
167
|
# """Bulk enrich up to 100 companies from a JSON file."""
|
|
@@ -184,8 +176,7 @@ async def discover(
|
|
|
184
176
|
# client = LegionClient()
|
|
185
177
|
# try:
|
|
186
178
|
# result = await client.company_bulk(
|
|
187
|
-
# items=items,
|
|
188
|
-
# limit=limit, titlecase=titlecase,
|
|
179
|
+
# items=items, titlecase=titlecase,
|
|
189
180
|
# )
|
|
190
181
|
# emit_json(result)
|
|
191
182
|
# emit_credits(client.last_credits_used, client.last_credits_remaining)
|
|
@@ -31,7 +31,6 @@ MinConfidence = Annotated[
|
|
|
31
31
|
RequiredFields = Annotated[
|
|
32
32
|
str | None, typer.Option("--required", help="Comma-separated required fields.")
|
|
33
33
|
]
|
|
34
|
-
MultiOpt = Annotated[bool, typer.Option("--multiple", "-m", help="Return multiple matches.")]
|
|
35
34
|
|
|
36
35
|
|
|
37
36
|
@app.command()
|
|
@@ -67,8 +66,6 @@ async def enrich(
|
|
|
67
66
|
postal_code: Annotated[
|
|
68
67
|
str | None, typer.Option("--postal-code", help="Postal/ZIP code.")
|
|
69
68
|
] = None,
|
|
70
|
-
multiple: MultiOpt = False,
|
|
71
|
-
limit: LimitOpt = 2,
|
|
72
69
|
min_confidence: MinConfidence = None,
|
|
73
70
|
titlecase: TitleOpt = False,
|
|
74
71
|
required_fields: RequiredFields = None,
|
|
@@ -82,7 +79,6 @@ async def enrich(
|
|
|
82
79
|
datalegion-cli person enrich --email john@example.com
|
|
83
80
|
datalegion-cli person enrich --name "Jane Doe" --company "Acme Corp"
|
|
84
81
|
datalegion-cli person enrich --phone "+14155551234" --fields "name,email,title"
|
|
85
|
-
datalegion-cli person enrich --linkedin-id 12345 --multiple --limit 3
|
|
86
82
|
echo '{"email":"jane@example.com"}' | datalegion-cli person enrich --stdin
|
|
87
83
|
"""
|
|
88
84
|
if stdin:
|
|
@@ -107,8 +103,6 @@ async def enrich(
|
|
|
107
103
|
birth_date=birth_date,
|
|
108
104
|
address=address,
|
|
109
105
|
postal_code=postal_code,
|
|
110
|
-
multiple_results=multiple,
|
|
111
|
-
limit=limit,
|
|
112
106
|
min_confidence=min_confidence,
|
|
113
107
|
titlecase=titlecase,
|
|
114
108
|
required_fields=required_fields,
|
|
@@ -194,8 +188,6 @@ async def discover(
|
|
|
194
188
|
# @async_command
|
|
195
189
|
# async def bulk(
|
|
196
190
|
# file: Annotated[str, typer.Argument(help="Path to JSON file with items array, or '-' for stdin.")],
|
|
197
|
-
# multiple: MultiOpt = False,
|
|
198
|
-
# limit: LimitOpt = 2,
|
|
199
191
|
# titlecase: TitleOpt = False,
|
|
200
192
|
# ) -> None:
|
|
201
193
|
# """Bulk enrich up to 100 people from a JSON file."""
|
|
@@ -211,8 +203,7 @@ async def discover(
|
|
|
211
203
|
# client = LegionClient()
|
|
212
204
|
# try:
|
|
213
205
|
# result = await client.person_bulk(
|
|
214
|
-
# items=items,
|
|
215
|
-
# limit=limit, titlecase=titlecase,
|
|
206
|
+
# items=items, titlecase=titlecase,
|
|
216
207
|
# )
|
|
217
208
|
# emit_json(result)
|
|
218
209
|
# emit_credits(client.last_credits_used, client.last_credits_remaining)
|
|
@@ -28,6 +28,7 @@ _DEFAULTS: dict[str, object] = {
|
|
|
28
28
|
"default_output": "json", # json | table | raw
|
|
29
29
|
"install_method": "", # brew | github | git | pip
|
|
30
30
|
"device_id": "", # random name for this machine's login key (see login.new_device_id)
|
|
31
|
+
"sandbox": False, # sample records, no credits (see client.SANDBOX_PATHS)
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
# Fields that are stored encrypted on disk
|
|
@@ -94,6 +95,8 @@ def load_config() -> dict[str, object]:
|
|
|
94
95
|
cfg["api_key"] = env_key
|
|
95
96
|
if env_url := os.environ.get("LEGION_API_URL"):
|
|
96
97
|
cfg["api_base_url"] = env_url
|
|
98
|
+
if env_sandbox := os.environ.get("LEGION_SANDBOX"):
|
|
99
|
+
cfg["sandbox"] = env_sandbox.strip().lower() in ("1", "true", "yes")
|
|
97
100
|
return cfg
|
|
98
101
|
|
|
99
102
|
|
|
@@ -127,3 +130,8 @@ def get_api_key() -> str:
|
|
|
127
130
|
|
|
128
131
|
def get_base_url() -> str:
|
|
129
132
|
return str(load_config().get("api_base_url", _DEFAULTS["api_base_url"]))
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def get_sandbox() -> bool:
|
|
136
|
+
"""Sandbox mode: the --sandbox flag, LEGION_SANDBOX, or `config set sandbox true`."""
|
|
137
|
+
return load_config().get("sandbox") is True
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import os
|
|
5
6
|
from typing import Annotated
|
|
6
7
|
|
|
7
8
|
import typer
|
|
@@ -36,9 +37,19 @@ app.add_typer(credits.app, name="credits")
|
|
|
36
37
|
def main_callback(
|
|
37
38
|
verbose: Annotated[bool, typer.Option("--verbose", "-v", help="Enable debug logging.")] = False,
|
|
38
39
|
quiet: Annotated[bool, typer.Option("--quiet", "-q", help="Suppress all log output.")] = False,
|
|
40
|
+
sandbox: Annotated[
|
|
41
|
+
bool,
|
|
42
|
+
typer.Option(
|
|
43
|
+
"--sandbox",
|
|
44
|
+
help="Return sample records from enrich, search and discover, using no credits.",
|
|
45
|
+
),
|
|
46
|
+
] = False,
|
|
39
47
|
) -> None:
|
|
40
48
|
"""Data Legion CLI."""
|
|
41
49
|
setup_logging(verbose=verbose, quiet=quiet)
|
|
50
|
+
if sandbox:
|
|
51
|
+
# Read by config.load_config, so every client this run creates is in sandbox mode.
|
|
52
|
+
os.environ["LEGION_SANDBOX"] = "1"
|
|
42
53
|
|
|
43
54
|
|
|
44
55
|
# ── Top-level commands ───────────────────────────────────────────────
|
|
@@ -210,3 +210,47 @@ class TestConvenienceMethods:
|
|
|
210
210
|
http = await self._setup_mock(mock_client, monkeypatch)
|
|
211
211
|
await mock_client.credits_usage(start_date="2026-01-01", end_date="2026-03-01")
|
|
212
212
|
assert http.get.call_args[0][0] == "/credits/usage"
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@pytest.mark.asyncio
|
|
216
|
+
class TestSandbox:
|
|
217
|
+
"""Sandbox mode sends only the endpoints the API mirrors to /sandbox/."""
|
|
218
|
+
|
|
219
|
+
async def _paths(self, monkeypatch, sandbox_env: str | None, endpoints: list[str]) -> list[str]:
|
|
220
|
+
if sandbox_env is None:
|
|
221
|
+
monkeypatch.delenv("LEGION_SANDBOX", raising=False)
|
|
222
|
+
else:
|
|
223
|
+
monkeypatch.setenv("LEGION_SANDBOX", sandbox_env)
|
|
224
|
+
client = LegionClient(base_url="https://x.local", api_key="legion_k")
|
|
225
|
+
mock_post = AsyncMock(return_value=make_response(200, {"matches": []}))
|
|
226
|
+
client._client = AsyncMock()
|
|
227
|
+
client._client.is_closed = False
|
|
228
|
+
client._client.post = mock_post
|
|
229
|
+
for endpoint in endpoints:
|
|
230
|
+
await client.request("POST", endpoint, payload={})
|
|
231
|
+
return [call.args[0] for call in mock_post.call_args_list]
|
|
232
|
+
|
|
233
|
+
async def test_sandbox_routes_only_mirrored_paths(self, monkeypatch):
|
|
234
|
+
paths = await self._paths(
|
|
235
|
+
monkeypatch, "1", ["/person/enrich", "/company/search", "/utility/clean"]
|
|
236
|
+
)
|
|
237
|
+
assert paths == ["/sandbox/person/enrich", "/sandbox/company/search", "/utility/clean"]
|
|
238
|
+
|
|
239
|
+
async def test_off_by_default(self, monkeypatch):
|
|
240
|
+
assert await self._paths(monkeypatch, None, ["/person/enrich"]) == ["/person/enrich"]
|
|
241
|
+
|
|
242
|
+
async def test_env_false_is_off(self, monkeypatch):
|
|
243
|
+
assert await self._paths(monkeypatch, "false", ["/person/enrich"]) == ["/person/enrich"]
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def test_sandbox_flag_turns_it_on(monkeypatch):
|
|
247
|
+
from typer.testing import CliRunner
|
|
248
|
+
|
|
249
|
+
from datalegion_cli.main import app
|
|
250
|
+
|
|
251
|
+
monkeypatch.delenv("LEGION_SANDBOX", raising=False)
|
|
252
|
+
CliRunner().invoke(app, ["--sandbox", "version"])
|
|
253
|
+
import os
|
|
254
|
+
|
|
255
|
+
assert os.environ.get("LEGION_SANDBOX") == "1"
|
|
256
|
+
monkeypatch.delenv("LEGION_SANDBOX", raising=False)
|
|
@@ -438,3 +438,31 @@ class TestCreditsUsage:
|
|
|
438
438
|
with _mock_client_request(response):
|
|
439
439
|
result = runner.invoke(app, ["credits", "usage"])
|
|
440
440
|
assert result.exit_code == 0
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
# ── Several matches per lookup is an enterprise feature: no CLI option ──
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
class TestEnrichOffersNoMultipleResults:
|
|
447
|
+
def test_enrich_rejects_multiple_and_limit(self, monkeypatch):
|
|
448
|
+
_mock_config(monkeypatch)
|
|
449
|
+
for entity, ident in (("person", ["--email", "a@b.co"]), ("company", ["--domain", "b.co"])):
|
|
450
|
+
for option in (["--multiple"], ["--limit", "3"]):
|
|
451
|
+
result = runner.invoke(app, [entity, "enrich", *ident, *option])
|
|
452
|
+
assert result.exit_code != 0, (entity, option)
|
|
453
|
+
|
|
454
|
+
def test_enrich_payload_carries_neither_field(self, monkeypatch):
|
|
455
|
+
_mock_config(monkeypatch)
|
|
456
|
+
sent: list[dict] = []
|
|
457
|
+
|
|
458
|
+
async def capture(self, method, endpoint, *, payload=None, params=None):
|
|
459
|
+
sent.append(payload or {})
|
|
460
|
+
return {"matches": [], "total": 0}
|
|
461
|
+
|
|
462
|
+
with patch("datalegion_cli.client.LegionClient.request", capture):
|
|
463
|
+
runner.invoke(app, ["person", "enrich", "--email", "a@b.co"])
|
|
464
|
+
runner.invoke(app, ["company", "enrich", "--domain", "b.co"])
|
|
465
|
+
assert len(sent) == 2
|
|
466
|
+
for payload in sent:
|
|
467
|
+
assert "multiple_results" not in payload
|
|
468
|
+
assert "limit" not in payload
|
|
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
|