Tier 5 - Code Refactoring: - iOS: Split NodeAppModel.swift (988→493 LOC) into focused modules - NodeCommandHandlers.swift: command routing (372 LOC) - NodeGatewaySync.swift: gateway sync, branding (153 LOC) - Android: Split NodeRuntime.kt (1268→756 LOC, 40% reduction) - NodeCommandHandlers.kt: command routing (326 LOC) - NodeGatewaySync.kt: gateway sync, A2UI helpers (294 LOC) Tier 6 - App Store Preparation: - iOS: Add PrivacyInfo.xcprivacy (iOS 17+ privacy manifest) - Android: Add ProGuard rules and enable minification for release builds Infrastructure (from earlier tiers): - iOS: Add OfflineMessageQueue for disconnected message queueing - iOS: Add PushManager for VoIP push foundation - iOS: Add TTSVoiceSettingsView for voice selection - Android: Add ClawdbotMessagingService for FCM push - Android: Add SyncWorker for background chat sync - Both: Deep linking support (clawdbot:// URL scheme) - Both: Empty states and loading indicators in settings Testing: - iOS: Add OfflineMessageQueueTests (8 tests) - Android: Add NodeGatewaySyncTest (11 tests) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
123 lines
3.1 KiB
Swift
123 lines
3.1 KiB
Swift
import Foundation
|
|
import Testing
|
|
@testable import Clawdbot
|
|
|
|
@Suite @MainActor struct OfflineMessageQueueTests {
|
|
@Test func enqueueAddsMessage() async {
|
|
let queue = OfflineMessageQueue()
|
|
queue.clear()
|
|
|
|
queue.enqueue(text: "Hello", sessionKey: "test-session")
|
|
|
|
#expect(queue.count == 1)
|
|
#expect(queue.pending.first?.text == "Hello")
|
|
#expect(queue.pending.first?.sessionKey == "test-session")
|
|
#expect(queue.pending.first?.retryCount == 0)
|
|
|
|
queue.clear()
|
|
}
|
|
|
|
@Test func dequeueRemovesMessage() async {
|
|
let queue = OfflineMessageQueue()
|
|
queue.clear()
|
|
|
|
queue.enqueue(text: "Message 1", sessionKey: "session")
|
|
queue.enqueue(text: "Message 2", sessionKey: "session")
|
|
|
|
let firstId = queue.pending.first!.id
|
|
queue.dequeue(firstId)
|
|
|
|
#expect(queue.count == 1)
|
|
#expect(queue.pending.first?.text == "Message 2")
|
|
|
|
queue.clear()
|
|
}
|
|
|
|
@Test func markFailedIncrementsRetryCount() async {
|
|
let queue = OfflineMessageQueue()
|
|
queue.clear()
|
|
|
|
queue.enqueue(text: "Retry test", sessionKey: "session")
|
|
let id = queue.pending.first!.id
|
|
|
|
queue.markFailed(id)
|
|
#expect(queue.pending.first?.retryCount == 1)
|
|
|
|
queue.markFailed(id)
|
|
#expect(queue.pending.first?.retryCount == 2)
|
|
|
|
queue.clear()
|
|
}
|
|
|
|
@Test func markFailedRemovesAfterMaxRetries() async {
|
|
let queue = OfflineMessageQueue()
|
|
queue.clear()
|
|
|
|
queue.enqueue(text: "Max retry test", sessionKey: "session")
|
|
let id = queue.pending.first!.id
|
|
|
|
// Max retries is 3
|
|
queue.markFailed(id) // retry 1
|
|
queue.markFailed(id) // retry 2
|
|
queue.markFailed(id) // retry 3 - should remove
|
|
|
|
#expect(queue.isEmpty)
|
|
|
|
queue.clear()
|
|
}
|
|
|
|
@Test func clearRemovesAllMessages() async {
|
|
let queue = OfflineMessageQueue()
|
|
queue.clear()
|
|
|
|
queue.enqueue(text: "Message 1", sessionKey: "s1")
|
|
queue.enqueue(text: "Message 2", sessionKey: "s2")
|
|
queue.enqueue(text: "Message 3", sessionKey: "s3")
|
|
|
|
#expect(queue.count == 3)
|
|
|
|
queue.clear()
|
|
|
|
#expect(queue.isEmpty)
|
|
#expect(queue.count == 0)
|
|
}
|
|
|
|
@Test func isEmptyReturnsCorrectValue() async {
|
|
let queue = OfflineMessageQueue()
|
|
queue.clear()
|
|
|
|
#expect(queue.isEmpty)
|
|
|
|
queue.enqueue(text: "Test", sessionKey: "session")
|
|
#expect(!queue.isEmpty)
|
|
|
|
queue.clear()
|
|
#expect(queue.isEmpty)
|
|
}
|
|
|
|
@Test func enqueueWithThinkingLevel() async {
|
|
let queue = OfflineMessageQueue()
|
|
queue.clear()
|
|
|
|
queue.enqueue(text: "Deep thought", sessionKey: "session", thinking: "high")
|
|
|
|
#expect(queue.pending.first?.thinking == "high")
|
|
|
|
queue.clear()
|
|
}
|
|
|
|
@Test func dequeueNonExistentIdDoesNothing() async {
|
|
let queue = OfflineMessageQueue()
|
|
queue.clear()
|
|
|
|
queue.enqueue(text: "Test", sessionKey: "session")
|
|
let originalCount = queue.count
|
|
|
|
queue.dequeue(UUID()) // Random ID that doesn't exist
|
|
|
|
#expect(queue.count == originalCount)
|
|
|
|
queue.clear()
|
|
}
|
|
}
|