ble-reticulum/migration/tests/native/test_ble_peer_session_manager.cpp

279 lines
9.8 KiB
C++

#include "../../protocol_core/BLEPeerSessionManager.h"
#include <cassert>
#include <cstdint>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
using namespace ble_reticulum;
namespace {
PeerIdentity identity_with_base(uint8_t base) {
PeerIdentity identity{};
for (size_t i = 0; i < identity.size(); ++i) {
identity[i] = static_cast<uint8_t>(base + i);
}
return identity;
}
std::vector<uint8_t> to_payload(const PeerIdentity& identity) {
return std::vector<uint8_t>(identity.begin(), identity.end());
}
ConnectionSnapshot snapshot(const std::string& address) {
ConnectionSnapshot snap;
snap.current.address = address;
snap.local_role = LocalRole::Peripheral;
return snap;
}
bool has_action(const HandshakeResult& result, SessionActionType type) {
for (const SessionAction& action : result.actions) {
if (action.type == type) {
return true;
}
}
return false;
}
void test_non_16_byte_payload() {
BLEPeerSessionManager manager;
auto snap = snapshot("AA:BB:CC:00:00:01");
std::vector<uint8_t> data{1, 2, 3};
HandshakeResult result =
manager.handleIdentityHandshake(snap, data.data(), data.size(), 10.0);
assert(result.decision == InputDecision::PassToReassembler);
assert(!result.consumed);
assert(!result.accepted);
assert(has_action(result, SessionActionType::PassToReassembler));
}
void test_new_16_byte_identity() {
BLEPeerSessionManager manager;
PeerIdentity identity = identity_with_base(0x10);
std::vector<uint8_t> data = to_payload(identity);
auto snap = snapshot("AA:BB:CC:00:00:02");
HandshakeResult result =
manager.handleIdentityHandshake(snap, data.data(), data.size(), 11.0);
assert(result.decision == InputDecision::AcceptedNewIdentity);
assert(result.consumed);
assert(result.accepted);
assert(result.identity_key == "1011121314151617");
assert(result.fragmenter_key == "101112131415161718191a1b1c1d1e1f");
assert(has_action(result, SessionActionType::AcceptNewIdentity));
assert(has_action(result, SessionActionType::CreateFragmentationState));
assert(has_action(result, SessionActionType::MarkPeerReady));
}
void test_known_identity_duplicate_same() {
BLEPeerSessionManager manager;
PeerIdentity identity = identity_with_base(0x20);
std::vector<uint8_t> data = to_payload(identity);
auto snap = snapshot("AA:BB:CC:00:00:03");
snap.known_identity_for_address = identity;
HandshakeResult result =
manager.handleIdentityHandshake(snap, data.data(), data.size(), 12.0);
assert(result.decision == InputDecision::ConsumedDuplicateSameIdentity);
assert(result.consumed);
assert(!result.accepted);
}
void test_known_identity_duplicate_mismatch() {
BLEPeerSessionManager manager;
PeerIdentity known = identity_with_base(0x30);
PeerIdentity incoming = identity_with_base(0x40);
std::vector<uint8_t> data = to_payload(incoming);
auto snap = snapshot("AA:BB:CC:00:00:04");
snap.known_identity_for_address = known;
HandshakeResult result =
manager.handleIdentityHandshake(snap, data.data(), data.size(), 13.0);
assert(result.decision == InputDecision::ConsumedDuplicateMismatchedIdentity);
assert(result.consumed);
assert(has_action(result, SessionActionType::Warn));
}
void test_duplicate_identity_active_elsewhere() {
BLEPeerSessionManager manager;
PeerIdentity identity = identity_with_base(0x50);
std::vector<uint8_t> data = to_payload(identity);
auto first = snapshot("AA:BB:CC:00:00:05");
manager.handleIdentityHandshake(first, data.data(), data.size(), 14.0);
auto duplicate = snapshot("AA:BB:CC:00:00:06");
duplicate.existing_address_for_identity = first.current.address;
duplicate.existing_address_connected = true;
duplicate.existing_address_in_peer_table = true;
HandshakeResult result =
manager.handleIdentityHandshake(duplicate, data.data(), data.size(), 15.0);
assert(result.decision == InputDecision::RejectedDuplicateIdentity);
assert(result.consumed);
assert(!result.accepted);
assert(result.should_disconnect_current);
assert(has_action(result, SessionActionType::DisconnectCurrentPeer));
}
void test_duplicate_identity_with_stale_pending_detach() {
BLEPeerSessionManager manager;
PeerIdentity identity = identity_with_base(0x60);
std::vector<uint8_t> data = to_payload(identity);
auto first = snapshot("AA:BB:CC:00:00:07");
manager.handleIdentityHandshake(first, data.data(), data.size(), 16.0);
auto rotated = snapshot("AA:BB:CC:00:00:08");
rotated.existing_address_for_identity = first.current.address;
rotated.identity_has_pending_detach = true;
HandshakeResult result =
manager.handleIdentityHandshake(rotated, data.data(), data.size(), 17.0);
assert(result.decision == InputDecision::AcceptedNewIdentity);
assert(result.accepted);
assert(has_action(result, SessionActionType::CleanupOldAddress));
assert(has_action(result, SessionActionType::UpdatePeerAddress));
auto view = manager.sessionByIdentity(identity);
assert(view.has_value());
assert(view->current_address == rotated.current.address);
}
void test_duplicate_identity_with_zombie_old_connection() {
BLEPeerSessionManager manager;
PeerIdentity identity = identity_with_base(0x70);
std::vector<uint8_t> data = to_payload(identity);
auto first = snapshot("AA:BB:CC:00:00:09");
manager.handleIdentityHandshake(first, data.data(), data.size(), 18.0);
auto replacement = snapshot("AA:BB:CC:00:00:0A");
replacement.existing_address_for_identity = first.current.address;
replacement.existing_address_connected = true;
replacement.existing_address_in_peer_table = true;
replacement.existing_connection_is_zombie = true;
HandshakeResult result =
manager.handleIdentityHandshake(replacement, data.data(), data.size(), 19.0);
assert(result.decision == InputDecision::AcceptedNewIdentity);
assert(result.accepted);
assert(result.should_disconnect_old);
assert(has_action(result, SessionActionType::DisconnectOldPeer));
assert(has_action(result, SessionActionType::CleanupOldAddress));
}
void test_mtu_provided() {
BLEPeerSessionManager manager;
PeerIdentity identity = identity_with_base(0x80);
std::vector<uint8_t> data = to_payload(identity);
auto snap = snapshot("AA:BB:CC:00:00:0B");
snap.negotiated_mtu = 185;
HandshakeResult result =
manager.handleIdentityHandshake(snap, data.data(), data.size(), 20.0);
assert(result.mtu == 185);
auto view = manager.sessionByIdentity(identity);
assert(view.has_value());
assert(view->mtu == 185);
}
void test_mtu_missing_fallback() {
BLEPeerSessionManager manager;
PeerIdentity identity = identity_with_base(0x90);
std::vector<uint8_t> data = to_payload(identity);
auto snap = snapshot("AA:BB:CC:00:00:0C");
HandshakeResult result =
manager.handleIdentityHandshake(snap, data.data(), data.size(), 21.0);
assert(result.mtu == 23);
}
void test_pending_identity_timeout() {
BLEPeerSessionManager manager(30.0, 45.0);
ConnectionId one{"AA:BB:CC:00:00:0D", 1};
ConnectionId two{"AA:BB:CC:00:00:0E", 2};
manager.markPendingIdentity(one, 100.0);
manager.markPendingIdentity(two, 120.0);
std::vector<ConnectionId> expired = manager.expiredPendingIdentities(131.0);
assert(expired.size() == 1);
assert(expired[0] == one);
manager.removePendingIdentity(one);
expired = manager.expiredPendingIdentities(200.0);
assert(expired.size() == 1);
assert(expired[0] == two);
}
void test_peer_address_update_fragmenter_key_unchanged() {
BLEPeerSessionManager manager;
PeerIdentity identity = identity_with_base(0xa0);
std::vector<uint8_t> data = to_payload(identity);
auto first = snapshot("AA:BB:CC:00:00:0F");
HandshakeResult first_result =
manager.handleIdentityHandshake(first, data.data(), data.size(), 22.0);
auto second = snapshot("AA:BB:CC:00:00:10");
second.existing_address_for_identity = first.current.address;
second.identity_has_pending_detach = true;
HandshakeResult second_result =
manager.handleIdentityHandshake(second, data.data(), data.size(), 23.0);
assert(first_result.fragmenter_key == second_result.fragmenter_key);
auto old_view = manager.sessionByAddress(first.current.address);
auto new_view = manager.sessionByAddress(second.current.address);
assert(!old_view.has_value());
assert(new_view.has_value());
assert(new_view->current_address == second.current.address);
}
void test_key_helpers() {
PeerIdentity identity = identity_with_base(0x01);
assert(BLEPeerSessionManager::computeIdentityKey(identity) == "0102030405060708");
assert(BLEPeerSessionManager::computeFragmenterKey(identity) ==
"0102030405060708090a0b0c0d0e0f10");
}
void test_identity_from_payload_rejects_non_16() {
std::vector<uint8_t> data{1, 2, 3};
bool threw = false;
try {
(void)BLEPeerSessionManager::identityFromPayload(data.data(), data.size());
} catch (const std::invalid_argument&) {
threw = true;
}
assert(threw);
}
} // namespace
int main() {
test_non_16_byte_payload();
test_new_16_byte_identity();
test_known_identity_duplicate_same();
test_known_identity_duplicate_mismatch();
test_duplicate_identity_active_elsewhere();
test_duplicate_identity_with_stale_pending_detach();
test_duplicate_identity_with_zombie_old_connection();
test_mtu_provided();
test_mtu_missing_fallback();
test_pending_identity_timeout();
test_peer_address_update_fragmenter_key_unchanged();
test_key_helpers();
test_identity_from_payload_rejects_non_16();
std::cout << "BLEPeerSessionManager native tests passed\n";
return 0;
}