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 }