github.com/asynkron/protoactor-go@v0.0.0-20240308120642-ef91a6abee75/cluster/cluster_test.go (about) 1 package cluster 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 "testing" 8 "time" 9 10 "github.com/asynkron/protoactor-go/actor" 11 "github.com/asynkron/protoactor-go/remote" 12 "github.com/stretchr/testify/assert" 13 ) 14 15 // inmemoryProvider use for test 16 type inmemoryProvider struct { 17 cluster *Cluster 18 members map[string]*Member 19 self *Member 20 } 21 22 func newInmemoryProvider() *inmemoryProvider { 23 return &inmemoryProvider{members: map[string]*Member{}} 24 } 25 26 func (p *inmemoryProvider) init(c *Cluster) error { 27 name := c.Config.Name 28 host, port, err := c.ActorSystem.GetHostPort() 29 if err != nil { 30 return err 31 } 32 p.cluster = c 33 p.self = &Member{ 34 Host: host, 35 Port: int32(port), 36 Id: fmt.Sprintf("%s@%s:%d", name, host, port), 37 Kinds: c.GetClusterKinds(), 38 } 39 40 return nil 41 } 42 43 func (p *inmemoryProvider) publishClusterTopologyEvent() { 44 var members Members 45 for _, m := range p.members { 46 members = append(members, m) 47 } 48 49 res := members 50 51 p.cluster.MemberList.UpdateClusterTopology(res) 52 // p.cluster.ActorSystem.EventStream.Publish(res) 53 } 54 55 func (p *inmemoryProvider) StartMember(c *Cluster) error { 56 err := p.init(c) 57 if err != nil { 58 return err 59 } 60 p.members[p.self.Id] = p.self 61 p.publishClusterTopologyEvent() 62 return nil 63 } 64 65 func (p *inmemoryProvider) StartClient(c *Cluster) error { 66 err := p.init(c) 67 if err != nil { 68 return err 69 } 70 p.publishClusterTopologyEvent() 71 return nil 72 } 73 74 func (p *inmemoryProvider) Shutdown(graceful bool) error { 75 delete(p.members, p.self.Id) 76 77 return nil 78 } 79 80 type fakeIdentityLookup struct { 81 m sync.Map 82 } 83 84 func (l *fakeIdentityLookup) Get(identity *ClusterIdentity) *actor.PID { 85 if val, ok := l.m.Load(identity.Identity); ok { 86 return val.(*actor.PID) 87 } else { 88 // pid := actor.NewPID("127.0.0.1", fmt.Sprintf("%s/%s", identity.Kind, identity.Identity)) 89 // l.m.Store(identity.Identity, pid) 90 // return pid 91 } 92 return nil 93 } 94 95 func (l *fakeIdentityLookup) RemovePid(identity *ClusterIdentity, pid *actor.PID) { 96 if existPid := l.Get(identity); existPid.Equal(pid) { 97 l.m.Delete(identity.Identity) 98 } 99 } 100 101 func (lu *fakeIdentityLookup) Setup(cluster *Cluster, kinds []string, isClient bool) { 102 } 103 104 func (lu *fakeIdentityLookup) Shutdown() { 105 } 106 107 func newClusterForTest(name string, cp ClusterProvider, opts ...ConfigOption) *Cluster { 108 system := actor.NewActorSystem() 109 lookup := fakeIdentityLookup{} 110 cfg := Configure(name, cp, &lookup, remote.Configure("127.0.0.1", 0), opts...) 111 c := New(system, cfg) 112 113 c.MemberList = NewMemberList(c) 114 c.Config.RequestTimeoutTime = 1 * time.Second 115 c.Remote = remote.NewRemote(system, c.Config.RemoteConfig) 116 c.IdentityLookup = &lookup 117 return c 118 } 119 120 func TestCluster_Call(t *testing.T) { 121 assert := assert.New(t) 122 123 members := Members{ 124 { 125 Id: "1", 126 Host: "nonhost", 127 Port: -1, 128 Kinds: []string{"kind"}, 129 }, 130 } 131 c := newClusterForTest("mycluster", nil) 132 c.MemberList.UpdateClusterTopology(members) 133 t.Run("invalid kind", func(t *testing.T) { 134 msg := struct{}{} 135 resp, err := c.Request("name", "nonkind", &msg) 136 assert.ErrorContains(err, "max retries") 137 assert.Nil(resp) 138 }) 139 140 testProps := actor.PropsFromFunc( 141 func(context actor.Context) { 142 switch msg := context.Message().(type) { 143 case *struct{ Code int }: 144 msg.Code++ 145 context.Respond(msg) 146 } 147 }) 148 pid := c.ActorSystem.Root.Spawn(testProps) 149 assert.NotNil(pid) 150 c.PidCache.Set("name", "kind", pid) 151 t.Run("normal", func(t *testing.T) { 152 msg := struct{ Code int }{9527} 153 resp, err := c.Request("name", "kind", &msg) 154 assert.NoError(err) 155 assert.Equal(&struct{ Code int }{9528}, resp) 156 }) 157 158 t.Run("timeout", func(t *testing.T) { 159 msg := struct{}{} 160 resp, err := c.Request("name", "kind", &msg, WithTimeout(time.Millisecond)) 161 assert.ErrorIs(err, context.DeadlineExceeded) 162 assert.Nil(resp) 163 }) 164 } 165 166 func TestCluster_Get(t *testing.T) { 167 t.Skipf("Maintaining") 168 cp := newInmemoryProvider() 169 kind := NewKind("kind", actor.PropsFromFunc(func(ctx actor.Context) { 170 switch msg := ctx.Message().(type) { 171 case *actor.Started: 172 _ = msg 173 } 174 })) 175 c := newClusterForTest("mycluster", cp, WithKinds(kind)) 176 c.StartMember() 177 cp.publishClusterTopologyEvent() 178 t.Run("invalid kind", func(t *testing.T) { 179 assert := assert.New(t) 180 assert.Equal(1, c.MemberList.Length()) 181 pid := c.Get("name", "nonkind") 182 assert.Nil(pid) 183 }) 184 185 t.Run("ok", func(t *testing.T) { 186 assert := assert.New(t) 187 pid := c.Get("name", "kind") 188 assert.NotNil(pid) 189 }) 190 }