feat(dav,obsidian): grupo dav completo (CardDAV/CalDAV client + split vcf/ics + import pipelines) + build_obsidian_graph + dav_list_calendars

Funciones reutilizables creadas esta sesion para el sistema self-hosted de contactos/calendario (Xandikos) y la app osint_web:
- grupo dav (infra): split_vcards, split_vevents_to_vcalendars, extract_or_make_uid, carddav_put_vcard, caldav_put_event, dav_list_resources, dav_get_resource, dav_list_calendars
- pipelines: import_vcf_to_carddav, import_ics_to_caldav
- obsidian: build_obsidian_graph (grafo agregado del vault)
This commit is contained in:
2026-06-12 00:43:59 +02:00
parent 4a0f0e9dc0
commit a76760edba
32 changed files with 2814 additions and 0 deletions
@@ -0,0 +1,40 @@
"""Tests para split_vcards. Puros, deterministas, sin I/O."""
import sys
import infra.split_vcards # noqa: F401
mod = sys.modules["infra.split_vcards"]
split_vcards = mod.split_vcards
_TWO = (
"BEGIN:VCARD\r\nVERSION:3.0\r\nFN:Ada Lovelace\r\nEND:VCARD\r\n"
"BEGIN:VCARD\r\nVERSION:3.0\r\nFN:Alan Turing\r\nEND:VCARD\r\n"
)
def test_dos_vcards_devuelve_dos():
cards = split_vcards(_TWO)
assert len(cards) == 2
def test_vcard_unico():
cards = split_vcards("BEGIN:VCARD\nFN:Solo\nEND:VCARD\n")
assert len(cards) == 1
assert "Solo" in cards[0]
def test_input_vacio_devuelve_lista_vacia():
assert split_vcards("") == []
assert split_vcards("ruido sin tarjetas") == []
def test_crlf_se_tolera():
lf = _TWO.replace("\r\n", "\n")
assert len(split_vcards(lf)) == 2
def test_cada_card_es_begin_end():
for c in split_vcards(_TWO):
assert c.startswith("BEGIN:VCARD")
assert c.endswith("END:VCARD")