summaryrefslogtreecommitdiffstats
path: root/src/go/collectors/go.d.plugin/modules/httpcheck/httpcheck_test.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/go/collectors/go.d.plugin/modules/httpcheck/httpcheck_test.go604
1 files changed, 604 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/httpcheck/httpcheck_test.go b/src/go/collectors/go.d.plugin/modules/httpcheck/httpcheck_test.go
new file mode 100644
index 000000000..dde5761eb
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/httpcheck/httpcheck_test.go
@@ -0,0 +1,604 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package httpcheck
+
+import (
+ "github.com/netdata/netdata/go/go.d.plugin/agent/module"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "testing"
+ "time"
+
+ "github.com/netdata/netdata/go/go.d.plugin/pkg/web"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+var (
+ dataConfigJSON, _ = os.ReadFile("testdata/config.json")
+ dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
+)
+
+func Test_testDataIsValid(t *testing.T) {
+ for name, data := range map[string][]byte{
+ "dataConfigJSON": dataConfigJSON,
+ "dataConfigYAML": dataConfigYAML,
+ } {
+ require.NotNil(t, data, name)
+ }
+}
+
+func TestHTTPCheck_ConfigurationSerialize(t *testing.T) {
+ module.TestConfigurationSerialize(t, &HTTPCheck{}, dataConfigJSON, dataConfigYAML)
+}
+
+func TestHTTPCheck_Init(t *testing.T) {
+ tests := map[string]struct {
+ wantFail bool
+ config Config
+ }{
+ "success if url set": {
+ wantFail: false,
+ config: Config{
+ HTTP: web.HTTP{
+ Request: web.Request{URL: "http://127.0.0.1:38001"},
+ },
+ },
+ },
+ "fail with default": {
+ wantFail: true,
+ config: New().Config,
+ },
+ "fail when URL not set": {
+ wantFail: true,
+ config: Config{
+ HTTP: web.HTTP{
+ Request: web.Request{URL: ""},
+ },
+ },
+ },
+ "fail if wrong response regex": {
+ wantFail: true,
+ config: Config{
+ HTTP: web.HTTP{
+ Request: web.Request{URL: "http://127.0.0.1:38001"},
+ },
+ ResponseMatch: "(?:qwe))",
+ },
+ },
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ httpCheck := New()
+ httpCheck.Config = test.config
+
+ if test.wantFail {
+ assert.Error(t, httpCheck.Init())
+ } else {
+ assert.NoError(t, httpCheck.Init())
+ }
+ })
+ }
+}
+
+func TestHTTPCheck_Charts(t *testing.T) {
+ tests := map[string]struct {
+ prepare func(t *testing.T) *HTTPCheck
+ wantCharts bool
+ }{
+ "no charts if not inited": {
+ wantCharts: false,
+ prepare: func(t *testing.T) *HTTPCheck {
+ return New()
+ },
+ },
+ "charts if inited": {
+ wantCharts: true,
+ prepare: func(t *testing.T) *HTTPCheck {
+ httpCheck := New()
+ httpCheck.URL = "http://127.0.0.1:38001"
+ require.NoError(t, httpCheck.Init())
+
+ return httpCheck
+ },
+ },
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ httpCheck := test.prepare(t)
+
+ if test.wantCharts {
+ assert.NotNil(t, httpCheck.Charts())
+ } else {
+ assert.Nil(t, httpCheck.Charts())
+ }
+ })
+ }
+}
+
+func TestHTTPCheck_Cleanup(t *testing.T) {
+ httpCheck := New()
+ assert.NotPanics(t, httpCheck.Cleanup)
+
+ httpCheck.URL = "http://127.0.0.1:38001"
+ require.NoError(t, httpCheck.Init())
+ assert.NotPanics(t, httpCheck.Cleanup)
+}
+
+func TestHTTPCheck_Check(t *testing.T) {
+ tests := map[string]struct {
+ prepare func() (httpCheck *HTTPCheck, cleanup func())
+ wantFail bool
+ }{
+ "success case": {wantFail: false, prepare: prepareSuccessCase},
+ "timeout case": {wantFail: false, prepare: prepareTimeoutCase},
+ "redirect success": {wantFail: false, prepare: prepareRedirectSuccessCase},
+ "redirect fail": {wantFail: false, prepare: prepareRedirectFailCase},
+ "bad status case": {wantFail: false, prepare: prepareBadStatusCase},
+ "bad content case": {wantFail: false, prepare: prepareBadContentCase},
+ "no connection case": {wantFail: false, prepare: prepareNoConnectionCase},
+ "cookie auth case": {wantFail: false, prepare: prepareCookieAuthCase},
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ httpCheck, cleanup := test.prepare()
+ defer cleanup()
+
+ require.NoError(t, httpCheck.Init())
+
+ if test.wantFail {
+ assert.Error(t, httpCheck.Check())
+ } else {
+ assert.NoError(t, httpCheck.Check())
+ }
+ })
+ }
+
+}
+
+func TestHTTPCheck_Collect(t *testing.T) {
+ tests := map[string]struct {
+ prepare func() (httpCheck *HTTPCheck, cleanup func())
+ update func(check *HTTPCheck)
+ wantMetrics map[string]int64
+ }{
+ "success case": {
+ prepare: prepareSuccessCase,
+ wantMetrics: map[string]int64{
+ "bad_content": 0,
+ "bad_header": 0,
+ "bad_status": 0,
+ "in_state": 2,
+ "length": 5,
+ "no_connection": 0,
+ "redirect": 0,
+ "success": 1,
+ "time": 0,
+ "timeout": 0,
+ },
+ },
+ "timeout case": {
+ prepare: prepareTimeoutCase,
+ wantMetrics: map[string]int64{
+ "bad_content": 0,
+ "bad_header": 0,
+ "bad_status": 0,
+ "in_state": 2,
+ "length": 0,
+ "no_connection": 0,
+ "redirect": 0,
+ "success": 0,
+ "time": 0,
+ "timeout": 1,
+ },
+ },
+ "redirect success case": {
+ prepare: prepareRedirectSuccessCase,
+ wantMetrics: map[string]int64{
+ "bad_content": 0,
+ "bad_header": 0,
+ "bad_status": 0,
+ "in_state": 2,
+ "length": 0,
+ "no_connection": 0,
+ "redirect": 0,
+ "success": 1,
+ "time": 0,
+ "timeout": 0,
+ },
+ },
+ "redirect fail case": {
+ prepare: prepareRedirectFailCase,
+ wantMetrics: map[string]int64{
+ "bad_content": 0,
+ "bad_header": 0,
+ "bad_status": 0,
+ "in_state": 2,
+ "length": 0,
+ "no_connection": 0,
+ "redirect": 1,
+ "success": 0,
+ "time": 0,
+ "timeout": 0,
+ },
+ },
+ "bad status case": {
+ prepare: prepareBadStatusCase,
+ wantMetrics: map[string]int64{
+ "bad_content": 0,
+ "bad_header": 0,
+ "bad_status": 1,
+ "in_state": 2,
+ "length": 0,
+ "no_connection": 0,
+ "redirect": 0,
+ "success": 0,
+ "time": 0,
+ "timeout": 0,
+ },
+ },
+ "bad content case": {
+ prepare: prepareBadContentCase,
+ wantMetrics: map[string]int64{
+ "bad_content": 1,
+ "bad_header": 0,
+ "bad_status": 0,
+ "in_state": 2,
+ "length": 17,
+ "no_connection": 0,
+ "redirect": 0,
+ "success": 0,
+ "time": 0,
+ "timeout": 0,
+ },
+ },
+ "no connection case": {
+ prepare: prepareNoConnectionCase,
+ wantMetrics: map[string]int64{
+ "bad_content": 0,
+ "bad_header": 0,
+ "bad_status": 0,
+ "in_state": 2,
+ "length": 0,
+ "no_connection": 1,
+ "redirect": 0,
+ "success": 0,
+ "time": 0,
+ "timeout": 0,
+ },
+ },
+ "header match include no value success case": {
+ prepare: prepareSuccessCase,
+ update: func(httpCheck *HTTPCheck) {
+ httpCheck.HeaderMatch = []headerMatchConfig{
+ {Key: "header-key2"},
+ }
+ },
+ wantMetrics: map[string]int64{
+ "bad_content": 0,
+ "bad_header": 0,
+ "bad_status": 0,
+ "in_state": 2,
+ "length": 5,
+ "no_connection": 0,
+ "redirect": 0,
+ "success": 1,
+ "time": 0,
+ "timeout": 0,
+ },
+ },
+ "header match include with value success case": {
+ prepare: prepareSuccessCase,
+ update: func(httpCheck *HTTPCheck) {
+ httpCheck.HeaderMatch = []headerMatchConfig{
+ {Key: "header-key2", Value: "= header-value"},
+ }
+ },
+ wantMetrics: map[string]int64{
+ "bad_content": 0,
+ "bad_header": 0,
+ "bad_status": 0,
+ "in_state": 2,
+ "length": 5,
+ "no_connection": 0,
+ "redirect": 0,
+ "success": 1,
+ "time": 0,
+ "timeout": 0,
+ },
+ },
+ "header match include no value bad headers case": {
+ prepare: prepareSuccessCase,
+ update: func(httpCheck *HTTPCheck) {
+ httpCheck.HeaderMatch = []headerMatchConfig{
+ {Key: "header-key99"},
+ }
+ },
+ wantMetrics: map[string]int64{
+ "bad_content": 0,
+ "bad_header": 1,
+ "bad_status": 0,
+ "in_state": 2,
+ "length": 5,
+ "no_connection": 0,
+ "redirect": 0,
+ "success": 0,
+ "time": 0,
+ "timeout": 0,
+ },
+ },
+ "header match include with value bad headers case": {
+ prepare: prepareSuccessCase,
+ update: func(httpCheck *HTTPCheck) {
+ httpCheck.HeaderMatch = []headerMatchConfig{
+ {Key: "header-key2", Value: "= header-value99"},
+ }
+ },
+ wantMetrics: map[string]int64{
+ "bad_content": 0,
+ "bad_header": 1,
+ "bad_status": 0,
+ "in_state": 2,
+ "length": 5,
+ "no_connection": 0,
+ "redirect": 0,
+ "success": 0,
+ "time": 0,
+ "timeout": 0,
+ },
+ },
+ "header match exclude no value success case": {
+ prepare: prepareSuccessCase,
+ update: func(httpCheck *HTTPCheck) {
+ httpCheck.HeaderMatch = []headerMatchConfig{
+ {Exclude: true, Key: "header-key99"},
+ }
+ },
+ wantMetrics: map[string]int64{
+ "bad_content": 0,
+ "bad_header": 0,
+ "bad_status": 0,
+ "in_state": 2,
+ "length": 5,
+ "no_connection": 0,
+ "redirect": 0,
+ "success": 1,
+ "time": 0,
+ "timeout": 0,
+ },
+ },
+ "header match exclude with value success case": {
+ prepare: prepareSuccessCase,
+ update: func(httpCheck *HTTPCheck) {
+ httpCheck.HeaderMatch = []headerMatchConfig{
+ {Exclude: true, Key: "header-key2", Value: "= header-value99"},
+ }
+ },
+ wantMetrics: map[string]int64{
+ "bad_content": 0,
+ "bad_header": 0,
+ "bad_status": 0,
+ "in_state": 2,
+ "length": 5,
+ "no_connection": 0,
+ "redirect": 0,
+ "success": 1,
+ "time": 0,
+ "timeout": 0,
+ },
+ },
+ "header match exclude no value bad headers case": {
+ prepare: prepareSuccessCase,
+ update: func(httpCheck *HTTPCheck) {
+ httpCheck.HeaderMatch = []headerMatchConfig{
+ {Exclude: true, Key: "header-key2"},
+ }
+ },
+ wantMetrics: map[string]int64{
+ "bad_content": 0,
+ "bad_header": 1,
+ "bad_status": 0,
+ "in_state": 2,
+ "length": 5,
+ "no_connection": 0,
+ "redirect": 0,
+ "success": 0,
+ "time": 0,
+ "timeout": 0,
+ },
+ },
+ "header match exclude with value bad headers case": {
+ prepare: prepareSuccessCase,
+ update: func(httpCheck *HTTPCheck) {
+ httpCheck.HeaderMatch = []headerMatchConfig{
+ {Exclude: true, Key: "header-key2", Value: "= header-value"},
+ }
+ },
+ wantMetrics: map[string]int64{
+ "bad_content": 0,
+ "bad_header": 1,
+ "bad_status": 0,
+ "in_state": 2,
+ "length": 5,
+ "no_connection": 0,
+ "redirect": 0,
+ "success": 0,
+ "time": 0,
+ "timeout": 0,
+ },
+ },
+ "cookie auth case": {
+ prepare: prepareCookieAuthCase,
+ wantMetrics: map[string]int64{
+ "bad_content": 0,
+ "bad_header": 0,
+ "bad_status": 0,
+ "in_state": 2,
+ "length": 0,
+ "no_connection": 0,
+ "redirect": 0,
+ "success": 1,
+ "time": 0,
+ "timeout": 0,
+ },
+ },
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ httpCheck, cleanup := test.prepare()
+ defer cleanup()
+
+ if test.update != nil {
+ test.update(httpCheck)
+ }
+
+ require.NoError(t, httpCheck.Init())
+
+ var mx map[string]int64
+
+ for i := 0; i < 2; i++ {
+ mx = httpCheck.Collect()
+ time.Sleep(time.Duration(httpCheck.UpdateEvery) * time.Second)
+ }
+
+ copyResponseTime(test.wantMetrics, mx)
+
+ require.Equal(t, test.wantMetrics, mx)
+ })
+ }
+}
+
+func prepareSuccessCase() (*HTTPCheck, func()) {
+ httpCheck := New()
+ httpCheck.UpdateEvery = 1
+ httpCheck.ResponseMatch = "match"
+
+ srv := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("header-key1", "header-value")
+ w.Header().Set("header-key2", "header-value")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte("match"))
+ }))
+
+ httpCheck.URL = srv.URL
+
+ return httpCheck, srv.Close
+}
+
+func prepareTimeoutCase() (*HTTPCheck, func()) {
+ httpCheck := New()
+ httpCheck.UpdateEvery = 1
+ httpCheck.Timeout = web.Duration(time.Millisecond * 100)
+
+ srv := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ time.Sleep(httpCheck.Timeout.Duration() + time.Millisecond*100)
+ }))
+
+ httpCheck.URL = srv.URL
+
+ return httpCheck, srv.Close
+}
+
+func prepareRedirectSuccessCase() (*HTTPCheck, func()) {
+ httpCheck := New()
+ httpCheck.UpdateEvery = 1
+ httpCheck.NotFollowRedirect = true
+ httpCheck.AcceptedStatuses = []int{301}
+
+ srv := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ http.Redirect(w, r, "https://example.com", http.StatusMovedPermanently)
+ }))
+
+ httpCheck.URL = srv.URL
+
+ return httpCheck, srv.Close
+}
+
+func prepareRedirectFailCase() (*HTTPCheck, func()) {
+ httpCheck := New()
+ httpCheck.UpdateEvery = 1
+ httpCheck.NotFollowRedirect = true
+
+ srv := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ http.Redirect(w, r, "https://example.com", http.StatusMovedPermanently)
+ }))
+
+ httpCheck.URL = srv.URL
+
+ return httpCheck, srv.Close
+}
+
+func prepareBadStatusCase() (*HTTPCheck, func()) {
+ httpCheck := New()
+ httpCheck.UpdateEvery = 1
+
+ srv := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusBadGateway)
+ }))
+
+ httpCheck.URL = srv.URL
+
+ return httpCheck, srv.Close
+}
+
+func prepareBadContentCase() (*HTTPCheck, func()) {
+ httpCheck := New()
+ httpCheck.UpdateEvery = 1
+ httpCheck.ResponseMatch = "no match"
+
+ srv := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte("hello and goodbye"))
+ }))
+
+ httpCheck.URL = srv.URL
+
+ return httpCheck, srv.Close
+}
+
+func prepareNoConnectionCase() (*HTTPCheck, func()) {
+ httpCheck := New()
+ httpCheck.UpdateEvery = 1
+ httpCheck.URL = "http://127.0.0.1:38001"
+
+ return httpCheck, func() {}
+}
+
+func prepareCookieAuthCase() (*HTTPCheck, func()) {
+ httpCheck := New()
+ httpCheck.UpdateEvery = 1
+ httpCheck.CookieFile = "testdata/cookie.txt"
+
+ srv := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ if _, err := r.Cookie("JSESSIONID"); err != nil {
+ w.WriteHeader(http.StatusUnauthorized)
+ } else {
+ w.WriteHeader(http.StatusOK)
+ }
+ }))
+
+ httpCheck.URL = srv.URL
+
+ return httpCheck, srv.Close
+}
+
+func copyResponseTime(dst, src map[string]int64) {
+ if v, ok := src["time"]; ok {
+ if _, ok := dst["time"]; ok {
+ dst["time"] = v
+ }
+ }
+}