teardrop-cli 0.3.3__tar.gz → 0.3.4__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.
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/PKG-INFO +1 -1
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/docs/cli-reference.md +9 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/pyproject.toml +1 -1
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/cli.py +19 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/commands/event_triggers.py +13 -1
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/commands/schedules.py +54 -3
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/smoke/conftest.py +1 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/test_schedules.py +111 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/.github/workflows/publish.yml +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/.github/workflows/test.yml +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/.gitignore +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/.vscode/settings.json +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/LICENSE +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/README.md +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/docs/cli-json-schema.md +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/_fixtures.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/_lazy.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/_templates.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/commands/__init__.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/commands/agent_tools.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/commands/auth.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/commands/balance.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/commands/chat.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/commands/config_cmd.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/commands/earnings.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/commands/llm_config.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/commands/marketplace.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/commands/mcp.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/commands/models.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/commands/quickstart.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/commands/run.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/commands/tools.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/commands/usage.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/config.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/formatting.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/src/teardrop_cli/pricing.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/conftest.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/e2e/README.md +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/e2e/__init__.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/e2e/conftest.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/e2e/test_auth_e2e.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/e2e/test_billing_e2e.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/e2e/test_marketplace_e2e.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/e2e/test_run_e2e.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/e2e/test_signup_e2e.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/e2e/test_siwe_login_e2e.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/smoke/__init__.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/smoke/test_config_persistence.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/smoke/test_keyring_integration.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/smoke/test_perf_lazygroup.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/smoke/test_siwe_signing.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/test_auth.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/test_billing.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/test_chat.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/test_config_cmd.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/test_credential_priority.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/test_earnings.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/test_event_triggers.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/test_init_and_byok.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/test_llm_config.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/test_marketplace.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/test_mcp.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/test_models.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/test_quickstart.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/test_run.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/test_signup.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/test_token_expiry_fallback.py +0 -0
- {teardrop_cli-0.3.3 → teardrop_cli-0.3.4}/tests/test_tools.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: teardrop-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Command-line interface for the Teardrop AI agent platform
|
|
5
5
|
Project-URL: Homepage, https://github.com/teardrop-ai/teardrop-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/teardrop-ai/teardrop-cli
|
|
@@ -133,6 +133,13 @@ teardrop schedules create \
|
|
|
133
133
|
--interval-seconds 3600 \
|
|
134
134
|
--json
|
|
135
135
|
|
|
136
|
+
# Read the prompt from a UTF-8 file (or '-' for stdin) instead of --prompt:
|
|
137
|
+
teardrop schedules create \
|
|
138
|
+
--name hourly-briefing \
|
|
139
|
+
--prompt-file ./task.md \
|
|
140
|
+
--interval-seconds 3600 \
|
|
141
|
+
--json
|
|
142
|
+
|
|
136
143
|
teardrop schedules list --json
|
|
137
144
|
teardrop schedules get <schedule-id> --json
|
|
138
145
|
teardrop schedules update <schedule-id> --enabled false --json
|
|
@@ -142,6 +149,8 @@ teardrop schedules delete <schedule-id>
|
|
|
142
149
|
teardrop schedules delete <schedule-id> --yes
|
|
143
150
|
```
|
|
144
151
|
|
|
152
|
+
Exactly one of `--prompt` or `--prompt-file` is required. `--prompt-file` reads UTF-8 (a leading BOM is stripped) and preserves newlines and emoji; pass `-` to read the prompt from stdin.
|
|
153
|
+
|
|
145
154
|
Non-JSON list output includes: `ID`, `Name`, `Enabled`, `Interval`, `Consecutive Failures`, `Last Run At`.
|
|
146
155
|
|
|
147
156
|
Run history output includes: `Run ID`, `Status`, `Cost (USDC)`, `Error Message`, `Executed At`.
|
|
@@ -8,11 +8,30 @@ modules are imported only when the subcommand is actually invoked, keeping
|
|
|
8
8
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
|
+
import sys
|
|
12
|
+
from contextlib import suppress
|
|
13
|
+
|
|
11
14
|
import click
|
|
12
15
|
|
|
13
16
|
from teardrop_cli._lazy import LazyGroup
|
|
14
17
|
|
|
15
18
|
|
|
19
|
+
def _force_utf8_stdio() -> None:
|
|
20
|
+
"""Reconfigure stdout/stderr to UTF-8.
|
|
21
|
+
|
|
22
|
+
On legacy Windows consoles the default codec (e.g. cp1252) cannot encode
|
|
23
|
+
non-ASCII characters such as emoji in prompts or JSON output, raising
|
|
24
|
+
``UnicodeEncodeError``. Forcing UTF-8 keeps ``--json`` and rich output
|
|
25
|
+
working regardless of the console code page.
|
|
26
|
+
"""
|
|
27
|
+
for stream in (sys.stdout, sys.stderr):
|
|
28
|
+
with suppress(AttributeError, ValueError):
|
|
29
|
+
stream.reconfigure(encoding="utf-8") # type: ignore[union-attr]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
_force_utf8_stdio()
|
|
33
|
+
|
|
34
|
+
|
|
16
35
|
@click.group(
|
|
17
36
|
name="teardrop",
|
|
18
37
|
cls=LazyGroup,
|
|
@@ -23,6 +23,15 @@ def _to_dict(value: Any) -> dict[str, Any]:
|
|
|
23
23
|
return dict(value)
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
def _list_items(value: Any) -> Any:
|
|
27
|
+
if isinstance(value, dict):
|
|
28
|
+
return value.get("items") or []
|
|
29
|
+
items = getattr(value, "items", None)
|
|
30
|
+
if items is not None and not callable(items):
|
|
31
|
+
return items
|
|
32
|
+
return value
|
|
33
|
+
|
|
34
|
+
|
|
26
35
|
def _to_page_dict(value: Any) -> dict[str, Any]:
|
|
27
36
|
data = _to_dict(value)
|
|
28
37
|
data["items"] = [_to_dict(item) for item in data.get("items") or []]
|
|
@@ -223,7 +232,10 @@ def list_cmd(
|
|
|
223
232
|
operation=lambda client: client.event_triggers.list(),
|
|
224
233
|
)
|
|
225
234
|
|
|
226
|
-
|
|
235
|
+
# The SDK returns an EventTriggerListResponse model with an ``items``
|
|
236
|
+
# field (plus ``next_cursor``). Iterating the model directly would yield
|
|
237
|
+
# its field tuples, so unwrap ``.items`` when present.
|
|
238
|
+
items = [_to_dict(item) for item in _list_items(result)]
|
|
227
239
|
if as_json:
|
|
228
240
|
print_json(items)
|
|
229
241
|
return
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
import sys
|
|
6
7
|
from collections.abc import Awaitable, Callable
|
|
8
|
+
from pathlib import Path
|
|
7
9
|
from typing import Annotated, Any
|
|
8
10
|
|
|
9
11
|
import typer
|
|
@@ -23,6 +25,32 @@ def _to_dict(value: Any) -> dict[str, Any]:
|
|
|
23
25
|
return dict(value)
|
|
24
26
|
|
|
25
27
|
|
|
28
|
+
def _list_items(value: Any) -> Any:
|
|
29
|
+
if isinstance(value, dict):
|
|
30
|
+
return value.get("items") or []
|
|
31
|
+
items = getattr(value, "items", None)
|
|
32
|
+
if items is not None and not callable(items):
|
|
33
|
+
return items
|
|
34
|
+
return value
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _read_prompt_file(path: str) -> str:
|
|
38
|
+
"""Read prompt text from *path* (UTF-8), or stdin when *path* is ``-``."""
|
|
39
|
+
from teardrop_cli.formatting import print_error
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
if path == "-":
|
|
43
|
+
return sys.stdin.read()
|
|
44
|
+
text = Path(path).read_text(encoding="utf-8-sig")
|
|
45
|
+
return text
|
|
46
|
+
except UnicodeError as exc:
|
|
47
|
+
print_error(f"Could not decode --prompt-file {path!r} as UTF-8: {exc}")
|
|
48
|
+
raise typer.Exit(1) from None
|
|
49
|
+
except OSError as exc:
|
|
50
|
+
print_error(f"Could not read --prompt-file {path!r}: {exc}")
|
|
51
|
+
raise typer.Exit(1) from None
|
|
52
|
+
|
|
53
|
+
|
|
26
54
|
def _to_page_dict(value: Any) -> dict[str, Any]:
|
|
27
55
|
data = _to_dict(value)
|
|
28
56
|
data["items"] = [_to_dict(item) for item in data.get("items") or []]
|
|
@@ -132,11 +160,22 @@ def _validate_callback_options(
|
|
|
132
160
|
@app.command()
|
|
133
161
|
def create(
|
|
134
162
|
name: Annotated[str, typer.Option("--name", help="Schedule name.")],
|
|
135
|
-
prompt: Annotated[str, typer.Option("--prompt", help="Prompt to run.")],
|
|
136
163
|
interval_seconds: Annotated[
|
|
137
164
|
int,
|
|
138
165
|
typer.Option("--interval-seconds", min=1, help="Run interval in seconds."),
|
|
139
166
|
],
|
|
167
|
+
prompt: Annotated[
|
|
168
|
+
str | None,
|
|
169
|
+
typer.Option("--prompt", help="Prompt to run. Mutually exclusive with --prompt-file."),
|
|
170
|
+
] = None,
|
|
171
|
+
prompt_file: Annotated[
|
|
172
|
+
str | None,
|
|
173
|
+
typer.Option(
|
|
174
|
+
"--prompt-file",
|
|
175
|
+
help="Read the prompt from a UTF-8 file. Use '-' to read from stdin. "
|
|
176
|
+
"Mutually exclusive with --prompt.",
|
|
177
|
+
),
|
|
178
|
+
] = None,
|
|
140
179
|
callback_url: Annotated[
|
|
141
180
|
str | None,
|
|
142
181
|
typer.Option("--callback-url", help="Optional callback URL invoked after each run."),
|
|
@@ -147,7 +186,16 @@ def create(
|
|
|
147
186
|
"""Create a new interval schedule."""
|
|
148
187
|
from teardrop import CreateScheduleRequest
|
|
149
188
|
|
|
150
|
-
from teardrop_cli.formatting import print_json, print_success
|
|
189
|
+
from teardrop_cli.formatting import print_error, print_json, print_success
|
|
190
|
+
|
|
191
|
+
if prompt is not None and prompt_file is not None:
|
|
192
|
+
print_error("Use either --prompt or --prompt-file, not both.")
|
|
193
|
+
raise typer.Exit(1) from None
|
|
194
|
+
if prompt is None and prompt_file is None:
|
|
195
|
+
print_error("Provide either --prompt or --prompt-file.")
|
|
196
|
+
raise typer.Exit(1) from None
|
|
197
|
+
if prompt_file is not None:
|
|
198
|
+
prompt = _read_prompt_file(prompt_file)
|
|
151
199
|
|
|
152
200
|
request = CreateScheduleRequest(
|
|
153
201
|
name=name,
|
|
@@ -185,7 +233,10 @@ def list_cmd(
|
|
|
185
233
|
operation=lambda client: client.schedules.list(),
|
|
186
234
|
)
|
|
187
235
|
|
|
188
|
-
|
|
236
|
+
# The SDK returns a ScheduledRunListResponse model with an ``items``
|
|
237
|
+
# field (plus ``next_cursor``). Iterating the model directly would yield
|
|
238
|
+
# its field tuples, so unwrap ``.items`` when present.
|
|
239
|
+
items = [_to_dict(item) for item in _list_items(result)]
|
|
189
240
|
if as_json:
|
|
190
241
|
print_json(items)
|
|
191
242
|
return
|
|
@@ -70,6 +70,84 @@ class TestCreate:
|
|
|
70
70
|
assert '"name": "hourly-sync"' in result.output
|
|
71
71
|
assert '"schedule_kind": "interval"' in result.output
|
|
72
72
|
|
|
73
|
+
def test_create_prompt_file(self, runner, patch_get_client, mock_client, tmp_path):
|
|
74
|
+
prompt_file = tmp_path / "task.md"
|
|
75
|
+
prompt_file.write_text("Line one\nLine two 📊\n", encoding="utf-8")
|
|
76
|
+
mock_client.schedules.create.return_value = _schedule(name="from-file")
|
|
77
|
+
result = runner.invoke(
|
|
78
|
+
app,
|
|
79
|
+
[
|
|
80
|
+
"schedules",
|
|
81
|
+
"create",
|
|
82
|
+
"--name",
|
|
83
|
+
"from-file",
|
|
84
|
+
"--prompt-file",
|
|
85
|
+
str(prompt_file),
|
|
86
|
+
"--interval-seconds",
|
|
87
|
+
"60",
|
|
88
|
+
],
|
|
89
|
+
)
|
|
90
|
+
assert result.exit_code == 0, result.output
|
|
91
|
+
request = mock_client.schedules.create.await_args.args[0]
|
|
92
|
+
assert request.prompt == "Line one\nLine two 📊\n"
|
|
93
|
+
|
|
94
|
+
def test_create_prompt_file_mutually_exclusive(self, runner, patch_get_client, mock_client):
|
|
95
|
+
result = runner.invoke(
|
|
96
|
+
app,
|
|
97
|
+
[
|
|
98
|
+
"schedules",
|
|
99
|
+
"create",
|
|
100
|
+
"--name",
|
|
101
|
+
"x",
|
|
102
|
+
"--prompt",
|
|
103
|
+
"inline",
|
|
104
|
+
"--prompt-file",
|
|
105
|
+
"somefile.md",
|
|
106
|
+
"--interval-seconds",
|
|
107
|
+
"60",
|
|
108
|
+
],
|
|
109
|
+
)
|
|
110
|
+
assert result.exit_code == 1
|
|
111
|
+
assert "not both" in result.output
|
|
112
|
+
|
|
113
|
+
def test_create_requires_prompt_source(self, runner, patch_get_client, mock_client):
|
|
114
|
+
result = runner.invoke(
|
|
115
|
+
app,
|
|
116
|
+
[
|
|
117
|
+
"schedules",
|
|
118
|
+
"create",
|
|
119
|
+
"--name",
|
|
120
|
+
"x",
|
|
121
|
+
"--interval-seconds",
|
|
122
|
+
"60",
|
|
123
|
+
],
|
|
124
|
+
)
|
|
125
|
+
assert result.exit_code == 1
|
|
126
|
+
assert "Provide either --prompt or --prompt-file" in result.output
|
|
127
|
+
mock_client.schedules.create.assert_not_awaited()
|
|
128
|
+
|
|
129
|
+
def test_create_prompt_file_rejects_invalid_utf8(
|
|
130
|
+
self, runner, patch_get_client, mock_client, tmp_path
|
|
131
|
+
):
|
|
132
|
+
prompt_file = tmp_path / "invalid.bin"
|
|
133
|
+
prompt_file.write_bytes(b"\xff\xfe\xfd")
|
|
134
|
+
result = runner.invoke(
|
|
135
|
+
app,
|
|
136
|
+
[
|
|
137
|
+
"schedules",
|
|
138
|
+
"create",
|
|
139
|
+
"--name",
|
|
140
|
+
"x",
|
|
141
|
+
"--prompt-file",
|
|
142
|
+
str(prompt_file),
|
|
143
|
+
"--interval-seconds",
|
|
144
|
+
"60",
|
|
145
|
+
],
|
|
146
|
+
)
|
|
147
|
+
assert result.exit_code == 1
|
|
148
|
+
assert "as UTF-8" in result.output
|
|
149
|
+
mock_client.schedules.create.assert_not_awaited()
|
|
150
|
+
|
|
73
151
|
|
|
74
152
|
class TestList:
|
|
75
153
|
def test_list(self, runner: CliRunner, patch_get_client, mock_client):
|
|
@@ -91,6 +169,39 @@ class TestList:
|
|
|
91
169
|
assert result.exit_code == 0, result.output
|
|
92
170
|
assert '"name": "nightly-report"' in result.output
|
|
93
171
|
|
|
172
|
+
def test_list_handles_sdk_list_response_model(
|
|
173
|
+
self, runner: CliRunner, patch_get_client, mock_client
|
|
174
|
+
):
|
|
175
|
+
# The real SDK returns a ScheduledRunListResponse model with an
|
|
176
|
+
# ``items`` field (plus ``next_cursor``), not a plain list.
|
|
177
|
+
from teardrop.models import ScheduledRun, ScheduledRunListResponse
|
|
178
|
+
|
|
179
|
+
model = ScheduledRunListResponse(
|
|
180
|
+
items=[
|
|
181
|
+
ScheduledRun(
|
|
182
|
+
**_schedule(
|
|
183
|
+
name="model-backed",
|
|
184
|
+
org_id="org_1",
|
|
185
|
+
user_id="user_1",
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
],
|
|
189
|
+
next_cursor=None,
|
|
190
|
+
)
|
|
191
|
+
mock_client.schedules.list.return_value = model
|
|
192
|
+
result = runner.invoke(app, ["schedules", "list"])
|
|
193
|
+
assert result.exit_code == 0, result.output
|
|
194
|
+
assert "model-backed" in result.output
|
|
195
|
+
|
|
196
|
+
def test_list_handles_dict_response_envelope(self, runner, patch_get_client, mock_client):
|
|
197
|
+
mock_client.schedules.list.return_value = {
|
|
198
|
+
"items": [_schedule(name="dict-backed")],
|
|
199
|
+
"next_cursor": None,
|
|
200
|
+
}
|
|
201
|
+
result = runner.invoke(app, ["schedules", "list", "--json"])
|
|
202
|
+
assert result.exit_code == 0, result.output
|
|
203
|
+
assert '"name": "dict-backed"' in result.output
|
|
204
|
+
|
|
94
205
|
|
|
95
206
|
class TestGet:
|
|
96
207
|
def test_get_json(self, runner: CliRunner, patch_get_client, mock_client):
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|