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  }