Initial reyna-cli implementation

This commit is contained in:
Adolfo Reyna
2026-06-10 22:29:02 -04:00
commit 97379a1046
20 changed files with 2261 additions and 0 deletions
View File
+92
View File
@@ -0,0 +1,92 @@
import json
import pytest
from unittest.mock import MagicMock, patch
from typer.testing import CliRunner
from reyna_cli.cli import app
from reyna_cli.immich import ImmichClient
from reyna_cli.mongo_direct import MongoDirectClient
from reyna_cli.zoom_direct import ZoomClient
runner = CliRunner()
@patch("reyna_cli.immich.httpx.Client")
def test_immich_client_stats(mock_client_class):
mock_client = mock_client_class.return_value
mock_client.request.return_value.json.return_value = {"total": 100}
mock_client.request.return_value.raise_for_status = MagicMock()
mock_client.request.return_value.content = b'{"total": 100}'
with patch.dict("os.environ", {"IMMICH_URL": "http://localhost:2283", "IMMICH_API_KEY": "test"}):
client = ImmichClient()
stats = client.statistics()
assert stats == {"total": 100}
@patch("pymongo.MongoClient")
def test_mongo_client_list_dbs(mock_mongo_class):
mock_client = mock_mongo_class.return_value
mock_client.list_database_names.return_value = ["db1", "db2"]
client = MongoDirectClient(uri="mongodb://localhost:27017")
dbs = client.databases()
assert dbs == ["db1", "db2"]
@patch("reyna_cli.zoom_direct.httpx.Client")
def test_zoom_client_token_and_user(mock_client_class):
mock_client = mock_client_class.return_value
# Mock token response
mock_token_resp = MagicMock()
mock_token_resp.json.return_value = {"access_token": "fake_token", "expires_in": 3600}
mock_token_resp.raise_for_status = MagicMock()
# Mock users response
mock_users_resp = MagicMock()
mock_users_resp.json.return_value = {"users": []}
mock_users_resp.raise_for_status = MagicMock()
mock_users_resp.content = b'{"users": []}'
mock_client.post.return_value = mock_token_resp
mock_client.request.return_value = mock_users_resp
with patch.dict("os.environ", {"ZOOM_ACCOUNT_ID": "acc", "ZOOM_CLIENT_ID": "cid", "ZOOM_CLIENT_SECRET": "csec"}):
client = ZoomClient()
users = client.users()
assert users == {"users": []}
assert client._token == "fake_token"
def test_cli_doctor_ram_plan():
result = runner.invoke(app, ["doctor", "--ram-plan", "--json"])
assert result.exit_code == 0
payload = json.loads(result.stdout)
assert "ram_diagnostics" in payload
@patch("reyna_cli.cli.ImmichClient")
def test_cli_immich_stats(mock_immich_class):
mock_instance = mock_immich_class.return_value
mock_instance.statistics.return_value = {"count": 42}
result = runner.invoke(app, ["immich", "stats", "--json"])
assert result.exit_code == 0
payload = json.loads(result.stdout)
assert payload["result"] == {"count": 42}
@patch("reyna_cli.cli.MongoClient")
def test_cli_mongo_dbs(mock_mongo_class):
mock_instance = mock_mongo_class.return_value
mock_instance.databases.return_value = ["test_db"]
result = runner.invoke(app, ["mongo", "dbs", "--json"])
assert result.exit_code == 0
payload = json.loads(result.stdout)
assert payload["result"] == ["test_db"]
@patch("reyna_cli.cli.ZoomClient")
def test_cli_zoom_user(mock_zoom_class):
mock_instance = mock_zoom_class.return_value
mock_instance.users.return_value = {"users": []}
result = runner.invoke(app, ["zoom", "user", "--json"])
assert result.exit_code == 0
payload = json.loads(result.stdout)
assert payload["result"] == {"users": []}
+118
View File
@@ -0,0 +1,118 @@
import json
from pathlib import Path
from typer.testing import CliRunner
from reyna_cli.cli import app
from reyna_cli.config import Device, Registry, get_device, load_registry
from reyna_cli.utils import infer_capabilities, resolve_tool_name
runner = CliRunner()
def test_resolve_tool_name():
available_tools = [
{"name": "draw_text", "description": "Draw text on screen"},
{"name": "get_battery", "description": "Get battery status"},
{"name": "play_audio_base64", "description": "Play audio"},
]
assert resolve_tool_name("text", available_tools) == "draw_text"
assert resolve_tool_name("battery", available_tools) == "get_battery"
assert resolve_tool_name("speech", available_tools) == "play_audio_base64"
assert resolve_tool_name("unknown", available_tools) is None
def test_infer_capabilities():
caps = infer_capabilities([{"name": "draw_text"}, {"name": "screenshot"}])
assert caps["text"] == "draw_text"
assert caps["screenshot"] == "screenshot"
assert caps["speech"] is None
def test_registry_mapping_yaml(tmp_path, monkeypatch):
registry_path = tmp_path / "local_devices.yaml"
registry_path.write_text(
"""
version: 1
devices:
esp32_screen:
display_name: ESP32 Screen
host: esp32-screen.local
url: http://esp32-screen.local/api/mcp
fallback_url: http://192.168.68.123/api/mcp
capabilities: [display, audio]
""",
encoding="utf-8",
)
monkeypatch.setenv("REYNA_DEVICES_REGISTRY", str(registry_path))
registry = load_registry()
device = get_device("screen", registry)
assert device is not None
assert device.id == "esp32_screen"
assert device.candidate_urls() == ["http://esp32-screen.local/api/mcp", "http://192.168.68.123/api/mcp"]
def test_config_get_device():
registry = Registry(devices=[Device(id="iphone_mcp", url="http://localhost:8080/api/mcp")])
assert get_device("iphone", registry).url == "http://localhost:8080/api/mcp"
assert get_device("nonexistent", registry) is None
def test_cli_doctor():
result = runner.invoke(app, ["doctor", "--json"])
assert result.exit_code == 0
assert json.loads(result.stdout)["ok"] is True
def test_cli_devices_list(monkeypatch, tmp_path):
registry_path = tmp_path / "local_devices.yaml"
registry_path.write_text("devices: {}\n", encoding="utf-8")
monkeypatch.setenv("REYNA_DEVICES_REGISTRY", str(registry_path))
result = runner.invoke(app, ["devices", "list", "--json"])
assert result.exit_code == 0
payload = json.loads(result.stdout)
assert payload["ok"] is True
assert payload["devices"] == []
def test_cli_json_args_invalid_fails():
result = runner.invoke(app, ["devices", "call", "nonexistent", "tool", "--args", "{invalid}", "--json"])
assert result.exit_code != 0
assert json.loads(result.stdout)["ok"] is False
def test_screen_has_subcommands():
result = runner.invoke(app, ["screen", "--help"])
assert result.exit_code == 0
assert "text" in result.stdout
assert "speak" in result.stdout
def test_devices_screen_has_subcommands():
result = runner.invoke(app, ["devices", "screen", "--help"])
assert result.exit_code == 0
assert "text" in result.stdout
def test_devices_arm_has_subcommands():
result = runner.invoke(app, ["devices", "arm", "--help"])
assert result.exit_code == 0
assert "state" in result.stdout
assert "wave" in result.stdout
assert "home" in result.stdout
def test_immich_has_subcommands():
result = runner.invoke(app, ["immich", "--help"])
assert result.exit_code == 0
assert "stats" in result.stdout
assert "albums" in result.stdout
assert "search" in result.stdout
def test_mongo_has_subcommands():
result = runner.invoke(app, ["mongo", "--help"])
assert result.exit_code == 0
assert "dbs" in result.stdout
assert "collections" in result.stdout
assert "find" in result.stdout