Files
reyna-cli/tests/test_calendar_events.py
Adolfo Reyna 9fd04b0ce4 feat(privacy-host): add signed native calendar contacts and reminders host
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.
2026-08-03 20:27:54 -04:00

295 lines
12 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Tests for calendar event list/create native wrappers and CLI routing – TDD, no live calls."""
from pathlib import Path
import json
import pytest
from typer.testing import CliRunner
from reyna_cli.cli import app
runner = CliRunner()
def test_native_calendar_events_list_success(monkeypatch):
from reyna_cli import privacy_host as ph_mod
captured = {}
class FakeClient:
def call(self, op, args):
captured["op"] = op
captured["args"] = args
return {"id": "abc", "ok": True, "result": {"protocol_version": "1.0.0", "operation": "calendar.events.list", "events": []}}
monkeypatch.setattr(ph_mod, "PrivacyClient", FakeClient)
result = ph_mod.native_calendar_events_list(start="2026-01-01T00:00:00Z", end="2026-01-02T00:00:00Z", calendar="Home", limit=25)
assert captured["op"] == "calendar.events.list"
assert captured["args"]["start"] == "2026-01-01T00:00:00Z"
assert captured["args"]["end"] == "2026-01-02T00:00:00Z"
assert captured["args"]["calendar"] == "Home"
assert captured["args"]["limit"] == 25
assert result["ok"] is True
assert result["source"] == "native_privacy_host"
def test_native_calendar_events_list_with_calendar_id(monkeypatch):
from reyna_cli import privacy_host as ph_mod
captured = {}
class FakeClient:
def call(self, op, args):
captured["args"] = args
return {"id": "abc", "ok": True, "result": {"events": []}}
monkeypatch.setattr(ph_mod, "PrivacyClient", FakeClient)
ph_mod.native_calendar_events_list(start="2026-01-01T00:00:00Z", end="2026-01-02T00:00:00Z", calendar_id="stable-id-123", limit=50)
assert captured["args"]["calendar_id"] == "stable-id-123"
assert "calendar" not in captured["args"] or captured["args"].get("calendar") is None
def test_native_calendar_events_list_failure_surfaces(monkeypatch):
from reyna_cli import privacy_host as ph_mod
from reyna_cli.privacy_client import PrivacyClientError
class FakeFail:
def call(self, op, args):
raise PrivacyClientError("privacy RPC returned ok=false: permission_required")
monkeypatch.setattr(ph_mod, "PrivacyClient", FakeFail)
with pytest.raises(PrivacyClientError):
ph_mod.native_calendar_events_list(start="2026-01-01T00:00:00Z", end="2026-01-02T00:00:00Z")
def test_native_calendar_event_create_success(monkeypatch):
from reyna_cli import privacy_host as ph_mod
captured = {}
class FakeClient:
def call(self, op, args):
captured["op"] = op
captured["args"] = args
return {"id": "c", "ok": True, "result": {"protocol_version": "1.0.0", "operation": "calendar.event.create", "event": {"id": "new"}}}
monkeypatch.setattr(ph_mod, "PrivacyClient", FakeClient)
result = ph_mod.native_calendar_event_create(
title="Meeting",
start="2026-01-01T10:00:00Z",
end="2026-01-01T11:00:00Z",
all_day=False,
notes="bring docs",
location="Room 1",
calendar_id="cal-id-1",
calendar=None,
)
assert captured["op"] == "calendar.event.create"
assert captured["args"]["title"] == "Meeting"
assert captured["args"]["start"] == "2026-01-01T10:00:00Z"
assert captured["args"]["end"] == "2026-01-01T11:00:00Z"
assert captured["args"]["all_day"] is False
assert captured["args"]["notes"] == "bring docs"
assert captured["args"]["calendar_id"] == "cal-id-1"
assert result["ok"] is True
def test_native_calendar_event_create_with_calendar_title(monkeypatch):
from reyna_cli import privacy_host as ph_mod
captured = {}
class FakeClient:
def call(self, op, args):
captured["args"] = args
return {"id": "c", "ok": True, "result": {"event": {"id": "new"}}}
monkeypatch.setattr(ph_mod, "PrivacyClient", FakeClient)
ph_mod.native_calendar_event_create(title="T", start="2026-01-01T10:00:00Z", end="2026-01-01T11:00:00Z", calendar="Home")
assert captured["args"]["calendar"] == "Home"
assert "calendar_id" not in captured["args"]
def test_no_mcp_in_new_wrappers():
from reyna_cli import privacy_host as ph_mod
src = Path(ph_mod.__file__).read_text()
# wrappers must still not contain MCP fallback
assert "call_macmini_tool" not in src
assert "MCPClient" not in src
def test_cli_events_uses_native_wrapper(monkeypatch):
calls = {"count": 0, "args": None}
def fake_events(start, end, calendar_id=None, calendar=None, limit=50):
calls["count"] += 1
calls["args"] = {"start": start, "end": end, "calendar_id": calendar_id, "calendar": calendar, "limit": limit}
return {"ok": True, "source": "native_privacy_host", "result": {"events": []}}
monkeypatch.setattr("reyna_cli.privacy_host.native_calendar_events_list", fake_events)
result = runner.invoke(app, ["macmini", "calendar", "events", "2026-01-01T00:00:00Z", "2026-01-02T00:00:00Z", "--json"])
assert result.exit_code == 0, result.stdout + result.stderr
payload = json.loads(result.stdout)
assert payload["ok"] is True
assert calls["count"] == 1
assert calls["args"]["start"] == "2026-01-01T00:00:00Z"
def test_cli_events_with_calendar_title(monkeypatch):
captured = {}
def fake_events(start, end, calendar_id=None, calendar=None, limit=50):
captured["calendar"] = calendar
captured["calendar_id"] = calendar_id
return {"ok": True, "source": "native_privacy_host", "result": {"events": []}}
monkeypatch.setattr("reyna_cli.privacy_host.native_calendar_events_list", fake_events)
result = runner.invoke(app, ["macmini", "calendar", "events", "2026-01-01T00:00:00Z", "2026-01-02T00:00:00Z", "--calendar", "Home", "--json"])
assert result.exit_code == 0, result.stdout + result.stderr
assert captured["calendar"] == "Home"
assert captured["calendar_id"] is None
def test_cli_events_with_calendar_index_resolves_once(monkeypatch):
# Track how many times native_calendar_list is called – must be exactly once for index compat
list_calls = {"count": 0}
events_calls = {"args": None}
def fake_list():
list_calls["count"] += 1
return {
"ok": True,
"source": "native_privacy_host",
"result": {
"protocol_version": "1.0.0",
"operation": "calendar.list",
"calendars": [
{"id": "id-0", "title": "Home", "source": "iCloud", "type": "caldav"},
{"id": "id-1", "title": "Work", "source": "iCloud", "type": "caldav"},
],
},
}
def fake_events(start, end, calendar_id=None, calendar=None, limit=50):
events_calls["args"] = {"calendar_id": calendar_id, "calendar": calendar}
return {"ok": True, "source": "native_privacy_host", "result": {"events": []}}
monkeypatch.setattr("reyna_cli.privacy_host.native_calendar_list", fake_list)
monkeypatch.setattr("reyna_cli.privacy_host.native_calendar_events_list", fake_events)
result = runner.invoke(app, ["macmini", "calendar", "events", "2026-01-01T00:00:00Z", "2026-01-02T00:00:00Z", "--calendar-index", "1", "--json"])
assert result.exit_code == 0, result.stdout + result.stderr
assert list_calls["count"] == 1, "must resolve calendar list exactly once"
assert events_calls["args"]["calendar_id"] == "id-1"
assert events_calls["args"]["calendar"] is None
def test_cli_events_invalid_calendar_index_returns_error(monkeypatch):
def fake_list():
return {
"ok": True,
"source": "native_privacy_host",
"result": {
"calendars": [
{"id": "id-0", "title": "Home", "source": "iCloud", "type": "caldav"},
]
},
}
monkeypatch.setattr("reyna_cli.privacy_host.native_calendar_list", fake_list)
monkeypatch.setattr("reyna_cli.privacy_host.native_calendar_events_list", lambda **kwargs: {"ok": True, "source": "native", "result": {}})
result = runner.invoke(app, ["macmini", "calendar", "events", "2026-01-01T00:00:00Z", "2026-01-02T00:00:00Z", "--calendar-index", "5", "--json"])
assert result.exit_code != 0
# fail() with json_output should emit ok:false payload
assert "out of range" in result.stdout or "out of range" in result.stderr or "ok" in result.stdout.lower()
def test_cli_create_uses_native_wrapper(monkeypatch):
captured = {}
def fake_create(title, start, end, all_day=False, notes=None, location=None, calendar_id=None, calendar=None):
captured["title"] = title
captured["calendar"] = calendar
captured["calendar_id"] = calendar_id
captured["notes"] = notes
return {"ok": True, "source": "native_privacy_host", "result": {"event": {"id": "new"}}}
monkeypatch.setattr("reyna_cli.privacy_host.native_calendar_event_create", fake_create)
result = runner.invoke(app, ["macmini", "calendar", "create", "Meeting", "2026-01-01T10:00:00Z", "2026-01-01T11:00:00Z", "--calendar", "Home", "--notes", "bring docs", "--json"])
assert result.exit_code == 0, result.stdout + result.stderr
assert captured["title"] == "Meeting"
assert captured["calendar"] == "Home"
assert captured["notes"] == "bring docs"
def test_cli_create_with_calendar_index(monkeypatch):
list_calls = {"count": 0}
create_calls = {}
def fake_list():
list_calls["count"] += 1
return {
"ok": True,
"source": "native_privacy_host",
"result": {"calendars": [{"id": "id-xyz", "title": "Home", "source": "iCloud", "type": "caldav"}]},
}
def fake_create(title, start, end, all_day=False, notes=None, location=None, calendar_id=None, calendar=None):
create_calls["calendar_id"] = calendar_id
create_calls["calendar"] = calendar
return {"ok": True, "source": "native_privacy_host", "result": {"event": {"id": "new"}}}
monkeypatch.setattr("reyna_cli.privacy_host.native_calendar_list", fake_list)
monkeypatch.setattr("reyna_cli.privacy_host.native_calendar_event_create", fake_create)
result = runner.invoke(app, ["macmini", "calendar", "create", "T", "2026-01-01T10:00:00Z", "2026-01-01T11:00:00Z", "--calendar-index", "0", "--json"])
assert result.exit_code == 0, result.stdout + result.stderr
assert list_calls["count"] == 1
assert create_calls["calendar_id"] == "id-xyz"
assert create_calls["calendar"] is None
def test_cli_create_no_default_calendar_first_arbitrary(monkeypatch):
# When no calendar specified, wrapper should receive None for both and then host will reject (no default)
captured = {}
def fake_create(title, start, end, all_day=False, notes=None, location=None, calendar_id=None, calendar=None):
captured["calendar_id"] = calendar_id
captured["calendar"] = calendar
return {"ok": True, "source": "native_privacy_host", "result": {"event": {"id": "new"}}}
monkeypatch.setattr("reyna_cli.privacy_host.native_calendar_event_create", fake_create)
result = runner.invoke(app, ["macmini", "calendar", "create", "Title", "2026-01-01T10:00:00Z", "2026-01-01T11:00:00Z", "--json"])
assert result.exit_code == 0, result.stdout + result.stderr
assert captured["calendar_id"] is None
assert captured["calendar"] is None
def test_cli_events_no_mcp_tool_call(monkeypatch):
# Prove no call_macmini_tool present in file after change
from reyna_cli import cli as cli_mod
src = Path(cli_mod.__file__).read_text()
# Find events function section – must not contain call_macmini_tool for calendar_list_events
# Overall file still may have call_macmini_tool for contacts etc, but our two commands must not use it
# So check that native wrappers are used
assert "native_calendar_events_list" in src
assert "native_calendar_event_create" in src
# Ensure the old patterns are gone from those specific functions by checking surrounding lines
# Simpler: assert the literal string call_macmini_tool("calendar_list_events" not present
assert 'calendar_list_events' not in src or 'call_macmini_tool(\"calendar_list_events\"' not in src
assert 'calendar_create_event' not in src or 'call_macmini_tool(\"calendar_create_event\"' not in src