github.com/volts-dev/volts@v0.0.0-20240120094013-5e9c65924106/registry/consul/registry_test.go (about)

     1  package consul
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"net"
     8  	"net/http"
     9  	"testing"
    10  	"time"
    11  
    12  	consul "github.com/hashicorp/consul/api"
    13  	"github.com/volts-dev/volts/registry"
    14  )
    15  
    16  type mockRegistry struct {
    17  	body   []byte
    18  	status int
    19  	err    error
    20  	url    string
    21  }
    22  
    23  func encodeData(obj interface{}) ([]byte, error) {
    24  	buf := bytes.NewBuffer(nil)
    25  	enc := json.NewEncoder(buf)
    26  	if err := enc.Encode(obj); err != nil {
    27  		return nil, err
    28  	}
    29  	return buf.Bytes(), nil
    30  }
    31  
    32  func newMockServer(rg *mockRegistry, l net.Listener) error {
    33  	mux := http.NewServeMux()
    34  	mux.HandleFunc(rg.url, func(w http.ResponseWriter, r *http.Request) {
    35  		if rg.err != nil {
    36  			http.Error(w, rg.err.Error(), 500)
    37  			return
    38  		}
    39  		w.WriteHeader(rg.status)
    40  		w.Write(rg.body)
    41  	})
    42  	return http.Serve(l, mux)
    43  }
    44  
    45  func newConsulTestRegistry(r *mockRegistry) (*consulRegistry, func()) {
    46  	l, err := net.Listen("tcp", "localhost:0")
    47  	if err != nil {
    48  		// blurgh?!!
    49  		panic(err.Error())
    50  	}
    51  	cfg := consul.DefaultConfig()
    52  	cfg.Address = l.Addr().String()
    53  
    54  	go newMockServer(r, l)
    55  
    56  	var cr = &consulRegistry{
    57  		config:      cfg,
    58  		Address:     []string{cfg.Address},
    59  		opts:        &registry.Config{},
    60  		register:    make(map[string]uint64),
    61  		lastChecked: make(map[string]time.Time),
    62  		queryOptions: &consul.QueryOptions{
    63  			AllowStale: true,
    64  		},
    65  	}
    66  	cr.Client()
    67  
    68  	return cr, func() {
    69  		l.Close()
    70  	}
    71  }
    72  
    73  func newServiceList(svc []*consul.ServiceEntry) []byte {
    74  	bts, _ := encodeData(svc)
    75  	return bts
    76  }
    77  
    78  func TestConsul_GetService_WithError(t *testing.T) {
    79  	cr, cl := newConsulTestRegistry(&mockRegistry{
    80  		err: errors.New("client-error"),
    81  		url: "/v1/health/service/service-name",
    82  	})
    83  	defer cl()
    84  
    85  	if _, err := cr.GetService("test-service"); err == nil {
    86  		t.Fatalf("Expected error not to be `nil`")
    87  	}
    88  }
    89  
    90  func TestConsul_GetService_WithHealthyServiceNodes(t *testing.T) {
    91  	// warning is still seen as healthy, critical is not
    92  	svcs := []*consul.ServiceEntry{
    93  		newServiceEntry(
    94  			"node-name-1", "node-address-1", "service-name", "v1.0.0",
    95  			[]*consul.HealthCheck{
    96  				newHealthCheck("node-name-1", "service-name", "passing"),
    97  				newHealthCheck("node-name-1", "service-name", "warning"),
    98  			},
    99  		),
   100  		newServiceEntry(
   101  			"node-name-2", "node-address-2", "service-name", "v1.0.0",
   102  			[]*consul.HealthCheck{
   103  				newHealthCheck("node-name-2", "service-name", "passing"),
   104  				newHealthCheck("node-name-2", "service-name", "warning"),
   105  			},
   106  		),
   107  	}
   108  
   109  	cr, cl := newConsulTestRegistry(&mockRegistry{
   110  		status: 200,
   111  		body:   newServiceList(svcs),
   112  		url:    "/v1/health/service/service-name",
   113  	})
   114  	defer cl()
   115  
   116  	svc, err := cr.GetService("service-name")
   117  	if err != nil {
   118  		t.Fatal("Unexpected error", err)
   119  	}
   120  
   121  	if exp, act := 1, len(svc); exp != act {
   122  		t.Fatalf("Expected len of svc to be `%d`, got `%d`.", exp, act)
   123  	}
   124  
   125  	if exp, act := 2, len(svc[0].Nodes); exp != act {
   126  		t.Fatalf("Expected len of nodes to be `%d`, got `%d`.", exp, act)
   127  	}
   128  }
   129  
   130  func TestConsul_GetService_WithUnhealthyServiceNode(t *testing.T) {
   131  	// warning is still seen as healthy, critical is not
   132  	svcs := []*consul.ServiceEntry{
   133  		newServiceEntry(
   134  			"node-name-1", "node-address-1", "service-name", "v1.0.0",
   135  			[]*consul.HealthCheck{
   136  				newHealthCheck("node-name-1", "service-name", "passing"),
   137  				newHealthCheck("node-name-1", "service-name", "warning"),
   138  			},
   139  		),
   140  		newServiceEntry(
   141  			"node-name-2", "node-address-2", "service-name", "v1.0.0",
   142  			[]*consul.HealthCheck{
   143  				newHealthCheck("node-name-2", "service-name", "passing"),
   144  				newHealthCheck("node-name-2", "service-name", "critical"),
   145  			},
   146  		),
   147  	}
   148  
   149  	cr, cl := newConsulTestRegistry(&mockRegistry{
   150  		status: 200,
   151  		body:   newServiceList(svcs),
   152  		url:    "/v1/health/service/service-name",
   153  	})
   154  	defer cl()
   155  
   156  	svc, err := cr.GetService("service-name")
   157  	if err != nil {
   158  		t.Fatal("Unexpected error", err)
   159  	}
   160  
   161  	if exp, act := 1, len(svc); exp != act {
   162  		t.Fatalf("Expected len of svc to be `%d`, got `%d`.", exp, act)
   163  	}
   164  
   165  	if exp, act := 1, len(svc[0].Nodes); exp != act {
   166  		t.Fatalf("Expected len of nodes to be `%d`, got `%d`.", exp, act)
   167  	}
   168  }
   169  
   170  func TestConsul_GetService_WithUnhealthyServiceNodes(t *testing.T) {
   171  	// warning is still seen as healthy, critical is not
   172  	svcs := []*consul.ServiceEntry{
   173  		newServiceEntry(
   174  			"node-name-1", "node-address-1", "service-name", "v1.0.0",
   175  			[]*consul.HealthCheck{
   176  				newHealthCheck("node-name-1", "service-name", "passing"),
   177  				newHealthCheck("node-name-1", "service-name", "critical"),
   178  			},
   179  		),
   180  		newServiceEntry(
   181  			"node-name-2", "node-address-2", "service-name", "v1.0.0",
   182  			[]*consul.HealthCheck{
   183  				newHealthCheck("node-name-2", "service-name", "passing"),
   184  				newHealthCheck("node-name-2", "service-name", "critical"),
   185  			},
   186  		),
   187  	}
   188  
   189  	cr, cl := newConsulTestRegistry(&mockRegistry{
   190  		status: 200,
   191  		body:   newServiceList(svcs),
   192  		url:    "/v1/health/service/service-name",
   193  	})
   194  	defer cl()
   195  
   196  	svc, err := cr.GetService("service-name")
   197  	if err != nil {
   198  		t.Fatal("Unexpected error", err)
   199  	}
   200  
   201  	if exp, act := 1, len(svc); exp != act {
   202  		t.Fatalf("Expected len of svc to be `%d`, got `%d`.", exp, act)
   203  	}
   204  
   205  	if exp, act := 0, len(svc[0].Nodes); exp != act {
   206  		t.Fatalf("Expected len of nodes to be `%d`, got `%d`.", exp, act)
   207  	}
   208  }