Files
bitchat/bitchatTests/Services/BLELocalIdentityStateStoreTests.swift
T
jackandGitHub d326cecb63 Fix BLE identity state races (#1428)
Converts BLEAnnounceThrottle to a lock-backed class and moves myPeerID/myPeerIDData/myNickname into a lock-backed BLELocalIdentityStateStore snapshot, so announces can never observe a split peer-ID/wire-ID during panic rotation. Serializes sendAnnounce onto the messageQueue barrier and moves the panic-reset pendingNoiseSessionQueues clear onto collectionsQueue (the queue every other mutation site uses).

Rebased onto main after #1431/#1432; integrates with #1431's panic-suspend structure (guard retained in both the outer sendAnnounce and the deferred worker).
2026-07-26 11:38:16 +02:00

58 lines
1.8 KiB
Swift

import BitFoundation
import Foundation
import Testing
@testable import bitchat
struct BLELocalIdentityStateStoreTests {
@Test
func identityReplacementUpdatesWireBytesAtomically() throws {
let initial = PeerID(str: "0011223344556677")
let replacement = PeerID(str: "8899aabbccddeeff")
let store = BLELocalIdentityStateStore(peerID: initial, nickname: "alice")
store.replacePeerIdentity(with: replacement)
let snapshot = store.snapshot()
#expect(snapshot.peerID == replacement)
#expect(snapshot.peerIDData == Data(hexString: replacement.id))
#expect(snapshot.nickname == "alice")
}
@Test
func concurrentReadsNeverObserveSplitIdentityState() {
let peerIDs = [
PeerID(str: "0011223344556677"),
PeerID(str: "8899aabbccddeeff")
]
let store = BLELocalIdentityStateStore(peerID: peerIDs[0], nickname: "alice")
let failures = LockedFailureRecorder()
DispatchQueue.concurrentPerform(iterations: 2_000) { index in
if index.isMultiple(of: 2) {
store.replacePeerIdentity(with: peerIDs[index % peerIDs.count])
} else {
store.setNickname(index.isMultiple(of: 3) ? "alice" : "bob")
}
let snapshot = store.snapshot()
let expectedWireID = Data(hexString: snapshot.peerID.id) ?? Data()
if snapshot.peerIDData != expectedWireID {
failures.record()
}
}
#expect(!failures.hasFailure)
}
}
private final class LockedFailureRecorder: @unchecked Sendable {
private let lock = NSLock()
private var failed = false
var hasFailure: Bool { lock.withLock { failed } }
func record() {
lock.withLock { failed = true }
}
}