refactor(contacts): address PR #719 follow-up review
Pulls the remaining review feedback into one commit: - Remove the double _normalize_contact_data call: the helper now assumes canonical keys, and create_contact normalises before calling it (update_contact already did). Docstring states the invariant. - Drop phone/organization from _SUPPORTED_CONTACT_KEYS; they never reach the unknown-key check post-normalisation. - Tighten generics to dict[str, Any] / list[str] across helpers and ContactsClient signatures. - Comment both URL-merge sites noting only the first URL is written. - Log a warning when fn is missing from contact_data. - Test coverage for _wrap_contact_field dropping value-less dicts and for the fn-missing warning; _vcard helper now mirrors the real call chain (normalise → build). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
ee1465c05c
commit
e2283ff28c
@@ -12,14 +12,20 @@ import pytest
|
||||
from nextcloud_mcp_server.client.contacts import (
|
||||
_build_contact_from_data,
|
||||
_normalize_contact_data,
|
||||
_wrap_contact_field,
|
||||
)
|
||||
|
||||
pytestmark = pytest.mark.unit
|
||||
|
||||
|
||||
def _vcard(**kwargs) -> str:
|
||||
"""Build a vCard from ``contact_data`` with a fixed uid, return the serialised text."""
|
||||
return _build_contact_from_data(kwargs, uid="unit-test-uid").to_vcard()
|
||||
"""Build a vCard from ``contact_data`` with a fixed uid, return the serialised text.
|
||||
|
||||
Mirrors ``create_contact``'s real call chain: normalise aliases first, then hand
|
||||
canonical keys to ``_build_contact_from_data``.
|
||||
"""
|
||||
data = _normalize_contact_data(kwargs)
|
||||
return _build_contact_from_data(data, uid="unit-test-uid").to_vcard()
|
||||
|
||||
|
||||
def test_issue_716_minimal_payload_keeps_all_fields():
|
||||
@@ -154,6 +160,33 @@ def test_dict_form_email_preserves_custom_type():
|
||||
assert "EMAIL;TYPE=WORK:work@example.com" in vcard
|
||||
|
||||
|
||||
def test_wrap_field_dict_without_value_is_dropped():
|
||||
"""Dict inputs lacking the ``value`` key are silently dropped so malformed
|
||||
payloads don't emit an EMAIL/TEL line pointing at nothing.
|
||||
"""
|
||||
assert _wrap_contact_field({"type": ["WORK"]}) == []
|
||||
# Mixed list: the valid entry survives, the value-less dict is omitted.
|
||||
out = _wrap_contact_field(
|
||||
[{"value": "ok@example.com", "type": ["WORK"]}, {"type": ["HOME"]}]
|
||||
)
|
||||
assert out == [{"value": "ok@example.com", "type": ["WORK"]}]
|
||||
|
||||
|
||||
def test_missing_fn_logs_warning(caplog):
|
||||
"""A missing ``fn`` should log a warning so operators notice malformed payloads."""
|
||||
import logging
|
||||
|
||||
with caplog.at_level(
|
||||
logging.WARNING, logger="nextcloud_mcp_server.client.contacts"
|
||||
):
|
||||
try:
|
||||
_build_contact_from_data({"email": "x@example.com"}, uid="no-fn-uid")
|
||||
except Exception:
|
||||
# pythonvCard4 may raise on missing fn; we only care about the warning log.
|
||||
pass
|
||||
assert any("fn" in r.message.lower() for r in caplog.records)
|
||||
|
||||
|
||||
class TestNormalizeContactData:
|
||||
"""Direct tests for the alias helper — it's load-bearing for update_contact too."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user