Add ChatViewModel testability and unit tests

- Add testable ChatViewModel initializer accepting Transport dependency
- Create MockTransport implementing full Transport protocol for testing
- Add 21 unit tests covering:
  - Initialization (delegate, services, nickname)
  - Message sending (basic, empty, mentions, commands)
  - Message receiving (delegate calls, public messages)
  - Peer connections (connect, disconnect, isPeerConnected)
  - Deduplication service integration
  - Private chat routing
  - Bluetooth state handling
  - Panic clear functionality
- Guard NotificationService methods against test environment crashes
- Make deduplicationService internal for test access

All 303 tests pass.
This commit is contained in:
jack
2025-11-25 09:20:38 -10:00
parent ec3c16176e
commit 3e680f40bf
4 changed files with 591 additions and 7 deletions
+11 -2
View File
@@ -16,10 +16,18 @@ import AppKit
final class NotificationService {
static let shared = NotificationService()
/// Returns true if running in test environment (XCTest, Swift Testing, or SPM tests)
private var isRunningTests: Bool {
NSClassFromString("XCTestCase") != nil ||
ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil ||
Bundle.main.bundleIdentifier == nil
}
private init() {}
func requestAuthorization() {
guard !isRunningTests else { return }
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
if granted {
// Permission granted
@@ -36,6 +44,7 @@ final class NotificationService {
userInfo: [String: Any]? = nil,
interruptionLevel: UNNotificationInterruptionLevel = .active
) {
guard !isRunningTests else { return }
let content = UNMutableNotificationContent()
content.title = title
content.body = body
+22 -5
View File
@@ -188,7 +188,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate, CommandContextProv
private let privateChatManager: PrivateChatManager
private let unifiedPeerService: UnifiedPeerService
private let autocompleteService: AutocompleteService
private let deduplicationService: MessageDeduplicationService
let deduplicationService: MessageDeduplicationService // internal for test access
// Computed properties for compatibility
@MainActor
@@ -417,17 +417,34 @@ final class ChatViewModel: ObservableObject, BitchatDelegate, CommandContextProv
private var nostrKeyMapping: [PeerID: String] = [:] // senderPeerID -> nostrPubkey
// MARK: - Initialization
@MainActor
init(
convenience init(
keychain: KeychainManagerProtocol,
idBridge: NostrIdentityBridge,
identityManager: SecureIdentityStateManagerProtocol
) {
self.init(
keychain: keychain,
idBridge: idBridge,
identityManager: identityManager,
transport: BLEService(keychain: keychain, idBridge: idBridge, identityManager: identityManager)
)
}
/// Testable initializer that accepts a Transport dependency.
/// Use this initializer for unit testing with MockTransport.
@MainActor
init(
keychain: KeychainManagerProtocol,
idBridge: NostrIdentityBridge,
identityManager: SecureIdentityStateManagerProtocol,
transport: Transport
) {
self.keychain = keychain
self.idBridge = idBridge
self.identityManager = identityManager
self.meshService = BLEService(keychain: keychain, idBridge: idBridge, identityManager: identityManager)
self.meshService = transport
self.publicMessagePipeline = PublicMessagePipeline()
// Load persisted read receipts
@@ -616,7 +633,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate, CommandContextProv
}
.store(in: &cancellables)
// Request notification permission
// Request notification permission (guards test environment internally)
NotificationService.shared.requestAuthorization()