cli-anything-transcode-api-v2 0.1.0__tar.gz → 0.1.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.
Files changed (21) hide show
  1. {cli_anything_transcode_api_v2-0.1.0 → cli_anything_transcode_api_v2-0.1.1}/PKG-INFO +1 -1
  2. {cli_anything_transcode_api_v2-0.1.0 → cli_anything_transcode_api_v2-0.1.1}/cli_anything_transcode_api_v2.egg-info/PKG-INFO +1 -1
  3. {cli_anything_transcode_api_v2-0.1.0 → cli_anything_transcode_api_v2-0.1.1}/cli_anything_transcode_api_v2.egg-info/SOURCES.txt +0 -3
  4. {cli_anything_transcode_api_v2-0.1.0 → cli_anything_transcode_api_v2-0.1.1}/setup.py +5 -2
  5. cli_anything_transcode_api_v2-0.1.0/cli_anything/transcode_api_v2/tests/__init__.py +0 -0
  6. cli_anything_transcode_api_v2-0.1.0/cli_anything/transcode_api_v2/tests/test_core.py +0 -237
  7. cli_anything_transcode_api_v2-0.1.0/cli_anything/transcode_api_v2/tests/test_full_e2e.py +0 -218
  8. {cli_anything_transcode_api_v2-0.1.0 → cli_anything_transcode_api_v2-0.1.1}/cli_anything/transcode_api_v2/__init__.py +0 -0
  9. {cli_anything_transcode_api_v2-0.1.0 → cli_anything_transcode_api_v2-0.1.1}/cli_anything/transcode_api_v2/__main__.py +0 -0
  10. {cli_anything_transcode_api_v2-0.1.0 → cli_anything_transcode_api_v2-0.1.1}/cli_anything/transcode_api_v2/core/__init__.py +0 -0
  11. {cli_anything_transcode_api_v2-0.1.0 → cli_anything_transcode_api_v2-0.1.1}/cli_anything/transcode_api_v2/core/client.py +0 -0
  12. {cli_anything_transcode_api_v2-0.1.0 → cli_anything_transcode_api_v2-0.1.1}/cli_anything/transcode_api_v2/core/session.py +0 -0
  13. {cli_anything_transcode_api_v2-0.1.0 → cli_anything_transcode_api_v2-0.1.1}/cli_anything/transcode_api_v2/skills/SKILL.md +0 -0
  14. {cli_anything_transcode_api_v2-0.1.0 → cli_anything_transcode_api_v2-0.1.1}/cli_anything/transcode_api_v2/transcode_api_v2_cli.py +0 -0
  15. {cli_anything_transcode_api_v2-0.1.0 → cli_anything_transcode_api_v2-0.1.1}/cli_anything/transcode_api_v2/utils/__init__.py +0 -0
  16. {cli_anything_transcode_api_v2-0.1.0 → cli_anything_transcode_api_v2-0.1.1}/cli_anything/transcode_api_v2/utils/formatting.py +0 -0
  17. {cli_anything_transcode_api_v2-0.1.0 → cli_anything_transcode_api_v2-0.1.1}/cli_anything_transcode_api_v2.egg-info/dependency_links.txt +0 -0
  18. {cli_anything_transcode_api_v2-0.1.0 → cli_anything_transcode_api_v2-0.1.1}/cli_anything_transcode_api_v2.egg-info/entry_points.txt +0 -0
  19. {cli_anything_transcode_api_v2-0.1.0 → cli_anything_transcode_api_v2-0.1.1}/cli_anything_transcode_api_v2.egg-info/requires.txt +0 -0
  20. {cli_anything_transcode_api_v2-0.1.0 → cli_anything_transcode_api_v2-0.1.1}/cli_anything_transcode_api_v2.egg-info/top_level.txt +0 -0
  21. {cli_anything_transcode_api_v2-0.1.0 → cli_anything_transcode_api_v2-0.1.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cli-anything-transcode-api-v2
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: CLI harness for transcode_api_v2 — video/audio transcoding orchestration API
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: click>=8.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cli-anything-transcode-api-v2
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: CLI harness for transcode_api_v2 — video/audio transcoding orchestration API
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: click>=8.0
@@ -6,9 +6,6 @@ cli_anything/transcode_api_v2/core/__init__.py
6
6
  cli_anything/transcode_api_v2/core/client.py
7
7
  cli_anything/transcode_api_v2/core/session.py
8
8
  cli_anything/transcode_api_v2/skills/SKILL.md
9
- cli_anything/transcode_api_v2/tests/__init__.py
10
- cli_anything/transcode_api_v2/tests/test_core.py
11
- cli_anything/transcode_api_v2/tests/test_full_e2e.py
12
9
  cli_anything/transcode_api_v2/utils/__init__.py
13
10
  cli_anything/transcode_api_v2/utils/formatting.py
14
11
  cli_anything_transcode_api_v2.egg-info/PKG-INFO
@@ -2,9 +2,12 @@ from setuptools import setup, find_namespace_packages
2
2
 
3
3
  setup(
4
4
  name="cli-anything-transcode-api-v2",
5
- version="0.1.0",
5
+ version="0.1.1",
6
6
  description="CLI harness for transcode_api_v2 — video/audio transcoding orchestration API",
7
- packages=find_namespace_packages(include=["cli_anything.*"]),
7
+ packages=find_namespace_packages(
8
+ include=["cli_anything.*"],
9
+ exclude=["cli_anything.transcode_api_v2.tests*"],
10
+ ),
8
11
  package_data={
9
12
  "cli_anything.transcode_api_v2": ["skills/*.md"],
10
13
  },
@@ -1,237 +0,0 @@
1
- """Unit tests for ApiClient, Session, and Formatting modules.
2
-
3
- Run without installing the package:
4
- cd /Users/a1/Desktop/transcode_api_v2/agent-harness
5
- python -m pytest cli_anything/transcode_api_v2/tests/test_core.py -v
6
-
7
- Or after pip install -e .:
8
- pytest cli_anything/transcode_api_v2/tests/test_core.py -v
9
- """
10
- from __future__ import annotations
11
-
12
- import hashlib
13
- import json
14
- import os
15
- import sys
16
-
17
- # Allow running without install
18
- sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "..", ".."))
19
-
20
- import pytest
21
-
22
- from cli_anything.transcode_api_v2.core.client import ApiClient, ApiError
23
- from cli_anything.transcode_api_v2.core.session import Session
24
- from cli_anything.transcode_api_v2.utils.formatting import (
25
- STATUS_LABELS,
26
- format_error,
27
- format_result,
28
- format_task_table,
29
- status_label,
30
- )
31
-
32
-
33
- # ── ApiClient unit tests ──────────────────────────────────────────────────────
34
-
35
- class TestApiClient:
36
- """Tests for ApiClient (all run in mock mode via env var)."""
37
-
38
- def setup_method(self):
39
- os.environ["TRANSCODE_API_MOCK"] = "1"
40
-
41
- def teardown_method(self):
42
- os.environ.pop("TRANSCODE_API_MOCK", None)
43
-
44
- def test_client_defaults_from_env(self):
45
- os.environ["TRANSCODE_API_BASE_URL"] = "http://myserver:9090"
46
- os.environ["TRANSCODE_API_SNAME"] = "myns"
47
- os.environ["TRANSCODE_API_AK"] = "aabbcc"
48
- os.environ["TRANSCODE_API_SK"] = "mysecret"
49
- try:
50
- c = ApiClient()
51
- assert c.base_url == "http://myserver:9090"
52
- assert c.sname == "myns"
53
- assert c.ak == "aabbcc"
54
- assert c.sk == "mysecret"
55
- finally:
56
- os.environ.pop("TRANSCODE_API_BASE_URL", None)
57
- os.environ.pop("TRANSCODE_API_SNAME", None)
58
- os.environ.pop("TRANSCODE_API_AK", None)
59
- os.environ.pop("TRANSCODE_API_SK", None)
60
-
61
- def test_client_sign_empty_params(self):
62
- import hmac as hmac_mod, hashlib, base64
63
- c = ApiClient(ak="myak", sk="mysk")
64
- sig = hmac_mod.new("mysk".encode(), "myak\n123\n456".encode(), hashlib.sha1).digest()
65
- expected = "myak:" + base64.b64encode(sig).decode()
66
- headers = c._standard_auth_headers("123", "456")
67
- assert headers["Authorization"] == expected
68
-
69
- def test_client_sign_sorted_params(self):
70
- # _standard_auth_headers always produces AK:base64(...) format
71
- c = ApiClient(ak="ak1234", sk="sk5678")
72
- headers = c._standard_auth_headers("111", "222")
73
- assert headers["Authorization"].startswith("ak1234:")
74
-
75
- def test_client_mock_task_add(self):
76
- c = ApiClient()
77
- result = c.task_add(service="transcode", args={"input": "s3://bucket/vid.mp4"})
78
- assert "tid" in result
79
- assert result["tid"] == 42
80
-
81
- def test_client_mock_task_get(self):
82
- c = ApiClient()
83
- result = c.task_get(12345)
84
- assert result.get("tid") == 1
85
- assert result.get("status") == 1
86
-
87
- def test_client_mock_task_get_many(self):
88
- c = ApiClient()
89
- result = c.task_get_many([1, 2, 3])
90
- # Falls through to default mock → {"tid": 42}
91
- assert isinstance(result, dict)
92
-
93
- def test_client_mock_media_call(self):
94
- c = ApiClient()
95
- result = c.media_call("VideoClip", {"sname": "ns", "args": {}})
96
- assert isinstance(result, dict)
97
-
98
- def test_client_mock_health(self):
99
- c = ApiClient()
100
- result = c.health()
101
- assert result.get("status") == "ok"
102
-
103
- def test_client_mock_template_list(self):
104
- c = ApiClient()
105
- result = c.template_list(eqid="eq001")
106
- assert "list" in result
107
- assert isinstance(result["list"], list)
108
-
109
- def test_api_error_message(self):
110
- err = ApiError(errno=403, errmsg="Forbidden", raw={"errno": 403})
111
- assert "403" in str(err)
112
- assert "Forbidden" in str(err)
113
- assert err.errno == 403
114
- assert err.errmsg == "Forbidden"
115
-
116
-
117
- # ── Session unit tests ────────────────────────────────────────────────────────
118
-
119
- class TestSession:
120
- """Tests for the in-memory Session store."""
121
-
122
- def test_session_store_and_get(self):
123
- s = Session()
124
- s.store("job1", {"tid": 100, "status": 0})
125
- assert s.get("job1") == {"tid": 100, "status": 0}
126
-
127
- def test_session_get_missing_raises(self):
128
- s = Session()
129
- with pytest.raises(KeyError, match="job_missing"):
130
- s.get("job_missing")
131
-
132
- def test_session_get_tid(self):
133
- s = Session()
134
- s.store("job1", {"tid": 99, "status": 1})
135
- assert s.get_tid("job1") == 99
136
-
137
- def test_session_get_tid_missing_raises(self):
138
- s = Session()
139
- s.store("notask", {"status": 1})
140
- with pytest.raises(ValueError, match="no tid"):
141
- s.get_tid("notask")
142
-
143
- def test_session_list_aliases_empty(self):
144
- s = Session()
145
- assert s.list_aliases() == []
146
-
147
- def test_session_list_aliases_nonempty(self):
148
- s = Session()
149
- s.store("a", {"tid": 1})
150
- s.store("b", {"tid": 2})
151
- assert set(s.list_aliases()) == {"a", "b"}
152
-
153
- def test_session_history(self):
154
- s = Session()
155
- s.store("a", {"tid": 1})
156
- s.store("b", {"tid": 2})
157
- hist = s.history()
158
- assert len(hist) == 2
159
- assert hist[0]["alias"] == "a"
160
- assert hist[1]["alias"] == "b"
161
-
162
- def test_session_clear(self):
163
- s = Session()
164
- s.store("a", {"tid": 1})
165
- s.clear()
166
- assert s.list_aliases() == []
167
- assert s.history() == []
168
-
169
- def test_session_summary_empty(self):
170
- s = Session()
171
- assert "(empty session)" in s.summary()
172
-
173
- def test_session_summary_nonempty(self):
174
- s = Session()
175
- s.store("myjob", {"tid": 55, "status": 6})
176
- summary = s.summary()
177
- assert "myjob" in summary
178
- assert "55" in summary
179
-
180
-
181
- # ── Formatting unit tests ─────────────────────────────────────────────────────
182
-
183
- class TestFormatting:
184
- """Tests for formatting helpers."""
185
-
186
- def test_status_label_known(self):
187
- assert status_label(0) == "queued"
188
- assert status_label(1) == "done"
189
- assert status_label(2) == "failed"
190
- assert status_label(3) == "sending"
191
- assert status_label(6) == "processing"
192
-
193
- def test_status_label_unknown(self):
194
- assert status_label(99) == "99"
195
-
196
- def test_status_label_none(self):
197
- assert status_label(None) == "?"
198
-
199
- def test_format_result_dict(self):
200
- out = format_result({"tid": 42, "status": 1})
201
- assert "42" in out
202
- # status should include human-readable label
203
- assert "done" in out
204
-
205
- def test_format_result_as_json(self):
206
- data = {"tid": 42}
207
- out = format_result(data, as_json=True)
208
- parsed = json.loads(out)
209
- assert parsed == data
210
-
211
- def test_format_result_string(self):
212
- assert format_result("hello") == "hello"
213
-
214
- def test_format_error_text(self):
215
- out = format_error("something went wrong")
216
- assert "ERR" in out
217
- assert "something went wrong" in out
218
-
219
- def test_format_error_json(self):
220
- out = format_error("boom", as_json=True)
221
- parsed = json.loads(out)
222
- assert parsed["errno"] == -1
223
- assert "boom" in parsed["errmsg"]
224
-
225
- def test_format_task_table_empty(self):
226
- assert "(no tasks)" in format_task_table([])
227
-
228
- def test_format_task_table_rows(self):
229
- tasks = [
230
- {"tid": 1, "status": 1, "progress": 100, "service": "transcode"},
231
- {"tid": 2, "status": 6, "progress": 50, "service": "clip"},
232
- ]
233
- out = format_task_table(tasks)
234
- assert "done" in out
235
- assert "processing" in out
236
- assert "transcode" in out
237
- assert "clip" in out
@@ -1,218 +0,0 @@
1
- """Subprocess / E2E tests for cli-anything-transcode-api-v2.
2
-
3
- All tests use TRANSCODE_API_MOCK=1 — no live server required.
4
- The _resolve_cli() helper finds the installed entry-point or skips when
5
- CLI_ANYTHING_FORCE_INSTALLED=1 is set and the binary isn't on PATH.
6
-
7
- Run:
8
- cd /Users/a1/Desktop/transcode_api_v2/agent-harness
9
- pip install -e . && pytest cli_anything/transcode_api_v2/tests/test_full_e2e.py -v
10
- """
11
- from __future__ import annotations
12
-
13
- import json
14
- import os
15
- import shutil
16
- import subprocess
17
- import sys
18
- from typing import Optional
19
-
20
- import pytest
21
-
22
-
23
- # ── Helper: locate installed CLI ──────────────────────────────────────────────
24
-
25
- def _resolve_cli(name: str) -> Optional[str]:
26
- """Return absolute path to named CLI, or None/skip depending on env."""
27
- path = shutil.which(name)
28
- if path:
29
- return path
30
- if os.environ.get("CLI_ANYTHING_FORCE_INSTALLED", "").strip() == "1":
31
- pytest.fail(f"CLI_ANYTHING_FORCE_INSTALLED=1 but '{name}' not found on PATH")
32
- pytest.skip(f"'{name}' not installed; run `pip install -e .` first")
33
-
34
-
35
- CLI_NAME = "cli-anything-transcode-api-v2"
36
- MOCK_ENV = {**os.environ, "TRANSCODE_API_MOCK": "1"}
37
-
38
-
39
- def _run(*args: str, env=None, input: str | None = None) -> subprocess.CompletedProcess:
40
- cli = _resolve_cli(CLI_NAME)
41
- return subprocess.run(
42
- [cli] + list(args),
43
- capture_output=True,
44
- text=True,
45
- env=env or MOCK_ENV,
46
- input=input,
47
- )
48
-
49
-
50
- # ── Top-level CLI ─────────────────────────────────────────────────────────────
51
-
52
- class TestTopLevel:
53
- def test_help(self):
54
- r = _run("--help")
55
- assert r.returncode == 0
56
- assert "task" in r.stdout
57
- assert "media" in r.stdout
58
- assert "monitor" in r.stdout
59
-
60
- def test_version_flag_absent_no_crash(self):
61
- """CLI should show help without crashing even without --version."""
62
- r = _run("--help")
63
- assert r.returncode == 0
64
-
65
- def test_json_flag_preserved_for_subcommand(self):
66
- r = _run("--json", "monitor", "health")
67
- assert r.returncode == 0
68
- parsed = json.loads(r.stdout.strip())
69
- assert isinstance(parsed, dict)
70
-
71
-
72
- # ── task group ────────────────────────────────────────────────────────────────
73
-
74
- class TestTaskGroup:
75
- def test_task_help(self):
76
- r = _run("task", "--help")
77
- assert r.returncode == 0
78
- assert "add" in r.stdout
79
-
80
- def test_task_add_mock(self):
81
- r = _run("task", "add",
82
- "--service", "transcode",
83
- "--args", '{"input":"s3://bucket/video.mp4"}')
84
- assert r.returncode == 0
85
- assert "42" in r.stdout # mock returns tid=42
86
-
87
- def test_task_add_json_output(self):
88
- r = _run("--json", "task", "add",
89
- "--service", "transcode",
90
- "--args", '{"input":"s3://bucket/video.mp4"}')
91
- assert r.returncode == 0
92
- data = json.loads(r.stdout.strip())
93
- assert "tid" in data
94
- assert data["tid"] == 42
95
-
96
- def test_task_get_mock(self):
97
- r = _run("task", "get", "12345")
98
- assert r.returncode == 0
99
- assert "1" in r.stdout # tid=1 from mock
100
-
101
- def test_task_get_json(self):
102
- r = _run("--json", "task", "get", "12345")
103
- assert r.returncode == 0
104
- data = json.loads(r.stdout.strip())
105
- assert data["tid"] == 1
106
- assert data["status"] == 1
107
-
108
- def test_task_stop_mock(self):
109
- r = _run("task", "stop", "12345")
110
- assert r.returncode == 0
111
-
112
- def test_task_get_many_mock(self):
113
- r = _run("task", "get-many", "1", "2", "3")
114
- assert r.returncode == 0
115
-
116
- def test_task_add_invalid_args_json(self):
117
- r = _run("task", "add",
118
- "--service", "transcode",
119
- "--args", "not-json")
120
- assert r.returncode != 0
121
- assert "ERR" in r.stderr or "Invalid" in r.stderr
122
-
123
-
124
- # ── media group ───────────────────────────────────────────────────────────────
125
-
126
- class TestMediaGroup:
127
- def test_media_actions_list(self):
128
- r = _run("media", "actions")
129
- assert r.returncode == 0
130
- assert "VideoClip" in r.stdout
131
- assert "Snapshot" in r.stdout
132
-
133
- def test_media_actions_json(self):
134
- r = _run("--json", "media", "actions")
135
- assert r.returncode == 0
136
- data = json.loads(r.stdout.strip())
137
- assert isinstance(data, list)
138
- assert "VideoClip" in data
139
-
140
- def test_media_call_mock(self):
141
- r = _run("media", "call", "VideoClip", '{"sname":"ns"}')
142
- assert r.returncode == 0
143
-
144
- def test_media_call_default_empty_body(self):
145
- r = _run("media", "call", "Snapshot")
146
- assert r.returncode == 0
147
-
148
- def test_media_get_mock(self):
149
- r = _run("media", "get", "99")
150
- assert r.returncode == 0
151
-
152
-
153
- # ── monitor group ─────────────────────────────────────────────────────────────
154
-
155
- class TestMonitorGroup:
156
- def test_monitor_health(self):
157
- r = _run("monitor", "health")
158
- assert r.returncode == 0
159
- assert "ok" in r.stdout
160
-
161
- def test_monitor_health_json(self):
162
- r = _run("--json", "monitor", "health")
163
- assert r.returncode == 0
164
- data = json.loads(r.stdout.strip())
165
- assert data.get("status") == "ok"
166
-
167
-
168
- # ── template group ────────────────────────────────────────────────────────────
169
-
170
- class TestTemplateGroup:
171
- def test_template_list_mock(self):
172
- r = _run("template", "list", "--eqid", "eq001")
173
- assert r.returncode == 0
174
-
175
- def test_template_get_mock(self):
176
- r = _run("template", "get", "--eqid", "eq001", "1")
177
- assert r.returncode == 0
178
-
179
-
180
- # ── session group ─────────────────────────────────────────────────────────────
181
-
182
- class TestSessionGroup:
183
- def test_session_list_empty(self):
184
- r = _run("session", "list")
185
- assert r.returncode == 0
186
- assert "(empty session)" in r.stdout
187
-
188
- def test_session_clear(self):
189
- r = _run("session", "clear")
190
- assert r.returncode == 0
191
- assert "cleared" in r.stdout.lower()
192
-
193
- def test_session_get_missing_alias(self):
194
- r = _run("session", "get", "nonexistent_alias_xyz")
195
- assert r.returncode != 0
196
-
197
-
198
- # ── REPL tests ────────────────────────────────────────────────────────────────
199
-
200
- class TestRepl:
201
- def test_repl_exit(self):
202
- r = _run(input="exit\n")
203
- assert r.returncode == 0
204
- assert "Bye" in r.stdout
205
-
206
- def test_repl_quit(self):
207
- r = _run(input="quit\n")
208
- assert r.returncode == 0
209
- assert "Bye" in r.stdout
210
-
211
- def test_repl_monitor_health(self):
212
- r = _run(input="monitor health\nexit\n")
213
- assert r.returncode == 0
214
- assert "ok" in r.stdout
215
-
216
- def test_repl_empty_line(self):
217
- r = _run(input="\nexit\n")
218
- assert r.returncode == 0