Files
reyna-cli/tests/test_reminders.py
T
2026-08-17 11:18:55 -04:00

316 lines
12 KiB
Python
Raw 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 reminders native wrappers and CLI – TDD fakes only, no live Reminders access."""
from pathlib import Path
import json
import pytest
from typer.testing import CliRunner
from reyna_cli.cli import app
runner = CliRunner()
def test_native_reminders_request_full_access_explicit_op(monkeypatch):
from reyna_cli import privacy_host as ph_mod
captured = {}
class FakeClient:
def __init__(self, timeout):
captured["timeout"] = timeout
def call(self, op, args):
captured["op"] = op
captured["args"] = args
return {"id": "x", "ok": True, "result": {"protocol_version": "1.0.0", "operation": "reminders.request_full_access", "status": "authorized"}}
monkeypatch.setattr(ph_mod, "PrivacyClient", FakeClient)
result = ph_mod.native_reminders_request_full_access()
assert captured["op"] == "reminders.request_full_access"
assert captured["args"] == {}
assert captured["timeout"] == 35
assert result["ok"] is True
assert result["result"]["status"] == "authorized"
def test_native_reminders_lists_direct_op(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": "1", "ok": True, "result": {"protocol_version": "1.0.0", "operation": "reminders.lists", "reminder_lists": [{"id": "a", "title": "Groceries", "source": "iCloud", "type": "caldav"}]}}
monkeypatch.setattr(ph_mod, "PrivacyClient", FakeClient)
result = ph_mod.native_reminders_lists()
assert captured["op"] == "reminders.lists"
assert captured["args"] == {}
assert result["ok"] is True
assert result["result"]["reminder_lists"][0]["title"] == "Groceries"
def test_native_reminders_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": "2", "ok": True, "result": {"reminders": [{"id": "r1", "list_id": "a", "list_title": "Groceries", "title": "Milk", "completed": False, "due": None, "priority": 0}]}}
monkeypatch.setattr(ph_mod, "PrivacyClient", FakeClient)
result = ph_mod.native_reminders_list(list_name="Groceries", limit=25)
assert captured["op"] == "reminders.list"
assert captured["args"]["list"] == "Groceries"
assert captured["args"]["limit"] == 25
assert result["result"]["reminders"][0]["title"] == "Milk"
def test_native_reminders_list_with_id_and_completed_filter(monkeypatch):
from reyna_cli import privacy_host as ph_mod
captured = {}
class FakeClient:
def call(self, op, args):
captured["args"] = args
return {"id": "2", "ok": True, "result": {"reminders": []}}
monkeypatch.setattr(ph_mod, "PrivacyClient", FakeClient)
ph_mod.native_reminders_list(list_id="stable-id", completed=True, limit=50)
assert captured["args"]["list_id"] == "stable-id"
assert captured["args"]["completed"] is True
assert captured["args"]["limit"] == 50
def test_native_reminders_create_success_with_list_title(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": "3", "ok": True, "result": {"created_reminder": {"id": "new", "list_id": "a", "list_title": "Groceries", "title": "Buy eggs"}}}
monkeypatch.setattr(ph_mod, "PrivacyClient", FakeClient)
result = ph_mod.native_reminders_create(title="Buy eggs", list_name="Groceries", notes="organic", due="2026-08-10T10:00:00Z", priority=1)
assert captured["op"] == "reminders.create"
assert captured["args"]["title"] == "Buy eggs"
assert captured["args"]["list"] == "Groceries"
assert captured["args"]["notes"] == "organic"
assert captured["args"]["due"] == "2026-08-10T10:00:00Z"
assert captured["args"]["priority"] == 1
assert result["ok"] is True
def test_native_reminders_create_requires_list():
from reyna_cli import privacy_host as ph_mod
with pytest.raises(ValueError, match="list must be specified"):
ph_mod.native_reminders_create(title="No list")
def test_native_reminders_create_validates_title():
from reyna_cli import privacy_host as ph_mod
with pytest.raises(ValueError, match="title must be nonempty"):
ph_mod.native_reminders_create(title="", list_name="X")
with pytest.raises(ValueError, match="title exceeds"):
ph_mod.native_reminders_create(title="A" * 1025, list_name="X")
def test_native_reminders_create_validates_limit():
from reyna_cli import privacy_host as ph_mod
with pytest.raises(ValueError, match="limit"):
ph_mod.native_reminders_list(limit=0)
with pytest.raises(ValueError, match="limit"):
ph_mod.native_reminders_list(limit=999)
def test_native_reminders_no_mcp_import():
from reyna_cli import privacy_host as ph_mod
import pathlib
src = pathlib.Path(ph_mod.__file__).read_text()
assert "call_macmini_tool" not in src
assert "macmini_client" not in src
assert "def native_reminders_request_full_access" in src
assert "reminders.request_full_access" in src
assert "def native_reminders_lists" in src
assert "def native_reminders_list" in src
assert "def native_reminders_create" in src
def test_cli_reminders_lists_uses_native(monkeypatch):
def fake_lists():
return {"ok": True, "source": "native_privacy_host", "result": {"reminder_lists": [{"id": "a", "title": "Groceries", "source": "iCloud", "type": "caldav"}]}}
monkeypatch.setattr("reyna_cli.privacy_host.native_reminders_lists", fake_lists)
res = runner.invoke(app, ["macmini", "reminders", "lists", "--json"])
assert res.exit_code == 0, res.stdout + res.stderr
payload = json.loads(res.stdout)
assert payload["ok"] is True
assert payload["result"]["reminder_lists"][0]["title"] == "Groceries"
def test_cli_reminders_list_uses_native(monkeypatch):
captured = {}
def fake_list(list_id=None, list_name=None, completed=None, limit=50):
captured["list_id"] = list_id
captured["list_name"] = list_name
captured["completed"] = completed
captured["limit"] = limit
return {"ok": True, "source": "native_privacy_host", "result": {"reminders": []}}
monkeypatch.setattr("reyna_cli.privacy_host.native_reminders_list", fake_list)
res = runner.invoke(app, ["macmini", "reminders", "list", "--list", "Groceries", "--limit", "10", "--json"])
assert res.exit_code == 0, res.stdout + res.stderr
assert captured["list_name"] == "Groceries"
assert captured["limit"] == 10
def test_cli_reminders_list_with_completed_flags(monkeypatch):
captured = {}
def fake_list(list_id=None, list_name=None, completed=None, limit=25):
captured["completed"] = completed
return {"ok": True, "source": "native_privacy_host", "result": {"reminders": []}}
monkeypatch.setattr("reyna_cli.privacy_host.native_reminders_list", fake_list)
res = runner.invoke(app, ["macmini", "reminders", "list", "--list", "Groceries", "--completed", "--json"])
assert res.exit_code == 0, res.stdout + res.stderr
assert captured["completed"] is True
res2 = runner.invoke(app, ["macmini", "reminders", "list", "--list", "Groceries", "--incomplete", "--json"])
assert res2.exit_code == 0, res2.stdout + res2.stderr
# --incomplete should set completed=False
assert captured["completed"] is False
def test_cli_reminders_create_requires_list():
res = runner.invoke(app, ["macmini", "reminders", "create", "Buy milk", "--json"])
assert res.exit_code != 0
def test_cli_reminders_create_with_title_and_list(monkeypatch):
captured = {}
def fake_create(title, list_id=None, list_name=None, notes=None, due=None, priority=None):
captured["title"] = title
captured["list_name"] = list_name
captured["list_id"] = list_id
captured["notes"] = notes
captured["due"] = due
captured["priority"] = priority
return {"ok": True, "source": "native_privacy_host", "result": {"created_reminder": {"id": "new", "list_id": "a", "list_title": "Groceries", "title": title}}}
monkeypatch.setattr("reyna_cli.privacy_host.native_reminders_create", fake_create)
res = runner.invoke(app, ["macmini", "reminders", "create", "Buy milk", "--list", "Groceries", "--notes", "2% please", "--json"])
assert res.exit_code == 0, res.stdout + res.stderr
assert captured["title"] == "Buy milk"
assert captured["list_name"] == "Groceries"
assert captured["notes"] == "2% please"
def test_cli_reminders_create_with_list_id_and_due_priority(monkeypatch):
captured = {}
def fake_create(title, list_id=None, list_name=None, notes=None, due=None, priority=None):
captured["list_id"] = list_id
captured["due"] = due
captured["priority"] = priority
return {"ok": True, "source": "native_privacy_host", "result": {"created_reminder": {"id": "new", "list_id": list_id or "", "list_title": "", "title": title}}}
monkeypatch.setattr("reyna_cli.privacy_host.native_reminders_create", fake_create)
res = runner.invoke(app, ["macmini", "reminders", "create", "Task", "--list-id", "abc-123", "--due", "2026-08-10T10:00:00Z", "--priority", "1", "--json"])
assert res.exit_code == 0, res.stdout + res.stderr
assert captured["list_id"] == "abc-123"
assert captured["due"] == "2026-08-10T10:00:00Z"
assert captured["priority"] == 1
def test_cli_reminders_no_mcp_tool_call_remaining():
from reyna_cli import cli as cli_mod
src = Path(cli_mod.__file__).read_text()
assert "native_reminders_lists" in src
assert "native_reminders_list" in src
assert "native_reminders_create" in src
assert 'call_macmini_tool("reminders_list_lists"' not in src
assert 'call_macmini_tool("reminders_list"' not in src
assert 'call_macmini_tool("reminders_create"' not in src
def test_cli_reminders_edit_uses_remindctl(monkeypatch):
captured = {}
def fake_run(args):
captured["args"] = args
return {"updated": {"id": "r1", "title": "New title"}}
monkeypatch.setattr("reyna_cli.cli.run_remindctl", fake_run)
res = runner.invoke(app, ["macmini", "reminders", "edit", "r1", "--title", "New title", "--due", "tomorrow", "--json"])
assert res.exit_code == 0
assert captured["args"] == ["edit", "r1", "--title", "New title", "--due", "tomorrow", "--json", "--no-input"]
assert json.loads(res.stdout)["source"] == "remindctl"
def test_cli_reminders_delete_requires_force(monkeypatch):
res = runner.invoke(app, ["macmini", "reminders", "delete", "r1", "--json"])
assert res.exit_code == 1
assert "--force" in res.stdout
def test_cli_reminders_delete_uses_remindctl_with_force(monkeypatch):
captured = {}
def fake_run(args):
captured["args"] = args
return {"deleted": ["r1"]}
monkeypatch.setattr("reyna_cli.cli.run_remindctl", fake_run)
res = runner.invoke(app, ["macmini", "reminders", "delete", "r1", "--force", "--json"])
assert res.exit_code == 0
assert captured["args"] == ["delete", "r1", "--force", "--json", "--no-input"]
def test_privacy_host_cli_has_reminders_authorize():
res = runner.invoke(app, ["privacy-host", "--help"])
assert res.exit_code == 0
assert "reminders-authorize" in res.stdout
def test_privacy_host_reminders_authorize_cli_help_mentions_prompt():
res = runner.invoke(app, ["privacy-host", "reminders-authorize", "--help"])
assert res.exit_code == 0
out = res.stdout.lower()
assert "reminders" in out
assert "permission" in out or "prompt" in out or "privacy" in out
def test_reminders_authorize_cli_no_generic_fallback(monkeypatch):
calls = {"count": 0}
def fake_native():
calls["count"] += 1
return {"ok": True, "source": "native_privacy_host", "result": {"protocol_version": "1.0.0", "operation": "reminders.request_full_access", "status": "authorized"}}
monkeypatch.setattr("reyna_cli.privacy_host.native_reminders_request_full_access", fake_native)
res = runner.invoke(app, ["privacy-host", "reminders-authorize", "--json"])
assert res.exit_code == 0, res.stdout + res.stderr
payload = json.loads(res.stdout)
assert payload["ok"] is True
assert payload["result"]["status"] == "authorized"
assert calls["count"] == 1