mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-24 22:45:19 +00:00
Periphery 3.7.4 audit of both schemes (macOS + iOS, intersected so platform-specific code is never touched), with test targets indexed and the share extension built. 277 dead declarations removed or demoted: dead forwarding wrappers (ChatViewModel+Nostr/+PrivateChat), removed- feature remnants (autocomplete command suggestions, back-swipe tuning, MediaSendError, GeohashParticipantTracker), unused Tor dormancy bindings, assign-only properties, unused parameters (renamed to _), and redundant public accessibility. 13 orphaned localization keys deleted across all 29 locales (old pre-#1392 location-notes UI, app_info warnings). Two real tests were flagged as unused because they never ran: Swift Testing methods missing @Test (NostrProtocolTests. testAckRoundTripNIP44V2_Delivered, NotificationStreamAssemblerTests. testAssemblesCompressedLargeFrame). Re-armed both; they pass. Deliberately kept, now recorded in .periphery.baseline.json: iOS-only code invisible to the CI macOS scan, C FFI signatures, keep-alive NWPathMonitor reference, InboundEventKey.eventID (dedup semantics), wifiBulk capability bit (reserved for Wi-Fi bulk work, used by BitFoundation package tests), and the String secureClear cluster (exercised by package tests). New: .periphery.yml config and an advisory Dead Code CI job (mirrors the SwiftLint precedent from #1361) that fails on findings not in the committed baseline. Verified: full macOS app suite, BitFoundation (119) and BitLogger (13) package tests green; periphery scan --strict exits clean. Co-authored-by: jack <jackjackbits@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
272 lines
9.5 KiB
Swift
272 lines
9.5 KiB
Swift
//
|
|
// MockBLEService.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 BitFoundation // to avoid unnecessary public's
|
|
@testable import bitchat
|
|
|
|
/// In-memory BLE test harness used by E2E/Integration tests.
|
|
///
|
|
/// Design:
|
|
/// - Global `registry` maps `peerID` -> service instance, and `adjacency` tracks
|
|
/// simulated connections between peers. Tests call `simulateConnectedPeer` /
|
|
/// `simulateDisconnectedPeer` to manage topology.
|
|
/// - `resetTestBus()` clears global state and is called in test `setUp()`.
|
|
/// - `messageDeliveryHandler` and `packetDeliveryHandler` let tests observe messages/packets
|
|
/// as they flow, enabling scenarios like manual encryption/relay.
|
|
/// - A thread-safe `seenMessageIDs` set prevents double-delivery races during flooding.
|
|
///
|
|
/// Flooding:
|
|
/// - `autoFloodEnabled` is disabled by default; Integration tests enable it in `setUp()` to
|
|
/// simulate broadcast propagation across the mesh. E2E tests keep it off and perform explicit
|
|
/// relays when needed.
|
|
final class MockBLEService: NSObject {
|
|
private let bus: MockBLEBus
|
|
|
|
// MARK: - Properties matching BLEService
|
|
|
|
weak var delegate: BitchatDelegate?
|
|
var myPeerID = PeerID(str: "MOCK1234")
|
|
var myNickname: String = "MockUser"
|
|
|
|
// Test-specific properties
|
|
var sentMessages: [(message: BitchatMessage, packet: BitchatPacket)] = []
|
|
var sentPackets: [BitchatPacket] = []
|
|
var connectedPeers: Set<PeerID> = []
|
|
var messageDeliveryHandler: ((BitchatMessage) -> Void)?
|
|
var packetDeliveryHandler: ((BitchatPacket) -> Void)?
|
|
|
|
// Compatibility properties for old tests
|
|
var mockNickname: String {
|
|
get { return myNickname }
|
|
set { myNickname = newValue }
|
|
}
|
|
|
|
var peerID: PeerID {
|
|
return myPeerID
|
|
}
|
|
|
|
// MARK: - Initialization
|
|
|
|
init(bus: MockBLEBus) {
|
|
self.bus = bus
|
|
}
|
|
|
|
// MARK: - In-memory test bus (for E2E/Integration)
|
|
|
|
/// Registers this instance on first use.
|
|
private func registerIfNeeded() {
|
|
bus.register(self, for: myPeerID)
|
|
}
|
|
|
|
/// Returns adjacent neighbors based on the current simulated topology.
|
|
private func neighbors() -> [MockBLEService] {
|
|
bus.neighbors(of: myPeerID)
|
|
}
|
|
|
|
func startServices() {
|
|
// Mock implementation - do nothing
|
|
}
|
|
|
|
func stopServices() {
|
|
// Mock implementation - do nothing
|
|
}
|
|
|
|
func isPeerConnected(_ peerID: PeerID) -> Bool {
|
|
return connectedPeers.contains(peerID)
|
|
}
|
|
|
|
func getPeerNicknames() -> [PeerID: String] {
|
|
var nicknames: [PeerID: String] = [:]
|
|
for peer in connectedPeers {
|
|
nicknames[peer] = "MockPeer_\(peer)"
|
|
}
|
|
return nicknames
|
|
}
|
|
|
|
/// Keep local echo synchronous so Swift Testing confirmations observe it deterministically.
|
|
private func deliverLocalEcho(_ message: BitchatMessage) {
|
|
delegate?.didReceiveMessage(message)
|
|
}
|
|
|
|
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.id.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)
|
|
|
|
deliverLocalEcho(message)
|
|
|
|
// Surface raw packet to tests that intercept/relay/encrypt
|
|
packetDeliveryHandler?(packet)
|
|
|
|
// Deliver public messages to adjacent peers via bus
|
|
if recipientID == nil {
|
|
for neighbor in neighbors() {
|
|
neighbor.simulateIncomingPacket(packet)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func sendPrivateMessage(_ content: String, to recipientPeerID: PeerID, 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.id.data(using: .utf8)!,
|
|
recipientID: recipientPeerID.id.data(using: .utf8)!,
|
|
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
|
|
payload: payload,
|
|
signature: nil,
|
|
ttl: 3
|
|
)
|
|
|
|
sentMessages.append((message, packet))
|
|
sentPackets.append(packet)
|
|
|
|
deliverLocalEcho(message)
|
|
|
|
// Surface raw packet to tests that intercept/relay/encrypt
|
|
packetDeliveryHandler?(packet)
|
|
|
|
// If directly connected to recipient, deliver only to them.
|
|
if bus.isDirectNeighbor(myPeerID, recipientPeerID),
|
|
let target = bus.service(for: recipientPeerID) {
|
|
target.simulateIncomingPacket(packet)
|
|
} else {
|
|
// Not directly connected: deliver to neighbors for relay
|
|
for neighbor in neighbors() where neighbor.peerID != recipientPeerID {
|
|
neighbor.simulateIncomingPacket(packet)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Test Helper Methods
|
|
|
|
func simulateConnectedPeer(_ peerID: PeerID) {
|
|
registerIfNeeded()
|
|
bus.connect(myPeerID, peerID)
|
|
connectedPeers.insert(peerID)
|
|
delegate?.didConnectToPeer(peerID)
|
|
delegate?.didUpdatePeerList(Array(connectedPeers))
|
|
}
|
|
|
|
func simulateDisconnectedPeer(_ peerID: PeerID) {
|
|
bus.disconnect(myPeerID, peerID)
|
|
connectedPeers.remove(peerID)
|
|
delegate?.didDisconnectFromPeer(peerID)
|
|
delegate?.didUpdatePeerList(Array(connectedPeers))
|
|
}
|
|
|
|
func simulateIncomingMessage(_ message: BitchatMessage) {
|
|
delegate?.didReceiveMessage(message)
|
|
// Also surface via test handler for E2E/Integration
|
|
messageDeliveryHandler?(message)
|
|
}
|
|
|
|
private var seenMessageIDs: Set<String> = []
|
|
private let seenLock = NSLock()
|
|
|
|
func simulateIncomingPacket(_ packet: BitchatPacket) {
|
|
// Process through the actual handling logic
|
|
if let message = BitchatMessage(packet.payload) {
|
|
var shouldDeliver = false
|
|
seenLock.lock()
|
|
if !seenMessageIDs.contains(message.id) {
|
|
seenMessageIDs.insert(message.id)
|
|
shouldDeliver = true
|
|
}
|
|
seenLock.unlock()
|
|
if shouldDeliver {
|
|
delegate?.didReceiveMessage(message)
|
|
// Also surface via test handler for E2E/Integration
|
|
messageDeliveryHandler?(message)
|
|
// Optional flooding for integration-style broadcast tests.
|
|
// When enabled, propagate a public broadcast across the entire connected
|
|
// component regardless of the original TTL to better emulate large-network
|
|
// broadcast expectations. De-duplication via seenMessageIDs prevents loops.
|
|
if bus.autoFloodEnabled,
|
|
packet.recipientID == nil,
|
|
!message.isPrivate {
|
|
let nextTTL = packet.ttl > 0 ? packet.ttl - 1 : 0
|
|
for neighbor in neighbors() {
|
|
// Avoid immediate echo loopback to sender if known
|
|
if let sender = message.senderPeerID, sender == neighbor.peerID { continue }
|
|
var relay = packet
|
|
relay.ttl = nextTTL
|
|
neighbor.simulateIncomingPacket(relay)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
packetDeliveryHandler?(packet)
|
|
}
|
|
|
|
func getConnectedPeers() -> [PeerID] {
|
|
return Array(connectedPeers)
|
|
}
|
|
|
|
// MARK: - Compatibility methods for old tests
|
|
|
|
func sendPrivateMessage(_ content: String, to recipientPeerID: PeerID, recipientNickname: String, messageID: String? = nil) {
|
|
sendPrivateMessage(content, to: recipientPeerID, recipientNickname: recipientNickname, messageID: messageID ?? UUID().uuidString)
|
|
}
|
|
}
|
|
|
|
// MARK: - Helpers
|
|
|
|
extension MockBLEService {
|
|
convenience init(peerID: PeerID, nickname: String, bus: MockBLEBus) {
|
|
self.init(bus: bus)
|
|
myPeerID = peerID
|
|
mockNickname = nickname
|
|
}
|
|
|
|
func simulateConnection(with otherPeer: MockBLEService) {
|
|
simulateConnectedPeer(otherPeer.myPeerID)
|
|
otherPeer.simulateConnectedPeer(myPeerID)
|
|
}
|
|
}
|