feat: support safe recurrence additions
Parse, persist, expand, and round-trip compatible RDATE values while preserving whole-series TUI behavior and conservative rewrite guards. Verified: normal, warning-as-error, and ASan/UBSan builds pass all 9 suites; storage failure paths preserve calendar and backup bytes; live PTY restores terminal state.
This commit is contained in:
@@ -246,6 +246,8 @@ void test_zoned_edit_preserves_series_and_rejects_dst_gap()
|
||||
};
|
||||
event.recurrence_exceptions.push_back(
|
||||
zoned_time(event.time_zone, nocal::Date{2026y / March / 9d}, 2, 30));
|
||||
event.recurrence_additions.push_back(
|
||||
zoned_time(event.time_zone, nocal::Date{2026y / March / 12d}, 2, 30));
|
||||
|
||||
nocal::tui::EventEditor editor{event};
|
||||
check(editor.field_value(nocal::tui::EditorField::start_date) == "2026-03-07" &&
|
||||
@@ -262,8 +264,9 @@ void test_zoned_edit_preserves_series_and_rejects_dst_gap()
|
||||
check(editor.result().time_basis == nocal::TimeBasis::zoned &&
|
||||
editor.result().time_zone == "America/New_York" &&
|
||||
editor.result().recurrence == event.recurrence &&
|
||||
editor.result().recurrence_additions == event.recurrence_additions &&
|
||||
editor.result().recurrence_exceptions == event.recurrence_exceptions,
|
||||
"editing preserves time basis, TZID, recurrence, and exceptions");
|
||||
"editing preserves time basis, TZID, recurrence additions, and exceptions");
|
||||
|
||||
nocal::tui::EventEditor series_kind_editor{event};
|
||||
focus(series_kind_editor, nocal::tui::EditorField::all_day);
|
||||
@@ -273,8 +276,21 @@ void test_zoned_edit_preserves_series_and_rejects_dst_gap()
|
||||
series_kind_editor.error().find("recurring series") != std::string_view::npos,
|
||||
"a recurring series rejects timed/all-day conversion to preserve its exceptions");
|
||||
|
||||
auto rdate_only = event;
|
||||
rdate_only.recurrence.reset();
|
||||
nocal::tui::EventEditor rdate_editor{rdate_only};
|
||||
check(rdate_editor.render(80, 18, false).find("EDIT ENTIRE RECURRING SERIES")
|
||||
!= std::string::npos,
|
||||
"an RDATE-only event is presented as a recurring series");
|
||||
focus(rdate_editor, nocal::tui::EditorField::all_day);
|
||||
(void)rdate_editor.handle_key(" ");
|
||||
check(rdate_editor.handle_key("\x13") == nocal::tui::EditorOutcome::active &&
|
||||
rdate_editor.error().find("recurring series") != std::string_view::npos,
|
||||
"an RDATE-only series rejects timed/all-day conversion");
|
||||
|
||||
auto one_off = event;
|
||||
one_off.recurrence.reset();
|
||||
one_off.recurrence_additions.clear();
|
||||
one_off.recurrence_exceptions.clear();
|
||||
nocal::tui::EventEditor all_day_editor{one_off};
|
||||
focus(all_day_editor, nocal::tui::EditorField::all_day);
|
||||
|
||||
Reference in New Issue
Block a user