Skip to content

refactor: inject the EventKit reads the allowlist depends on - #12

Merged
k3KAW8Pnf7mkmdSMPHz27 merged 1 commit into
mainfrom
refactor/calendar-source-seam
Oct 2, 2026
Merged

k3KAW8Pnf7mkmdSMPHz27 merged 1 commit into
mainfrom
refactor/calendar-source-seam

Conversation

@k3KAW8Pnf7mkmdSMPHz27

Copy link
Copy Markdown
Owner

The allowlist fix in v2.0.0 shipped untested against EventKit, because ReminderService was never instantiated in a test: every read went straight to EKEventStore, which needs a Reminders permission dialog CI can't click. This moves that boundary behind a small protocol so the glue runs in CI.

Change

  • ReminderStore covers the five EventKit calls behind reads and the allowlist: the calendar listing, the two reminder predicates, the fetch, and its cancel. EKEventStore conforms with one wrapper method; the other four already exist on it.
  • ReminderService gains an internal init taking a ReminderStore. The public init is unchanged and passes its own EKEventStore.
  • No behaviour change. The two calendars(for:) reads, the two predicate calls, and the fetch now go through the protocol.

Tests

The stub serves unsaved EKCalendars, which get real, distinct identifiers at creation without Reminders access, and records the calendar count behind every predicate it builds. That count is what makes the fail-open bug observable: EventKit reads an empty calendar array as "all calendars".

  • A stale allowlist builds no predicate and returns no reminders and no lists.
  • A partially resolving allowlist queries exactly the one calendar that resolved.
  • Unrestricted queries all calendars.
  • Startup validation reports unresolved IDs, counts resolved ones, and is fatal only when none resolve.
  • An empty machine is empty, not misconfigured, unless restricted.

Mutation checks. Removing the fail-closed guard breaks two tests (a predicate gets built with 0 calendars). Making validation count configured IDs instead of resolved ones breaks two tests on four assertions. Full suite: 170 tests pass with complete strict-concurrency checking and warnings-as-errors; release build clean.

A first draft of this seam covered only the calendar listing, and its headline test couldn't fail: getLists returns [] for a stale allowlist with or without the guard. The mutation check caught that, which is why the seam also covers the predicate and fetch calls.

Not covered: the per-reminder checks on update and delete, which need real EKReminders. ListAccessPolicy already covers the rule they apply.

refactor: title, so this doesn't release.

ReminderService now takes a ReminderStore for the calendar listing, the two
reminder predicates, and the fetch. EKEventStore conforms with one wrapper
method, and the public init is unchanged.

That lets CI exercise the allowlist glue against unsaved EKCalendars, which
get real identifiers at creation and need no Reminders access: a stale
allowlist builds no predicate (so it can't become EventKit's "all
calendars"), a partial one queries only what resolved, and startup
validation counts resolved IDs rather than configured ones. Removing either
guard fails the new tests.
@k3KAW8Pnf7mkmdSMPHz27
k3KAW8Pnf7mkmdSMPHz27 merged commit abcbb4f into main Oct 2, 2026
3 checks passed
@k3KAW8Pnf7mkmdSMPHz27
k3KAW8Pnf7mkmdSMPHz27 deleted the refactor/calendar-source-seam branch October 2, 2026 21:13
@eventkit-tap-bumper

Copy link
Copy Markdown

🎉 This PR is included in version 3.0.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant