Skip to main content

Testing Guide

Running the Test Suite

xcodebuild test \
-project apps/lucidpal-ios/LucidPal.xcodeproj \
-scheme LucidPal \
-destination 'platform=iOS Simulator,name=iPhone 16'

All tests run on the iOS Simulator — no physical device required.

Test File Inventory

FileWhat it Covers
CalendarActionControllerTests.swiftJSON → calendar action dispatch (create, update, delete)
CalendarActionControllerHelpersTests.swiftHelper functions used by the controller
CalendarActionModelsDecodingTests.swiftCodable decoding of calendar action payloads
CalendarCancellationTests.swiftEvent cancellation flows
CalendarConfirmationTests.swiftUser confirmation prompts before mutations
CalendarDomainTypesTests.swiftValue-type invariants on domain models
CalendarFreeSlotEngineTests.swiftFree-slot algorithm (unit)
CalendarFreeSlotIntegrationTests.swiftFree-slot algorithm (integration with mock calendar)
CalendarServiceTests.swiftCalendarService CRUD against mock EventKit
CalendarViewSmokeTests.swiftCalendar view renders without crash
ConflictResolutionTests.swiftOverlapping event conflict logic
PendingCalendarUpdateTests.swiftPending-update state transitions
ChatViewModelTests.swiftDeletion/confirmation actions on ChatViewModel
ChatViewModelEdgeCaseTests.swiftEdge cases (empty input, nil state, rapid sends)
ChatViewModelMessageHandlingTests.swiftMessage append and role assignment
ChatViewModelPersistenceTests.swiftSession persistence via MockChatHistoryManager
ChatViewModelSendMessageTests.swiftSend flow with mocked LLM
ChatViewModelSpeechTests.swiftSpeech trigger and stop integration
ChatViewModelStreamTests.swiftStreaming token accumulation
ChatViewModelSuggestedPromptsTests.swiftSuggested prompts display logic
ChatViewModelSystemPromptTests.swiftSystem prompt injection
ChatViewModelWebSearchTests.swiftWeb search result injection into chat
LlamaActorTests.swiftObservable state + LLMConstants sanity checks (no model load)
LLMConstantsTests.swiftNumeric constant invariants
LLMServiceProtocolTests.swiftProtocol conformance contract
VisionImageProcessorTests.swiftImage resize/aspect ratio/base64 encoding
SessionManagerTests.swiftSession CRUD and lifecycle
SessionManagerMigrationTests.swiftLegacy data migration
SessionListViewModelTests.swiftSession list state management
SessionListViewModelCalendarTests.swiftCalendar integration in session list
SpeechServiceTests.swiftSpeechService start/stop/error states
WhisperSpeechServiceTests.swiftWhisper transcription path
AirPodsVoiceCoordinatorTests.swiftAirPods audio route detection
AudioRouteMonitorTests.swiftAudio route change notifications
SystemPromptBuilderTests.swiftSystem prompt assembly
ContextServiceTests.swiftContext injection into prompts
SuggestedPromptsProviderTests.swiftPrompt suggestion generation
WebSearchServiceTests.swiftWeb search service request/response
ModelDownloaderTests.swiftGGUF model download state machine
ModelDownloadViewModelTests.swiftDownload progress UI state
ModelInfoTests.swiftModel metadata parsing
HabitStoreTests.swiftHabit CRUD and streak logic
NotesStoreTests.swiftNotes persistence
ChatHistoryManagerTests.swiftChat history read/write
ChatMessageTests.swiftChatMessage value semantics
AppSettingsTests.swiftSettings read/write and defaults
SettingsViewModelTests.swiftSettings view state
OnboardingTests.swiftOnboarding step sequencing
DebugLogStoreTests.swiftDebug log capture
DesignConstantsTests.swiftUI constant invariants
UserDefaultsKeysTests.swiftKey uniqueness and type safety
HapticServiceProtocolTests.swiftHaptic protocol contract
SiriCalendarBridgeTests.swiftSiri → calendar bridge
SiriContextStoreTests.swiftSiri context persistence
SiriIntentTests.swiftIntent handling
SiriPendingEventTests.swiftPending event from Siri
ShortcutIntentTests.swiftApp shortcut intents
ViewSmokeTests.swiftKey views render without crash
CalendarViewSmokeTests.swiftCalendar view renders without crash
SnapshotTests.swiftVisual regression snapshots

