mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 17:45:19 +00:00
* Add comprehensive test coverage for ChatViewModel and BLEService This commit adds 14 new tests to improve the safety net for future refactoring of ChatViewModel and BLEService: New test files: - ChatViewModelTorTests: Tor lifecycle notification handlers (8 tests) - ChatViewModelDeliveryStatusTests: Delivery status state machine (6 tests) - ChatViewModelRefactoringTests: Command routing and message handling (4 tests) - BLEServiceCoreTests: Packet deduplication and stale broadcast filtering (2 tests) - PublicMessagePipelineTests: Message ordering and deduplication (4 tests) - MessageRouterTests: Transport selection and outbox behavior (4 tests) - PrivateChatManagerTests: Chat selection and read receipts (2 tests) - UnifiedPeerServiceTests: Fingerprint resolution and blocking (2 tests) - RelayControllerTests: TTL, handshake, and fragment relay logic (4 tests) Modified files: - MockTransport: Added peer snapshot publishing on connect/disconnect - TestHelpers: Added waitUntil polling helper for async tests - MockIdentityManager: Extended for new test scenarios Total: 454 tests across 64 suites (was 440) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Fix flaky test by using waitUntil instead of fixed sleep Replace fixed 200ms sleep with waitUntil helper for more reliable async assertion in routing tests. This prevents timing issues on CI. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: jack <jackjackbits@users.noreply.github.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
167 lines
4.7 KiB
Swift
167 lines
4.7 KiB
Swift
//
|
|
// PublicMessagePipelineTests.swift
|
|
// bitchatTests
|
|
//
|
|
// Tests for PublicMessagePipeline ordering and deduplication.
|
|
//
|
|
|
|
import Testing
|
|
import Foundation
|
|
@testable import bitchat
|
|
|
|
@MainActor
|
|
private final class TestPipelineDelegate: PublicMessagePipelineDelegate {
|
|
private let dedupService = MessageDeduplicationService()
|
|
var messages: [BitchatMessage] = []
|
|
|
|
func pipelineCurrentMessages(_ pipeline: PublicMessagePipeline) -> [BitchatMessage] {
|
|
messages
|
|
}
|
|
|
|
func pipeline(_ pipeline: PublicMessagePipeline, setMessages messages: [BitchatMessage]) {
|
|
self.messages = messages
|
|
}
|
|
|
|
func pipeline(_ pipeline: PublicMessagePipeline, normalizeContent content: String) -> String {
|
|
dedupService.normalizedContentKey(content)
|
|
}
|
|
|
|
func pipeline(_ pipeline: PublicMessagePipeline, contentTimestampForKey key: String) -> Date? {
|
|
dedupService.contentTimestamp(forKey: key)
|
|
}
|
|
|
|
func pipeline(_ pipeline: PublicMessagePipeline, recordContentKey key: String, timestamp: Date) {
|
|
dedupService.recordContentKey(key, timestamp: timestamp)
|
|
}
|
|
|
|
func pipelineTrimMessages(_ pipeline: PublicMessagePipeline) {}
|
|
|
|
func pipelinePrewarmMessage(_ pipeline: PublicMessagePipeline, message: BitchatMessage) {}
|
|
|
|
func pipelineSetBatchingState(_ pipeline: PublicMessagePipeline, isBatching: Bool) {}
|
|
}
|
|
|
|
struct PublicMessagePipelineTests {
|
|
|
|
@Test @MainActor
|
|
func flush_sortsByTimestamp() async {
|
|
let pipeline = PublicMessagePipeline()
|
|
let delegate = TestPipelineDelegate()
|
|
pipeline.delegate = delegate
|
|
|
|
let earlier = Date().addingTimeInterval(-10)
|
|
let later = Date()
|
|
|
|
let messageA = BitchatMessage(
|
|
id: "a",
|
|
sender: "A",
|
|
content: "Later",
|
|
timestamp: later,
|
|
isRelay: false
|
|
)
|
|
let messageB = BitchatMessage(
|
|
id: "b",
|
|
sender: "A",
|
|
content: "Earlier",
|
|
timestamp: earlier,
|
|
isRelay: false
|
|
)
|
|
|
|
pipeline.enqueue(messageA)
|
|
pipeline.enqueue(messageB)
|
|
pipeline.flushIfNeeded()
|
|
|
|
#expect(delegate.messages.map { $0.id } == ["b", "a"])
|
|
}
|
|
|
|
@Test @MainActor
|
|
func flush_deduplicatesByContentWithinWindow() async {
|
|
let pipeline = PublicMessagePipeline()
|
|
let delegate = TestPipelineDelegate()
|
|
pipeline.delegate = delegate
|
|
|
|
let now = Date()
|
|
let messageA = BitchatMessage(
|
|
id: "a",
|
|
sender: "A",
|
|
content: "Same",
|
|
timestamp: now,
|
|
isRelay: false
|
|
)
|
|
let messageB = BitchatMessage(
|
|
id: "b",
|
|
sender: "A",
|
|
content: "Same",
|
|
timestamp: now.addingTimeInterval(0.2),
|
|
isRelay: false
|
|
)
|
|
|
|
pipeline.enqueue(messageA)
|
|
pipeline.enqueue(messageB)
|
|
pipeline.flushIfNeeded()
|
|
|
|
#expect(delegate.messages.count == 1)
|
|
#expect(delegate.messages.first?.content == "Same")
|
|
}
|
|
|
|
@Test @MainActor
|
|
func lateInsert_meshAppendsRecentOlderMessage() async {
|
|
let pipeline = PublicMessagePipeline()
|
|
let delegate = TestPipelineDelegate()
|
|
pipeline.delegate = delegate
|
|
pipeline.updateActiveChannel(.mesh)
|
|
|
|
let base = Date()
|
|
let newer = BitchatMessage(
|
|
id: "new",
|
|
sender: "A",
|
|
content: "New",
|
|
timestamp: base,
|
|
isRelay: false
|
|
)
|
|
let older = BitchatMessage(
|
|
id: "old",
|
|
sender: "A",
|
|
content: "Old",
|
|
timestamp: base.addingTimeInterval(-5),
|
|
isRelay: false
|
|
)
|
|
|
|
delegate.messages = [newer]
|
|
pipeline.enqueue(older)
|
|
pipeline.flushIfNeeded()
|
|
|
|
#expect(delegate.messages.map { $0.id } == ["new", "old"])
|
|
}
|
|
|
|
@Test @MainActor
|
|
func lateInsert_locationInsertsByTimestamp() async {
|
|
let pipeline = PublicMessagePipeline()
|
|
let delegate = TestPipelineDelegate()
|
|
pipeline.delegate = delegate
|
|
pipeline.updateActiveChannel(.location(GeohashChannel(level: .city, geohash: "u4pruydq")))
|
|
|
|
let base = Date()
|
|
let newer = BitchatMessage(
|
|
id: "new",
|
|
sender: "A",
|
|
content: "New",
|
|
timestamp: base,
|
|
isRelay: false
|
|
)
|
|
let older = BitchatMessage(
|
|
id: "old",
|
|
sender: "A",
|
|
content: "Old",
|
|
timestamp: base.addingTimeInterval(-5),
|
|
isRelay: false
|
|
)
|
|
|
|
delegate.messages = [newer]
|
|
pipeline.enqueue(older)
|
|
pipeline.flushIfNeeded()
|
|
|
|
#expect(delegate.messages.map { $0.id } == ["old", "new"])
|
|
}
|
|
}
|