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: ®istry.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 }