Mock Inventory

All mocks live in Tests/ and conform to the corresponding protocol:

MockProtocolUsed By
MockLLMService.swiftLLMServiceProtocolAll ChatViewModel tests
MockCalendarService.swiftCalendarServiceProtocolCalendar + ChatViewModel tests
MockCalendarActionController.swiftCalendarActionControllerProtocolChatViewModel tests
MockChatHistoryManager.swiftChatHistoryManagerProtocolPersistence tests
MockContextService.swiftContextServiceProtocolPrompt-building tests
MockHapticService.swiftHapticServiceProtocolViewModel tests
MockLocationService.swiftLocationServiceProtocolContext/system-prompt tests
MockModelDownloader.swiftModelDownloaderProtocolDownload flow tests
MockSessionManager.swiftSessionManagerProtocolSession tests
MockSpeechService.swiftSpeechServiceProtocolSpeech/AirPods tests
MockSuggestedPromptsProvider.swiftSuggestedPromptsProviderProtocolChatViewModel tests
MockSystemPromptBuilder.swiftSystemPromptBuilderProtocolChatViewModel tests
MockWebSearchService.swiftWebSearchServiceProtocolWeb search tests
MockAppSettings.swiftAppSettingsProtocolSettings + controller tests

Testing Patterns

ViewModel tests

ChatViewModel and SessionListViewModel are instantiated with a *Dependencies struct that accepts protocol types. Tests inject mocks at construction time:

viewModel = ChatViewModel(
dependencies: ChatViewModelDependencies(
llmService: MockLLMService(),
calendarService: MockCalendarService(),
settings: MockAppSettings(),
// ...
)
)

All test classes are annotated @MainActor to match the production actor isolation. setUp uses async throws.

Service-layer tests

Controllers (e.g., CalendarActionController) accept their dependencies through initializer injection. Tests pass mock services and assert on the mock's recorded calls (e.g., mock.createdEvents.count).

Value-type / constants tests

Files like LLMConstantsTests, DesignConstantsTests, and UserDefaultsKeysTests assert numeric bounds and key uniqueness directly — no mocks needed.

Image processing tests

VisionImageProcessorTests creates synthetic UIImage instances via a makeTestImage(size:) helper and exercises the processor's public API without network or file I/O.

Smoke / snapshot tests

ViewSmokeTests and SnapshotTests instantiate SwiftUI views and verify they render without crashing. Snapshot tests capture reference images stored alongside the test target.

What is NOT Tested

AreaReason
LlamaActor generate / loadRequires a GGUF model file and ARM64 device; CI runs on Simulator
Real EKEventStore mutationsEventKit requires user permission grant; tests use MockCalendarService
Real CLGeocoderNetwork-dependent; replaced by MockLocationService
Full UI interaction testsNo XCUITest suite exists yet
On-device Whisper transcriptionHardware-dependent; WhisperSpeechService is mocked above the C layer

LlamaActorTests deliberately covers only observable state (isLoaded) and LLMConstants so CI can pass without hardware.

Adding a New Test File

  1. Create Tests/MyFeatureTests.swift inside the lucidpal-ios Xcode project (target: LucidPalTests).
  2. Import and mark the actor:
import XCTest
@testable import LucidPal

@MainActor
final class MyFeatureTests: XCTestCase {
override func setUp() async throws {
try await super.setUp()
// inject mocks
}

func testSomething() async throws {
// arrange → act → assert
}
}
  1. If your feature has external dependencies (network, EventKit, CoreLocation), add a corresponding Mock*.swift file conforming to the relevant protocol.
  2. Run the suite locally with the xcodebuild test command above before opening a PR.