9fd04b0ce4
Add the owner-only AF_UNIX Reyna CLI privacy host, strict signed-app installation, and typed native routing for Calendar, Contacts, and Reminders.\n\nAdd bounded system-status paths and config-only direct local-service wrappers. Preserve MacMiniMCP pending explicit cutover approval.\n\nApple Notes is intentionally deferred: no native Notes operations, Apple Events declaration, or Automation helper are included; legacy Notes handling remains untouched.
268 lines
9.8 KiB
Python
268 lines
9.8 KiB
Python
"""Tests for remaining coverage migration — fully consolidated no-Notes CLI.
|
|
|
|
This file replaces the old Notes-native tests. Validates:
|
|
- privacy contract contains only calendar/contacts/reminders/system/speech/apple_llm (no notes)
|
|
- coverage matrix exists and says Notes deferred + legacy untouched
|
|
- system info still works via native host
|
|
- local-services direct wrappers offline safe (no Notes)
|
|
- docs mention Notes deferred
|
|
"""
|
|
|
|
import json
|
|
import pytest
|
|
from typer.testing import CliRunner
|
|
from unittest.mock import MagicMock
|
|
|
|
from reyna_cli.cli import app
|
|
|
|
runner = CliRunner()
|
|
|
|
# ─── Coverage matrix existence ───────────────────────────────────────────
|
|
|
|
def test_coverage_matrix_exists():
|
|
from pathlib import Path
|
|
p = Path(__file__).parents[1] / "docs" / "remaining-coverage-matrix.md"
|
|
assert p.exists(), f"matrix doc missing at {p}"
|
|
content = p.read_text()
|
|
# Must mention Notes deferred
|
|
assert "Notes" in content
|
|
assert "deferred" in content.lower(), "matrix must say Notes deferred"
|
|
assert "legacy" in content.lower()
|
|
assert "untouched" in content.lower()
|
|
|
|
|
|
# ─── Privacy contract — no notes, deferred ────────────────────────────────
|
|
|
|
def test_privacy_contract_no_notes_ops():
|
|
from reyna_cli.privacy_contract import ALLOWED_OPERATIONS, _COMMAND_TO_OPERATION
|
|
|
|
for op in ALLOWED_OPERATIONS.keys():
|
|
assert not op.startswith("notes."), f"forbidden notes op {op} present — Notes deferred"
|
|
|
|
forbidden = {"notes.list", "notes.read", "notes.create", "notes.request_access"}
|
|
for fo in forbidden:
|
|
assert fo not in ALLOWED_OPERATIONS
|
|
|
|
for cmd, op in _COMMAND_TO_OPERATION.items():
|
|
assert not op.startswith("notes.")
|
|
assert not cmd.startswith("notes_")
|
|
|
|
# Should still contain calendar/contacts/reminders/system/speech
|
|
assert "calendar.list" in ALLOWED_OPERATIONS
|
|
assert "contacts.search" in ALLOWED_OPERATIONS
|
|
assert "reminders.lists" in ALLOWED_OPERATIONS
|
|
assert "system.get_info" in ALLOWED_OPERATIONS
|
|
assert "apple_llm.check" in ALLOWED_OPERATIONS
|
|
|
|
|
|
def test_privacy_contract_mapping():
|
|
from reyna_cli.privacy_contract import command_to_operation
|
|
|
|
assert command_to_operation("system_get_info") == "system.get_info"
|
|
assert command_to_operation("apple_llm_check") == "apple_llm.check"
|
|
|
|
|
|
# ─── System info wrappers still work ─────────────────────────────────────
|
|
|
|
def test_native_system_get_info_wrapper(monkeypatch):
|
|
from reyna_cli import privacy_host as ph_mod
|
|
|
|
class FakeClient:
|
|
def call(self, op, args):
|
|
assert op == "system.get_info"
|
|
return {"id": "x", "ok": True, "result": {"system_info": {"macos_version": "26.0"}}}
|
|
|
|
monkeypatch.setattr(ph_mod, "PrivacyClient", FakeClient)
|
|
res = ph_mod.native_system_get_info()
|
|
assert res["ok"] is True
|
|
|
|
|
|
def test_cli_system_info_uses_native(monkeypatch):
|
|
def fake_native():
|
|
return {"ok": True, "source": "native_privacy_host", "result": {"system_info": {"macos_version": "15.0"}}}
|
|
|
|
monkeypatch.setattr("reyna_cli.privacy_host.native_system_get_info", fake_native)
|
|
result = runner.invoke(app, ["macmini", "system-info", "--json"])
|
|
assert result.exit_code == 0
|
|
payload = json.loads(result.stdout)
|
|
assert payload["ok"] is True
|
|
|
|
|
|
# ─── No Notes CLI ─────────────────────────────────────────────────────────
|
|
|
|
def test_cli_macmini_no_notes_subcommand():
|
|
result = runner.invoke(app, ["macmini", "--help"])
|
|
assert result.exit_code == 0
|
|
assert "notes" not in result.stdout.lower(), f"macmini must not list notes — Notes deferred, got: {result.stdout}"
|
|
|
|
|
|
def test_cli_privacy_host_no_notes_authorize():
|
|
result = runner.invoke(app, ["privacy-host", "--help"])
|
|
assert result.exit_code == 0
|
|
# notes-authorize must be gone
|
|
assert "notes" not in result.stdout.lower()
|
|
|
|
|
|
# ─── Local services direct wrappers offline safe ───────────────────────────
|
|
|
|
def test_speech_direct_config_offline():
|
|
from reyna_cli.local_services_direct import SpeechDirectClient
|
|
|
|
c = SpeechDirectClient().config_status()
|
|
assert "say_available" in c
|
|
assert "source" in c
|
|
assert c["source"] == "direct"
|
|
assert isinstance(c.get("say_path"), str)
|
|
|
|
|
|
def test_speech_direct_validate_args():
|
|
from reyna_cli.local_services_direct import SpeechDirectClient
|
|
|
|
cli = SpeechDirectClient()
|
|
ok = cli.synthesize_args("hello", voice="Alex", rate=200)
|
|
assert ok["text"] == "hello"
|
|
with pytest.raises(ValueError):
|
|
cli.synthesize_args("", voice="Alex")
|
|
with pytest.raises(ValueError):
|
|
cli.synthesize_args("hi", rate=10)
|
|
|
|
|
|
def test_kokoro_config_offline_no_network():
|
|
from reyna_cli.local_services_direct import KokoroDirectClient
|
|
|
|
c = KokoroDirectClient(url="http://127.0.0.1:7332").config_status()
|
|
assert c["url"] == "http://127.0.0.1:7332"
|
|
assert c["source"] == "direct"
|
|
assert "note" in c
|
|
v = KokoroDirectClient().validate_synthesize("hello world")
|
|
assert v["offline_validation"] is True
|
|
with pytest.raises(ValueError):
|
|
KokoroDirectClient().validate_synthesize("")
|
|
|
|
|
|
def test_kokoro_no_secret_exposure(monkeypatch):
|
|
from reyna_cli.local_services_direct import KokoroDirectClient
|
|
|
|
monkeypatch.setenv("KSAY_URL", "http://127.0.0.1:7332")
|
|
c = KokoroDirectClient().config_status()
|
|
for k in c:
|
|
assert "token" not in k.lower() or "password" not in str(c[k]).lower()
|
|
|
|
|
|
def test_voicebox_config_offline():
|
|
from reyna_cli.local_services_direct import VoiceboxDirectClient
|
|
|
|
c = VoiceboxDirectClient().config_status()
|
|
assert "url" in c
|
|
assert c["source"] == "direct"
|
|
assert "known_profiles" in c
|
|
v = VoiceboxDirectClient().validate_generate("hello", profile="Aiden")
|
|
assert v["offline_validation"] is True
|
|
|
|
|
|
def test_apple_llm_config_offline():
|
|
from reyna_cli.local_services_direct import AppleLLMDirectClient
|
|
|
|
c = AppleLLMDirectClient().config_status()
|
|
assert "swift_available" in c or "swiftc_available" in c
|
|
assert c["source"] == "direct"
|
|
v = AppleLLMDirectClient().validate_polish("hello world", mode="line")
|
|
assert v["offline_validation"] is True
|
|
|
|
|
|
def test_image_config_offline_no_key_exposure(monkeypatch):
|
|
from reyna_cli.local_services_direct import ImageDirectClient
|
|
|
|
monkeypatch.delenv("GEMINI_API_KEY", raising=False)
|
|
c = ImageDirectClient().config_status()
|
|
assert c["source"] == "direct"
|
|
assert "gemini_api_key_configured" in c
|
|
assert c["gemini_api_key_configured"] is False
|
|
assert "GEMINI_API_KEY" not in json.dumps(c)
|
|
|
|
monkeypatch.setenv("GEMINI_API_KEY", "secret123")
|
|
c2 = ImageDirectClient().config_status()
|
|
assert c2["gemini_api_key_configured"] is True
|
|
assert "secret123" not in json.dumps(c2)
|
|
|
|
|
|
def test_system_direct_offline():
|
|
from reyna_cli.local_services_direct import SystemDirectClient
|
|
|
|
c = SystemDirectClient().config_status()
|
|
assert c["requires_tcc"] is False
|
|
info = SystemDirectClient().get_info_offline()
|
|
assert "macos_version" in info
|
|
|
|
|
|
# ─── CLI local-services commands offline ──────────────────────────────────
|
|
|
|
def test_cli_local_services_speech_config():
|
|
result = runner.invoke(app, ["local-services", "speech", "config", "--json"])
|
|
assert result.exit_code == 0
|
|
payload = json.loads(result.stdout)
|
|
assert payload["ok"] is True
|
|
assert payload["source"] == "direct"
|
|
|
|
|
|
def test_cli_local_services_kokoro_config():
|
|
result = runner.invoke(app, ["local-services", "kokoro", "config", "--json"])
|
|
assert result.exit_code == 0
|
|
payload = json.loads(result.stdout)
|
|
assert payload["result"]["source"] == "direct"
|
|
|
|
|
|
def test_cli_local_services_voicebox_config():
|
|
result = runner.invoke(app, ["local-services", "voicebox", "config", "--json"])
|
|
assert result.exit_code == 0
|
|
|
|
|
|
def test_cli_local_services_apple_llm_config():
|
|
result = runner.invoke(app, ["local-services", "apple-llm", "config", "--json"])
|
|
assert result.exit_code == 0
|
|
|
|
|
|
def test_cli_local_services_image_config():
|
|
result = runner.invoke(app, ["local-services", "image", "config", "--json"])
|
|
assert result.exit_code == 0
|
|
payload = json.loads(result.stdout)
|
|
assert payload["ok"] is True
|
|
|
|
|
|
def test_cli_local_services_system_info():
|
|
result = runner.invoke(app, ["local-services", "system", "info", "--json"])
|
|
assert result.exit_code == 0
|
|
|
|
|
|
def test_no_mcp_imports_in_direct_wrappers():
|
|
from pathlib import Path
|
|
|
|
p = Path(__file__).parents[1] / "src" / "reyna_cli" / "local_services_direct.py"
|
|
src = p.read_text()
|
|
assert "MCPClient" not in src
|
|
assert "call_macmini_tool" not in src
|
|
assert "macmini_client" not in src
|
|
assert "httpx.Client" not in src
|
|
assert "requests.get" not in src
|
|
|
|
|
|
def test_direct_wrappers_no_credential_exposure():
|
|
from pathlib import Path
|
|
|
|
src = (Path(__file__).parents[1] / "src" / "reyna_cli" / "local_services_direct.py").read_text()
|
|
assert "DECO_PASSWORD" not in src
|
|
from reyna_cli.local_services_direct import KokoroDirectClient, VoiceboxDirectClient, AppleLLMDirectClient
|
|
|
|
for c in [KokoroDirectClient().config_status(), VoiceboxDirectClient().config_status(), AppleLLMDirectClient().config_status()]:
|
|
for k, v in c.items():
|
|
if isinstance(v, str):
|
|
assert len(v) < 5000
|
|
|
|
|
|
def test_no_notes_wrappers_in_privacy_host():
|
|
from pathlib import Path
|
|
|
|
src = (Path(__file__).parents[1] / "src" / "reyna_cli" / "privacy_host.py").read_text()
|
|
assert "native_notes" not in src
|
|
assert "NotesProvider" not in src
|