Skip to content

Give CoreModule injectable side-effect ports - #78

Merged
Skeyelab merged 1 commit into
masterfrom
Skeyelab/gh-issue-71-coremodule-ports
Jun 17, 2026
Merged

Skeyelab merged 1 commit into
masterfrom
Skeyelab/gh-issue-71-coremodule-ports

Conversation

@Skeyelab

Copy link
Copy Markdown
Member

Closes #71

Summary

CoreModule was the largest untested built-in RoutingModule (0% coverage, 173 lines) with four try!s on FileManager/Contacts/JSON. This injects side-effect ports so every command is testable, and removes the try!s.

  • ContactNameService port + CNContactNameService adapter — /name no longer reaches CNContactStore directly. Adapter does authorization check + search + create-or-update behind one setGivenName(_:forHandle:) that throws instead of try!. Fakeable in tests.
  • Clock port (RealClock) — /send's inter-message delay is an injected async sleep, so the send loop runs instantly and deterministically under test.
  • SendRateLimiter — per-handle concurrency bookkeeping pulled out of /send into a sync, deterministic unit (replaces the inline currentSends dict + MAXIMUM_CONCURRENT_SENDS).
  • Removed the dead FileManager.createDirectory try! in init (the appsupport URL was never used) — init now does no filesystem work and can't crash.
  • barf try! → try? + "nil" fallback.
  • Extracted performSend(_:text:times:delay:) for an awaitable send loop.

Built with TDD

One behavior per cycle. Simple commands locked as characterization tests first, then new ports driven red→green: SendRateLimiter (acquire/release/per-handle) → /send delivery + concurrency rejection (fake Clock + pre-filled limiter) → ContactNameService (/name auth/validation/create/update/throw) → init no-filesystem.

Test plan

  • bash ./.github/scripts/test-macos.sh — 146 tests, all pass (was 125)
  • CoreModuleTests — /ping /version /whoami (named + unknown) /barf thanks; /send validation (type/count/delay/text) + delivery + concurrency limit; /name not-authorized / missing-arg / non-text / create+confirm / save-throws; init registers routes with no filesystem side effects
  • SendRateLimiterTests — acquire up to max, reject, release frees slot, per-handle quota

Acceptance criteria

  • Each built-in command has happy-path test via JaredMock
  • /send concurrency limit tested with injectable clock (+ SendRateLimiter)
  • /name tested without real Contacts permission
  • No new try! on contact operations in production path (and removed the existing ones)
  • Existing tests pass

🤖 Generated with Claude Code

Inject ports so built-in commands are testable without CNContactStore,
FileManager, or real timing:
- ContactNameService (/name) + CNContactNameService adapter — no try! on
  contact ops; create-or-update behind one method
- Clock — async sleep port; /send delays instant under test
- SendRateLimiter — per-handle concurrency, deterministic (no async games)

Remove dead FileManager.createDirectory try! from init; barf try! -> try?.
Extract performSend for an awaitable send loop.

Adds CoreModuleTests + SendRateLimiterTests (21 tests): every command
happy path via JaredMock, /send validation + concurrency limit + delivery,
/name auth/validation/create/update/throw, init no-filesystem. Built TDD,
one behavior per cycle.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Skeyelab
Skeyelab merged commit 41bcfaa into master Jun 17, 2026
2 checks passed
Skeyelab added a commit that referenced this pull request Aug 23, 2026
Inject ports so built-in commands are testable without CNContactStore,
FileManager, or real timing:
- ContactNameService (/name) + CNContactNameService adapter — no try! on
  contact ops; create-or-update behind one method
- Clock — async sleep port; /send delays instant under test
- SendRateLimiter — per-handle concurrency, deterministic (no async games)

Remove dead FileManager.createDirectory try! from init; barf try! -> try?.
Extract performSend for an awaitable send loop.

Adds CoreModuleTests + SendRateLimiterTests (21 tests): every command
happy path via JaredMock, /send validation + concurrency limit + delivery,
/name auth/validation/create/update/throw, init no-filesystem. Built TDD,
one behavior per cycle.

Co-authored-by: Eric Dahl <edahl@nd.edu>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

Architecture: give CoreModule injectable side-effect ports

2 participants