- Add Apple Sign-In backend (JWKS verification, account linking, session management) - Implement push notification deep linking with NotificationDeepLinkRouter - Add jailbreak detection, runtime integrity monitoring, secure enclave service - Implement OAuth social login, token refresh, and secure logout flows - Add image caching (memory/disk), optimizer, upload queue, async semaphore - Implement notification analytics, type preferences, and category setup - Expand UI test suite with UITestBase, accessibility, auth flow, performance tests - Add CI pipeline for iOS UI tests (3 device sizes) and performance benchmarks - Restructure Xcode project to manual groups with KordantWidgets target - Add SwiftLint, Swift Collections/Algorithms/GoogleSignIn dependencies - Update project.yml for XcodeGen with new targets and configurations
174 lines
5.7 KiB
Swift
174 lines
5.7 KiB
Swift
import Testing
|
|
@testable import Kordant
|
|
import SwiftUI
|
|
import OSLog
|
|
|
|
// MARK: - LaunchTimer Tests
|
|
|
|
struct LaunchTimerTests {
|
|
@Test("LaunchTimer tracks elapsed time since process start")
|
|
func elapsedSinceProcessStart() {
|
|
let timer = LaunchTimer.shared
|
|
let elapsed = timer.elapsedSinceProcessStart
|
|
#expect(elapsed >= 0)
|
|
}
|
|
|
|
@Test("LaunchTimer measures phase start and end")
|
|
func measurePhase() {
|
|
let timer = LaunchTimer.shared
|
|
let id = timer.startPhase("TestPhase")
|
|
#expect(id >= 0)
|
|
|
|
// Simulate some work
|
|
try? Task.checkCancellation()
|
|
|
|
timer.endPhase("TestPhase", signpostID: id)
|
|
|
|
let report = timer.report()
|
|
#expect(report["TestPhase_start"] != nil)
|
|
#expect(report["TestPhase_end"] != nil)
|
|
#expect(report["TestPhase_duration"] != nil)
|
|
#expect(report["total"] != nil)
|
|
}
|
|
|
|
@Test("LaunchTimer logs events")
|
|
func logEvent() {
|
|
let timer = LaunchTimer.shared
|
|
timer.logEvent("TestEvent", "test message")
|
|
|
|
let report = timer.report()
|
|
#expect(report["TestEvent"] != nil)
|
|
}
|
|
|
|
@Test("LaunchTimer report includes total time")
|
|
func reportContainsTotal() {
|
|
let timer = LaunchTimer.shared
|
|
let report = timer.report()
|
|
#expect(report["total"] != nil)
|
|
#expect(report["total"]! >= 0)
|
|
}
|
|
}
|
|
|
|
// MARK: - Launch Performance Tests
|
|
|
|
struct LaunchPerformanceTests {
|
|
/// Measures AuthService initialization time (should be fast, < 10ms)
|
|
@Test("AuthService init is fast (no blocking work)")
|
|
@MainActor
|
|
func authServiceInitTime() {
|
|
let keychain = MockKeychainService()
|
|
let apiClient = MockAuthAPIClient()
|
|
|
|
let start = Date()
|
|
let service = AuthService(keychain: keychain, apiClient: apiClient)
|
|
let elapsed = -start.timeIntervalSinceNow
|
|
|
|
// AuthService init should be nearly instantaneous since restoreSession is deferred
|
|
#expect(elapsed < 0.01, "AuthService init took \(elapsed)s (expected < 10ms)")
|
|
}
|
|
|
|
/// Measures session restoration time
|
|
@Test("AuthService restoreSession completes quickly")
|
|
@MainActor
|
|
func sessionRestoreTime() async {
|
|
let keychain = MockKeychainService()
|
|
let apiClient = MockAuthAPIClient()
|
|
let service = AuthService(keychain: keychain, apiClient: apiClient)
|
|
|
|
let start = Date()
|
|
service.restoreSession()
|
|
let elapsed = -start.timeIntervalSinceNow
|
|
|
|
// Session restore involves keychain lookups, should be fast
|
|
#expect(elapsed < 0.05, "Session restore took \(elapsed)s (expected < 50ms)")
|
|
}
|
|
|
|
/// Measures SecurityManager initialization (should be lightweight)
|
|
@Test("SecurityManager init is fast")
|
|
@MainActor
|
|
func securityManagerInitTime() {
|
|
let start = Date()
|
|
_ = SecurityManager.shared
|
|
let elapsed = -start.timeIntervalSinceNow
|
|
|
|
// SecurityManager should be lazy, init should be instant
|
|
#expect(elapsed < 0.01, "SecurityManager init took \(elapsed)s (expected < 10ms)")
|
|
}
|
|
|
|
/// Measures NetworkMonitor initialization (should be lazy)
|
|
@Test("NetworkMonitor init is fast")
|
|
func networkMonitorInitTime() {
|
|
let start = Date()
|
|
let monitor = NetworkMonitor()
|
|
let elapsed = -start.timeIntervalSinceNow
|
|
|
|
// NetworkMonitor should not start monitoring on init
|
|
#expect(elapsed < 0.01, "NetworkMonitor init took \(elapsed)s (expected < 10ms)")
|
|
monitor.stopMonitoring()
|
|
}
|
|
|
|
/// Measures ImageCacheService initialization (should be lazy)
|
|
@Test("ImageCacheService shared init is fast")
|
|
@MainActor
|
|
func imageCacheServiceInitTime() {
|
|
let start = Date()
|
|
_ = ImageCacheService.shared
|
|
let elapsed = -start.timeIntervalSinceNow
|
|
|
|
// ImageCacheService should not load metadata on init
|
|
#expect(elapsed < 0.05, "ImageCacheService init took \(elapsed)s (expected < 50ms)")
|
|
}
|
|
}
|
|
|
|
// MARK: - Lazy Loading Verification Tests
|
|
|
|
struct LazyLoadingTests {
|
|
@Test("AuthService does not restore session in init")
|
|
@MainActor
|
|
func authServiceNoRestoreOnInit() {
|
|
let keychain = MockKeychainService()
|
|
let apiClient = MockAuthAPIClient()
|
|
|
|
// Store a token in keychain
|
|
try? keychain.store(key: "jwt", value: Data("test-token".utf8))
|
|
try? keychain.store(key: "currentUser", value: try! JSONEncoder().encode(
|
|
User(id: "1", name: "Test", email: "test@test.com")
|
|
))
|
|
|
|
let service = AuthService(keychain: keychain, apiClient: apiClient)
|
|
|
|
// Session should NOT be restored in init
|
|
#expect(service.state == .unauthenticated)
|
|
#expect(service.currentUser == nil)
|
|
|
|
// After explicit restore, state should update
|
|
service.restoreSession()
|
|
#expect(service.state == .authenticated)
|
|
}
|
|
|
|
@Test("NetworkMonitor does not start monitoring on init")
|
|
func networkMonitorLazyStart() {
|
|
let monitor = NetworkMonitor()
|
|
// The monitor property should exist but monitoring should not have started
|
|
// We can't directly check the private flag, but we verify the behavior
|
|
// by checking that the default isConnected value hasn't changed
|
|
#expect(monitor.isConnected == true) // Default value, not from actual monitoring
|
|
monitor.stopMonitoring()
|
|
}
|
|
}
|
|
|
|
// MARK: - Build Configuration Tests
|
|
|
|
struct BuildConfigTests {
|
|
@Test("LaunchTimer is available in all configurations")
|
|
func launchTimerAvailable() {
|
|
let timer = LaunchTimer.shared
|
|
#expect(timer != nil)
|
|
}
|
|
|
|
@Test("Build configuration is accessible")
|
|
func buildConfig() {
|
|
#expect(ProcessInfo.processInfo.operatingSystemVersionString.count > 0)
|
|
}
|
|
}
|