"""Tests for contacts native wrappers and CLI – TDD fakes only, no live Contacts 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_contacts_search_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": "contacts.search", "contacts": [{"id": "1", "name": "Alice", "organization": "OrgA", "modifiedAt": "2026-01-01T00:00:00Z"}]}} monkeypatch.setattr(ph_mod, "PrivacyClient", FakeClient) result = ph_mod.native_contacts_search(query="alice", limit=20) assert captured["op"] == "contacts.search" assert captured["args"]["query"] == "alice" assert captured["args"]["limit"] == 20 assert result["ok"] is True assert result["source"] == "native_privacy_host" assert len(result["result"]["contacts"]) == 1 def test_native_contacts_search_no_query(monkeypatch): from reyna_cli import privacy_host as ph_mod captured = {} class FakeClient: def call(self, op, args): captured["args"] = args return {"id": "x", "ok": True, "result": {"contacts": []}} monkeypatch.setattr(ph_mod, "PrivacyClient", FakeClient) ph_mod.native_contacts_search(query=None, limit=10) assert "query" not in captured["args"] def test_native_contacts_read_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": "a", "ok": True, "result": {"protocol_version": "1.0.0", "operation": "contacts.read", "contact": {"id": "1", "name": "Alice"}}} monkeypatch.setattr(ph_mod, "PrivacyClient", FakeClient) result = ph_mod.native_contacts_read(contact_id="1") assert captured["op"] == "contacts.read" assert captured["args"]["id"] == "1" assert result["ok"] is True def test_native_contacts_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": "b", "ok": True, "result": {"protocol_version": "1.0.0", "operation": "contacts.create", "created_contact": {"id": "new", "name": "Alice", "organization": ""}}} monkeypatch.setattr(ph_mod, "PrivacyClient", FakeClient) result = ph_mod.native_contacts_create(first_name="Alice", last_name="Smith", email="alice@example.com") assert captured["op"] == "contacts.create" assert captured["args"]["firstName"] == "Alice" assert captured["args"]["lastName"] == "Smith" assert captured["args"]["email"]["value"] == "alice@example.com" assert result["ok"] is True def test_native_contacts_request_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": "contacts.request_access", "status": "authorized"}} monkeypatch.setattr(ph_mod, "PrivacyClient", FakeClient) result = ph_mod.native_contacts_request_access() assert captured["op"] == "contacts.request_access" assert captured["args"] == {} assert captured["timeout"] == 35 assert result["result"]["status"] == "authorized" def test_native_contacts_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_contacts_request_access" in src assert "contacts.request_access" in src assert "def native_contacts_search" in src assert "def native_contacts_read" in src assert "def native_contacts_create" in src def test_cli_contacts_search_uses_native(monkeypatch): def fake_search(query=None, limit=20): return {"ok": True, "source": "native_privacy_host", "result": {"contacts": [{"id": "1", "name": "Alice", "organization": "", "modifiedAt": "2026-01-01T00:00:00Z"}]}} monkeypatch.setattr("reyna_cli.privacy_host.native_contacts_search", fake_search) res = runner.invoke(app, ["macmini", "contacts", "search", "--query", "alice", "--json"]) assert res.exit_code == 0, res.stdout + res.stderr payload = json.loads(res.stdout) assert payload["ok"] is True def test_cli_contacts_read_uses_native(monkeypatch): def fake_read(contact_id): return {"ok": True, "source": "native_privacy_host", "result": {"contact": {"id": contact_id, "name": "Alice", "firstName": "Alice", "lastName": "", "organization": "", "jobTitle": "", "emails": [], "phones": [], "modifiedAt": "2026-01-01T00:00:00Z"}}} monkeypatch.setattr("reyna_cli.privacy_host.native_contacts_read", fake_read) res = runner.invoke(app, ["macmini", "contacts", "read", "abc-123", "--json"]) assert res.exit_code == 0, res.stdout + res.stderr payload = json.loads(res.stdout) assert payload["ok"] is True def test_cli_contacts_create_uses_native(monkeypatch): captured = {} def fake_create(first_name=None, last_name=None, organization=None, job_title=None, note=None, email=None, phone=None): captured["first_name"] = first_name captured["email"] = email return {"ok": True, "source": "native_privacy_host", "result": {"created_contact": {"id": "new", "name": "Alice", "organization": ""}}} monkeypatch.setattr("reyna_cli.privacy_host.native_contacts_create", fake_create) res = runner.invoke(app, ["macmini", "contacts", "create", "--first-name", "Alice", "--email", "alice@example.com", "--json"]) assert res.exit_code == 0, res.stdout + res.stderr assert captured["first_name"] == "Alice" assert captured["email"] == "alice@example.com" def test_cli_contacts_no_mcp_tool_call_remaining(): from reyna_cli import cli as cli_mod src = Path(cli_mod.__file__).read_text() assert "native_contacts_search" in src assert "native_contacts_read" in src assert "native_contacts_create" in src assert 'call_macmini_tool("contacts_search"' not in src assert 'call_macmini_tool("contacts_read"' not in src assert 'call_macmini_tool("contacts_create"' not in src