Files
rdp-proxy/agents/rap-node-agent/internal/mesh/peer_cache_test.go
T

227 lines
6.9 KiB
Go

package mesh
import (
"testing"
"time"
)
func TestPeerCacheSelectsAdjacentWarmPeersWithinLimit(t *testing.T) {
local := PeerIdentity{ClusterID: "cluster-1", NodeID: "node-b"}
cache := NewPeerCache(PeerCacheConfig{
Local: local,
PeerEndpoints: map[string]string{
"node-a": "quic://node-a:19443",
"node-r": "quic://node-r:19443",
"node-c": "quic://node-c:19443",
},
Routes: []SyntheticRoute{
peerCacheRoute("route-1", []string{"node-a", local.NodeID, "node-r", "node-c"}),
},
RecoverySeeds: []PeerRecoverySeed{
{NodeID: "node-seed", Endpoint: "quic://seed.example.test:19443", Transport: "direct_quic", Priority: 10},
},
WarmPeerLimit: 2,
Now: time.Date(2026, 4, 28, 12, 0, 0, 0, time.UTC),
})
warm := cache.WarmPeerIDs()
if len(warm) != 2 || warm[0] != "node-a" || warm[1] != "node-r" {
t.Fatalf("warm peers = %+v, want adjacent node-a/node-r", warm)
}
snapshot := cache.Snapshot()
if snapshot.PeerCount != 4 || snapshot.RecoverySeedCount != 1 {
t.Fatalf("unexpected snapshot counts: %+v", snapshot)
}
}
func TestPeerCachePromotesRecoverySeedAfterRoutePeers(t *testing.T) {
local := PeerIdentity{ClusterID: "cluster-1", NodeID: "node-b"}
cache := NewPeerCache(PeerCacheConfig{
Local: local,
Routes: []SyntheticRoute{
peerCacheRoute("route-1", []string{"node-a", local.NodeID, "node-r"}),
},
RecoverySeeds: []PeerRecoverySeed{
{NodeID: "node-seed", Endpoint: "quic://seed.example.test:19443", Transport: "direct_quic", ConnectivityMode: "direct", Priority: 1},
},
WarmPeerLimit: 3,
Now: time.Date(2026, 4, 28, 12, 0, 0, 0, time.UTC),
})
warm := cache.WarmPeerIDs()
if len(warm) != 3 || warm[0] != "node-a" || warm[1] != "node-r" || warm[2] != "node-seed" {
t.Fatalf("warm peers = %+v, want adjacent peers then seed", warm)
}
seed, ok := peerCacheEntryByID(cache.Snapshot(), "node-seed")
if !ok || !seed.RecoverySeed || seed.WarmReason != "recovery_seed" {
t.Fatalf("unexpected seed entry: %+v", seed)
}
}
func TestPeerCacheUsesBestEndpointCandidate(t *testing.T) {
local := PeerIdentity{ClusterID: "cluster-1", NodeID: "node-a"}
now := time.Date(2026, 4, 28, 12, 0, 0, 0, time.UTC)
cache := NewPeerCache(PeerCacheConfig{
Local: local,
PeerEndpointCandidates: map[string][]PeerEndpointCandidate{
"node-b": {
{
EndpointID: "node-b-relay",
NodeID: "node-b",
Transport: "relay_quic",
Address: "relay.example.test",
Reachability: "relay",
ConnectivityMode: "relay_required",
Priority: 20,
},
{
EndpointID: "node-b-public",
NodeID: "node-b",
Transport: "direct_quic",
Address: "quic://203.0.113.20:19443",
Reachability: "public",
NATType: "none",
ConnectivityMode: "direct",
Priority: 1,
LastVerifiedAt: &now,
},
},
},
WarmPeerLimit: 1,
Now: now,
})
entry, ok := peerCacheEntryByID(cache.Snapshot(), "node-b")
if !ok {
t.Fatal("node-b missing from cache")
}
if entry.BestCandidateID != "node-b-public" || !entry.Warm {
t.Fatalf("unexpected candidate selection: %+v", entry)
}
}
func TestPeerCacheAppliesEndpointHealthObservations(t *testing.T) {
local := PeerIdentity{ClusterID: "cluster-1", NodeID: "node-a"}
now := time.Date(2026, 5, 16, 12, 0, 0, 0, time.UTC)
cache := NewPeerCache(PeerCacheConfig{
Local: local,
PeerEndpointCandidates: map[string][]PeerEndpointCandidate{
"node-b": {
{
EndpointID: "node-b-quic",
NodeID: "node-b",
Transport: "direct_quic",
Address: "quic://node-b.example.test:19443",
Reachability: "public",
NATType: "none",
ConnectivityMode: "direct",
Priority: 1,
LastVerifiedAt: &now,
},
{
EndpointID: "node-b-ice",
NodeID: "node-b",
Transport: "ice_quic",
Address: "quic://node-b.example.test:19444",
Reachability: "public",
NATType: "none",
ConnectivityMode: "direct",
Priority: 1,
LastVerifiedAt: &now,
},
},
},
PeerEndpointObservations: map[string]EndpointCandidateHealthObservation{
"node-b-quic": {
EndpointID: "node-b-quic",
FailureCount: 2,
LastFailureReason: "session_open_failed",
ReliabilityScore: 35,
ObservedAt: now,
},
},
WarmPeerLimit: 1,
Now: now,
})
entry, ok := peerCacheEntryByID(cache.Snapshot(), "node-b")
if !ok {
t.Fatal("node-b missing from cache")
}
if entry.BestCandidateID != "node-b-ice" || entry.Endpoint != "quic://node-b.example.test:19444" {
t.Fatalf("peer cache did not apply endpoint observations: %+v", entry)
}
if !containsString(entry.BestScoreReasons, "transport:ice_quic") {
t.Fatalf("peer cache did not expose score reasons: %+v", entry.BestScoreReasons)
}
}
func TestPeerCacheUsesPreferredCorporateEndpointAddress(t *testing.T) {
local := PeerIdentity{ClusterID: "cluster-1", NodeID: "node-a"}
cache := NewPeerCache(PeerCacheConfig{
Local: local,
PeerEndpoints: map[string]string{
"node-b": "quic://node-b.public.example.test:19443",
},
PeerEndpointCandidates: map[string][]PeerEndpointCandidate{
"node-b": {
{
EndpointID: "node-b-public",
NodeID: "node-b",
Transport: "direct_quic",
Address: "quic://node-b.public.example.test:19443",
Reachability: "public",
NATType: "none",
ConnectivityMode: "direct",
Region: "corp-eu",
Priority: 10,
},
{
EndpointID: "node-b-corp-lan",
NodeID: "node-b",
Transport: "lan_quic",
Address: "quic://10.24.10.20:19443",
Reachability: "private",
NATType: "none",
ConnectivityMode: "direct",
Region: "corp-eu",
Priority: 1,
PolicyTags: []string{"corp-lan"},
},
},
},
PreferredRegion: "corp-eu",
WarmPeerLimit: 1,
Now: time.Date(2026, 4, 28, 12, 0, 0, 0, time.UTC),
})
entry, ok := peerCacheEntryByID(cache.Snapshot(), "node-b")
if !ok {
t.Fatal("node-b missing from peer cache")
}
if entry.BestCandidateID != "node-b-corp-lan" || entry.Endpoint != "quic://10.24.10.20:19443" {
t.Fatalf("peer cache did not choose corp LAN endpoint: %+v", entry)
}
}
func peerCacheRoute(routeID string, hops []string) SyntheticRoute {
return SyntheticRoute{
RouteID: routeID,
ClusterID: "cluster-1",
SourceNodeID: hops[0],
DestinationNodeID: hops[len(hops)-1],
Hops: append([]string{}, hops...),
AllowedChannels: []string{SyntheticChannelFabricControl},
ExpiresAt: time.Now().UTC().Add(time.Hour),
}
}
func peerCacheEntryByID(snapshot PeerCacheSnapshot, nodeID string) (PeerCacheEntry, bool) {
for _, entry := range snapshot.Entries {
if entry.NodeID == nodeID {
return entry, true
}
}
return PeerCacheEntry{}, false
}