Convert the remaining tests to Swift Testing (#781)

* SwiftTesting: NoiseProtocolTests + BinaryProtocolPaddingTests

* SwiftTesting: `NotificationStreamAssemblerTests`

* SwiftTesting: `NostrProtocolTests`

* SwiftTesting: `BinaryProtocolTests`

* SwiftTesting: `PeerIDTests`

* SwiftTesting: `BLEServiceTests`

* SwiftTesting: `CommandProcessorTests`

* SwiftTesting: `GCSFilterTests`

* SwiftTesting: `GeohashBookmarksStoreTests`

* Remove `peerID` test constants

* Remove PeerID + String interop from tests

* Refactor IntegrationTests to extract state management

* Refactor global state management of MockBLEService

* NoiseProtocolSwiftTests: `actor` -> `struct`

* Remove measurement tests w/ no benchmark

* `NoiseProtocolSwiftTests` -> `NoiseProtocolTests`

* SwiftTesting: `LocationChannelsTests`

* SwiftTesting: `GossipSyncManagerTests`

* SwiftTesting: `LocationNotesManagerTests`

* Global `sleep` function for tests

* SwiftTesting: `IntegrationTests`

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
This commit is contained in:
Islam
2025-10-15 00:33:35 +01:00
committed by islam
co-authored by jack
parent 790dcda8e5
commit 5267489fa2
24 changed files with 1504 additions and 1707 deletions
+120 -209
View File
@@ -6,119 +6,89 @@
// For more information, see <https://unlicense.org>
//
import XCTest
import Testing
import Foundation
@testable import bitchat
final class BinaryProtocolTests: XCTestCase {
struct BinaryProtocolTests {
// MARK: - Basic Encoding/Decoding Tests
func testBasicPacketEncodingDecoding() throws {
@Test func basicPacketEncodingDecoding() throws {
let originalPacket = TestHelpers.createTestPacket()
// Encode
guard let encodedData = BinaryProtocol.encode(originalPacket) else {
XCTFail("Failed to encode packet")
return
}
// Decode
guard let decodedPacket = BinaryProtocol.decode(encodedData) else {
XCTFail("Failed to decode packet")
return
}
let encodedData = try #require(BinaryProtocol.encode(originalPacket), "Failed to encode packet")
let decodedPacket = try #require(BinaryProtocol.decode(encodedData), "Failed to decode packet")
// Verify
XCTAssertEqual(decodedPacket.type, originalPacket.type)
XCTAssertEqual(decodedPacket.ttl, originalPacket.ttl)
XCTAssertEqual(decodedPacket.timestamp, originalPacket.timestamp)
XCTAssertEqual(decodedPacket.payload, originalPacket.payload)
#expect(decodedPacket.type == originalPacket.type)
#expect(decodedPacket.ttl == originalPacket.ttl)
#expect(decodedPacket.timestamp == originalPacket.timestamp)
#expect(decodedPacket.payload == originalPacket.payload)
// Sender ID should match (accounting for padding)
let originalSenderID = originalPacket.senderID.prefix(BinaryProtocol.senderIDSize)
let decodedSenderID = decodedPacket.senderID.trimmingNullBytes()
XCTAssertEqual(decodedSenderID, originalSenderID)
#expect(decodedSenderID == originalSenderID)
}
func testPacketWithRecipient() throws {
let recipientID = TestConstants.testPeerID2
@Test func packetWithRecipient() throws {
let recipientID = PeerID(str: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789")
let packet = TestHelpers.createTestPacket(recipientID: recipientID)
// Encode and decode
guard let encodedData = BinaryProtocol.encode(packet),
let decodedPacket = BinaryProtocol.decode(encodedData) else {
XCTFail("Failed to encode/decode packet with recipient")
return
}
let encodedData = try #require(BinaryProtocol.encode(packet), "Failed to encode packet with recipient")
let decodedPacket = try #require(BinaryProtocol.decode(encodedData), "Failed to decode packet with recipient")
// Verify recipient
XCTAssertNotNil(decodedPacket.recipientID)
#expect(decodedPacket.recipientID != nil)
let decodedRecipientID = decodedPacket.recipientID?.trimmingNullBytes()
XCTAssertTrue(String(data: decodedRecipientID!, encoding: .utf8) == recipientID)
// TODO: Check if this is intended that the decoding only gets the first 8
#expect(String(data: decodedRecipientID!, encoding: .utf8) == "abcdef01")
}
func testPacketWithSignature() throws {
let packet = TestHelpers.createTestPacket(
signature: TestConstants.testSignature
)
// Encode and decode
guard let encodedData = BinaryProtocol.encode(packet),
let decodedPacket = BinaryProtocol.decode(encodedData) else {
XCTFail("Failed to encode/decode packet with signature")
return
}
@Test func packetWithSignature() throws {
let packet = TestHelpers.createTestPacket(signature: TestConstants.testSignature)
let encodedData = try #require(BinaryProtocol.encode(packet), "Failed to encode packet with signature")
let decodedPacket = try #require(BinaryProtocol.decode(encodedData), "Failed to decode packet with signature")
// Verify signature
XCTAssertNotNil(decodedPacket.signature)
XCTAssertEqual(decodedPacket.signature, TestConstants.testSignature)
#expect(decodedPacket.signature != nil)
#expect(decodedPacket.signature == TestConstants.testSignature)
}
// MARK: - Compression Tests
func testPayloadCompression() throws {
// Create a large, compressible payload above current threshold (2048B)
@Test("Create a large, compressible payload above current threshold (2048B)")
func payloadCompression() throws {
let repeatedString = String(repeating: "This is a test message. ", count: 200)
let largePayload = repeatedString.data(using: .utf8)!
let packet = TestHelpers.createTestPacket(payload: largePayload)
// Encode (should compress)
guard let encodedData = BinaryProtocol.encode(packet) else {
XCTFail("Failed to encode packet with large payload")
return
}
let encodedData = try #require(BinaryProtocol.encode(packet), "Failed to encode packet with large payload")
// The encoded size should be smaller than uncompressed due to compression
let uncompressedSize = BinaryProtocol.headerSize + BinaryProtocol.senderIDSize + largePayload.count
XCTAssertLessThan(encodedData.count, uncompressedSize)
#expect(encodedData.count < uncompressedSize)
// Decode and verify
guard let decodedPacket = BinaryProtocol.decode(encodedData) else {
XCTFail("Failed to decode compressed packet")
return
}
let decodedPacket = try #require(BinaryProtocol.decode(encodedData), "Failed to decode compressed packet")
XCTAssertEqual(decodedPacket.payload, largePayload)
#expect(decodedPacket.payload == largePayload)
}
func testSmallPayloadNoCompression() throws {
// Small payloads should not be compressed
@Test("Small payloads should not be compressed")
func smallPayloadNoCompression() throws {
let smallPayload = "Hi".data(using: .utf8)!
let packet = TestHelpers.createTestPacket(payload: smallPayload)
guard let encodedData = BinaryProtocol.encode(packet),
let decodedPacket = BinaryProtocol.decode(encodedData) else {
XCTFail("Failed to encode/decode small packet")
return
}
XCTAssertEqual(decodedPacket.payload, smallPayload)
let encodedData = try #require(BinaryProtocol.encode(packet), "Failed to encode small packet")
let decodedPacket = try #require(BinaryProtocol.decode(encodedData), "Failed to decode small packet")
#expect(decodedPacket.payload == smallPayload)
}
// MARK: - Message Padding Tests
func testMessagePadding() throws {
@Test func messagePadding() throws {
let payloads = [
"Short",
String(repeating: "Medium length message content ", count: 10), // ~300 bytes
@@ -130,43 +100,32 @@ final class BinaryProtocolTests: XCTestCase {
for payload in payloads {
let packet = TestHelpers.createTestPacket(payload: payload.data(using: .utf8)!)
guard let encodedData = BinaryProtocol.encode(packet) else {
XCTFail("Failed to encode packet")
continue
}
let encodedData = try #require(BinaryProtocol.encode(packet), "Failed to encode packet")
// Verify padding creates standard block sizes up to configured limit (no 4096 bucket currently)
let blockSizes = [256, 512, 1024, 2048]
if encodedData.count <= 2048 {
XCTAssertTrue(blockSizes.contains(encodedData.count), "Encoded size \(encodedData.count) is not a standard block size")
#expect(blockSizes.contains(encodedData.count), "Encoded size \(encodedData.count) is not a standard block size")
} else {
// For very large payloads we expect no additional padding beyond raw size
XCTAssertGreaterThan(encodedData.count, 2048)
#expect(encodedData.count > 2048)
}
encodedSizes.insert(encodedData.count)
// Verify decoding works
guard let decodedPacket = BinaryProtocol.decode(encodedData) else {
XCTFail("Failed to decode padded packet")
continue
}
XCTAssertEqual(String(data: decodedPacket.payload, encoding: .utf8), payload)
let decodedPacket = try #require(BinaryProtocol.decode(encodedData), "Failed to decode padded packet")
#expect(String(data: decodedPacket.payload, encoding: .utf8) == payload)
}
// Different payload sizes (within <=2048) may map to the same bucket depending on compression.
// Require at least one padded size to be present.
XCTAssertGreaterThanOrEqual(encodedSizes.filter { $0 <= 2048 }.count, 1, "Expected at least one padded size up to 2048, got \(encodedSizes)")
#expect(encodedSizes.filter { $0 <= 2048 }.count >= 1, "Expected at least one padded size up to 2048, got \(encodedSizes)")
}
func testInvalidPKCS7PaddingIsRejected() throws {
@Test func invalidPKCS7PaddingIsRejected() throws {
let pkt = TestHelpers.createTestPacket(payload: Data(repeating: 0x41, count: 50)) // small
guard let enc0 = BinaryProtocol.encode(pkt) else {
XCTFail("encode failed")
return
}
let enc0 = try #require(BinaryProtocol.encode(pkt), "encode failed")
// Force padding to known block for test stability
var enc = MessagePadding.pad(enc0, toSize: 256)
let unpadded = MessagePadding.unpad(enc)
@@ -177,39 +136,33 @@ final class BinaryProtocolTests: XCTestCase {
let maybe = BinaryProtocol.decode(enc)
// If decode still succeeds (nested pad edge case), at least ensure payload integrity
if let pkt2 = maybe {
XCTAssertEqual(pkt2.payload, pkt.payload)
#expect(pkt2.payload == pkt.payload)
} else {
XCTAssertNil(maybe)
#expect(maybe == nil)
}
} else {
// If no padding was applied, just assert decode succeeds (nothing to test)
XCTAssertNotNil(BinaryProtocol.decode(enc))
#expect(BinaryProtocol.decode(enc) != nil)
}
}
// MARK: - Message Encoding/Decoding Tests
func testMessageEncodingDecoding() throws {
@Test func messageEncodingDecoding() throws {
let message = TestHelpers.createTestMessage()
guard let payload = message.toBinaryPayload() else {
XCTFail("Failed to encode message to binary")
return
}
let payload = try #require(message.toBinaryPayload(), "Failed to encode message to binary")
guard let decodedMessage = BitchatMessage(payload) else {
XCTFail("Failed to decode message from binary")
return
}
let decodedMessage = try #require(BitchatMessage(payload), "Failed to decode message from binary")
XCTAssertEqual(decodedMessage.content, message.content)
XCTAssertEqual(decodedMessage.sender, message.sender)
XCTAssertEqual(decodedMessage.senderPeerID, message.senderPeerID)
XCTAssertEqual(decodedMessage.isPrivate, message.isPrivate)
#expect(decodedMessage.content == message.content)
#expect(decodedMessage.sender == message.sender)
#expect(decodedMessage.senderPeerID == message.senderPeerID)
#expect(decodedMessage.isPrivate == message.isPrivate)
// Timestamp should be close (within 1 second due to conversion)
let timeDiff = abs(decodedMessage.timestamp.timeIntervalSince(message.timestamp))
XCTAssertLessThan(timeDiff, 1.0)
#expect(timeDiff < 1)
}
func testPrivateMessageEncoding() throws {
@@ -218,30 +171,22 @@ final class BinaryProtocolTests: XCTestCase {
recipientNickname: TestConstants.testNickname2
)
guard let payload = message.toBinaryPayload(),
let decodedMessage = BitchatMessage(payload) else {
XCTFail("Failed to encode/decode private message")
return
}
let payload = try #require(message.toBinaryPayload(), "Failed to encode private message")
let decodedMessage = try #require(BitchatMessage(payload), "Failed to decode private message")
XCTAssertTrue(decodedMessage.isPrivate)
XCTAssertEqual(decodedMessage.recipientNickname, TestConstants.testNickname2)
#expect(decodedMessage.isPrivate)
#expect(decodedMessage.recipientNickname == TestConstants.testNickname2)
}
func testMessageWithMentions() throws {
@Test func messageWithMentions() throws {
let mentions = [TestConstants.testNickname2, TestConstants.testNickname3]
let message = TestHelpers.createTestMessage(mentions: mentions)
guard let payload = message.toBinaryPayload(),
let decodedMessage = BitchatMessage(payload) else {
XCTFail("Failed to encode/decode message with mentions")
return
}
XCTAssertEqual(decodedMessage.mentions, mentions)
let payload = try #require(message.toBinaryPayload(), "Failed to encode message with mentions")
let decodedMessage = try #require(BitchatMessage(payload), "Failed to decode message with mentions")
#expect(decodedMessage.mentions == mentions)
}
func testRelayMessageEncoding() throws {
@Test func relayMessageEncoding() throws {
let message = BitchatMessage(
id: UUID().uuidString,
sender: TestConstants.testNickname1,
@@ -251,105 +196,77 @@ final class BinaryProtocolTests: XCTestCase {
originalSender: TestConstants.testNickname3,
isPrivate: false,
recipientNickname: nil,
senderPeerID: TestConstants.testPeerID1,
mentions: nil
)
guard let payload = message.toBinaryPayload(),
let decodedMessage = BitchatMessage(payload) else {
XCTFail("Failed to encode/decode relay message")
return
}
XCTAssertTrue(decodedMessage.isRelay)
XCTAssertEqual(decodedMessage.originalSender, TestConstants.testNickname3)
let payload = try #require(message.toBinaryPayload(), "Failed to encode relay message")
let decodedMessage = try #require(BitchatMessage(payload), "Failed to decode relay message")
#expect(decodedMessage.isRelay)
#expect(decodedMessage.originalSender == TestConstants.testNickname3)
}
// MARK: - Edge Cases and Error Handling
func testInvalidDataDecoding() {
// Too small data
@Test("Too small data")
func invalidDataDecoding() throws {
let tooSmall = Data(repeating: 0, count: 5)
XCTAssertNil(BinaryProtocol.decode(tooSmall))
#expect(BinaryProtocol.decode(tooSmall) == nil)
// Random data
let random = TestHelpers.generateRandomData(length: 100)
XCTAssertNil(BinaryProtocol.decode(random))
#expect(BinaryProtocol.decode(random) == nil)
// Corrupted header
let packet = TestHelpers.createTestPacket()
guard var encoded = BinaryProtocol.encode(packet) else {
XCTFail("Failed to encode test packet")
return
}
var encoded = try #require(BinaryProtocol.encode(packet), "Failed to encode test packet")
// Corrupt the version byte
encoded[0] = 0xFF
XCTAssertNil(BinaryProtocol.decode(encoded))
#expect(BinaryProtocol.decode(encoded) == nil)
}
func testLargeMessageHandling() throws {
// Test maximum size handling
@Test("Test maximum size handling")
func largeMessageHandling() throws {
let largeContent = String(repeating: "X", count: 65535) // Max uint16
let message = TestHelpers.createTestMessage(content: largeContent)
guard let payload = message.toBinaryPayload(),
let decodedMessage = BitchatMessage(payload) else {
XCTFail("Failed to handle large message")
return
}
XCTAssertEqual(decodedMessage.content, largeContent)
let payload = try #require(message.toBinaryPayload(), "Failed to handle large message")
let decodedMessage = try #require(BitchatMessage(payload), "Failed to handle large message")
#expect(decodedMessage.content == largeContent)
}
func testEmptyFieldsHandling() throws {
// Test message with empty content
@Test("Test message with empty content")
func emptyFieldsHandling() throws {
let emptyMessage = TestHelpers.createTestMessage(content: "")
guard let payload = emptyMessage.toBinaryPayload(),
let decodedMessage = BitchatMessage(payload) else {
XCTFail("Failed to handle empty message")
return
}
XCTAssertEqual(decodedMessage.content, "")
let payload = try #require(emptyMessage.toBinaryPayload(), "Failed to handle empty message")
let decodedMessage = try #require(BitchatMessage(payload), "Failed to handle empty message")
#expect(decodedMessage.content.isEmpty)
}
// MARK: - Protocol Version Tests
func testProtocolVersionHandling() throws {
// Test with supported version (version is always 1 in init)
@Test("Test with supported version (version is always 1 in init)")
func protocolVersionHandling() throws {
let packet = TestHelpers.createTestPacket()
guard let encoded = BinaryProtocol.encode(packet),
let decoded = BinaryProtocol.decode(encoded) else {
XCTFail("Failed to encode/decode packet with version")
return
}
XCTAssertEqual(decoded.version, 1)
let encoded = try #require(BinaryProtocol.encode(packet), "Failed to encode packet with version")
let decoded = try #require(BinaryProtocol.decode(encoded), "Failed to decode packet with version")
#expect(decoded.version == 1)
}
func testUnsupportedProtocolVersion() throws {
// Create packet data with unsupported version
@Test("Create packet data with unsupported version")
func unsupportedProtocolVersion() throws {
let packet = TestHelpers.createTestPacket()
guard var encoded = BinaryProtocol.encode(packet) else {
XCTFail("Failed to encode packet")
return
}
var encoded = try #require(BinaryProtocol.encode(packet), "Failed to encode packet")
// Manually change version byte to unsupported value
encoded[0] = 99 // Unsupported version
// Should fail to decode
XCTAssertNil(BinaryProtocol.decode(encoded))
#expect(BinaryProtocol.decode(encoded) == nil)
}
// MARK: - Bounds Checking Tests (Crash Prevention)
func testMalformedPacketWithInvalidPayloadLength() throws {
// Test the specific crash scenario: payloadLength = 193 (0xc1) but only 30 bytes available
@Test("Test the specific crash scenario: payloadLength = 193 (0xc1) but only 30 bytes available")
func malformedPacketWithInvalidPayloadLength() throws {
var malformedData = Data()
// Valid header (13 bytes)
@@ -379,20 +296,17 @@ final class BinaryProtocolTests: XCTestCase {
}
// Total data is now 30 bytes, but payloadLength claims 193
XCTAssertEqual(malformedData.count, 30)
#expect(malformedData.count == 30)
// This should not crash - should return nil gracefully
let result = BinaryProtocol.decode(malformedData)
XCTAssertNil(result, "Malformed packet with invalid payload length should return nil, not crash")
#expect(result == nil, "Malformed packet with invalid payload length should return nil, not crash")
}
func testTruncatedPacketHandling() throws {
// Test various truncation scenarios
@Test("Test various truncation scenarios")
func truncatedPacketHandling() throws {
let packet = TestHelpers.createTestPacket()
guard let validEncoded = BinaryProtocol.encode(packet) else {
XCTFail("Failed to encode test packet")
return
}
let validEncoded = try #require(BinaryProtocol.encode(packet), "Failed to encode test packet")
// Test truncation at various points
let truncationPoints = [0, 5, 10, 15, 20, 25]
@@ -400,12 +314,12 @@ final class BinaryProtocolTests: XCTestCase {
for point in truncationPoints {
let truncated = validEncoded.prefix(point)
let result = BinaryProtocol.decode(truncated)
XCTAssertNil(result, "Truncated packet at \(point) bytes should return nil, not crash")
#expect(result == nil, "Truncated packet at \(point) bytes should return nil, not crash")
}
}
func testMalformedCompressedPacket() throws {
// Test compressed packet with invalid original size
@Test("Test compressed packet with invalid original size")
func malformedCompressedPacket() throws {
var malformedData = Data()
// Valid header
@@ -434,11 +348,11 @@ final class BinaryProtocolTests: XCTestCase {
// Should handle this gracefully
let result = BinaryProtocol.decode(malformedData)
XCTAssertNil(result, "Malformed compressed packet should return nil, not crash")
#expect(result == nil, "Malformed compressed packet should return nil, not crash")
}
func testExcessivelyLargePayloadLength() throws {
// Test packet claiming extremely large payload
@Test("Test packet claiming extremely large payload")
func excessivelyLargePayloadLength() throws {
var malformedData = Data()
// Valid header
@@ -467,11 +381,11 @@ final class BinaryProtocolTests: XCTestCase {
// Should handle this gracefully without trying to allocate massive amounts of memory
let result = BinaryProtocol.decode(malformedData)
XCTAssertNil(result, "Packet with excessive payload length should return nil, not crash")
#expect(result == nil, "Packet with excessive payload length should return nil, not crash")
}
func testCompressedPacketWithInvalidOriginalSize() throws {
// Test compressed packet with unreasonable original size
@Test("Test compressed packet with unreasonable original size")
func compressedPacketWithInvalidOriginalSize() throws {
var malformedData = Data()
// Valid header
@@ -509,11 +423,11 @@ final class BinaryProtocolTests: XCTestCase {
}
let result = BinaryProtocol.decode(malformedData)
XCTAssertNil(result, "Compressed packet with invalid original size should return nil, not crash")
#expect(result == nil, "Compressed packet with invalid original size should return nil, not crash")
}
func testMaliciousPacketWithIntegerOverflow() throws {
// Test packet designed to cause integer overflow
@Test("Test packet designed to cause integer overflow")
func maliciousPacketWithIntegerOverflow() throws {
var maliciousData = Data()
// Valid header
@@ -548,27 +462,24 @@ final class BinaryProtocolTests: XCTestCase {
// Should handle gracefully without integer overflow issues
let result = BinaryProtocol.decode(maliciousData)
XCTAssertNil(result, "Malicious packet designed for integer overflow should return nil, not crash")
#expect(result == nil, "Malicious packet designed for integer overflow should return nil, not crash")
}
func testPartialHeaderData() throws {
// Test packets with incomplete headers
@Test("Test packets with incomplete headers")
func partialHeaderData() throws {
let headerSizes = [0, 1, 5, 10, 12] // Various incomplete header sizes
for size in headerSizes {
let partialData = Data(repeating: 0x01, count: size)
let result = BinaryProtocol.decode(partialData)
XCTAssertNil(result, "Partial header data (\(size) bytes) should return nil, not crash")
#expect(result == nil, "Partial header data (\(size) bytes) should return nil, not crash")
}
}
func testBoundaryConditions() throws {
// Test exact boundary conditions
@Test("Test exact boundary conditions")
func boundaryConditions() throws {
let packet = TestHelpers.createTestPacket()
guard let validEncoded = BinaryProtocol.encode(packet) else {
XCTFail("Failed to encode test packet")
return
}
let validEncoded = try #require(BinaryProtocol.encode(packet), "Failed to encode test packet")
// If truncation only removes padding, decode may still succeed. Compute unpadded size.
let unpadded = MessagePadding.unpad(validEncoded)
@@ -576,7 +487,7 @@ final class BinaryProtocolTests: XCTestCase {
let cut = max(1, unpadded.count - 10)
let truncatedCore = unpadded.prefix(cut)
let result = BinaryProtocol.decode(truncatedCore)
XCTAssertNil(result, "Truncated core frame should return nil, not crash")
#expect(result == nil, "Truncated core frame should return nil, not crash")
// Test minimum valid size - create a valid minimal packet
var minData = Data()