openclaw/apps/ios/Tests/OfflineMessageQueueTests.swift
ronitchidara fdc7e11891 mobile: Phase 4 polish - refactoring, app store prep, and infrastructure
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>
2026-01-28 17:01:20 +05:30

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()
}
}