Add comprehensive test suite for bitchat

- Created test utilities and helpers for common test operations
- Implemented Binary Protocol tests covering encoding/decoding, compression, and padding
- Added Noise Protocol tests for handshake, encryption, and session management
- Created Public Chat E2E tests for broadcasting, routing, TTL, and mesh topologies
- Implemented Private Chat E2E tests for direct messaging, delivery ACKs, and retry logic
- Added Integration tests for multi-peer scenarios, network resilience, and mixed traffic patterns
- Created mock implementations for BluetoothMeshService and NoiseSession

Test coverage includes:
- Protocol layer (binary encoding, message serialization)
- Security layer (Noise handshake, encryption/decryption)
- Application layer (public/private messaging, delivery tracking)
- Network scenarios (mesh topology, partitions, churn)
- Performance and stress testing
This commit is contained in:
jack
2025-07-23 08:56:13 +02:00
parent fd0ef35487
commit 96136ec364
10 changed files with 2670 additions and 0 deletions
@@ -0,0 +1,33 @@
//
// TestConstants.swift
// bitchatTests
//
// This is free and unencumbered software released into the public domain.
// For more information, see <https://unlicense.org>
//
import Foundation
struct TestConstants {
static let defaultTimeout: TimeInterval = 5.0
static let shortTimeout: TimeInterval = 1.0
static let longTimeout: TimeInterval = 10.0
static let testPeerID1 = "PEER1234"
static let testPeerID2 = "PEER5678"
static let testPeerID3 = "PEER9012"
static let testPeerID4 = "PEER3456"
static let testNickname1 = "Alice"
static let testNickname2 = "Bob"
static let testNickname3 = "Charlie"
static let testNickname4 = "David"
static let testMessage1 = "Hello, World!"
static let testMessage2 = "How are you?"
static let testMessage3 = "This is a test message"
static let testLongMessage = String(repeating: "This is a long message. ", count: 100)
static let testRoomID = "test-room"
static let testSignature = Data(repeating: 0xAB, count: 64)
}
@@ -0,0 +1,126 @@
//
// TestHelpers.swift
// bitchatTests
//
// This is free and unencumbered software released into the public domain.
// For more information, see <https://unlicense.org>
//
import Foundation
import CryptoKit
@testable import bitchat
class TestHelpers {
// MARK: - Key Generation
static func generateTestKeyPair() -> (privateKey: Curve25519.KeyAgreement.PrivateKey, publicKey: Curve25519.KeyAgreement.PublicKey) {
let privateKey = Curve25519.KeyAgreement.PrivateKey()
let publicKey = privateKey.publicKey
return (privateKey, publicKey)
}
static func generateTestIdentity(peerID: String, nickname: String) -> UserProfile {
let (privateKey, publicKey) = generateTestKeyPair()
return UserProfile(
nickname: nickname,
peerID: peerID,
publicKey: publicKey.rawRepresentation,
privateKey: privateKey.rawRepresentation
)
}
// MARK: - Message Creation
static func createTestMessage(
content: String = TestConstants.testMessage1,
sender: String = TestConstants.testNickname1,
senderPeerID: String = TestConstants.testPeerID1,
isPrivate: Bool = false,
recipientNickname: String? = nil,
mentions: [String]? = nil
) -> BitchatMessage {
return BitchatMessage(
id: UUID().uuidString,
sender: sender,
content: content,
timestamp: Date(),
isRelay: false,
originalSender: nil,
isPrivate: isPrivate,
recipientNickname: recipientNickname,
senderPeerID: senderPeerID,
mentions: mentions
)
}
static func createTestPacket(
type: UInt8 = 0x01,
senderID: String = TestConstants.testPeerID1,
recipientID: String? = nil,
payload: Data = "test payload".data(using: .utf8)!,
ttl: UInt8 = 3
) -> BitchatPacket {
return BitchatPacket(
type: type,
senderID: senderID.data(using: .utf8)!,
recipientID: recipientID?.data(using: .utf8),
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
payload: payload,
signature: nil,
ttl: ttl
)
}
// MARK: - Data Generation
static func generateRandomData(length: Int) -> Data {
var data = Data(count: length)
_ = data.withUnsafeMutableBytes { bytes in
SecRandomCopyBytes(kSecRandomDefault, length, bytes.baseAddress!)
}
return data
}
static func generateTestPeerID() -> String {
return "PEER" + UUID().uuidString.prefix(8)
}
// MARK: - Async Helpers
static func waitFor(_ condition: @escaping () -> Bool, timeout: TimeInterval = TestConstants.defaultTimeout) async throws {
let start = Date()
while !condition() {
if Date().timeIntervalSince(start) > timeout {
throw TestError.timeout
}
try await Task.sleep(nanoseconds: 10_000_000) // 10ms
}
}
static func expectAsync<T>(
timeout: TimeInterval = TestConstants.defaultTimeout,
operation: @escaping () async throws -> T
) async throws -> T {
return try await withThrowingTaskGroup(of: T.self) { group in
group.addTask {
return try await operation()
}
group.addTask {
try await Task.sleep(nanoseconds: UInt64(timeout * 1_000_000_000))
throw TestError.timeout
}
let result = try await group.next()!
group.cancelAll()
return result
}
}
}
enum TestError: Error {
case timeout
case unexpectedValue
case testFailure(String)
}