583 lines
23 KiB
Go
583 lines
23 KiB
Go
|
|
package notify
|
||
|
|
|
||
|
|
import (
|
||
|
|
"b612.me/stario"
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
"math"
|
||
|
|
"net"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestGetClientRuntimeSnapshotDefaults(t *testing.T) {
|
||
|
|
client := NewClient()
|
||
|
|
snapshot, err := GetClientRuntimeSnapshot(client)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("GetClientRuntimeSnapshot failed: %v", err)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.OwnerState, "idle"; got != want {
|
||
|
|
t.Fatalf("OwnerState mismatch: got %q want %q", got, want)
|
||
|
|
}
|
||
|
|
if snapshot.Alive {
|
||
|
|
t.Fatalf("Alive mismatch: got %v want false", snapshot.Alive)
|
||
|
|
}
|
||
|
|
if snapshot.SessionEpoch != 0 {
|
||
|
|
t.Fatalf("SessionEpoch mismatch: got %d want 0", snapshot.SessionEpoch)
|
||
|
|
}
|
||
|
|
if snapshot.TransportAttached {
|
||
|
|
t.Fatalf("TransportAttached mismatch: got %v want false", snapshot.TransportAttached)
|
||
|
|
}
|
||
|
|
if snapshot.HasRuntimeQueue {
|
||
|
|
t.Fatalf("HasRuntimeQueue mismatch: got %v want false", snapshot.HasRuntimeQueue)
|
||
|
|
}
|
||
|
|
if !snapshot.HasRuntimeStopCtx {
|
||
|
|
t.Fatalf("HasRuntimeStopCtx mismatch: got %v want true", snapshot.HasRuntimeStopCtx)
|
||
|
|
}
|
||
|
|
if snapshot.ConnectSource != "" || snapshot.ConnectNetwork != "" || snapshot.ConnectAddress != "" || snapshot.CanReconnect {
|
||
|
|
t.Fatalf("unexpected default connect source snapshot: %+v", snapshot)
|
||
|
|
}
|
||
|
|
if snapshot.Retry != (ConnectionRetrySnapshot{}) {
|
||
|
|
t.Fatalf("Retry snapshot mismatch: %+v", snapshot.Retry)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestGetServerRuntimeSnapshotDefaults(t *testing.T) {
|
||
|
|
server := NewServer()
|
||
|
|
snapshot, err := GetServerRuntimeSnapshot(server)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("GetServerRuntimeSnapshot failed: %v", err)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.OwnerState, "idle"; got != want {
|
||
|
|
t.Fatalf("OwnerState mismatch: got %q want %q", got, want)
|
||
|
|
}
|
||
|
|
if snapshot.Alive {
|
||
|
|
t.Fatalf("Alive mismatch: got %v want false", snapshot.Alive)
|
||
|
|
}
|
||
|
|
if snapshot.ClientCount != 0 {
|
||
|
|
t.Fatalf("ClientCount mismatch: got %d want 0", snapshot.ClientCount)
|
||
|
|
}
|
||
|
|
if snapshot.DetachedClientCount != 0 {
|
||
|
|
t.Fatalf("DetachedClientCount mismatch: got %d want 0", snapshot.DetachedClientCount)
|
||
|
|
}
|
||
|
|
if snapshot.DetachedReattachableClientCount != 0 {
|
||
|
|
t.Fatalf("DetachedReattachableClientCount mismatch: got %d want 0", snapshot.DetachedReattachableClientCount)
|
||
|
|
}
|
||
|
|
if snapshot.DetachedExpiredClientCount != 0 {
|
||
|
|
t.Fatalf("DetachedExpiredClientCount mismatch: got %d want 0", snapshot.DetachedExpiredClientCount)
|
||
|
|
}
|
||
|
|
if snapshot.DetachedClientKeepSec != 0 {
|
||
|
|
t.Fatalf("DetachedClientKeepSec mismatch: got %d want 0", snapshot.DetachedClientKeepSec)
|
||
|
|
}
|
||
|
|
if snapshot.TransportAttached {
|
||
|
|
t.Fatalf("TransportAttached mismatch: got %v want false", snapshot.TransportAttached)
|
||
|
|
}
|
||
|
|
if snapshot.HasRuntimeQueue {
|
||
|
|
t.Fatalf("HasRuntimeQueue mismatch: got %v want false", snapshot.HasRuntimeQueue)
|
||
|
|
}
|
||
|
|
if !snapshot.HasRuntimeStopCtx {
|
||
|
|
t.Fatalf("HasRuntimeStopCtx mismatch: got %v want true", snapshot.HasRuntimeStopCtx)
|
||
|
|
}
|
||
|
|
if snapshot.Retry != (ConnectionRetrySnapshot{}) {
|
||
|
|
t.Fatalf("Retry snapshot mismatch: %+v", snapshot.Retry)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestGetRuntimeSnapshotRejectsNil(t *testing.T) {
|
||
|
|
if _, err := GetClientRuntimeSnapshot(nil); !errors.Is(err, errClientRuntimeSnapshotNil) {
|
||
|
|
t.Fatalf("GetClientRuntimeSnapshot nil error = %v, want %v", err, errClientRuntimeSnapshotNil)
|
||
|
|
}
|
||
|
|
if _, err := GetServerRuntimeSnapshot(nil); !errors.Is(err, errServerRuntimeSnapshotNil) {
|
||
|
|
t.Fatalf("GetServerRuntimeSnapshot nil error = %v, want %v", err, errServerRuntimeSnapshotNil)
|
||
|
|
}
|
||
|
|
if _, err := GetClientConnRuntimeSnapshot(nil); !errors.Is(err, errClientConnRuntimeSnapshotNil) {
|
||
|
|
t.Fatalf("GetClientConnRuntimeSnapshot nil error = %v, want %v", err, errClientConnRuntimeSnapshotNil)
|
||
|
|
}
|
||
|
|
if _, err := GetServerDetachedClientRuntimeSnapshots(nil); !errors.Is(err, errServerDetachedClientRuntimeSnapshotNil) {
|
||
|
|
t.Fatalf("GetServerDetachedClientRuntimeSnapshots nil error = %v, want %v", err, errServerDetachedClientRuntimeSnapshotNil)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestGetRuntimeSnapshotExposesDetachedTransport(t *testing.T) {
|
||
|
|
client := NewClient().(*ClientCommon)
|
||
|
|
clientStopCtx, clientStopFn := context.WithCancel(context.Background())
|
||
|
|
defer clientStopFn()
|
||
|
|
clientQueue := stario.NewQueueCtx(clientStopCtx, 4, math.MaxUint32)
|
||
|
|
clientConnLeft, clientConnRight := net.Pipe()
|
||
|
|
defer clientConnLeft.Close()
|
||
|
|
defer clientConnRight.Close()
|
||
|
|
client.setClientSessionRuntime(&clientSessionRuntime{
|
||
|
|
conn: clientConnLeft,
|
||
|
|
stopCtx: clientStopCtx,
|
||
|
|
stopFn: clientStopFn,
|
||
|
|
queue: clientQueue,
|
||
|
|
epoch: 1,
|
||
|
|
})
|
||
|
|
client.markSessionStarted()
|
||
|
|
client.clearClientSessionRuntimeTransport()
|
||
|
|
|
||
|
|
clientSnapshot, err := GetClientRuntimeSnapshot(client)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("GetClientRuntimeSnapshot failed: %v", err)
|
||
|
|
}
|
||
|
|
if clientSnapshot.TransportAttached {
|
||
|
|
t.Fatalf("client TransportAttached mismatch: got %v want false", clientSnapshot.TransportAttached)
|
||
|
|
}
|
||
|
|
if clientSnapshot.HasRuntimeConn {
|
||
|
|
t.Fatalf("client HasRuntimeConn mismatch: got %v want false", clientSnapshot.HasRuntimeConn)
|
||
|
|
}
|
||
|
|
if !clientSnapshot.HasRuntimeQueue {
|
||
|
|
t.Fatalf("client HasRuntimeQueue mismatch: got %v want true", clientSnapshot.HasRuntimeQueue)
|
||
|
|
}
|
||
|
|
if !clientSnapshot.HasRuntimeStopCtx {
|
||
|
|
t.Fatalf("client HasRuntimeStopCtx mismatch: got %v want true", clientSnapshot.HasRuntimeStopCtx)
|
||
|
|
}
|
||
|
|
|
||
|
|
server := NewServer().(*ServerCommon)
|
||
|
|
server.markSessionStarted()
|
||
|
|
stopCtx, stopFn := context.WithCancel(context.Background())
|
||
|
|
defer stopFn()
|
||
|
|
queue := stario.NewQueueCtx(stopCtx, 4, math.MaxUint32)
|
||
|
|
server.setServerSessionRuntime(&serverSessionRuntime{
|
||
|
|
stopCtx: stopCtx,
|
||
|
|
stopFn: stopFn,
|
||
|
|
queue: queue,
|
||
|
|
listener: &stubListener{},
|
||
|
|
})
|
||
|
|
server.clearServerSessionRuntimeTransport()
|
||
|
|
|
||
|
|
serverSnapshot, err := GetServerRuntimeSnapshot(server)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("GetServerRuntimeSnapshot failed: %v", err)
|
||
|
|
}
|
||
|
|
if serverSnapshot.TransportAttached {
|
||
|
|
t.Fatalf("server TransportAttached mismatch: got %v want false", serverSnapshot.TransportAttached)
|
||
|
|
}
|
||
|
|
if serverSnapshot.HasRuntimeListener || serverSnapshot.HasRuntimeUDPListener {
|
||
|
|
t.Fatalf("server runtime listener flags mismatch: %+v", serverSnapshot)
|
||
|
|
}
|
||
|
|
if !serverSnapshot.HasRuntimeQueue {
|
||
|
|
t.Fatalf("server HasRuntimeQueue mismatch: got %v want true", serverSnapshot.HasRuntimeQueue)
|
||
|
|
}
|
||
|
|
if !serverSnapshot.HasRuntimeStopCtx {
|
||
|
|
t.Fatalf("server HasRuntimeStopCtx mismatch: got %v want true", serverSnapshot.HasRuntimeStopCtx)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestGetServerRuntimeSnapshotCountsDetachedBoundPeers(t *testing.T) {
|
||
|
|
server := NewServer().(*ServerCommon)
|
||
|
|
server.SetDetachedClientKeepSec(15)
|
||
|
|
stopCtx, stopFn := context.WithCancel(context.Background())
|
||
|
|
defer stopFn()
|
||
|
|
queue := stario.NewQueueCtx(stopCtx, 4, math.MaxUint32)
|
||
|
|
server.setServerSessionRuntime(&serverSessionRuntime{
|
||
|
|
stopCtx: stopCtx,
|
||
|
|
stopFn: stopFn,
|
||
|
|
queue: queue,
|
||
|
|
})
|
||
|
|
server.markSessionStarted()
|
||
|
|
|
||
|
|
boundAttachedLeft, boundAttachedRight := net.Pipe()
|
||
|
|
defer boundAttachedRight.Close()
|
||
|
|
boundAttached, _, _ := newRegisteredServerClientForTest(t, server, "bound-attached", boundAttachedLeft, stopCtx, stopFn)
|
||
|
|
boundAttached.markClientConnIdentityBound()
|
||
|
|
boundAttached.markClientConnStreamTransport()
|
||
|
|
|
||
|
|
boundDetachedLeft, boundDetachedRight := net.Pipe()
|
||
|
|
defer boundDetachedRight.Close()
|
||
|
|
boundDetached, _, _ := newRegisteredServerClientForTest(t, server, "bound-detached", boundDetachedLeft, stopCtx, stopFn)
|
||
|
|
boundDetached.markClientConnIdentityBound()
|
||
|
|
boundDetached.markClientConnStreamTransport()
|
||
|
|
boundDetached.markClientConnTransportDetached("read error", errors.New("boom"))
|
||
|
|
boundDetached.clearClientConnSessionRuntimeTransport()
|
||
|
|
|
||
|
|
unboundDetachedLeft, unboundDetachedRight := net.Pipe()
|
||
|
|
defer unboundDetachedRight.Close()
|
||
|
|
unboundDetached, _, _ := newRegisteredServerClientForTest(t, server, "unbound-detached", unboundDetachedLeft, stopCtx, stopFn)
|
||
|
|
unboundDetached.markClientConnStreamTransport()
|
||
|
|
unboundDetached.markClientConnTransportDetached("read error", errors.New("boom"))
|
||
|
|
unboundDetached.clearClientConnSessionRuntimeTransport()
|
||
|
|
|
||
|
|
snapshot, err := GetServerRuntimeSnapshot(server)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("GetServerRuntimeSnapshot failed: %v", err)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.ClientCount, 3; got != want {
|
||
|
|
t.Fatalf("ClientCount mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.DetachedClientCount, 1; got != want {
|
||
|
|
t.Fatalf("DetachedClientCount mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.DetachedReattachableClientCount, 1; got != want {
|
||
|
|
t.Fatalf("DetachedReattachableClientCount mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.DetachedExpiredClientCount, 0; got != want {
|
||
|
|
t.Fatalf("DetachedExpiredClientCount mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.DetachedClientKeepSec, int64(15); got != want {
|
||
|
|
t.Fatalf("DetachedClientKeepSec mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestGetClientConnRuntimeSnapshotExposesDetachState(t *testing.T) {
|
||
|
|
server := NewServer().(*ServerCommon)
|
||
|
|
server.SetDetachedClientKeepSec(15)
|
||
|
|
stopCtx, stopFn := context.WithCancel(context.Background())
|
||
|
|
defer stopFn()
|
||
|
|
|
||
|
|
left, right := net.Pipe()
|
||
|
|
defer right.Close()
|
||
|
|
client, _, _ := newRegisteredServerClientForTest(t, server, "peer-runtime", left, stopCtx, stopFn)
|
||
|
|
client.markClientConnIdentityBound()
|
||
|
|
client.markClientConnStreamTransport()
|
||
|
|
client.setClientConnLastHeartbeatUnix(time.Now().Unix())
|
||
|
|
client.markClientConnTransportDetached("read error", errors.New("boom"))
|
||
|
|
client.clearClientConnSessionRuntimeTransport()
|
||
|
|
|
||
|
|
snapshot, err := GetClientConnRuntimeSnapshot(client)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("GetClientConnRuntimeSnapshot failed: %v", err)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.ClientID, "peer-runtime"; got != want {
|
||
|
|
t.Fatalf("ClientID mismatch: got %q want %q", got, want)
|
||
|
|
}
|
||
|
|
if !snapshot.Alive {
|
||
|
|
t.Fatalf("Alive mismatch: got %v want true", snapshot.Alive)
|
||
|
|
}
|
||
|
|
if !snapshot.IdentityBound {
|
||
|
|
t.Fatal("IdentityBound mismatch: got false want true")
|
||
|
|
}
|
||
|
|
if !snapshot.UsesStreamTransport {
|
||
|
|
t.Fatal("UsesStreamTransport mismatch: got false want true")
|
||
|
|
}
|
||
|
|
if snapshot.TransportAttached {
|
||
|
|
t.Fatalf("TransportAttached mismatch: got %v want false", snapshot.TransportAttached)
|
||
|
|
}
|
||
|
|
if snapshot.HasRuntimeConn {
|
||
|
|
t.Fatalf("HasRuntimeConn mismatch: got %v want false", snapshot.HasRuntimeConn)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.TransportGeneration, uint64(1); got != want {
|
||
|
|
t.Fatalf("TransportGeneration mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.TransportDetachReason, "read error"; got != want {
|
||
|
|
t.Fatalf("TransportDetachReason mismatch: got %q want %q", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.TransportDetachKind, clientConnTransportDetachKindReadError; got != want {
|
||
|
|
t.Fatalf("TransportDetachKind mismatch: got %q want %q", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.TransportDetachGeneration, uint64(1); got != want {
|
||
|
|
t.Fatalf("TransportDetachGeneration mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.TransportDetachError, "boom"; got != want {
|
||
|
|
t.Fatalf("TransportDetachError mismatch: got %q want %q", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.TransportAttachCount, uint64(1); got != want {
|
||
|
|
t.Fatalf("TransportAttachCount mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.TransportDetachCount, uint64(1); got != want {
|
||
|
|
t.Fatalf("TransportDetachCount mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if snapshot.LastTransportAttachAt.IsZero() {
|
||
|
|
t.Fatal("LastTransportAttachAt should be recorded")
|
||
|
|
}
|
||
|
|
if got, want := snapshot.DetachedClientKeepSec, int64(15); got != want {
|
||
|
|
t.Fatalf("DetachedClientKeepSec mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if snapshot.TransportDetachedAt.IsZero() {
|
||
|
|
t.Fatal("TransportDetachedAt should be recorded")
|
||
|
|
}
|
||
|
|
if !snapshot.TransportDetachHasExpiry {
|
||
|
|
t.Fatal("TransportDetachHasExpiry mismatch: got false want true")
|
||
|
|
}
|
||
|
|
if got, want := snapshot.TransportDetachExpiry, snapshot.TransportDetachedAt.Add(15*time.Second); !got.Equal(want) {
|
||
|
|
t.Fatalf("TransportDetachExpiry mismatch: got %v want %v", got, want)
|
||
|
|
}
|
||
|
|
if snapshot.TransportDetachExpired {
|
||
|
|
t.Fatal("TransportDetachExpired mismatch: got true want false")
|
||
|
|
}
|
||
|
|
if snapshot.TransportDetachRemaining <= 0 || snapshot.TransportDetachRemaining > 15*time.Second {
|
||
|
|
t.Fatalf("TransportDetachRemaining mismatch: got %v want within (0,15s]", snapshot.TransportDetachRemaining)
|
||
|
|
}
|
||
|
|
if !snapshot.ReattachEligible {
|
||
|
|
t.Fatal("ReattachEligible mismatch: got false want true")
|
||
|
|
}
|
||
|
|
if snapshot.LastHeartbeatAt.IsZero() {
|
||
|
|
t.Fatal("LastHeartbeatAt should be recorded")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestGetServerDetachedClientRuntimeSnapshotsFiltersAndSorts(t *testing.T) {
|
||
|
|
server := NewServer().(*ServerCommon)
|
||
|
|
server.SetDetachedClientKeepSec(12)
|
||
|
|
stopCtx, stopFn := context.WithCancel(context.Background())
|
||
|
|
defer stopFn()
|
||
|
|
queue := stario.NewQueueCtx(stopCtx, 4, math.MaxUint32)
|
||
|
|
server.setServerSessionRuntime(&serverSessionRuntime{
|
||
|
|
stopCtx: stopCtx,
|
||
|
|
stopFn: stopFn,
|
||
|
|
queue: queue,
|
||
|
|
})
|
||
|
|
server.markSessionStarted()
|
||
|
|
|
||
|
|
detachedBLeft, detachedBRight := net.Pipe()
|
||
|
|
defer detachedBRight.Close()
|
||
|
|
detachedB, _, _ := newRegisteredServerClientForTest(t, server, "peer-b", detachedBLeft, stopCtx, stopFn)
|
||
|
|
detachedB.markClientConnIdentityBound()
|
||
|
|
detachedB.markClientConnStreamTransport()
|
||
|
|
detachedB.markClientConnTransportDetached("read error", errors.New("boom-b"))
|
||
|
|
detachedB.clearClientConnSessionRuntimeTransport()
|
||
|
|
|
||
|
|
detachedALeft, detachedARight := net.Pipe()
|
||
|
|
defer detachedARight.Close()
|
||
|
|
detachedA, _, _ := newRegisteredServerClientForTest(t, server, "peer-a", detachedALeft, stopCtx, stopFn)
|
||
|
|
detachedA.markClientConnIdentityBound()
|
||
|
|
detachedA.markClientConnStreamTransport()
|
||
|
|
detachedA.markClientConnTransportDetached("heartbeat timeout", nil)
|
||
|
|
detachedA.clearClientConnSessionRuntimeTransport()
|
||
|
|
|
||
|
|
attachedLeft, attachedRight := net.Pipe()
|
||
|
|
defer attachedRight.Close()
|
||
|
|
attached, _, _ := newRegisteredServerClientForTest(t, server, "peer-c", attachedLeft, stopCtx, stopFn)
|
||
|
|
attached.markClientConnIdentityBound()
|
||
|
|
attached.markClientConnStreamTransport()
|
||
|
|
|
||
|
|
unboundLeft, unboundRight := net.Pipe()
|
||
|
|
defer unboundRight.Close()
|
||
|
|
unbound, _, _ := newRegisteredServerClientForTest(t, server, "peer-d", unboundLeft, stopCtx, stopFn)
|
||
|
|
unbound.markClientConnStreamTransport()
|
||
|
|
unbound.markClientConnTransportDetached("read error", errors.New("boom-d"))
|
||
|
|
unbound.clearClientConnSessionRuntimeTransport()
|
||
|
|
|
||
|
|
snapshots, err := GetServerDetachedClientRuntimeSnapshots(server)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("GetServerDetachedClientRuntimeSnapshots failed: %v", err)
|
||
|
|
}
|
||
|
|
if got, want := len(snapshots), 2; got != want {
|
||
|
|
t.Fatalf("detached snapshot count mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshots[0].ClientID, "peer-a"; got != want {
|
||
|
|
t.Fatalf("first detached snapshot client mismatch: got %q want %q", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshots[1].ClientID, "peer-b"; got != want {
|
||
|
|
t.Fatalf("second detached snapshot client mismatch: got %q want %q", got, want)
|
||
|
|
}
|
||
|
|
for _, snapshot := range snapshots {
|
||
|
|
if snapshot.TransportAttached {
|
||
|
|
t.Fatalf("detached snapshot should report transport detached: %+v", snapshot)
|
||
|
|
}
|
||
|
|
if !snapshot.IdentityBound || !snapshot.UsesStreamTransport {
|
||
|
|
t.Fatalf("detached snapshot identity/transport flags mismatch: %+v", snapshot)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.DetachedClientKeepSec, int64(12); got != want {
|
||
|
|
t.Fatalf("detached snapshot keep seconds mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if snapshot.TransportDetachedAt.IsZero() {
|
||
|
|
t.Fatalf("detached snapshot should record detached time: %+v", snapshot)
|
||
|
|
}
|
||
|
|
if !snapshot.ReattachEligible {
|
||
|
|
t.Fatalf("detached snapshot should be reattach eligible within keep window: %+v", snapshot)
|
||
|
|
}
|
||
|
|
if snapshot.TransportDetachExpired {
|
||
|
|
t.Fatalf("detached snapshot should not be expired yet: %+v", snapshot)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestGetServerRuntimeSnapshotCountsExpiredDetachedPeers(t *testing.T) {
|
||
|
|
server := NewServer().(*ServerCommon)
|
||
|
|
server.SetDetachedClientKeepSec(5)
|
||
|
|
stopCtx, stopFn := context.WithCancel(context.Background())
|
||
|
|
defer stopFn()
|
||
|
|
queue := stario.NewQueueCtx(stopCtx, 4, math.MaxUint32)
|
||
|
|
server.setServerSessionRuntime(&serverSessionRuntime{
|
||
|
|
stopCtx: stopCtx,
|
||
|
|
stopFn: stopFn,
|
||
|
|
queue: queue,
|
||
|
|
})
|
||
|
|
server.markSessionStarted()
|
||
|
|
|
||
|
|
activeLeft, activeRight := net.Pipe()
|
||
|
|
defer activeRight.Close()
|
||
|
|
active, _, _ := newRegisteredServerClientForTest(t, server, "peer-active", activeLeft, stopCtx, stopFn)
|
||
|
|
active.markClientConnIdentityBound()
|
||
|
|
active.markClientConnStreamTransport()
|
||
|
|
active.markClientConnTransportDetached("read error", errors.New("boom"))
|
||
|
|
active.clearClientConnSessionRuntimeTransport()
|
||
|
|
|
||
|
|
expiredLeft, expiredRight := net.Pipe()
|
||
|
|
defer expiredRight.Close()
|
||
|
|
expired, _, _ := newRegisteredServerClientForTest(t, server, "peer-expired-server", expiredLeft, stopCtx, stopFn)
|
||
|
|
expired.markClientConnIdentityBound()
|
||
|
|
expired.markClientConnStreamTransport()
|
||
|
|
expired.setClientConnTransportDetachState(&clientConnTransportDetachState{
|
||
|
|
Reason: "heartbeat timeout",
|
||
|
|
At: time.Now().Add(-10 * time.Second),
|
||
|
|
})
|
||
|
|
expired.clearClientConnSessionRuntimeTransport()
|
||
|
|
|
||
|
|
snapshot, err := GetServerRuntimeSnapshot(server)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("GetServerRuntimeSnapshot failed: %v", err)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.DetachedClientCount, 2; got != want {
|
||
|
|
t.Fatalf("DetachedClientCount mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.DetachedReattachableClientCount, 1; got != want {
|
||
|
|
t.Fatalf("DetachedReattachableClientCount mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.DetachedExpiredClientCount, 1; got != want {
|
||
|
|
t.Fatalf("DetachedExpiredClientCount mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestGetClientConnRuntimeSnapshotMarksExpiredDetachWindow(t *testing.T) {
|
||
|
|
server := NewServer().(*ServerCommon)
|
||
|
|
server.SetDetachedClientKeepSec(5)
|
||
|
|
stopCtx, stopFn := context.WithCancel(context.Background())
|
||
|
|
defer stopFn()
|
||
|
|
|
||
|
|
left, right := net.Pipe()
|
||
|
|
defer right.Close()
|
||
|
|
client, _, _ := newRegisteredServerClientForTest(t, server, "peer-expired", left, stopCtx, stopFn)
|
||
|
|
client.markClientConnIdentityBound()
|
||
|
|
client.markClientConnStreamTransport()
|
||
|
|
client.setClientConnTransportDetachState(&clientConnTransportDetachState{
|
||
|
|
Reason: "heartbeat timeout",
|
||
|
|
At: time.Now().Add(-10 * time.Second),
|
||
|
|
})
|
||
|
|
client.clearClientConnSessionRuntimeTransport()
|
||
|
|
|
||
|
|
snapshot, err := GetClientConnRuntimeSnapshot(client)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("GetClientConnRuntimeSnapshot failed: %v", err)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.TransportDetachKind, clientConnTransportDetachKindHeartbeatTimeout; got != want {
|
||
|
|
t.Fatalf("TransportDetachKind mismatch: got %q want %q", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.TransportGeneration, uint64(1); got != want {
|
||
|
|
t.Fatalf("TransportGeneration mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.TransportDetachGeneration, uint64(1); got != want {
|
||
|
|
t.Fatalf("TransportDetachGeneration mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if !snapshot.TransportDetachHasExpiry {
|
||
|
|
t.Fatal("TransportDetachHasExpiry mismatch: got false want true")
|
||
|
|
}
|
||
|
|
if !snapshot.TransportDetachExpired {
|
||
|
|
t.Fatal("TransportDetachExpired mismatch: got false want true")
|
||
|
|
}
|
||
|
|
if got := snapshot.TransportDetachRemaining; got != 0 {
|
||
|
|
t.Fatalf("TransportDetachRemaining mismatch: got %v want 0", got)
|
||
|
|
}
|
||
|
|
if snapshot.ReattachEligible {
|
||
|
|
t.Fatal("ReattachEligible mismatch: got true want false")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestGetClientConnRuntimeSnapshotKeepsUnlimitedDetachReattachable(t *testing.T) {
|
||
|
|
server := NewServer().(*ServerCommon)
|
||
|
|
stopCtx, stopFn := context.WithCancel(context.Background())
|
||
|
|
defer stopFn()
|
||
|
|
|
||
|
|
left, right := net.Pipe()
|
||
|
|
defer right.Close()
|
||
|
|
client, _, _ := newRegisteredServerClientForTest(t, server, "peer-unlimited", left, stopCtx, stopFn)
|
||
|
|
client.markClientConnIdentityBound()
|
||
|
|
client.markClientConnStreamTransport()
|
||
|
|
client.markClientConnTransportDetached("custom detach", errors.New("boom"))
|
||
|
|
client.clearClientConnSessionRuntimeTransport()
|
||
|
|
|
||
|
|
snapshot, err := GetClientConnRuntimeSnapshot(client)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("GetClientConnRuntimeSnapshot failed: %v", err)
|
||
|
|
}
|
||
|
|
if snapshot.TransportDetachHasExpiry {
|
||
|
|
t.Fatal("TransportDetachHasExpiry mismatch: got true want false")
|
||
|
|
}
|
||
|
|
if got, want := snapshot.TransportGeneration, uint64(1); got != want {
|
||
|
|
t.Fatalf("TransportGeneration mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.TransportDetachGeneration, uint64(1); got != want {
|
||
|
|
t.Fatalf("TransportDetachGeneration mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if !snapshot.TransportDetachExpiry.IsZero() {
|
||
|
|
t.Fatalf("TransportDetachExpiry mismatch: got %v want zero", snapshot.TransportDetachExpiry)
|
||
|
|
}
|
||
|
|
if snapshot.TransportDetachExpired {
|
||
|
|
t.Fatal("TransportDetachExpired mismatch: got true want false")
|
||
|
|
}
|
||
|
|
if got := snapshot.TransportDetachRemaining; got != 0 {
|
||
|
|
t.Fatalf("TransportDetachRemaining mismatch: got %v want 0", got)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.TransportDetachKind, clientConnTransportDetachKindOther; got != want {
|
||
|
|
t.Fatalf("TransportDetachKind mismatch: got %q want %q", got, want)
|
||
|
|
}
|
||
|
|
if !snapshot.ReattachEligible {
|
||
|
|
t.Fatal("ReattachEligible mismatch: got false want true")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestGetClientConnRuntimeSnapshotIncrementsTransportGenerationOnReattach(t *testing.T) {
|
||
|
|
server := NewServer().(*ServerCommon)
|
||
|
|
stopCtx, stopFn := context.WithCancel(context.Background())
|
||
|
|
defer stopFn()
|
||
|
|
|
||
|
|
firstLeft, firstRight := net.Pipe()
|
||
|
|
defer firstRight.Close()
|
||
|
|
client, _, _ := newRegisteredServerClientForTest(t, server, "peer-reattach-generation", firstLeft, stopCtx, stopFn)
|
||
|
|
client.markClientConnIdentityBound()
|
||
|
|
|
||
|
|
initial, err := GetClientConnRuntimeSnapshot(client)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("GetClientConnRuntimeSnapshot initial failed: %v", err)
|
||
|
|
}
|
||
|
|
if got, want := initial.TransportGeneration, uint64(1); got != want {
|
||
|
|
t.Fatalf("initial TransportGeneration mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if got := initial.TransportDetachGeneration; got != 0 {
|
||
|
|
t.Fatalf("initial TransportDetachGeneration mismatch: got %d want 0", got)
|
||
|
|
}
|
||
|
|
|
||
|
|
server.detachClientSessionTransport(client, "read error", errors.New("boom"))
|
||
|
|
|
||
|
|
detached, err := GetClientConnRuntimeSnapshot(client)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("GetClientConnRuntimeSnapshot detached failed: %v", err)
|
||
|
|
}
|
||
|
|
if got, want := detached.TransportGeneration, uint64(1); got != want {
|
||
|
|
t.Fatalf("detached TransportGeneration mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if got, want := detached.TransportDetachGeneration, uint64(1); got != want {
|
||
|
|
t.Fatalf("detached TransportDetachGeneration mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
|
||
|
|
secondLeft, secondRight := net.Pipe()
|
||
|
|
defer secondRight.Close()
|
||
|
|
if err := server.attachAcceptedLogicalTransport(client.LogicalConn(), nil, secondLeft); err != nil {
|
||
|
|
t.Fatalf("attachAcceptedLogicalTransport failed: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
reattached, err := GetClientConnRuntimeSnapshot(client)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("GetClientConnRuntimeSnapshot reattached failed: %v", err)
|
||
|
|
}
|
||
|
|
if !reattached.TransportAttached {
|
||
|
|
t.Fatalf("reattached TransportAttached mismatch: got %v want true", reattached.TransportAttached)
|
||
|
|
}
|
||
|
|
if got, want := reattached.TransportGeneration, uint64(2); got != want {
|
||
|
|
t.Fatalf("reattached TransportGeneration mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if got := reattached.TransportDetachGeneration; got != 0 {
|
||
|
|
t.Fatalf("reattached TransportDetachGeneration mismatch: got %d want 0", got)
|
||
|
|
}
|
||
|
|
if got, want := reattached.TransportAttachCount, uint64(2); got != want {
|
||
|
|
t.Fatalf("reattached TransportAttachCount mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if got, want := reattached.TransportDetachCount, uint64(1); got != want {
|
||
|
|
t.Fatalf("reattached TransportDetachCount mismatch: got %d want %d", got, want)
|
||
|
|
}
|
||
|
|
if reattached.ReattachEligible {
|
||
|
|
t.Fatal("reattached ReattachEligible mismatch: got true want false")
|
||
|
|
}
|
||
|
|
}
|