mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 17:25:20 +00:00
* Refactor: move Nostr embedding and TLVs out of BLE; add NostrEmbeddedBitChat, Packets, PeerIDUtils; centralize MessageDeduplicator; update ChatViewModel to use new helpers; remove AI_CONTEXT.md and CLAUDE.md * Rename class to BLEService with compatibility alias; move mention parsing out of BLE; emit low-level BLE events to delegate; unify hex helpers; accept 64-hex in isPeerConnected; add PeerIDResolver * Project: rename file to BLEService.swift and update Xcode project; keep typealias SimplifiedBluetoothService = BLEService for compatibility * Remove SimplifiedBluetoothService alias; update app code to use BLEService explicitly * Tests: rename MockSimplifiedBluetoothService to MockBLEService; update typealiases and Xcode project * Docs: update comments to refer to BLEService (tests, protocol, noise service) * Tests: rename SimplifiedBluetoothServiceTests to BLEServiceTests; update project references and class names * Introduce Transport protocol; BLEService conforms; document delegate-only event pattern in BLEService; keep publishers internal for UnifiedPeerService * Adopt Transport end-to-end: add TransportPeerSnapshot + publishers; BLEService maps to Transport snapshots; UnifiedPeerService consumes Transport; ChatViewModel holds Transport * Fix Transport integration: replace getPeerFingerprint with getFingerprint(for:); update PrivateChatManager and CommandProcessor to use Transport; add BLEService.getFingerprint(for:); update PeerManager to use Transport * Refactor transport and BLE/Nostr layers; unify UI events; fix MainActor isolation - Rename SimplifiedBluetoothService to BLEService and slim responsibilities - Introduce Transport protocol and peerEventsDelegate for UI updates - Add NostrTransport and MessageRouter to route PM/read/favorite via BLE or Nostr - Centralize TLVs, PeerID utils, and MessageDeduplicator outside BLE - Update UnifiedPeerService and ChatViewModel to use Transport and delegate events - Fix MainActor isolation: route delegate calls via Task on MainActor; update notifyUI helper - Adjust related files and tests accordingly * BLEService: remove internal publishers; switch to delegate-only events - Drop legacy messages/peers/fullPeers publishers - Provide lightweight peerSnapshotSubject only to satisfy Transport - Rework publishFullPeerData to build snapshots from internal state and notify delegate + subject - Remove all peersPublisher.send call sites - Keep UnifiedPeerService on delegate updates exclusively * Remove inlined Nostr send helpers from ChatViewModel; route via MessageRouter - Replace direct Nostr sends (PM, ACKs, favorites) with MessageRouter - Add router method for delivery ACKs and implement NostrTransport.sendDeliveryAck - Simplify ChatViewModel favorite notification path to use router - Keep Nostr receive handling intact; reduce duplication * Fix ReadReceipt initializer usage in ChatViewModel (readerID + readerNickname) * Fix unused variable warning: replace shadowed 'nostrPubkey' bind with boolean check in ChatViewModel * Fix queued PM format: use TLV for pending messages after Noise handshake - Pending messages (including first-time favorite notifications) now use the same TLV encoding as normal sends - Ensures ChatViewModel can decode on first send, even if handshake completes after queuing --------- Co-authored-by: jack <jackjackbits@users.noreply.github.com>
283 lines
8.9 KiB
Swift
283 lines
8.9 KiB
Swift
//
|
|
// BLEServiceTests.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 BLEServiceTests: XCTestCase {
|
|
|
|
var service: MockBLEService!
|
|
|
|
override func setUp() {
|
|
super.setUp()
|
|
service = MockBLEService()
|
|
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) {}
|
|
}
|