Files
agendula/app/src/test/resources/vtodo/README.md
T
makiolaj a30114efbb sync(chunk 1): VTODO mapper with an unknown-property round-trip
A hand-rolled content-line model instead of ical4j: a raw (name, params,
value) tree is what the round-trip requirement wants, and a typed model
normalises away exactly what has to survive. lib-recur already does RRULE
and java.time is native at minSdk 29, so the 2.2 MB of zone data and the
registry shims buy nothing. Deviates from SYNC.md's library table — see
SYNC-PLAN.md decision 4.

- domain/ical: parser, serialiser, value codecs. No Android, no data types,
  so the floret-kit extraction stays a file move.
- data/tasks/ical/VTodoMapper: VTODO <-> TaskEntity. Claims a property only
  when it can reproduce it exactly; everything else round-trips verbatim
  through TaskEntity.unknown_properties, which already existed at v1 — no
  migration needed, and BEGIN/END lines carry the nesting the plan thought
  needed a second table.
- 19 fixtures as the specification, with the canonical comparison harness
  from SYNC.md: no property lost, modulo the enumerated allowlist.
- ICalendarWriter now delegates folding and escaping rather than carrying
  its own copy.

VALARM ownership settled: neither side writes the other's alarms. VALARMs
round-trip in the residue, local reminders stay in task_alarms. The
setAlarm collision was the provider's; the two stores are now disjoint.
2026-09-04 16:45:03 +02:00

3.2 KiB

VTODO fixture corpus

The mapper's specification, not its regression net. Every file here is a shape that was found in the wild and that a plausible implementation gets wrong. A clean, server-generated VTODO catches nothing and is deliberately absent.

RoundTripTest asserts every fixture survives parse → map → write → serialise with no property lost, compared as a canonical multiset modulo the allowlist in docs/SYNC-PLAN.md § chunk 1 (PRODID, DTSTAMP, LAST-MODIFIED, SEQUENCE, VTIMEZONE bodies, fold positions, parameter quoting).

File What it is for
nextcloud-shared-confidential.ics A CLASS:CONFIDENTIAL task as Nextcloud rewrites it on GET from a shared calendar — DUE, STATUS, COMPLETED, PERCENT-COMPLETE, PRIORITY and RELATED-TO already stripped, ETag left untouched. Re-PUTting this destroys the owner's task
recurring-master-overrides.ics Master plus two RECURRENCE-ID overrides in one resource, sharing a UID
valarm-unknown-nested.ics Unknown properties inside a VALARM — the case a flat property-per-row model cannot represent
unknown-component.ics An entirely unknown sub-component
unknown-tzid.ics A TZID no device tzdb knows, with its own VTIMEZONE
uid-special-chars.ics A UID containing / and @ — the href sanitiser must not assume it is filename-safe
rrule-due-no-dtstart.ics RRULE + DUE and no DTSTART: no well-defined RECURRENCE-ID, undefined in RFC 5545, ubiquitous in the wild
moz-alarm-props.ics X-MOZ-LASTACK / X-MOZ-SNOOZE-TIME. Dropping them causes documented Thunderbird alarm storms
apple-sort-order.ics X-APPLE-SORT-ORDER — preserve, never interpret
floating-time.ics DTSTART with neither Z nor TZID. Readable, not reproducible: it must round-trip from the residue, not be rewritten as a guess
completion-model-a.ics Completion as a RECURRENCE-ID override, master stays open — jtx Board, Thunderbird. The model we write
completion-model-b.ics Master's DUE advanced in place, completion cleared — tasks.org, Evolution, Nextcloud in practice
completion-model-c.ics STATUS:COMPLETED on the master, killing the series — Nextcloud Tasks ≤ 0.17. Always a bug; we read it, we never write it
completion-model-d.ics The completed occurrence detached as a new task with a new UID — OpenTasks
quoted-param-colon.ics A quoted parameter value containing a colon and a comma. The value separator is the first colon outside quotes
mixed-value-types.ics DTSTART;VALUE=DATE with a timed DUE. One is_all_day flag cannot author both, so the odd one out must stay in the residue rather than be flattened to a date
malformed-scalars.ics PRIORITY:11, PERCENT-COMPLETE:150, SEQUENCE:x, STATUS:X-DEFERRED, CLASS:X-INTERNAL — values no spec allows. Clamping or defaulting any of them is a silent rewrite of somebody's data
two-timezones.ics DTSTART and DUE in different zones. One timezone column cannot author both
folded-utf8.ics A line folded mid-emoji-adjacent, to prove folding counts octets and splits on character boundaries