feat: add local reminders and owned calendar synchronization

This commit is contained in:
Codex
2026-10-13 00:11:48 +08:00
parent ffd3920c0d
commit 2c4256b880
13 changed files with 978 additions and 8 deletions
+73
View File
@@ -35,6 +35,63 @@ void main() {
);
});
tearDown(() => controller.dispose());
test(
'failed calendar cleanup is retried after reopening while sync is off',
() async {
final provider = CleanupCalendar()..failDelete = true;
final first = AppController(
store,
notifications,
provider,
controller.clock,
);
await first.initialize();
await first.disableCalendar(deleteEvents: true);
expect(first.lastError, isNotNull);
first.dispose();
provider.failDelete = false;
final reopened = AppController(
store,
notifications,
provider,
controller.clock,
);
await reopened.initialize();
expect(provider.deletions, 2);
expect(reopened.snapshot.settings.calendarEnabled, isFalse);
reopened.dispose();
},
);
test(
'notification cleanup failure does not prevent calendar cleanup',
() async {
final broken = AppController(
store,
BrokenCancellation(),
calendar,
controller.clock,
);
await broken.initialize();
await broken.clearCourses();
expect(calendar.deletions, 1);
expect(broken.snapshot.courses, isEmpty);
broken.dispose();
},
);
test(
'denied calendar permission and unavailable calendars stay off',
() async {
await controller.initialize();
calendar.access = false;
await controller.enableCalendarFromUserAction('local');
expect(controller.snapshot.settings.calendarEnabled, isFalse);
expect(controller.calendarSyncError, isNotNull);
calendar.access = true;
await controller.enableCalendarFromUserAction('readonly-or-missing');
expect(controller.snapshot.settings.calendarEnabled, isFalse);
expect(calendar.synchronizations, 0);
},
);
test('a new successful demo action clears a stale save error', () async {
await controller.initialize();
store.failSave = true;
@@ -121,3 +178,19 @@ class DamagedStore extends MemoryStore {
@override
Future<AppSnapshot?> load() async => throw const FormatException('damaged');
}
class CleanupCalendar extends FakeCalendar {
bool failDelete = false;
@override
Future<void> deleteOwned(Map<String, String> ids) async {
deletions++;
if (failDelete) {
throw StateError('denied');
}
}
}
class BrokenCancellation extends FakeNotifications {
@override
Future<void> cancelOwned() async => throw StateError('denied');
}