github.com/netdata/go.d.plugin@v0.58.1/modules/haproxy/haproxy_test.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package haproxy 4 5 import ( 6 "net/http" 7 "net/http/httptest" 8 "os" 9 "testing" 10 11 "github.com/netdata/go.d.plugin/pkg/tlscfg" 12 "github.com/netdata/go.d.plugin/pkg/web" 13 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 ) 17 18 var ( 19 v2310Metrics, _ = os.ReadFile("testdata/v2.3.10/metrics.txt") 20 ) 21 22 func Test_Testdata(t *testing.T) { 23 for name, data := range map[string][]byte{ 24 "v2310Metrics": v2310Metrics, 25 } { 26 require.NotNilf(t, data, name) 27 } 28 } 29 30 func TestNew(t *testing.T) { 31 assert.IsType(t, (*Haproxy)(nil), New()) 32 } 33 34 func TestHaproxy_Init(t *testing.T) { 35 tests := map[string]struct { 36 config Config 37 wantFail bool 38 }{ 39 "success on default config": { 40 config: New().Config, 41 }, 42 "fails on unset 'url'": { 43 wantFail: true, 44 config: Config{HTTP: web.HTTP{ 45 Request: web.Request{}, 46 }}, 47 }, 48 "fails on invalid TLSCA": { 49 wantFail: true, 50 config: Config{ 51 HTTP: web.HTTP{ 52 Client: web.Client{ 53 TLSConfig: tlscfg.TLSConfig{TLSCA: "testdata/tls"}, 54 }, 55 }}, 56 }, 57 } 58 59 for name, test := range tests { 60 t.Run(name, func(t *testing.T) { 61 rdb := New() 62 rdb.Config = test.config 63 64 if test.wantFail { 65 assert.False(t, rdb.Init()) 66 } else { 67 assert.True(t, rdb.Init()) 68 } 69 }) 70 } 71 } 72 73 func TestHaproxy_Charts(t *testing.T) { 74 assert.NotNil(t, New().Charts()) 75 } 76 77 func TestHaproxy_Cleanup(t *testing.T) { 78 assert.NotPanics(t, New().Cleanup) 79 } 80 81 func TestHaproxy_Check(t *testing.T) { 82 tests := map[string]struct { 83 wantFail bool 84 prepare func(t *testing.T) (h *Haproxy, cleanup func()) 85 }{ 86 "success on valid response v2.3.1": { 87 wantFail: false, 88 prepare: prepareCaseHaproxyV231Metrics, 89 }, 90 "fails on response with unexpected metrics (not HAProxy)": { 91 wantFail: true, 92 prepare: prepareCaseNotHaproxyMetrics, 93 }, 94 "fails on 404 response": { 95 wantFail: true, 96 prepare: prepareCase404Response, 97 }, 98 "fails on connection refused": { 99 wantFail: true, 100 prepare: prepareCaseConnectionRefused, 101 }, 102 } 103 104 for name, test := range tests { 105 t.Run(name, func(t *testing.T) { 106 h, cleanup := test.prepare(t) 107 defer cleanup() 108 109 if test.wantFail { 110 assert.False(t, h.Check()) 111 } else { 112 assert.True(t, h.Check()) 113 } 114 }) 115 } 116 } 117 118 func TestHaproxy_Collect(t *testing.T) { 119 tests := map[string]struct { 120 prepare func(t *testing.T) (h *Haproxy, cleanup func()) 121 wantCollected map[string]int64 122 }{ 123 "success on valid response v2.3.1": { 124 prepare: prepareCaseHaproxyV231Metrics, 125 wantCollected: map[string]int64{ 126 "haproxy_backend_bytes_in_proxy_proxy1": 21057046294, 127 "haproxy_backend_bytes_in_proxy_proxy2": 2493759083896, 128 "haproxy_backend_bytes_out_proxy_proxy1": 41352782609, 129 "haproxy_backend_bytes_out_proxy_proxy2": 5131407558, 130 "haproxy_backend_current_queue_proxy_proxy1": 1, 131 "haproxy_backend_current_queue_proxy_proxy2": 1, 132 "haproxy_backend_current_sessions_proxy_proxy1": 1, 133 "haproxy_backend_current_sessions_proxy_proxy2": 1322, 134 "haproxy_backend_http_responses_1xx_proxy_proxy1": 1, 135 "haproxy_backend_http_responses_1xx_proxy_proxy2": 4130401, 136 "haproxy_backend_http_responses_2xx_proxy_proxy1": 21338013, 137 "haproxy_backend_http_responses_2xx_proxy_proxy2": 1, 138 "haproxy_backend_http_responses_3xx_proxy_proxy1": 10004, 139 "haproxy_backend_http_responses_3xx_proxy_proxy2": 1, 140 "haproxy_backend_http_responses_4xx_proxy_proxy1": 10170758, 141 "haproxy_backend_http_responses_4xx_proxy_proxy2": 1, 142 "haproxy_backend_http_responses_5xx_proxy_proxy1": 3075, 143 "haproxy_backend_http_responses_5xx_proxy_proxy2": 1, 144 "haproxy_backend_http_responses_other_proxy_proxy1": 5657, 145 "haproxy_backend_http_responses_other_proxy_proxy2": 1, 146 "haproxy_backend_queue_time_average_proxy_proxy1": 0, 147 "haproxy_backend_queue_time_average_proxy_proxy2": 0, 148 "haproxy_backend_response_time_average_proxy_proxy1": 52, 149 "haproxy_backend_response_time_average_proxy_proxy2": 1, 150 "haproxy_backend_sessions_proxy_proxy1": 31527507, 151 "haproxy_backend_sessions_proxy_proxy2": 4131723, 152 }, 153 }, 154 "fails on response with unexpected metrics (not HAProxy)": { 155 prepare: prepareCaseNotHaproxyMetrics, 156 }, 157 "fails on 404 response": { 158 prepare: prepareCase404Response, 159 }, 160 "fails on connection refused": { 161 prepare: prepareCaseConnectionRefused, 162 }, 163 } 164 165 for name, test := range tests { 166 t.Run(name, func(t *testing.T) { 167 h, cleanup := test.prepare(t) 168 defer cleanup() 169 170 ms := h.Collect() 171 172 assert.Equal(t, test.wantCollected, ms) 173 if len(test.wantCollected) > 0 { 174 ensureCollectedHasAllChartsDimsVarsIDs(t, h, ms) 175 } 176 }) 177 } 178 } 179 180 func prepareCaseHaproxyV231Metrics(t *testing.T) (*Haproxy, func()) { 181 t.Helper() 182 srv := httptest.NewServer(http.HandlerFunc( 183 func(w http.ResponseWriter, r *http.Request) { 184 _, _ = w.Write(v2310Metrics) 185 })) 186 h := New() 187 h.URL = srv.URL 188 require.True(t, h.Init()) 189 190 return h, srv.Close 191 } 192 193 func prepareCaseNotHaproxyMetrics(t *testing.T) (*Haproxy, func()) { 194 t.Helper() 195 srv := httptest.NewServer(http.HandlerFunc( 196 func(w http.ResponseWriter, r *http.Request) { 197 _, _ = w.Write([]byte(` 198 # HELP haproxy_backend_http_responses_total Total number of HTTP responses. 199 # TYPE haproxy_backend_http_responses_total counter 200 application_backend_http_responses_total{proxy="infra-traefik-web",code="1xx"} 0 201 application_backend_http_responses_total{proxy="infra-vernemq-ws",code="1xx"} 4130401 202 application_backend_http_responses_total{proxy="infra-traefik-web",code="2xx"} 21338013 203 application_backend_http_responses_total{proxy="infra-vernemq-ws",code="2xx"} 0 204 application_backend_http_responses_total{proxy="infra-traefik-web",code="3xx"} 10004 205 application_backend_http_responses_total{proxy="infra-vernemq-ws",code="3xx"} 0 206 application_backend_http_responses_total{proxy="infra-traefik-web",code="4xx"} 10170758 207 application_backend_http_responses_total{proxy="infra-vernemq-ws",code="4xx"} 0 208 application_backend_http_responses_total{proxy="infra-traefik-web",code="5xx"} 3075 209 application_backend_http_responses_total{proxy="infra-vernemq-ws",code="5xx"} 0 210 application_backend_http_responses_total{proxy="infra-traefik-web",code="other"} 5657 211 application_backend_http_responses_total{proxy="infra-vernemq-ws",code="other"} 0 212 `)) 213 })) 214 h := New() 215 h.URL = srv.URL 216 require.True(t, h.Init()) 217 218 return h, srv.Close 219 } 220 221 func prepareCase404Response(t *testing.T) (*Haproxy, func()) { 222 t.Helper() 223 srv := httptest.NewServer(http.HandlerFunc( 224 func(w http.ResponseWriter, r *http.Request) { 225 w.WriteHeader(http.StatusNotFound) 226 })) 227 h := New() 228 h.URL = srv.URL 229 require.True(t, h.Init()) 230 231 return h, srv.Close 232 } 233 234 func prepareCaseConnectionRefused(t *testing.T) (*Haproxy, func()) { 235 t.Helper() 236 h := New() 237 h.URL = "http://127.0.0.1:38001" 238 require.True(t, h.Init()) 239 240 return h, func() {} 241 } 242 243 func ensureCollectedHasAllChartsDimsVarsIDs(t *testing.T, h *Haproxy, ms map[string]int64) { 244 for _, chart := range *h.Charts() { 245 if chart.Obsolete { 246 continue 247 } 248 for _, dim := range chart.Dims { 249 _, ok := ms[dim.ID] 250 assert.Truef(t, ok, "chart '%s' dim '%s': no dim in collected", dim.ID, chart.ID) 251 } 252 for _, v := range chart.Vars { 253 _, ok := ms[v.ID] 254 assert.Truef(t, ok, "chart '%s' dim '%s': no dim in collected", v.ID, chart.ID) 255 } 256 } 257 }