test(caldav+carddav): rm xfail mark on known cases
This commit is contained in:
@@ -12,11 +12,16 @@ Fixtures:
|
|||||||
* `fresh_addressbook` — a brand-new address book per test; yields
|
* `fresh_addressbook` — a brand-new address book per test; yields
|
||||||
the server-authoritative URL as a string; teardown DELETEs it.
|
the server-authoritative URL as a string; teardown DELETEs it.
|
||||||
|
|
||||||
Same emitter-gap caveats as `test_ical_coverage.py`: the server
|
Coverage: the sanity tests cover the FN/N/EMAIL core; the
|
||||||
regenerates vCard bodies from stored DTO fields on GET, so
|
extended round-trips (ORG/TITLE/NOTE/TEL/ADR) each pin a
|
||||||
properties beyond FN / N / EMAIL may be silently dropped. Tests
|
parser + emitter pair. Any regression that drops a property
|
||||||
here split into sanity (must round-trip) vs xfail (documented
|
on the round-trip fails the corresponding test.
|
||||||
gaps).
|
|
||||||
|
The CardDAV emitter regenerates vCard bodies from stored DTO
|
||||||
|
fields on GET — properties without a DTO field (BDAY, PHOTO,
|
||||||
|
categories, custom X-*) still don't survive. Same emitter-gap
|
||||||
|
class as `test_ical_coverage.py`; would be closed by serving
|
||||||
|
the stored vcard_data verbatim or extending the DTO.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -25,7 +30,6 @@ import textwrap
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import caldav
|
import caldav
|
||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
# ─────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────
|
||||||
@@ -187,32 +191,13 @@ def test_addressbook_shows_up_in_propfind(
|
|||||||
|
|
||||||
|
|
||||||
# ─────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────
|
||||||
# Documented gaps — vCard properties the server currently drops
|
# Extended round-trips — properties beyond the FN/N/EMAIL core.
|
||||||
# on GET. Same shape as the CalDAV emitter gap: server rebuilds
|
# Each has a parse_vcard branch + a contact_to_vcard emitter
|
||||||
# the response body from stored DTO fields; properties not in
|
# branch; loss of any of these on a real-client sync would
|
||||||
# the DTO surface are silently dropped.
|
# silently break the corresponding UI slot (job title, phone,
|
||||||
|
# address, notes).
|
||||||
# ─────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
_TEL_URI_PARSER_BUG_REASON = (
|
|
||||||
"contact_service.rs::parse_vcard splits the TEL line by ':' "
|
|
||||||
"and takes .nth(1) as the number — a URI-form value like "
|
|
||||||
"`TEL;TYPE=cell;VALUE=uri:tel:+15551234567` gets sliced to "
|
|
||||||
"'tel' (the middle segment), losing the actual phone number. "
|
|
||||||
"Real clients (Apple Contacts, DAVx⁵) commonly emit the URI "
|
|
||||||
"form. Fix: split on the FIRST ':' only, or parse the "
|
|
||||||
"parameter list properly. Own fix branch."
|
|
||||||
)
|
|
||||||
|
|
||||||
_ADR_UNPARSED_REASON = (
|
|
||||||
"contact_service.rs::parse_vcard has NO handler for ADR — the "
|
|
||||||
"structured-address property (RFC 6350 §6.3.1) is silently "
|
|
||||||
"dropped at PUT time. DTO carries an `address: Vec<Address>` "
|
|
||||||
"field the emitter honours; parser just never populates it. "
|
|
||||||
"Fix: extend the match with an ADR branch that splits on ';' "
|
|
||||||
"into (pobox, ext, street, city, region, postal, country) — "
|
|
||||||
"mirror the emitter's format at contact_service.rs::195-ish."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_vcard_org_and_title_survive_round_trip(
|
def test_vcard_org_and_title_survive_round_trip(
|
||||||
dav_client: caldav.DAVClient, fresh_addressbook: str
|
dav_client: caldav.DAVClient, fresh_addressbook: str
|
||||||
@@ -253,13 +238,16 @@ def test_vcard_note_survives_round_trip(
|
|||||||
assert "KubeCon 2026" in fetched
|
assert "KubeCon 2026" in fetched
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reason=_TEL_URI_PARSER_BUG_REASON, strict=False)
|
|
||||||
def test_vcard_tel_uri_form_survives_round_trip(
|
def test_vcard_tel_uri_form_survives_round_trip(
|
||||||
dav_client: caldav.DAVClient, fresh_addressbook: str
|
dav_client: caldav.DAVClient, fresh_addressbook: str
|
||||||
) -> None:
|
) -> None:
|
||||||
"""TEL (RFC 6350 §6.4.1) with URI-form value + TYPE parameter —
|
"""TEL (RFC 6350 §6.4.1) with URI-form value + TYPE parameter —
|
||||||
the shape Apple Contacts / DAVx⁵ send for every phone number.
|
the shape Apple Contacts / DAVx⁵ send for every phone number.
|
||||||
See _TEL_URI_PARSER_BUG_REASON."""
|
|
||||||
|
Passes after fix/carddav-parser-tel-adr: parse_vcard splits on
|
||||||
|
the first `:` (was `split(':').nth(1)`) so `VALUE=uri:tel:...`
|
||||||
|
survives; the `tel:` URI scheme is stripped so the stored
|
||||||
|
number is a bare `+15551234567`."""
|
||||||
uid = f"cov-tel-{uuid.uuid4().hex[:8]}"
|
uid = f"cov-tel-{uuid.uuid4().hex[:8]}"
|
||||||
body = _minimal_vcard(
|
body = _minimal_vcard(
|
||||||
uid,
|
uid,
|
||||||
@@ -271,13 +259,16 @@ def test_vcard_tel_uri_form_survives_round_trip(
|
|||||||
assert "+15551234567" in fetched
|
assert "+15551234567" in fetched
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reason=_ADR_UNPARSED_REASON, strict=False)
|
|
||||||
def test_vcard_adr_survives_round_trip(
|
def test_vcard_adr_survives_round_trip(
|
||||||
dav_client: caldav.DAVClient, fresh_addressbook: str
|
dav_client: caldav.DAVClient, fresh_addressbook: str
|
||||||
) -> None:
|
) -> None:
|
||||||
"""ADR (RFC 6350 §6.3.1) with structured components. Semicolon
|
"""ADR (RFC 6350 §6.3.1) with structured components. Semicolon
|
||||||
is the structured-value separator. See _ADR_UNPARSED_REASON —
|
is the structured-value separator.
|
||||||
parser has no ADR branch at all."""
|
|
||||||
|
Passes after fix/carddav-parser-tel-adr: the parser now has an
|
||||||
|
ADR branch that splits the 7-part structured value into
|
||||||
|
(street, city, state, postal_code, country) matching the
|
||||||
|
emitter shape at contact_service.rs::generate_vcard."""
|
||||||
uid = f"cov-adr-{uuid.uuid4().hex[:8]}"
|
uid = f"cov-adr-{uuid.uuid4().hex[:8]}"
|
||||||
body = _minimal_vcard(
|
body = _minimal_vcard(
|
||||||
uid,
|
uid,
|
||||||
|
|||||||
@@ -27,19 +27,6 @@ import uuid
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
import caldav
|
import caldav
|
||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
_TIME_RANGE_PARSER_BUG_REASON = (
|
|
||||||
"caldav_adapter.rs:~105 + ~172 parses time-range start/end as "
|
|
||||||
"RFC 3339 (`2026-01-01T09:30:00Z`), but CalDAV clients send "
|
|
||||||
"iCalendar DATE-TIME (`20260101T093000Z` — RFC 4791 §9.9). "
|
|
||||||
"Parse fails, time_range becomes None, handle_report falls "
|
|
||||||
"through to list_events → returns every event regardless of "
|
|
||||||
"window. Fix: chrono::NaiveDateTime::parse_from_str with "
|
|
||||||
"`%Y%m%dT%H%M%SZ` (RFC 3339 as fallback). Own fix branch, "
|
|
||||||
"e.g. fix/caldav-time-range-parser."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# ─────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────
|
||||||
@@ -110,7 +97,6 @@ def _seed_three_events(calendar: caldav.Calendar) -> list[str]:
|
|||||||
# ─────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reason=_TIME_RANGE_PARSER_BUG_REASON, strict=False)
|
|
||||||
def test_calendar_query_time_range_returns_events_in_window(
|
def test_calendar_query_time_range_returns_events_in_window(
|
||||||
fresh_calendar: caldav.Calendar,
|
fresh_calendar: caldav.Calendar,
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -149,7 +135,6 @@ def test_calendar_query_time_range_returns_events_in_window(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reason=_TIME_RANGE_PARSER_BUG_REASON, strict=False)
|
|
||||||
def test_calendar_query_time_range_after_all_events_returns_empty(
|
def test_calendar_query_time_range_after_all_events_returns_empty(
|
||||||
fresh_calendar: caldav.Calendar,
|
fresh_calendar: caldav.Calendar,
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -174,7 +159,6 @@ def test_calendar_query_time_range_after_all_events_returns_empty(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reason=_TIME_RANGE_PARSER_BUG_REASON, strict=False)
|
|
||||||
def test_calendar_query_time_range_before_all_events_returns_empty(
|
def test_calendar_query_time_range_before_all_events_returns_empty(
|
||||||
fresh_calendar: caldav.Calendar,
|
fresh_calendar: caldav.Calendar,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user