mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 23:25:20 +00:00
132 lines
4.5 KiB
Swift
132 lines
4.5 KiB
Swift
//
|
|
// TestNetworkHelper.swift
|
|
// bitchatTests
|
|
//
|
|
// Extracted shared, mutable integration state for nodes and noise sessions.
|
|
// Keeps test containers nonmutating (Swift Testing-friendly).
|
|
//
|
|
|
|
import Foundation
|
|
import CryptoKit
|
|
import Testing
|
|
@testable import BitFoundation // to avoid unnecessary public's
|
|
@testable import bitchat
|
|
|
|
final class TestNetworkHelper {
|
|
// Public, read-only views for tests; mutation only through methods
|
|
var nodes: [String: MockBLEService] = [:]
|
|
var noiseManagers: [String: NoiseSessionManager] = [:]
|
|
let mockKeychain = MockKeychain()
|
|
private let bus = MockBLEBus(autoFloodEnabled: true)
|
|
|
|
// MARK: - Node/Manager management
|
|
|
|
@discardableResult
|
|
func createNode(_ name: String, peerID: PeerID) -> MockBLEService {
|
|
let node = MockBLEService(bus: bus)
|
|
node.myPeerID = peerID
|
|
node.mockNickname = name
|
|
nodes[name] = node
|
|
|
|
// This synchronous helper directly drives all three XX messages and
|
|
// has no transport callback loop for delayed collision recovery.
|
|
let key = Curve25519.KeyAgreement.PrivateKey()
|
|
noiseManagers[name] = NoiseSessionManager(
|
|
localStaticKey: key,
|
|
keychain: mockKeychain,
|
|
recentInitiatorCompletionGracePeriod: 0
|
|
)
|
|
return node
|
|
}
|
|
|
|
// MARK: - Topology
|
|
|
|
func connect(_ a: String, _ b: String) {
|
|
guard let n1 = nodes[a], let n2 = nodes[b] else { return }
|
|
n1.simulateConnectedPeer(n2.peerID)
|
|
n2.simulateConnectedPeer(n1.peerID)
|
|
}
|
|
|
|
func disconnect(_ a: String, _ b: String) {
|
|
guard let n1 = nodes[a], let n2 = nodes[b] else { return }
|
|
n1.simulateDisconnectedPeer(n2.peerID)
|
|
n2.simulateDisconnectedPeer(n1.peerID)
|
|
}
|
|
|
|
func connectFullMesh() {
|
|
let names = Array(nodes.keys)
|
|
for i in 0..<names.count {
|
|
for j in (i+1)..<names.count {
|
|
connect(names[i], names[j])
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Relay
|
|
|
|
func setupRelay(_ nodeName: String, nextHops: [String]) {
|
|
guard let node = nodes[nodeName] else { return }
|
|
node.packetDeliveryHandler = { [weak self] packet in
|
|
guard let self else { return }
|
|
guard packet.ttl > 1 else { return }
|
|
|
|
if let message = BitchatMessage(packet.payload) {
|
|
guard message.senderPeerID != node.peerID else { return }
|
|
|
|
let relayMessage = BitchatMessage(
|
|
id: message.id,
|
|
sender: message.sender,
|
|
content: message.content,
|
|
timestamp: message.timestamp,
|
|
isRelay: true,
|
|
originalSender: message.isRelay ? message.originalSender : message.sender,
|
|
isPrivate: message.isPrivate,
|
|
recipientNickname: message.recipientNickname,
|
|
senderPeerID: message.senderPeerID,
|
|
mentions: message.mentions
|
|
)
|
|
|
|
if let relayPayload = relayMessage.toBinaryPayload() {
|
|
let relayPacket = BitchatPacket(
|
|
type: packet.type,
|
|
senderID: packet.senderID,
|
|
recipientID: packet.recipientID,
|
|
timestamp: packet.timestamp,
|
|
payload: relayPayload,
|
|
signature: packet.signature,
|
|
ttl: packet.ttl - 1
|
|
)
|
|
|
|
for hop in nextHops {
|
|
self.nodes[hop]?.simulateIncomingPacket(relayPacket)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Noise sessions
|
|
|
|
func establishNoiseSession(_ node1: String, _ node2: String) throws {
|
|
guard let manager1 = noiseManagers[node1],
|
|
let manager2 = noiseManagers[node2],
|
|
let peer1ID = nodes[node1]?.peerID,
|
|
let peer2ID = nodes[node2]?.peerID else { return }
|
|
|
|
let msg1 = try manager1.initiateHandshake(with: peer2ID)
|
|
let msg2 = try #require(
|
|
try manager2.handleIncomingHandshake(
|
|
from: peer1ID,
|
|
message: msg1
|
|
)
|
|
)
|
|
let msg3 = try #require(
|
|
try manager1.handleIncomingHandshake(
|
|
from: peer2ID,
|
|
message: msg2
|
|
)
|
|
)
|
|
_ = try manager2.handleIncomingHandshake(from: peer1ID, message: msg3)
|
|
}
|
|
}
|