github.com/asynkron/protoactor-go@v0.0.0-20240308120642-ef91a6abee75/cluster/informer_test.go (about)

     1  package cluster
     2  
     3  import (
     4  	"fmt"
     5  	"log/slog"
     6  	"sync"
     7  	"testing"
     8  
     9  	"github.com/asynkron/gofun/set"
    10  	"google.golang.org/protobuf/types/known/anypb"
    11  )
    12  
    13  func TestInformer_SetState(t *testing.T) {
    14  	t.Parallel()
    15  
    16  	a := func() set.Set[string] {
    17  		return set.New[string]()
    18  	}
    19  
    20  	s := &MemberHeartbeat{
    21  		ActorStatistics: &ActorStatistics{},
    22  	}
    23  
    24  	i := newInformer("member1", a, 3, 3, slog.Default())
    25  	i.SetState("heartbeat", s)
    26  }
    27  
    28  func TestInformer_GetState(t *testing.T) {
    29  	t.Parallel()
    30  
    31  	a := func() set.Set[string] {
    32  		return set.New[string]()
    33  	}
    34  
    35  	s := &MemberHeartbeat{
    36  		ActorStatistics: &ActorStatistics{},
    37  	}
    38  
    39  	i := newInformer("member1", a, 3, 3, slog.Default())
    40  	i.SetState("heartbeat", s)
    41  
    42  	m := i.GetState("heartbeat")
    43  
    44  	x, ok := m["member1"]
    45  
    46  	if !ok {
    47  		t.Error("not ok")
    48  	}
    49  
    50  	var s2 MemberHeartbeat
    51  	err := x.Value.UnmarshalTo(&s2)
    52  	if err != nil {
    53  		t.Error("unmarshal state error")
    54  	}
    55  }
    56  
    57  func TestInformer_ReceiveState(t *testing.T) {
    58  	t.Parallel()
    59  
    60  	a := func() set.Set[string] {
    61  		return set.New[string]()
    62  	}
    63  
    64  	s := &MemberHeartbeat{
    65  		ActorStatistics: &ActorStatistics{},
    66  	}
    67  	dummyValue, _ := anypb.New(s)
    68  
    69  	i := newInformer("member1", a, 3, 3, slog.Default())
    70  	i.SetState("heartbeat", s)
    71  
    72  	remoteState := &GossipState{
    73  		Members: GossipMemberStates{
    74  			"member2": {
    75  				Values: GossipKeyValues{
    76  					"heartbeat": {
    77  						Value:          dummyValue,
    78  						SequenceNumber: 1,
    79  					},
    80  				},
    81  			},
    82  		},
    83  	}
    84  
    85  	i.ReceiveState(remoteState)
    86  
    87  	m := i.GetState("heartbeat")
    88  
    89  	var ok bool
    90  
    91  	m1, ok := m["member1"]
    92  
    93  	if !ok {
    94  		t.Error("member1 is missing")
    95  	}
    96  
    97  	var s1 MemberHeartbeat
    98  
    99  	err := m1.Value.UnmarshalTo(&s1)
   100  	if err != nil {
   101  		t.Error("unmarshal member1 state error")
   102  	}
   103  
   104  	// ensure we see member2 after receiving state
   105  	m2, ok := m["member2"]
   106  
   107  	if !ok {
   108  		t.Error("member2 is missing")
   109  	}
   110  
   111  	var s2 MemberHeartbeat
   112  
   113  	err = m2.Value.UnmarshalTo(&s2)
   114  
   115  	if err != nil {
   116  		t.Error("unmarshal member2 state error")
   117  	}
   118  }
   119  
   120  func TestInformer_SendState(t *testing.T) {
   121  	t.Parallel()
   122  
   123  	a := func() set.Set[string] {
   124  		return set.New[string]()
   125  	}
   126  	wg := &sync.WaitGroup{}
   127  	wg.Add(1)
   128  
   129  	sendState := func(memberStateDelta *MemberStateDelta, member *Member) {
   130  		fmt.Printf("%+v\n", memberStateDelta) //nolint:forbidigo
   131  		wg.Done()
   132  	}
   133  
   134  	s := &MemberHeartbeat{
   135  		ActorStatistics: &ActorStatistics{},
   136  	}
   137  
   138  	i := newInformer("member1", a, 3, 3, slog.Default())
   139  	i.SetState("heartbeat", s)
   140  	// the cluster sees two nodes. itself and member2
   141  	i.UpdateClusterTopology(&ClusterTopology{
   142  		Members: []*Member{
   143  			{
   144  				Id:   "member2",
   145  				Host: "member2",
   146  				Port: 123,
   147  			},
   148  			{
   149  				Id:   "member1",
   150  				Host: "member1",
   151  				Port: 333,
   152  			},
   153  		},
   154  	})
   155  
   156  	// gossip never sends to self, so the only member we can send to is member2
   157  	i.SendState(sendState)
   158  	wg.Wait()
   159  }
   160  
   161  func TestInformer_UpdateClusterTopology(t *testing.T) {
   162  	t.Parallel()
   163  
   164  	a := func() set.Set[string] {
   165  		return set.New[string]()
   166  	}
   167  
   168  	s := &MemberHeartbeat{
   169  		ActorStatistics: &ActorStatistics{},
   170  	}
   171  	i := newInformer("member1", a, 3, 3, slog.Default())
   172  	i.SetState("heartbeat", s)
   173  	// the cluster sees two nodes. itself and member2
   174  	i.UpdateClusterTopology(&ClusterTopology{
   175  		Members: []*Member{
   176  			{
   177  				Id:   "member2",
   178  				Host: "member2",
   179  				Port: 123,
   180  			},
   181  			{
   182  				Id:   "member1",
   183  				Host: "member1",
   184  				Port: 333,
   185  			},
   186  		},
   187  	})
   188  
   189  	// TODO: how do we check that the cluster topology was updated?
   190  }
   191  
   192  func TestInformer_GetMemberStateDelta(t *testing.T) {
   193  	t.Parallel()
   194  
   195  	a := func() set.Set[string] {
   196  		return set.New[string]()
   197  	}
   198  
   199  	s := &MemberHeartbeat{
   200  		ActorStatistics: &ActorStatistics{},
   201  	}
   202  
   203  	i := newInformer("member1", a, 3, 3, slog.Default())
   204  	i.SetState("heartbeat", s)
   205  
   206  	m := i.GetMemberStateDelta("member1")
   207  
   208  	if m == nil {
   209  		t.Error("member state delta is nil")
   210  	}
   211  }