mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 01:05:19 +00:00
Refactor/robustness (#446)
* Refactor BitChat for improved robustness and performance Major refactoring to simplify architecture and fix critical issues: Architecture Improvements: - Replace complex BluetoothMeshService with simplified SimplifiedBluetoothService - Consolidate message routing and peer management into unified services - Remove redundant caching layers and optimize performance Bug Fixes: - Fix critical BLE peer mapping corruption in mesh networks - Fix encrypted message routing failures in multi-peer scenarios - Fix app freezes and Main Thread Checker warnings - Fix BLE message delivery in dual-role connections - Fix favorite toggle UI not updating instantly - Fix Nostr offline messaging with 24-hour message filtering Features: - Add command processor for chat commands - Add autocomplete service for mentions and commands - Improve private chat management with dedicated service - Add unified peer service for consistent state management Performance: - Optimize BLE reconnection speed - Reduce excessive logging throughout codebase - Improve message deduplication efficiency - Optimize UI updates and state management * Improvements refactor robust (#441) * remove unused code * remove more * TLV for announcement * restore * restore * restore? * messages tlv too (#442) * Fix Nostr notification and read receipt issues, add TLV encoding, cleanup unused code ## Notification & Read Receipt Fixes - Fixed toolbar notification icon appearing incorrectly on app restart for already-read messages - Fixed read receipts being incorrectly deleted on startup when privateChats was empty - Fixed messages not being marked as read when opening chat for first time - Fixed senderPeerID not being updated during message consolidation - Added startup phase logic to block old messages (>30s) while allowing recent ones - Fixed unread status checking across all storage locations (ephemeral, stable Noise keys, temporary Nostr IDs) ## TLV Encoding Implementation - Implemented Type-Length-Value encoding for private message payloads - Added PrivateMessagePacket struct with TLV encode/decode methods - Enhanced message structure for better extensibility and robustness ## Code Cleanup - Removed unused PeerStateManager class and related dependencies - Removed dead protocol types (DeliveryAck, ProtocolAck/Nack, NoiseIdentityAnnouncement) - Cleaned up BitchatDelegate by removing unused methods - Removed excessive debug logging throughout ChatViewModel - Added test-only ProtocolNack helper for integration tests ## Technical Details - Messages stored under three ID types: ephemeral peer IDs, stable Noise key hexes, temporary Nostr IDs - Fixed cleanupOldReadReceipts() to skip when privateChats is empty or during startup - Updated message consolidation to properly update senderPeerID - Restored NIP-17 timestamp randomization (±15 minutes) for privacy --------- Co-authored-by: jack <jackjackbits@users.noreply.github.com> Co-authored-by: callebtc <93376500+callebtc@users.noreply.github.com>
This commit is contained in:
@@ -16,9 +16,6 @@ final class PrivateChatE2ETests: XCTestCase {
|
||||
var bob: MockBluetoothMeshService!
|
||||
var charlie: MockBluetoothMeshService!
|
||||
|
||||
var deliveryTracker: DeliveryTracker!
|
||||
var retryService: MessageRetryService!
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
@@ -27,22 +24,13 @@ final class PrivateChatE2ETests: XCTestCase {
|
||||
bob = createMockService(peerID: TestConstants.testPeerID2, nickname: TestConstants.testNickname2)
|
||||
charlie = createMockService(peerID: TestConstants.testPeerID3, nickname: TestConstants.testNickname3)
|
||||
|
||||
// Setup delivery tracking
|
||||
deliveryTracker = DeliveryTracker.shared
|
||||
retryService = MessageRetryService.shared
|
||||
retryService.meshService = alice
|
||||
|
||||
// Clear any existing state
|
||||
deliveryTracker.clearDeliveryStatus(for: "")
|
||||
retryService.clearRetryQueue()
|
||||
// Delivery tracking is now handled internally by SimplifiedBluetoothService
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
alice = nil
|
||||
bob = nil
|
||||
charlie = nil
|
||||
deliveryTracker = nil
|
||||
retryService = nil
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
@@ -104,227 +92,16 @@ final class PrivateChatE2ETests: XCTestCase {
|
||||
|
||||
// MARK: - Delivery Acknowledgment Tests
|
||||
|
||||
func testDeliveryAckGeneration() {
|
||||
simulateConnection(alice, bob)
|
||||
|
||||
let messageID = UUID().uuidString
|
||||
let expectation = XCTestExpectation(description: "Delivery status updated")
|
||||
|
||||
// Monitor delivery status
|
||||
let cancellable = deliveryTracker.deliveryStatusUpdated.sink { update in
|
||||
if update.messageID == messageID {
|
||||
switch update.status {
|
||||
case .delivered(let recipient, _):
|
||||
XCTAssertEqual(recipient, TestConstants.testNickname2)
|
||||
expectation.fulfill()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup Bob to generate ACK
|
||||
bob.packetDeliveryHandler = { packet in
|
||||
if let message = BitchatMessage.fromBinaryPayload(packet.payload),
|
||||
message.isPrivate {
|
||||
// Generate ACK
|
||||
if let ack = self.deliveryTracker.generateAck(
|
||||
for: message,
|
||||
myPeerID: TestConstants.testPeerID2,
|
||||
myNickname: TestConstants.testNickname2,
|
||||
hopCount: 1
|
||||
) {
|
||||
// Send ACK back
|
||||
let ackData = ack.encode()!
|
||||
let ackPacket = TestHelpers.createTestPacket(
|
||||
type: 0x03,
|
||||
senderID: TestConstants.testPeerID2,
|
||||
recipientID: TestConstants.testPeerID1,
|
||||
payload: ackData
|
||||
)
|
||||
self.alice.simulateIncomingPacket(ackPacket)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup Alice to process ACK
|
||||
alice.packetDeliveryHandler = { packet in
|
||||
if packet.type == 0x03 {
|
||||
if let ack = DeliveryAck.decode(from: packet.payload) {
|
||||
self.deliveryTracker.processDeliveryAck(ack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Track the message
|
||||
let trackedMessage = BitchatMessage(
|
||||
id: messageID,
|
||||
sender: TestConstants.testNickname1,
|
||||
content: TestConstants.testMessage1,
|
||||
timestamp: Date(),
|
||||
isRelay: false,
|
||||
originalSender: nil,
|
||||
isPrivate: true,
|
||||
recipientNickname: TestConstants.testNickname2,
|
||||
senderPeerID: TestConstants.testPeerID1,
|
||||
mentions: nil
|
||||
)
|
||||
|
||||
deliveryTracker.trackMessage(
|
||||
trackedMessage,
|
||||
recipientID: TestConstants.testPeerID2,
|
||||
recipientNickname: TestConstants.testNickname2
|
||||
)
|
||||
|
||||
// Send the message
|
||||
alice.sendPrivateMessage(
|
||||
TestConstants.testMessage1,
|
||||
to: TestConstants.testPeerID2,
|
||||
recipientNickname: TestConstants.testNickname2,
|
||||
messageID: messageID
|
||||
)
|
||||
|
||||
wait(for: [expectation], timeout: TestConstants.defaultTimeout)
|
||||
cancellable.cancel()
|
||||
}
|
||||
// NOTE: DeliveryTracker has been removed in SimplifiedBluetoothService.
|
||||
// Delivery tracking is now handled internally.
|
||||
|
||||
func testDeliveryTimeout() {
|
||||
// Don't connect peers - message should timeout
|
||||
let messageID = UUID().uuidString
|
||||
let expectation = XCTestExpectation(description: "Delivery failed due to timeout")
|
||||
|
||||
// Use shared instance (can't create new one due to private init)
|
||||
let shortTimeoutTracker = DeliveryTracker.shared
|
||||
|
||||
let cancellable = shortTimeoutTracker.deliveryStatusUpdated.sink { update in
|
||||
if update.messageID == messageID {
|
||||
switch update.status {
|
||||
case .failed(let reason):
|
||||
XCTAssertTrue(reason.contains("not delivered"))
|
||||
expectation.fulfill()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let trackedMessage = BitchatMessage(
|
||||
id: messageID,
|
||||
sender: TestConstants.testNickname1,
|
||||
content: TestConstants.testMessage1,
|
||||
timestamp: Date(),
|
||||
isRelay: false,
|
||||
originalSender: nil,
|
||||
isPrivate: true,
|
||||
recipientNickname: TestConstants.testNickname2,
|
||||
senderPeerID: TestConstants.testPeerID1,
|
||||
mentions: nil
|
||||
)
|
||||
|
||||
// Track with short timeout (will use default 30s for private messages)
|
||||
shortTimeoutTracker.trackMessage(
|
||||
trackedMessage,
|
||||
recipientID: TestConstants.testPeerID2,
|
||||
recipientNickname: TestConstants.testNickname2
|
||||
)
|
||||
|
||||
// Don't actually send - let it timeout
|
||||
// For testing, we'll manually trigger timeout
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
shortTimeoutTracker.clearDeliveryStatus(for: messageID)
|
||||
shortTimeoutTracker.deliveryStatusUpdated.send((messageID: messageID, status: .failed(reason: "Message not delivered")))
|
||||
}
|
||||
|
||||
wait(for: [expectation], timeout: TestConstants.shortTimeout)
|
||||
cancellable.cancel()
|
||||
}
|
||||
|
||||
// MARK: - Message Retry Tests
|
||||
|
||||
func testMessageRetryOnFailure() {
|
||||
let messageContent = "Retry test message"
|
||||
let expectation = XCTestExpectation(description: "Message retried")
|
||||
|
||||
var sendCount = 0
|
||||
|
||||
// Override send to count attempts
|
||||
alice.messageDeliveryHandler = { message in
|
||||
if message.content == messageContent {
|
||||
sendCount += 1
|
||||
if sendCount == 2 { // Original + 1 retry
|
||||
expectation.fulfill()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add to retry queue
|
||||
retryService.addMessageForRetry(
|
||||
content: messageContent,
|
||||
isPrivate: true,
|
||||
recipientPeerID: TestConstants.testPeerID2,
|
||||
recipientNickname: TestConstants.testNickname2
|
||||
)
|
||||
|
||||
// Simulate connection after delay to trigger retry
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) {
|
||||
self.simulateConnection(self.alice, self.bob)
|
||||
}
|
||||
|
||||
wait(for: [expectation], timeout: TestConstants.defaultTimeout)
|
||||
XCTAssertGreaterThanOrEqual(sendCount, 1)
|
||||
}
|
||||
// NOTE: MessageRetryService has been removed in SimplifiedBluetoothService.
|
||||
// Retry logic is now handled internally.
|
||||
|
||||
func testRetryQueueOrdering() {
|
||||
// Add multiple messages to retry queue
|
||||
let messages = [
|
||||
(content: "First", timestamp: Date().addingTimeInterval(-10)),
|
||||
(content: "Second", timestamp: Date().addingTimeInterval(-5)),
|
||||
(content: "Third", timestamp: Date())
|
||||
]
|
||||
|
||||
for msg in messages {
|
||||
retryService.addMessageForRetry(
|
||||
content: msg.content,
|
||||
isPrivate: true,
|
||||
recipientPeerID: TestConstants.testPeerID2,
|
||||
recipientNickname: TestConstants.testNickname2,
|
||||
originalTimestamp: msg.timestamp
|
||||
)
|
||||
}
|
||||
|
||||
XCTAssertEqual(retryService.getRetryQueueCount(), 3)
|
||||
|
||||
var receivedOrder: [String] = []
|
||||
let expectation = XCTestExpectation(description: "Messages received in order")
|
||||
|
||||
bob.messageDeliveryHandler = { message in
|
||||
receivedOrder.append(message.content)
|
||||
if receivedOrder.count == 3 {
|
||||
expectation.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
// Connect to trigger retry
|
||||
simulateConnection(alice, bob)
|
||||
|
||||
wait(for: [expectation], timeout: TestConstants.defaultTimeout)
|
||||
|
||||
// Verify order maintained
|
||||
XCTAssertEqual(receivedOrder, ["First", "Second", "Third"])
|
||||
}
|
||||
|
||||
func testRetryQueueMaxSize() {
|
||||
// Try to add more than max queue size
|
||||
for i in 0..<60 {
|
||||
retryService.addMessageForRetry(
|
||||
content: "Message \(i)",
|
||||
recipientPeerID: TestConstants.testPeerID2
|
||||
)
|
||||
}
|
||||
|
||||
// Should not exceed max size (50)
|
||||
XCTAssertLessThanOrEqual(retryService.getRetryQueueCount(), 50)
|
||||
}
|
||||
|
||||
// MARK: - End-to-End Encryption Tests
|
||||
|
||||
@@ -491,24 +268,7 @@ final class PrivateChatE2ETests: XCTestCase {
|
||||
|
||||
// MARK: - Error Handling Tests
|
||||
|
||||
func testPrivateMessageToUnknownPeer() {
|
||||
// Alice not connected to anyone
|
||||
let expectation = XCTestExpectation(description: "Message added to retry queue")
|
||||
|
||||
retryService.addMessageForRetry(
|
||||
content: TestConstants.testMessage1,
|
||||
isPrivate: true,
|
||||
recipientPeerID: TestConstants.testPeerID2,
|
||||
recipientNickname: TestConstants.testNickname2
|
||||
)
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
XCTAssertEqual(self.retryService.getRetryQueueCount(), 1)
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
wait(for: [expectation], timeout: TestConstants.shortTimeout)
|
||||
}
|
||||
// NOTE: This test relied on MessageRetryService which has been removed
|
||||
|
||||
func testDuplicateAckPrevention() {
|
||||
simulateConnection(alice, bob)
|
||||
@@ -537,6 +297,8 @@ final class PrivateChatE2ETests: XCTestCase {
|
||||
)
|
||||
|
||||
// Generate multiple ACKs for same message
|
||||
// NOTE: DeliveryTracker has been removed - this test is no longer applicable
|
||||
/*
|
||||
for _ in 0..<3 {
|
||||
if let ack = deliveryTracker.generateAck(
|
||||
for: message,
|
||||
@@ -554,6 +316,7 @@ final class PrivateChatE2ETests: XCTestCase {
|
||||
alice.simulateIncomingPacket(ackPacket)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Should only generate one ACK
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
|
||||
@@ -287,7 +287,7 @@ final class IntegrationTests: XCTestCase {
|
||||
|
||||
// Track identity announcements
|
||||
nodes["Bob"]!.packetDeliveryHandler = { packet in
|
||||
if packet.type == MessageType.noiseIdentityAnnounce.rawValue {
|
||||
if packet.type == 0x04 { // noiseIdentityAnnounce was removed
|
||||
bobReceivedIdentityAnnounce = true
|
||||
if aliceReceivedIdentityAnnounce {
|
||||
expectation.fulfill()
|
||||
@@ -296,7 +296,7 @@ final class IntegrationTests: XCTestCase {
|
||||
}
|
||||
|
||||
nodes["Alice"]!.packetDeliveryHandler = { packet in
|
||||
if packet.type == MessageType.noiseIdentityAnnounce.rawValue {
|
||||
if packet.type == 0x04 { // noiseIdentityAnnounce was removed
|
||||
aliceReceivedIdentityAnnounce = true
|
||||
if bobReceivedIdentityAnnounce {
|
||||
expectation.fulfill()
|
||||
@@ -347,7 +347,7 @@ final class IntegrationTests: XCTestCase {
|
||||
let handshakeExpectation = XCTestExpectation(description: "New handshake completed")
|
||||
|
||||
nodes["Bob"]!.packetDeliveryHandler = { packet in
|
||||
if packet.type == MessageType.noiseHandshakeInit.rawValue {
|
||||
if packet.type == 0x05 { // noiseHandshakeInit was removed
|
||||
// Bob initiates new handshake after restart
|
||||
do {
|
||||
let response = try self.noiseManagers["Alice"]!.handleIncomingHandshake(
|
||||
@@ -357,7 +357,7 @@ final class IntegrationTests: XCTestCase {
|
||||
if let resp = response {
|
||||
// Send response back to Bob
|
||||
let responsePacket = TestHelpers.createTestPacket(
|
||||
type: MessageType.noiseHandshakeResp.rawValue,
|
||||
type: 0x06, // noiseHandshakeResp was removed
|
||||
payload: resp
|
||||
)
|
||||
self.nodes["Bob"]!.simulateIncomingPacket(responsePacket)
|
||||
@@ -365,7 +365,7 @@ final class IntegrationTests: XCTestCase {
|
||||
} catch {
|
||||
XCTFail("Handshake handling failed: \(error)")
|
||||
}
|
||||
} else if packet.type == MessageType.noiseHandshakeResp.rawValue && packet.senderID.hexEncodedString() == TestConstants.testPeerID1 {
|
||||
} else if packet.type == 0x06 // noiseHandshakeResp was removed && packet.senderID.hexEncodedString() == TestConstants.testPeerID1 {
|
||||
// Final handshake message (message 3 in XX pattern)
|
||||
handshakeExpectation.fulfill()
|
||||
}
|
||||
@@ -552,7 +552,7 @@ final class IntegrationTests: XCTestCase {
|
||||
|
||||
let nackData = nack.toBinaryData()
|
||||
let nackPacket = TestHelpers.createTestPacket(
|
||||
type: MessageType.protocolNack.rawValue,
|
||||
type: 0x08, // protocolNack was removed
|
||||
payload: nackData
|
||||
)
|
||||
self.nodes["Alice"]!.simulateIncomingPacket(nackPacket)
|
||||
@@ -560,7 +560,7 @@ final class IntegrationTests: XCTestCase {
|
||||
// Bob clears session
|
||||
self.noiseManagers["Bob"]!.removeSession(for: TestConstants.testPeerID1)
|
||||
}
|
||||
} else if packet.type == MessageType.noiseHandshakeInit.rawValue {
|
||||
} else if packet.type == 0x05 { // noiseHandshakeInit was removed
|
||||
// Bob receives handshake init from Alice after NACK
|
||||
do {
|
||||
let response = try self.noiseManagers["Bob"]!.handleIncomingHandshake(
|
||||
@@ -569,7 +569,7 @@ final class IntegrationTests: XCTestCase {
|
||||
)
|
||||
if let resp = response {
|
||||
let responsePacket = TestHelpers.createTestPacket(
|
||||
type: MessageType.noiseHandshakeResp.rawValue,
|
||||
type: 0x06, // noiseHandshakeResp was removed
|
||||
payload: resp
|
||||
)
|
||||
self.nodes["Alice"]!.simulateIncomingPacket(responsePacket)
|
||||
@@ -582,7 +582,7 @@ final class IntegrationTests: XCTestCase {
|
||||
|
||||
// Setup Alice's handler to clear session on NACK and initiate handshake
|
||||
nodes["Alice"]!.packetDeliveryHandler = { packet in
|
||||
if packet.type == MessageType.protocolNack.rawValue {
|
||||
if packet.type == 0x08 { // protocolNack was removed
|
||||
// Alice receives NACK - clear session
|
||||
self.noiseManagers["Alice"]!.removeSession(for: TestConstants.testPeerID2)
|
||||
|
||||
@@ -590,14 +590,14 @@ final class IntegrationTests: XCTestCase {
|
||||
do {
|
||||
let handshakeInit = try self.noiseManagers["Alice"]!.initiateHandshake(with: TestConstants.testPeerID2)
|
||||
let handshakePacket = TestHelpers.createTestPacket(
|
||||
type: MessageType.noiseHandshakeInit.rawValue,
|
||||
type: 0x05, // noiseHandshakeInit was removed
|
||||
payload: handshakeInit
|
||||
)
|
||||
self.nodes["Bob"]!.simulateIncomingPacket(handshakePacket)
|
||||
} catch {
|
||||
XCTFail("Alice failed to initiate handshake: \(error)")
|
||||
}
|
||||
} else if packet.type == MessageType.noiseHandshakeResp.rawValue {
|
||||
} else if packet.type == 0x06 // noiseHandshakeResp was removed {
|
||||
// Complete handshake
|
||||
do {
|
||||
let final = try self.noiseManagers["Alice"]!.handleIncomingHandshake(
|
||||
@@ -606,7 +606,7 @@ final class IntegrationTests: XCTestCase {
|
||||
)
|
||||
if let finalMsg = final {
|
||||
let finalPacket = TestHelpers.createTestPacket(
|
||||
type: MessageType.noiseHandshakeResp.rawValue,
|
||||
type: 0x06, // noiseHandshakeResp was removed
|
||||
payload: finalMsg
|
||||
)
|
||||
self.nodes["Bob"]!.simulateIncomingPacket(finalPacket)
|
||||
|
||||
@@ -7,139 +7,8 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MultipeerConnectivity
|
||||
import CoreBluetooth
|
||||
@testable import bitchat
|
||||
|
||||
class MockBluetoothMeshService: BluetoothMeshService {
|
||||
var sentMessages: [(message: BitchatMessage, packet: BitchatPacket)] = []
|
||||
var sentPackets: [BitchatPacket] = []
|
||||
var connectedPeers: Set<String> = []
|
||||
var messageDeliveryHandler: ((BitchatMessage) -> Void)?
|
||||
var packetDeliveryHandler: ((BitchatPacket) -> Void)?
|
||||
|
||||
// Override these properties
|
||||
var mockNickname: String = "MockUser"
|
||||
|
||||
override var myPeerID: String {
|
||||
didSet {
|
||||
// Update when changed
|
||||
}
|
||||
}
|
||||
|
||||
var nickname: String {
|
||||
return mockNickname
|
||||
}
|
||||
|
||||
var peerID: String {
|
||||
return myPeerID
|
||||
}
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
self.myPeerID = "MOCK1234"
|
||||
}
|
||||
|
||||
func simulateConnectedPeer(_ peerID: String) {
|
||||
connectedPeers.insert(peerID)
|
||||
delegate?.didConnectToPeer(peerID)
|
||||
delegate?.didUpdatePeerList(Array(connectedPeers))
|
||||
}
|
||||
|
||||
func simulateDisconnectedPeer(_ peerID: String) {
|
||||
connectedPeers.remove(peerID)
|
||||
delegate?.didDisconnectFromPeer(peerID)
|
||||
delegate?.didUpdatePeerList(Array(connectedPeers))
|
||||
}
|
||||
|
||||
override func sendMessage(_ content: String, mentions: [String], to room: String? = nil, messageID: String? = nil, timestamp: Date? = nil) {
|
||||
let message = BitchatMessage(
|
||||
id: messageID ?? UUID().uuidString,
|
||||
sender: mockNickname,
|
||||
content: content,
|
||||
timestamp: timestamp ?? Date(),
|
||||
isRelay: false,
|
||||
originalSender: nil,
|
||||
isPrivate: false,
|
||||
recipientNickname: nil,
|
||||
senderPeerID: myPeerID,
|
||||
mentions: mentions.isEmpty ? nil : mentions
|
||||
)
|
||||
|
||||
if let payload = message.toBinaryPayload() {
|
||||
let packet = BitchatPacket(
|
||||
type: 0x01,
|
||||
senderID: myPeerID.data(using: .utf8)!,
|
||||
recipientID: nil,
|
||||
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
|
||||
payload: payload,
|
||||
signature: nil,
|
||||
ttl: 3
|
||||
)
|
||||
|
||||
sentMessages.append((message, packet))
|
||||
sentPackets.append(packet)
|
||||
|
||||
// Simulate local echo
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.delegate?.didReceiveMessage(message)
|
||||
}
|
||||
|
||||
// Call delivery handler if set
|
||||
messageDeliveryHandler?(message)
|
||||
}
|
||||
}
|
||||
|
||||
override func sendPrivateMessage(_ content: String, to recipientPeerID: String, recipientNickname: String, messageID: String? = nil) {
|
||||
let message = BitchatMessage(
|
||||
id: messageID ?? UUID().uuidString,
|
||||
sender: mockNickname,
|
||||
content: content,
|
||||
timestamp: Date(),
|
||||
isRelay: false,
|
||||
originalSender: nil,
|
||||
isPrivate: true,
|
||||
recipientNickname: recipientNickname,
|
||||
senderPeerID: myPeerID,
|
||||
mentions: nil
|
||||
)
|
||||
|
||||
if let payload = message.toBinaryPayload() {
|
||||
let packet = BitchatPacket(
|
||||
type: 0x01,
|
||||
senderID: myPeerID.data(using: .utf8)!,
|
||||
recipientID: recipientPeerID.data(using: .utf8)!,
|
||||
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
|
||||
payload: payload,
|
||||
signature: nil,
|
||||
ttl: 3
|
||||
)
|
||||
|
||||
sentMessages.append((message, packet))
|
||||
sentPackets.append(packet)
|
||||
|
||||
// Simulate local echo
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.delegate?.didReceiveMessage(message)
|
||||
}
|
||||
|
||||
// Call delivery handler if set
|
||||
messageDeliveryHandler?(message)
|
||||
}
|
||||
}
|
||||
|
||||
func simulateIncomingMessage(_ message: BitchatMessage) {
|
||||
delegate?.didReceiveMessage(message)
|
||||
}
|
||||
|
||||
func simulateIncomingPacket(_ packet: BitchatPacket) {
|
||||
// Process through the actual handling logic
|
||||
if let message = BitchatMessage.fromBinaryPayload(packet.payload) {
|
||||
delegate?.didReceiveMessage(message)
|
||||
}
|
||||
packetDeliveryHandler?(packet)
|
||||
}
|
||||
|
||||
func getConnectedPeers() -> [String] {
|
||||
return Array(connectedPeers)
|
||||
}
|
||||
}
|
||||
// Compatibility wrapper for old tests - please use MockSimplifiedBluetoothService directly
|
||||
typealias MockBluetoothMeshService = MockSimplifiedBluetoothService
|
||||
@@ -0,0 +1,227 @@
|
||||
//
|
||||
// MockSimplifiedBluetoothService.swift
|
||||
// bitchatTests
|
||||
//
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
// For more information, see <https://unlicense.org>
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreBluetooth
|
||||
@testable import bitchat
|
||||
|
||||
// Mock implementation that mimics SimplifiedBluetoothService behavior
|
||||
class MockSimplifiedBluetoothService: NSObject {
|
||||
|
||||
// MARK: - Properties matching SimplifiedBluetoothService
|
||||
|
||||
weak var delegate: BitchatDelegate?
|
||||
var myPeerID: String = "MOCK1234"
|
||||
var myNickname: String = "MockUser"
|
||||
|
||||
// Test-specific properties
|
||||
var sentMessages: [(message: BitchatMessage, packet: BitchatPacket)] = []
|
||||
var sentPackets: [BitchatPacket] = []
|
||||
var connectedPeers: Set<String> = []
|
||||
var messageDeliveryHandler: ((BitchatMessage) -> Void)?
|
||||
var packetDeliveryHandler: ((BitchatPacket) -> Void)?
|
||||
|
||||
// Compatibility properties for old tests
|
||||
var mockNickname: String {
|
||||
get { return myNickname }
|
||||
set { myNickname = newValue }
|
||||
}
|
||||
|
||||
var nickname: String {
|
||||
return myNickname
|
||||
}
|
||||
|
||||
var peerID: String {
|
||||
return myPeerID
|
||||
}
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Methods matching SimplifiedBluetoothService
|
||||
|
||||
func setNickname(_ nickname: String) {
|
||||
self.myNickname = nickname
|
||||
}
|
||||
|
||||
func startServices() {
|
||||
// Mock implementation - do nothing
|
||||
}
|
||||
|
||||
func stopServices() {
|
||||
// Mock implementation - do nothing
|
||||
}
|
||||
|
||||
func isPeerConnected(_ peerID: String) -> Bool {
|
||||
return connectedPeers.contains(peerID)
|
||||
}
|
||||
|
||||
func getPeerNicknames() -> [String: String] {
|
||||
var nicknames: [String: String] = [:]
|
||||
for peer in connectedPeers {
|
||||
nicknames[peer] = "MockPeer_\(peer)"
|
||||
}
|
||||
return nicknames
|
||||
}
|
||||
|
||||
func getPeers() -> [String: String] {
|
||||
return getPeerNicknames()
|
||||
}
|
||||
|
||||
func sendMessage(_ content: String, mentions: [String] = [], to recipientID: String? = nil, messageID: String? = nil, timestamp: Date? = nil) {
|
||||
let message = BitchatMessage(
|
||||
id: messageID ?? UUID().uuidString,
|
||||
sender: myNickname,
|
||||
content: content,
|
||||
timestamp: timestamp ?? Date(),
|
||||
isRelay: false,
|
||||
originalSender: nil,
|
||||
isPrivate: recipientID != nil,
|
||||
recipientNickname: nil,
|
||||
senderPeerID: myPeerID,
|
||||
mentions: mentions.isEmpty ? nil : mentions
|
||||
)
|
||||
|
||||
if let payload = message.toBinaryPayload() {
|
||||
let packet = BitchatPacket(
|
||||
type: 0x01,
|
||||
senderID: myPeerID.data(using: .utf8)!,
|
||||
recipientID: recipientID?.data(using: .utf8),
|
||||
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
|
||||
payload: payload,
|
||||
signature: nil,
|
||||
ttl: 3
|
||||
)
|
||||
|
||||
sentMessages.append((message, packet))
|
||||
sentPackets.append(packet)
|
||||
|
||||
// Simulate local echo
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.delegate?.didReceiveMessage(message)
|
||||
}
|
||||
|
||||
// Call delivery handler if set
|
||||
messageDeliveryHandler?(message)
|
||||
}
|
||||
}
|
||||
|
||||
func sendPrivateMessage(_ content: String, to recipientPeerID: String, recipientNickname: String, messageID: String) {
|
||||
let message = BitchatMessage(
|
||||
id: messageID,
|
||||
sender: myNickname,
|
||||
content: content,
|
||||
timestamp: Date(),
|
||||
isRelay: false,
|
||||
originalSender: nil,
|
||||
isPrivate: true,
|
||||
recipientNickname: recipientNickname,
|
||||
senderPeerID: myPeerID,
|
||||
mentions: nil
|
||||
)
|
||||
|
||||
if let payload = message.toBinaryPayload() {
|
||||
let packet = BitchatPacket(
|
||||
type: 0x01,
|
||||
senderID: myPeerID.data(using: .utf8)!,
|
||||
recipientID: recipientPeerID.data(using: .utf8)!,
|
||||
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
|
||||
payload: payload,
|
||||
signature: nil,
|
||||
ttl: 3
|
||||
)
|
||||
|
||||
sentMessages.append((message, packet))
|
||||
sentPackets.append(packet)
|
||||
|
||||
// Simulate local echo
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.delegate?.didReceiveMessage(message)
|
||||
}
|
||||
|
||||
// Call delivery handler if set
|
||||
messageDeliveryHandler?(message)
|
||||
}
|
||||
}
|
||||
|
||||
func sendFavoriteNotification(to peerID: String, isFavorite: Bool) {
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
func sendReadReceipt(_ receipt: ReadReceipt, to peerID: String) {
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
func sendBroadcastAnnounce() {
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
func getPeerFingerprint(_ peerID: String) -> String? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getNoiseSessionState(for peerID: String) -> LazyHandshakeState {
|
||||
return .none
|
||||
}
|
||||
|
||||
func triggerHandshake(with peerID: String) {
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
func emergencyDisconnectAll() {
|
||||
connectedPeers.removeAll()
|
||||
delegate?.didUpdatePeerList([])
|
||||
}
|
||||
|
||||
func getNoiseService() -> NoiseEncryptionService {
|
||||
return NoiseEncryptionService()
|
||||
}
|
||||
|
||||
func getFingerprint(for peerID: String) -> String? {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MARK: - Test Helper Methods
|
||||
|
||||
func simulateConnectedPeer(_ peerID: String) {
|
||||
connectedPeers.insert(peerID)
|
||||
delegate?.didConnectToPeer(peerID)
|
||||
delegate?.didUpdatePeerList(Array(connectedPeers))
|
||||
}
|
||||
|
||||
func simulateDisconnectedPeer(_ peerID: String) {
|
||||
connectedPeers.remove(peerID)
|
||||
delegate?.didDisconnectFromPeer(peerID)
|
||||
delegate?.didUpdatePeerList(Array(connectedPeers))
|
||||
}
|
||||
|
||||
func simulateIncomingMessage(_ message: BitchatMessage) {
|
||||
delegate?.didReceiveMessage(message)
|
||||
}
|
||||
|
||||
func simulateIncomingPacket(_ packet: BitchatPacket) {
|
||||
// Process through the actual handling logic
|
||||
if let message = BitchatMessage.fromBinaryPayload(packet.payload) {
|
||||
delegate?.didReceiveMessage(message)
|
||||
}
|
||||
packetDeliveryHandler?(packet)
|
||||
}
|
||||
|
||||
func getConnectedPeers() -> [String] {
|
||||
return Array(connectedPeers)
|
||||
}
|
||||
|
||||
// MARK: - Compatibility methods for old tests
|
||||
|
||||
func sendPrivateMessage(_ content: String, to recipientPeerID: String, recipientNickname: String, messageID: String? = nil) {
|
||||
sendPrivateMessage(content, to: recipientPeerID, recipientNickname: recipientNickname, messageID: messageID ?? UUID().uuidString)
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,7 @@ final class NostrProtocolTests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Enable secure logging for tests
|
||||
SecureLogger.enableLogging()
|
||||
// SecureLogger is always enabled
|
||||
}
|
||||
|
||||
func testNIP17MessageRoundTrip() throws {
|
||||
@@ -39,7 +38,7 @@ final class NostrProtocolTests: XCTestCase {
|
||||
print("Gift wrap pubkey: \(giftWrap.pubkey)")
|
||||
|
||||
// Decrypt the gift wrap
|
||||
let (decryptedContent, senderPubkey) = try NostrProtocol.decryptPrivateMessage(
|
||||
let (decryptedContent, senderPubkey, timestamp) = try NostrProtocol.decryptPrivateMessage(
|
||||
giftWrap: giftWrap,
|
||||
recipientIdentity: recipient
|
||||
)
|
||||
@@ -48,7 +47,12 @@ final class NostrProtocolTests: XCTestCase {
|
||||
XCTAssertEqual(decryptedContent, originalContent)
|
||||
XCTAssertEqual(senderPubkey, sender.publicKeyHex)
|
||||
|
||||
print("✅ Successfully decrypted message: '\(decryptedContent)' from \(senderPubkey)")
|
||||
// Verify timestamp is reasonable (within last minute)
|
||||
let messageDate = Date(timeIntervalSince1970: TimeInterval(timestamp))
|
||||
let timeDiff = abs(messageDate.timeIntervalSinceNow)
|
||||
XCTAssertLessThan(timeDiff, 60, "Message timestamp should be recent")
|
||||
|
||||
print("✅ Successfully decrypted message: '\(decryptedContent)' from \(senderPubkey) at \(messageDate)")
|
||||
}
|
||||
|
||||
func testGiftWrapUsesUniqueEphemeralKeys() throws {
|
||||
@@ -75,11 +79,11 @@ final class NostrProtocolTests: XCTestCase {
|
||||
print("Message 2 gift wrap pubkey: \(message2.pubkey)")
|
||||
|
||||
// Both should decrypt successfully
|
||||
let (content1, _) = try NostrProtocol.decryptPrivateMessage(
|
||||
let (content1, _, _) = try NostrProtocol.decryptPrivateMessage(
|
||||
giftWrap: message1,
|
||||
recipientIdentity: recipient
|
||||
)
|
||||
let (content2, _) = try NostrProtocol.decryptPrivateMessage(
|
||||
let (content2, _, _) = try NostrProtocol.decryptPrivateMessage(
|
||||
giftWrap: message2,
|
||||
recipientIdentity: recipient
|
||||
)
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
//
|
||||
// SimplifiedBluetoothServiceTests.swift
|
||||
// bitchatTests
|
||||
//
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
// For more information, see <https://unlicense.org>
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import CoreBluetooth
|
||||
@testable import bitchat
|
||||
|
||||
final class SimplifiedBluetoothServiceTests: XCTestCase {
|
||||
|
||||
var service: MockSimplifiedBluetoothService!
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
service = MockSimplifiedBluetoothService()
|
||||
service.myPeerID = "TEST1234"
|
||||
service.mockNickname = "TestUser"
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
service = nil
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
// MARK: - Basic Functionality Tests
|
||||
|
||||
func testServiceInitialization() {
|
||||
XCTAssertNotNil(service)
|
||||
XCTAssertEqual(service.myPeerID, "TEST1234")
|
||||
XCTAssertEqual(service.myNickname, "TestUser")
|
||||
}
|
||||
|
||||
func testPeerConnection() {
|
||||
// Test connecting a peer
|
||||
service.simulateConnectedPeer("PEER5678")
|
||||
XCTAssertTrue(service.isPeerConnected("PEER5678"))
|
||||
XCTAssertEqual(service.getConnectedPeers().count, 1)
|
||||
|
||||
// Test disconnecting a peer
|
||||
service.simulateDisconnectedPeer("PEER5678")
|
||||
XCTAssertFalse(service.isPeerConnected("PEER5678"))
|
||||
XCTAssertEqual(service.getConnectedPeers().count, 0)
|
||||
}
|
||||
|
||||
func testMultiplePeerConnections() {
|
||||
service.simulateConnectedPeer("PEER1")
|
||||
service.simulateConnectedPeer("PEER2")
|
||||
service.simulateConnectedPeer("PEER3")
|
||||
|
||||
XCTAssertEqual(service.getConnectedPeers().count, 3)
|
||||
XCTAssertTrue(service.isPeerConnected("PEER1"))
|
||||
XCTAssertTrue(service.isPeerConnected("PEER2"))
|
||||
XCTAssertTrue(service.isPeerConnected("PEER3"))
|
||||
|
||||
service.simulateDisconnectedPeer("PEER2")
|
||||
XCTAssertEqual(service.getConnectedPeers().count, 2)
|
||||
XCTAssertFalse(service.isPeerConnected("PEER2"))
|
||||
}
|
||||
|
||||
// MARK: - Message Sending Tests
|
||||
|
||||
func testSendPublicMessage() {
|
||||
let expectation = XCTestExpectation(description: "Message sent")
|
||||
|
||||
service.delegate = MockBitchatDelegate { message in
|
||||
XCTAssertEqual(message.content, "Hello, world!")
|
||||
XCTAssertEqual(message.sender, "TestUser")
|
||||
XCTAssertFalse(message.isPrivate)
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
service.sendMessage("Hello, world!")
|
||||
|
||||
wait(for: [expectation], timeout: 1.0)
|
||||
XCTAssertEqual(service.sentMessages.count, 1)
|
||||
}
|
||||
|
||||
func testSendPrivateMessage() {
|
||||
let expectation = XCTestExpectation(description: "Private message sent")
|
||||
|
||||
service.delegate = MockBitchatDelegate { message in
|
||||
XCTAssertEqual(message.content, "Secret message")
|
||||
XCTAssertEqual(message.sender, "TestUser")
|
||||
XCTAssertTrue(message.isPrivate)
|
||||
XCTAssertEqual(message.recipientNickname, "Bob")
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
service.sendPrivateMessage("Secret message", to: "PEER5678", recipientNickname: "Bob", messageID: "MSG123")
|
||||
|
||||
wait(for: [expectation], timeout: 1.0)
|
||||
XCTAssertEqual(service.sentMessages.count, 1)
|
||||
}
|
||||
|
||||
func testSendMessageWithMentions() {
|
||||
let expectation = XCTestExpectation(description: "Message with mentions sent")
|
||||
|
||||
service.delegate = MockBitchatDelegate { message in
|
||||
XCTAssertEqual(message.content, "@alice @bob check this out")
|
||||
XCTAssertEqual(message.mentions, ["alice", "bob"])
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
service.sendMessage("@alice @bob check this out", mentions: ["alice", "bob"])
|
||||
|
||||
wait(for: [expectation], timeout: 1.0)
|
||||
}
|
||||
|
||||
// MARK: - Message Reception Tests
|
||||
|
||||
func testSimulateIncomingMessage() {
|
||||
let expectation = XCTestExpectation(description: "Message received")
|
||||
|
||||
service.delegate = MockBitchatDelegate { message in
|
||||
XCTAssertEqual(message.content, "Incoming message")
|
||||
XCTAssertEqual(message.sender, "RemoteUser")
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
let incomingMessage = BitchatMessage(
|
||||
id: "MSG456",
|
||||
sender: "RemoteUser",
|
||||
content: "Incoming message",
|
||||
timestamp: Date(),
|
||||
isRelay: false,
|
||||
originalSender: nil,
|
||||
isPrivate: false,
|
||||
recipientNickname: nil,
|
||||
senderPeerID: "REMOTE123",
|
||||
mentions: nil
|
||||
)
|
||||
|
||||
service.simulateIncomingMessage(incomingMessage)
|
||||
|
||||
wait(for: [expectation], timeout: 1.0)
|
||||
}
|
||||
|
||||
func testSimulateIncomingPacket() {
|
||||
let expectation = XCTestExpectation(description: "Packet processed")
|
||||
|
||||
service.delegate = MockBitchatDelegate { message in
|
||||
XCTAssertEqual(message.content, "Packet message")
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
let message = BitchatMessage(
|
||||
id: "MSG789",
|
||||
sender: "PacketSender",
|
||||
content: "Packet message",
|
||||
timestamp: Date(),
|
||||
isRelay: false,
|
||||
originalSender: nil,
|
||||
isPrivate: false,
|
||||
recipientNickname: nil,
|
||||
senderPeerID: "PACKET123",
|
||||
mentions: nil
|
||||
)
|
||||
|
||||
guard let payload = message.toBinaryPayload() else {
|
||||
XCTFail("Failed to create binary payload")
|
||||
return
|
||||
}
|
||||
|
||||
let packet = BitchatPacket(
|
||||
type: 0x01,
|
||||
senderID: "PACKET123".data(using: .utf8)!,
|
||||
recipientID: nil,
|
||||
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
|
||||
payload: payload,
|
||||
signature: nil,
|
||||
ttl: 3
|
||||
)
|
||||
|
||||
service.simulateIncomingPacket(packet)
|
||||
|
||||
wait(for: [expectation], timeout: 1.0)
|
||||
}
|
||||
|
||||
// MARK: - Peer Nickname Tests
|
||||
|
||||
func testGetPeerNicknames() {
|
||||
service.simulateConnectedPeer("PEER1")
|
||||
service.simulateConnectedPeer("PEER2")
|
||||
|
||||
let nicknames = service.getPeerNicknames()
|
||||
XCTAssertEqual(nicknames.count, 2)
|
||||
XCTAssertEqual(nicknames["PEER1"], "MockPeer_PEER1")
|
||||
XCTAssertEqual(nicknames["PEER2"], "MockPeer_PEER2")
|
||||
}
|
||||
|
||||
// MARK: - Service State Tests
|
||||
|
||||
func testStartStopServices() {
|
||||
// These are mock implementations, just ensure they don't crash
|
||||
service.startServices()
|
||||
service.stopServices()
|
||||
|
||||
// Service should still be functional after start/stop
|
||||
service.simulateConnectedPeer("PEER999")
|
||||
XCTAssertTrue(service.isPeerConnected("PEER999"))
|
||||
}
|
||||
|
||||
// MARK: - Message Delivery Handler Tests
|
||||
|
||||
func testMessageDeliveryHandler() {
|
||||
let expectation = XCTestExpectation(description: "Delivery handler called")
|
||||
|
||||
service.messageDeliveryHandler = { message in
|
||||
XCTAssertEqual(message.content, "Test delivery")
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
service.sendMessage("Test delivery")
|
||||
|
||||
wait(for: [expectation], timeout: 1.0)
|
||||
}
|
||||
|
||||
func testPacketDeliveryHandler() {
|
||||
let expectation = XCTestExpectation(description: "Packet handler called")
|
||||
|
||||
service.packetDeliveryHandler = { packet in
|
||||
XCTAssertEqual(packet.type, 0x01)
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
let message = BitchatMessage(
|
||||
id: "PKT123",
|
||||
sender: "TestSender",
|
||||
content: "Test packet",
|
||||
timestamp: Date(),
|
||||
isRelay: false,
|
||||
originalSender: nil,
|
||||
isPrivate: false,
|
||||
recipientNickname: nil,
|
||||
senderPeerID: "TEST123",
|
||||
mentions: nil
|
||||
)
|
||||
|
||||
guard let payload = message.toBinaryPayload() else {
|
||||
XCTFail("Failed to create payload")
|
||||
return
|
||||
}
|
||||
|
||||
let packet = BitchatPacket(
|
||||
type: 0x01,
|
||||
senderID: "TEST123".data(using: .utf8)!,
|
||||
recipientID: nil,
|
||||
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
|
||||
payload: payload,
|
||||
signature: nil,
|
||||
ttl: 3
|
||||
)
|
||||
|
||||
service.simulateIncomingPacket(packet)
|
||||
|
||||
wait(for: [expectation], timeout: 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Mock Delegate Helper
|
||||
|
||||
private class MockBitchatDelegate: BitchatDelegate {
|
||||
private let messageHandler: (BitchatMessage) -> Void
|
||||
|
||||
init(_ handler: @escaping (BitchatMessage) -> Void) {
|
||||
self.messageHandler = handler
|
||||
}
|
||||
|
||||
func didReceiveMessage(_ message: BitchatMessage) {
|
||||
messageHandler(message)
|
||||
}
|
||||
|
||||
func didConnectToPeer(_ peerID: String) {}
|
||||
func didDisconnectFromPeer(_ peerID: String) {}
|
||||
func didUpdatePeerList(_ peers: [String]) {}
|
||||
func isFavorite(fingerprint: String) -> Bool { return false }
|
||||
func didUpdateMessageDeliveryStatus(_ messageID: String, status: DeliveryStatus) {}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// LegacyTestProtocolTypes.swift
|
||||
// bitchatTests
|
||||
//
|
||||
// Minimal legacy protocol types used only by tests to simulate old flows.
|
||||
// These are not part of production code anymore.
|
||||
|
||||
import Foundation
|
||||
|
||||
struct ProtocolNack {
|
||||
let originalPacketID: String
|
||||
let nackID: String
|
||||
let senderID: String
|
||||
let receiverID: String
|
||||
let packetType: UInt8
|
||||
let reason: String
|
||||
let errorCode: UInt8
|
||||
|
||||
enum ErrorCode: UInt8 {
|
||||
case unknown = 0
|
||||
case decryptionFailed = 2
|
||||
}
|
||||
|
||||
init(originalPacketID: String, senderID: String, receiverID: String, packetType: UInt8, reason: String, errorCode: ErrorCode = .unknown) {
|
||||
self.originalPacketID = originalPacketID
|
||||
self.nackID = UUID().uuidString
|
||||
self.senderID = senderID
|
||||
self.receiverID = receiverID
|
||||
self.packetType = packetType
|
||||
self.reason = reason
|
||||
self.errorCode = errorCode.rawValue
|
||||
}
|
||||
|
||||
func toBinaryData() -> Data {
|
||||
// Tests don't parse the payload; return a compact encoding for completeness
|
||||
var data = Data()
|
||||
data.appendUUID(originalPacketID)
|
||||
data.appendUUID(nackID)
|
||||
data.append(UInt8(packetType))
|
||||
data.append(UInt8(errorCode))
|
||||
data.appendString(reason)
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user