github.com/netdata/go.d.plugin@v0.58.1/modules/pika/pika_test.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package pika
     4  
     5  import (
     6  	"context"
     7  	"errors"
     8  	"os"
     9  	"testing"
    10  
    11  	"github.com/netdata/go.d.plugin/pkg/tlscfg"
    12  
    13  	"github.com/go-redis/redis/v8"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  var (
    19  	redisInfoAll, _ = os.ReadFile("testdata/redis/info_all.txt")
    20  	v340InfoAll, _  = os.ReadFile("testdata/v3.4.0/info_all.txt")
    21  )
    22  
    23  func Test_Testdata(t *testing.T) {
    24  	for name, data := range map[string][]byte{
    25  		"redisInfoAll": redisInfoAll,
    26  		"v340InfoAll":  v340InfoAll,
    27  	} {
    28  		require.NotNilf(t, data, name)
    29  	}
    30  }
    31  
    32  func TestNew(t *testing.T) {
    33  	assert.IsType(t, (*Pika)(nil), New())
    34  }
    35  
    36  func TestPika_Init(t *testing.T) {
    37  	tests := map[string]struct {
    38  		config   Config
    39  		wantFail bool
    40  	}{
    41  		"success on default config": {
    42  			config: New().Config,
    43  		},
    44  		"fails on unset 'address'": {
    45  			wantFail: true,
    46  			config:   Config{Address: ""},
    47  		},
    48  		"fails on invalid 'address' format": {
    49  			wantFail: true,
    50  			config:   Config{Address: "127.0.0.1:9221"},
    51  		},
    52  		"fails on invalid TLSCA": {
    53  			wantFail: true,
    54  			config: Config{
    55  				Address:   "redis://@127.0.0.1:9221",
    56  				TLSConfig: tlscfg.TLSConfig{TLSCA: "testdata/tls"},
    57  			},
    58  		},
    59  	}
    60  
    61  	for name, test := range tests {
    62  		t.Run(name, func(t *testing.T) {
    63  			pika := New()
    64  			pika.Config = test.config
    65  
    66  			if test.wantFail {
    67  				assert.False(t, pika.Init())
    68  			} else {
    69  				assert.True(t, pika.Init())
    70  			}
    71  		})
    72  	}
    73  }
    74  
    75  func TestPika_Check(t *testing.T) {
    76  	tests := map[string]struct {
    77  		prepare  func(t *testing.T) *Pika
    78  		wantFail bool
    79  	}{
    80  		"success on valid response v3.4.0": {
    81  			prepare: preparePikaV340,
    82  		},
    83  		"fails on error on Info": {
    84  			wantFail: true,
    85  			prepare:  preparePikaErrorOnInfo,
    86  		},
    87  		"fails on response from not Pika instance": {
    88  			wantFail: true,
    89  			prepare:  preparePikaWithRedisMetrics,
    90  		},
    91  	}
    92  
    93  	for name, test := range tests {
    94  		t.Run(name, func(t *testing.T) {
    95  			pika := test.prepare(t)
    96  
    97  			if test.wantFail {
    98  				assert.False(t, pika.Check())
    99  			} else {
   100  				assert.True(t, pika.Check())
   101  			}
   102  		})
   103  	}
   104  }
   105  
   106  func TestPika_Charts(t *testing.T) {
   107  	pika := New()
   108  	require.True(t, pika.Init())
   109  
   110  	assert.NotNil(t, pika.Charts())
   111  }
   112  
   113  func TestPika_Cleanup(t *testing.T) {
   114  	pika := New()
   115  	assert.NotPanics(t, pika.Cleanup)
   116  
   117  	require.True(t, pika.Init())
   118  	m := &mockRedisClient{}
   119  	pika.pdb = m
   120  
   121  	pika.Cleanup()
   122  
   123  	assert.True(t, m.calledClose)
   124  }
   125  
   126  func TestPika_Collect(t *testing.T) {
   127  	tests := map[string]struct {
   128  		prepare       func(t *testing.T) *Pika
   129  		wantCollected map[string]int64
   130  	}{
   131  		"success on valid response v3.4.0": {
   132  			prepare: preparePikaV340,
   133  			wantCollected: map[string]int64{
   134  				"cmd_INFO_calls":             1,
   135  				"cmd_SET_calls":              2,
   136  				"arch_bits":                  64,
   137  				"connected_clients":          1,
   138  				"connected_slaves":           0,
   139  				"db0_hashes_expires_keys":    0,
   140  				"db0_hashes_invalid_keys":    0,
   141  				"db0_hashes_keys":            0,
   142  				"db0_lists_expires_keys":     0,
   143  				"db0_lists_invalid_keys":     0,
   144  				"db0_lists_keys":             0,
   145  				"db0_sets_expires_keys":      0,
   146  				"db0_sets_invalid_keys":      0,
   147  				"db0_sets_keys":              0,
   148  				"db0_strings_expires_keys":   0,
   149  				"db0_strings_invalid_keys":   0,
   150  				"db0_strings_keys":           0,
   151  				"db0_zsets_expires_keys":     0,
   152  				"db0_zsets_invalid_keys":     0,
   153  				"db0_zsets_keys":             0,
   154  				"instantaneous_ops_per_sec":  0,
   155  				"log_size":                   4272814,
   156  				"process_id":                 1,
   157  				"server_id":                  1,
   158  				"sync_thread_num":            6,
   159  				"tcp_port":                   9221,
   160  				"thread_num":                 1,
   161  				"total_commands_processed":   3,
   162  				"total_connections_received": 3,
   163  				"uptime_in_days":             1,
   164  				"uptime_in_seconds":          1884,
   165  				"used_cpu_sys":               158200,
   166  				"used_cpu_sys_children":      30,
   167  				"used_cpu_user":              22050,
   168  				"used_cpu_user_children":     20,
   169  				"used_memory":                8198,
   170  			},
   171  		},
   172  		"fails on error on Info": {
   173  			prepare: preparePikaErrorOnInfo,
   174  		},
   175  		"fails on response from not Pika instance": {
   176  			prepare: preparePikaWithRedisMetrics,
   177  		},
   178  	}
   179  
   180  	for name, test := range tests {
   181  		t.Run(name, func(t *testing.T) {
   182  			pika := test.prepare(t)
   183  
   184  			ms := pika.Collect()
   185  
   186  			assert.Equal(t, test.wantCollected, ms)
   187  			if len(test.wantCollected) > 0 {
   188  				ensureCollectedHasAllChartsDimsVarsIDs(t, pika, ms)
   189  				ensureCollectedCommandsAddedToCharts(t, pika)
   190  				ensureCollectedDbsAddedToCharts(t, pika)
   191  			}
   192  		})
   193  	}
   194  }
   195  
   196  func preparePikaV340(t *testing.T) *Pika {
   197  	pika := New()
   198  	require.True(t, pika.Init())
   199  	pika.pdb = &mockRedisClient{
   200  		result: v340InfoAll,
   201  	}
   202  	return pika
   203  }
   204  
   205  func preparePikaErrorOnInfo(t *testing.T) *Pika {
   206  	pika := New()
   207  	require.True(t, pika.Init())
   208  	pika.pdb = &mockRedisClient{
   209  		errOnInfo: true,
   210  	}
   211  	return pika
   212  }
   213  
   214  func preparePikaWithRedisMetrics(t *testing.T) *Pika {
   215  	pika := New()
   216  	require.True(t, pika.Init())
   217  	pika.pdb = &mockRedisClient{
   218  		result: redisInfoAll,
   219  	}
   220  	return pika
   221  }
   222  
   223  func ensureCollectedHasAllChartsDimsVarsIDs(t *testing.T, pika *Pika, ms map[string]int64) {
   224  	for _, chart := range *pika.Charts() {
   225  		if chart.Obsolete {
   226  			continue
   227  		}
   228  		for _, dim := range chart.Dims {
   229  			_, ok := ms[dim.ID]
   230  			assert.Truef(t, ok, "chart '%s' dim '%s': no dim in collected", dim.ID, chart.ID)
   231  		}
   232  		for _, v := range chart.Vars {
   233  			_, ok := ms[v.ID]
   234  			assert.Truef(t, ok, "chart '%s' dim '%s': no dim in collected", v.ID, chart.ID)
   235  		}
   236  	}
   237  }
   238  
   239  func ensureCollectedCommandsAddedToCharts(t *testing.T, pika *Pika) {
   240  	for _, id := range []string{
   241  		chartCommandsCalls.ID,
   242  	} {
   243  		chart := pika.Charts().Get(id)
   244  		require.NotNilf(t, chart, "'%s' chart is not in charts", id)
   245  		assert.Lenf(t, chart.Dims, len(pika.collectedCommands),
   246  			"'%s' chart unexpected number of dimensions", id)
   247  	}
   248  }
   249  
   250  func ensureCollectedDbsAddedToCharts(t *testing.T, pika *Pika) {
   251  	for _, id := range []string{
   252  		chartDbStringsKeys.ID,
   253  		chartDbStringsExpiresKeys.ID,
   254  		chartDbStringsInvalidKeys.ID,
   255  		chartDbHashesKeys.ID,
   256  		chartDbHashesExpiresKeys.ID,
   257  		chartDbHashesInvalidKeys.ID,
   258  		chartDbListsKeys.ID,
   259  		chartDbListsExpiresKeys.ID,
   260  		chartDbListsInvalidKeys.ID,
   261  		chartDbZsetsKeys.ID,
   262  		chartDbZsetsExpiresKeys.ID,
   263  		chartDbZsetsInvalidKeys.ID,
   264  		chartDbSetsKeys.ID,
   265  		chartDbSetsExpiresKeys.ID,
   266  		chartDbSetsInvalidKeys.ID,
   267  	} {
   268  		chart := pika.Charts().Get(id)
   269  		require.NotNilf(t, chart, "'%s' chart is not in charts", id)
   270  		assert.Lenf(t, chart.Dims, len(pika.collectedDbs),
   271  			"'%s' chart unexpected number of dimensions", id)
   272  	}
   273  }
   274  
   275  type mockRedisClient struct {
   276  	errOnInfo   bool
   277  	result      []byte
   278  	calledClose bool
   279  }
   280  
   281  func (m mockRedisClient) Info(_ context.Context, _ ...string) (cmd *redis.StringCmd) {
   282  	if m.errOnInfo {
   283  		cmd = redis.NewStringResult("", errors.New("error on Info"))
   284  	} else {
   285  		cmd = redis.NewStringResult(string(m.result), nil)
   286  	}
   287  	return cmd
   288  }
   289  
   290  func (m *mockRedisClient) Close() error {
   291  	m.calledClose = true
   292  	return nil
   293  }