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

     1  package consul
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"strconv"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/asynkron/protoactor-go/cluster/identitylookup/disthash"
    11  
    12  	"github.com/asynkron/protoactor-go/actor"
    13  	"github.com/asynkron/protoactor-go/cluster"
    14  	"github.com/asynkron/protoactor-go/remote"
    15  	"github.com/stretchr/testify/assert"
    16  )
    17  
    18  func newClusterForTest(name string, addr string, cp cluster.ClusterProvider) *cluster.Cluster {
    19  	host, _port, err := net.SplitHostPort(addr)
    20  	if err != nil {
    21  		panic(err)
    22  	}
    23  	port, _ := strconv.Atoi(_port)
    24  	remoteConfig := remote.Configure(host, port)
    25  	lookup := disthash.New()
    26  	config := cluster.Configure(name, cp, lookup, remoteConfig)
    27  	// return cluster.NewForTest(system, config)
    28  
    29  	system := actor.NewActorSystem()
    30  	c := cluster.New(system, config)
    31  
    32  	// use for test without start remote
    33  	c.ActorSystem.ProcessRegistry.Address = addr
    34  	c.MemberList = cluster.NewMemberList(c)
    35  	c.Remote = remote.NewRemote(c.ActorSystem, c.Config.RemoteConfig)
    36  	return c
    37  }
    38  
    39  func TestStartMember(t *testing.T) {
    40  	if testing.Short() {
    41  		return
    42  	}
    43  	a := assert.New(t)
    44  
    45  	p, _ := New()
    46  	defer p.Shutdown(true)
    47  
    48  	c := newClusterForTest("mycluster", "127.0.0.1:8000", p)
    49  	eventstream := c.ActorSystem.EventStream
    50  	ch := make(chan interface{}, 16)
    51  	eventstream.Subscribe(func(m interface{}) {
    52  		if _, ok := m.(*cluster.ClusterTopology); ok {
    53  			ch <- m
    54  		}
    55  	})
    56  
    57  	err := p.StartMember(c)
    58  	a.NoError(err)
    59  
    60  	select {
    61  	case <-time.After(10 * time.Second):
    62  		a.FailNow("no member joined yet")
    63  
    64  	case m := <-ch:
    65  		msg := m.(*cluster.ClusterTopology)
    66  		// member joined
    67  		members := []*cluster.Member{
    68  			{
    69  				// Id:    "mycluster@127.0.0.1:8000",
    70  				Id:    fmt.Sprintf("%s", c.ActorSystem.ID),
    71  				Host:  "127.0.0.1",
    72  				Port:  8000,
    73  				Kinds: []string{},
    74  			},
    75  		}
    76  
    77  		expected := &cluster.ClusterTopology{
    78  			Members:      members,
    79  			Joined:       members,
    80  			Left:         []*cluster.Member{},
    81  			TopologyHash: msg.TopologyHash,
    82  		}
    83  		a.Equal(expected, msg)
    84  	}
    85  }
    86  
    87  func TestRegisterMultipleMembers(t *testing.T) {
    88  	if testing.Short() {
    89  		return
    90  	}
    91  	a := assert.New(t)
    92  
    93  	members := []struct {
    94  		cluster string
    95  		host    string
    96  		port    int
    97  	}{
    98  		{"mycluster2", "127.0.0.1", 8001},
    99  		{"mycluster2", "127.0.0.1", 8002},
   100  		{"mycluster2", "127.0.0.1", 8003},
   101  	}
   102  
   103  	p, _ := New()
   104  	defer p.Shutdown(true)
   105  	for _, member := range members {
   106  		addr := fmt.Sprintf("%s:%d", member.host, member.port)
   107  		_p, _ := New()
   108  		c := newClusterForTest(member.cluster, addr, _p)
   109  		err := p.StartMember(c)
   110  		a.NoError(err)
   111  		t.Cleanup(func() {
   112  			_p.Shutdown(true)
   113  		})
   114  	}
   115  
   116  	entries, _, err := p.client.Health().Service("mycluster2", "", true, nil)
   117  	a.NoError(err)
   118  
   119  	found := false
   120  	for _, entry := range entries {
   121  		found = false
   122  		for _, member := range members {
   123  			if entry.Service.Port == member.port {
   124  				found = true
   125  			}
   126  		}
   127  		a.Truef(found, "Member port not found - ExtensionID:%v Address: %v:%v",
   128  			entry.Service.ID, entry.Service.Address, entry.Service.Port)
   129  	}
   130  }
   131  
   132  func TestUpdateTTL_DoesNotReregisterAfterShutdown(t *testing.T) {
   133  	if testing.Short() {
   134  		return
   135  	}
   136  	a := assert.New(t)
   137  
   138  	p, _ := New()
   139  	c := newClusterForTest("mycluster5", "127.0.0.1:8001", p)
   140  
   141  	shutdownShouldHaveResolved := make(chan bool, 1)
   142  
   143  	err := p.StartMember(c)
   144  	a.NoError(err)
   145  
   146  	time.Sleep(time.Second)
   147  	found, _ := findService(t, p)
   148  	a.True(found, "service was not registered in consul")
   149  
   150  	go func() {
   151  		// if after 5 seconds `Shutdown` did not resolve, assume that it will not resolve until `blockingUpdateTTL` resolves
   152  		time.Sleep(5 * time.Second)
   153  		shutdownShouldHaveResolved <- true
   154  	}()
   155  
   156  	err = p.Shutdown(true)
   157  	a.NoError(err)
   158  	shutdownShouldHaveResolved <- true
   159  
   160  	// since `UpdateTTL` runs in a separate goroutine we need to wait until it is actually finished before checking the member's clusterstatus
   161  	p.updateTTLWaitGroup.Wait()
   162  	found, status := findService(t, p)
   163  	a.Falsef(found, "service was still registered in consul after shutdown (service status: %s)", status)
   164  }
   165  
   166  func findService(t *testing.T, p *Provider) (found bool, status string) {
   167  	service := p.cluster.Config.Name
   168  	port := p.cluster.Config.RemoteConfig.Port
   169  	entries, _, err := p.client.Health().Service(service, "", false, nil)
   170  	if err != nil {
   171  		t.Error(err)
   172  	}
   173  
   174  	for _, entry := range entries {
   175  		if entry.Service.Port == port {
   176  			return true, entry.Checks.AggregatedStatus()
   177  		}
   178  	}
   179  	return false, ""
   180  }