"""Tests para carddav_put_vcard. Smoke deterministas: monkeypatchean urllib.request.urlopen para capturar el Request object (URL, method, headers, body) sin enviarlo a un servidor real. """ import base64 import sys import infra.carddav_put_vcard # noqa: F401 mod = sys.modules["infra.carddav_put_vcard"] carddav_put_vcard = mod.carddav_put_vcard class _FakeResp: status = 201 def __enter__(self): return self def __exit__(self, *a): return False def _capture(monkeypatch): captured = {} def fake_urlopen(req, timeout=None, context=None): captured["url"] = req.full_url captured["method"] = req.get_method() captured["headers"] = dict(req.header_items()) captured["body"] = req.data return _FakeResp() monkeypatch.setattr(mod.urllib.request, "urlopen", fake_urlopen) return captured def _call(): return carddav_put_vcard( base_url="https://dav.example.com", username="enmanuel", password="secret-pw", collection_path="/enmanuel/contacts/addressbook/", uid="abc-123@google.com", vcard_text="BEGIN:VCARD\r\nFN:Ada\r\nUID:abc-123@google.com\r\nEND:VCARD", ) def test_construye_request_put_con_headers_correctos(monkeypatch): cap = _capture(monkeypatch) res = _call() assert res == {"status": "ok", "http_status": 201, "url": cap["url"]} assert cap["method"] == "PUT" def test_url_se_forma_con_uid_saneado(monkeypatch): cap = _capture(monkeypatch) _call() # El '@' del uid se sanea a '_'. assert cap["url"].endswith("/enmanuel/contacts/addressbook/abc-123_google.com.vcf") def test_content_type_es_text_vcard(monkeypatch): cap = _capture(monkeypatch) _call() # urllib capitaliza las claves de header. headers = {k.lower(): v for k, v in cap["headers"].items()} assert headers["content-type"] == "text/vcard; charset=utf-8" def test_basic_auth_header_correcto(monkeypatch): cap = _capture(monkeypatch) _call() headers = {k.lower(): v for k, v in cap["headers"].items()} expected = "Basic " + base64.b64encode(b"enmanuel:secret-pw").decode("ascii") assert headers["authorization"] == expected def test_httperror_devuelve_status_error(monkeypatch): def fake_urlopen(req, timeout=None, context=None): raise mod.urllib.error.HTTPError(req.full_url, 409, "Conflict", {}, None) monkeypatch.setattr(mod.urllib.request, "urlopen", fake_urlopen) res = _call() assert res["status"] == "error" assert res["http_status"] == 409