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