package mesh import ( "context" "net/http" "net/http/httptest" "testing" "time" ) func TestPeerConnectionManagerProbesDirectAndDefersRendezvous(t *testing.T) { now := time.Date(2026, 4, 28, 12, 0, 0, 0, time.UTC) current := now server := httptest.NewServer(Server{ Local: PeerIdentity{ClusterID: "cluster-1", NodeID: "node-b"}, }.Handler()) defer server.Close() local := PeerIdentity{ClusterID: "cluster-1", NodeID: "node-a"} cache := NewPeerCache(PeerCacheConfig{ Local: local, PeerEndpointCandidates: map[string][]PeerEndpointCandidate{ "node-b": { { EndpointID: "node-b-direct", NodeID: "node-b", Transport: "direct_tcp_tls", Address: server.URL, Reachability: "private", ConnectivityMode: "direct", PolicyTags: []string{"corp-lan", "same-site"}, Priority: 1, }, }, "node-c": { { EndpointID: "node-c-relay", NodeID: "node-c", Transport: "relay", Address: "relay://fabric/node-c", Reachability: "relay", ConnectivityMode: "relay_required", Priority: 1, }, }, }, WarmPeerLimit: 2, Now: now, }) tracker := NewPeerConnectionTracker(cache.Snapshot(), now) manager := NewPeerConnectionManager(PeerConnectionManagerConfig{ Local: local, PeerCache: cache, Tracker: tracker, ProbeTimeout: time.Second, Now: func() time.Time { current = current.Add(10 * time.Millisecond) return current }, }) cycle := manager.ProbeOnce(context.Background()) if cycle.Attempted != 1 || cycle.Succeeded != 1 || cycle.Deferred != 1 || cycle.RendezvousRequiredCount != 1 { t.Fatalf("unexpected cycle: %+v", cycle) } snapshot := tracker.Snapshot() if snapshot.Ready != 1 || snapshot.Waiting != 1 { t.Fatalf("unexpected tracker snapshot: %+v", snapshot) } if cycle.Results[0].NodeID != "node-b" || cycle.Results[0].LinkStatus != PeerConnectionProbeReachable { t.Fatalf("direct peer was not probed first: %+v", cycle.Results) } if cycle.Results[1].NodeID != "node-c" || cycle.Results[1].LinkStatus != PeerConnectionProbeDeferred { t.Fatalf("relay peer was not deferred: %+v", cycle.Results) } } func TestPeerConnectionManagerRecordsFailureAndSuppressesActiveBackoff(t *testing.T) { now := time.Date(2026, 4, 28, 12, 0, 0, 0, time.UTC) current := now local := PeerIdentity{ClusterID: "cluster-1", NodeID: "node-a"} cache := NewPeerCache(PeerCacheConfig{ Local: local, PeerEndpoints: map[string]string{ "node-b": "http://127.0.0.1:1", }, WarmPeerLimit: 1, Now: now, }) tracker := NewPeerConnectionTracker(cache.Snapshot(), now) manager := NewPeerConnectionManager(PeerConnectionManagerConfig{ Local: local, PeerCache: cache, Tracker: tracker, HTTPClient: &http.Client{Timeout: 20 * time.Millisecond}, ProbeTimeout: 20 * time.Millisecond, Now: func() time.Time { current = current.Add(10 * time.Millisecond) return current }, }) for i := 0; i < 3; i++ { manager.ProbeOnce(context.Background()) } backoff := tracker.Snapshot() if backoff.Backoff != 1 { t.Fatalf("expected backoff after repeated failures: %+v", backoff) } cycle := manager.ProbeOnce(context.Background()) if cycle.Attempted != 0 || len(cycle.Results) != 0 { t.Fatalf("active backoff peer should not be attempted: %+v", cycle) } } func TestPeerConnectionManagerProbesRelayControlLease(t *testing.T) { now := time.Date(2026, 4, 28, 12, 0, 0, 0, time.UTC) current := now server := httptest.NewServer(Server{ Local: PeerIdentity{ClusterID: "cluster-1", NodeID: "node-r"}, }.Handler()) defer server.Close() local := PeerIdentity{ClusterID: "cluster-1", NodeID: "node-a"} leases := []PeerRendezvousLease{ { LeaseID: "lease-node-b-via-node-r", PeerNodeID: "node-b", RelayNodeID: "node-r", RelayEndpoint: server.URL, Transport: "relay_control", ConnectivityMode: "relay_required", Priority: 10, ControlPlaneOnly: true, IssuedAt: now.Add(-time.Minute), ExpiresAt: now.Add(time.Minute), }, } cache := NewPeerCache(PeerCacheConfig{ Local: local, PeerEndpointCandidates: map[string][]PeerEndpointCandidate{ "node-b": { { EndpointID: "node-b-relay", NodeID: "node-b", Transport: "relay", Address: "relay://fabric/node-b", Reachability: "relay", ConnectivityMode: "relay_required", Priority: 10, }, }, }, RendezvousLeases: leases, WarmPeerLimit: 1, Now: now, }) tracker := NewPeerConnectionTracker(cache.Snapshot(), now) manager := NewPeerConnectionManager(PeerConnectionManagerConfig{ Local: local, PeerCache: cache, Tracker: tracker, RendezvousLeases: leases, ProbeTimeout: time.Second, Now: func() time.Time { current = current.Add(10 * time.Millisecond) return current }, }) cycle := manager.ProbeOnce(context.Background()) if cycle.Attempted != 1 || cycle.Succeeded != 1 || cycle.Deferred != 0 || cycle.RelayControlCount != 1 || cycle.RendezvousResolvedCount != 1 || cycle.RendezvousRequiredCount != 0 { t.Fatalf("unexpected relay-control cycle: %+v", cycle) } if len(cycle.Results) != 1 || cycle.Results[0].NodeID != "node-b" || cycle.Results[0].RelayNodeID != "node-r" || cycle.Results[0].ConnectionState.State != PeerConnectionRelayReady { t.Fatalf("unexpected relay-control result: %+v", cycle.Results) } snapshot := tracker.Snapshot() if snapshot.RelayReady != 1 || snapshot.Waiting != 0 { t.Fatalf("unexpected tracker snapshot: %+v", snapshot) } }