master
go 372 lines 14.8 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package monit
4
5 import (
6 "context"
7 "net/http"
8 "net/http/httptest"
9 "os"
10 "testing"
11
12 "github.com/netdata/netdata/go/plugins/pkg/web"
13 "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/collecttest"
14
15 "github.com/stretchr/testify/assert"
16 "github.com/stretchr/testify/require"
17 )
18
19 var (
20 dataConfigJSON, _ = os.ReadFile("testdata/config.json")
21 dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
22
23 dataStatus, _ = os.ReadFile("testdata/v5.33.0/status.xml")
24 )
25
26 func Test_testDataIsValid(t *testing.T) {
27 for name, data := range map[string][]byte{
28 "dataConfigJSON": dataConfigJSON,
29 "dataConfigYAML": dataConfigYAML,
30 "dataStatus": dataStatus,
31 } {
32 require.NotNil(t, data, name)
33
34 }
35 }
36
37 func TestCollector_ConfigurationSerialize(t *testing.T) {
38 collecttest.TestConfigurationSerialize(t, &Collector{}, dataConfigJSON, dataConfigYAML)
39 }
40
41 func TestCollector_Init(t *testing.T) {
42 tests := map[string]struct {
43 wantFail bool
44 config Config
45 }{
46 "success with default": {
47 wantFail: false,
48 config: New().Config,
49 },
50 "fail when URL not set": {
51 wantFail: true,
52 config: Config{
53 HTTPConfig: web.HTTPConfig{
54 RequestConfig: web.RequestConfig{URL: ""},
55 },
56 },
57 },
58 }
59
60 for name, test := range tests {
61 t.Run(name, func(t *testing.T) {
62 collr := New()
63 collr.Config = test.config
64
65 if test.wantFail {
66 assert.Error(t, collr.Init(context.Background()))
67 } else {
68 assert.NoError(t, collr.Init(context.Background()))
69 }
70 })
71 }
72 }
73
74 func TestCollector_Check(t *testing.T) {
75 tests := map[string]struct {
76 wantFail bool
77 prepare func(t *testing.T) (monit *Collector, cleanup func())
78 }{
79 "success on valid response": {
80 wantFail: false,
81 prepare: caseOk,
82 },
83 "fail on unexpected XML response": {
84 wantFail: true,
85 prepare: caseUnexpectedXMLResponse,
86 },
87 "fail on invalid data response": {
88 wantFail: true,
89 prepare: caseInvalidDataResponse,
90 },
91 "fail on connection refused": {
92 wantFail: true,
93 prepare: caseConnectionRefused,
94 },
95 "fail on 404 response": {
96 wantFail: true,
97 prepare: case404,
98 },
99 }
100
101 for name, test := range tests {
102 t.Run(name, func(t *testing.T) {
103 collr, cleanup := test.prepare(t)
104 defer cleanup()
105
106 if test.wantFail {
107 assert.Error(t, collr.Check(context.Background()))
108 } else {
109 assert.NoError(t, collr.Check(context.Background()))
110 }
111 })
112 }
113 }
114
115 func TestCollector_Charts(t *testing.T) {
116 assert.NotNil(t, New().Charts())
117 }
118
119 func TestCollector_Collect(t *testing.T) {
120 tests := map[string]struct {
121 prepare func(t *testing.T) (monit *Collector, cleanup func())
122 wantNumOfCharts int
123 wantMetrics map[string]int64
124 }{
125 "success on valid response": {
126 prepare: caseOk,
127 wantNumOfCharts: len(baseCharts) + len(serviceCheckChartsTmpl)*25,
128 wantMetrics: map[string]int64{
129 "service_check_type_directory_name_directoryAlert_status_error": 1,
130 "service_check_type_directory_name_directoryAlert_status_initializing": 0,
131 "service_check_type_directory_name_directoryAlert_status_not_monitored": 0,
132 "service_check_type_directory_name_directoryAlert_status_ok": 0,
133 "service_check_type_directory_name_directoryDisabled_status_error": 0,
134 "service_check_type_directory_name_directoryDisabled_status_initializing": 0,
135 "service_check_type_directory_name_directoryDisabled_status_not_monitored": 1,
136 "service_check_type_directory_name_directoryDisabled_status_ok": 0,
137 "service_check_type_directory_name_directoryNotExists_status_error": 1,
138 "service_check_type_directory_name_directoryNotExists_status_initializing": 0,
139 "service_check_type_directory_name_directoryNotExists_status_not_monitored": 0,
140 "service_check_type_directory_name_directoryNotExists_status_ok": 0,
141 "service_check_type_directory_name_directoryOk_status_error": 0,
142 "service_check_type_directory_name_directoryOk_status_initializing": 0,
143 "service_check_type_directory_name_directoryOk_status_not_monitored": 0,
144 "service_check_type_directory_name_directoryOk_status_ok": 1,
145 "service_check_type_file_name_fileAlert_status_error": 1,
146 "service_check_type_file_name_fileAlert_status_initializing": 0,
147 "service_check_type_file_name_fileAlert_status_not_monitored": 0,
148 "service_check_type_file_name_fileAlert_status_ok": 0,
149 "service_check_type_file_name_fileDisabled_status_error": 0,
150 "service_check_type_file_name_fileDisabled_status_initializing": 0,
151 "service_check_type_file_name_fileDisabled_status_not_monitored": 1,
152 "service_check_type_file_name_fileDisabled_status_ok": 0,
153 "service_check_type_file_name_fileNotExists_status_error": 1,
154 "service_check_type_file_name_fileNotExists_status_initializing": 0,
155 "service_check_type_file_name_fileNotExists_status_not_monitored": 0,
156 "service_check_type_file_name_fileNotExists_status_ok": 0,
157 "service_check_type_file_name_fileOk_status_error": 0,
158 "service_check_type_file_name_fileOk_status_initializing": 0,
159 "service_check_type_file_name_fileOk_status_not_monitored": 0,
160 "service_check_type_file_name_fileOk_status_ok": 1,
161 "service_check_type_filesystem_name_filesystemAlert_status_error": 1,
162 "service_check_type_filesystem_name_filesystemAlert_status_initializing": 0,
163 "service_check_type_filesystem_name_filesystemAlert_status_not_monitored": 0,
164 "service_check_type_filesystem_name_filesystemAlert_status_ok": 0,
165 "service_check_type_filesystem_name_filesystemDisabled_status_error": 0,
166 "service_check_type_filesystem_name_filesystemDisabled_status_initializing": 0,
167 "service_check_type_filesystem_name_filesystemDisabled_status_not_monitored": 1,
168 "service_check_type_filesystem_name_filesystemDisabled_status_ok": 0,
169 "service_check_type_filesystem_name_filesystemNotExists_status_error": 1,
170 "service_check_type_filesystem_name_filesystemNotExists_status_initializing": 0,
171 "service_check_type_filesystem_name_filesystemNotExists_status_not_monitored": 0,
172 "service_check_type_filesystem_name_filesystemNotExists_status_ok": 0,
173 "service_check_type_filesystem_name_filsystemOk_status_error": 0,
174 "service_check_type_filesystem_name_filsystemOk_status_initializing": 0,
175 "service_check_type_filesystem_name_filsystemOk_status_not_monitored": 0,
176 "service_check_type_filesystem_name_filsystemOk_status_ok": 1,
177 "service_check_type_host_name_hostAlert_status_error": 1,
178 "service_check_type_host_name_hostAlert_status_initializing": 0,
179 "service_check_type_host_name_hostAlert_status_not_monitored": 0,
180 "service_check_type_host_name_hostAlert_status_ok": 0,
181 "service_check_type_host_name_hostDisabled_status_error": 0,
182 "service_check_type_host_name_hostDisabled_status_initializing": 0,
183 "service_check_type_host_name_hostDisabled_status_not_monitored": 1,
184 "service_check_type_host_name_hostDisabled_status_ok": 0,
185 "service_check_type_host_name_hostNotExists_status_error": 1,
186 "service_check_type_host_name_hostNotExists_status_initializing": 0,
187 "service_check_type_host_name_hostNotExists_status_not_monitored": 0,
188 "service_check_type_host_name_hostNotExists_status_ok": 0,
189 "service_check_type_host_name_hostOk_status_error": 0,
190 "service_check_type_host_name_hostOk_status_initializing": 0,
191 "service_check_type_host_name_hostOk_status_not_monitored": 0,
192 "service_check_type_host_name_hostOk_status_ok": 1,
193 "service_check_type_network_name_networkAlert_status_error": 1,
194 "service_check_type_network_name_networkAlert_status_initializing": 0,
195 "service_check_type_network_name_networkAlert_status_not_monitored": 0,
196 "service_check_type_network_name_networkAlert_status_ok": 0,
197 "service_check_type_network_name_networkDisabled_status_error": 0,
198 "service_check_type_network_name_networkDisabled_status_initializing": 0,
199 "service_check_type_network_name_networkDisabled_status_not_monitored": 1,
200 "service_check_type_network_name_networkDisabled_status_ok": 0,
201 "service_check_type_network_name_networkNotExists_status_error": 1,
202 "service_check_type_network_name_networkNotExists_status_initializing": 0,
203 "service_check_type_network_name_networkNotExists_status_not_monitored": 0,
204 "service_check_type_network_name_networkNotExists_status_ok": 0,
205 "service_check_type_network_name_networkOk_status_error": 0,
206 "service_check_type_network_name_networkOk_status_initializing": 0,
207 "service_check_type_network_name_networkOk_status_not_monitored": 0,
208 "service_check_type_network_name_networkOk_status_ok": 1,
209 "service_check_type_process_name_processAlert_status_error": 1,
210 "service_check_type_process_name_processAlert_status_initializing": 0,
211 "service_check_type_process_name_processAlert_status_not_monitored": 0,
212 "service_check_type_process_name_processAlert_status_ok": 0,
213 "service_check_type_process_name_processDisabled_status_error": 0,
214 "service_check_type_process_name_processDisabled_status_initializing": 0,
215 "service_check_type_process_name_processDisabled_status_not_monitored": 1,
216 "service_check_type_process_name_processDisabled_status_ok": 0,
217 "service_check_type_process_name_processNotExists_status_error": 1,
218 "service_check_type_process_name_processNotExists_status_initializing": 0,
219 "service_check_type_process_name_processNotExists_status_not_monitored": 0,
220 "service_check_type_process_name_processNotExists_status_ok": 0,
221 "service_check_type_process_name_processOk_status_error": 0,
222 "service_check_type_process_name_processOk_status_initializing": 0,
223 "service_check_type_process_name_processOk_status_not_monitored": 0,
224 "service_check_type_process_name_processOk_status_ok": 1,
225 "service_check_type_system_name_pve-deb-work_status_error": 0,
226 "service_check_type_system_name_pve-deb-work_status_initializing": 0,
227 "service_check_type_system_name_pve-deb-work_status_not_monitored": 0,
228 "service_check_type_system_name_pve-deb-work_status_ok": 1,
229 "uptime": 33,
230 },
231 },
232 "fail on unexpected XML response": {
233 prepare: caseUnexpectedXMLResponse,
234 wantNumOfCharts: len(baseCharts),
235 wantMetrics: nil,
236 },
237 "fail on invalid data response": {
238 prepare: caseInvalidDataResponse,
239 wantNumOfCharts: len(baseCharts),
240 wantMetrics: nil,
241 },
242 "fail on connection refused": {
243 prepare: caseConnectionRefused,
244 wantNumOfCharts: len(baseCharts),
245 wantMetrics: nil,
246 },
247 "fail on 404 response": {
248 prepare: case404,
249 wantNumOfCharts: len(baseCharts),
250 wantMetrics: nil,
251 },
252 }
253
254 for name, test := range tests {
255 t.Run(name, func(t *testing.T) {
256 collr, cleanup := test.prepare(t)
257 defer cleanup()
258
259 _ = collr.Check(context.Background())
260
261 mx := collr.Collect(context.Background())
262
263 require.Equal(t, test.wantMetrics, mx)
264
265 if len(test.wantMetrics) > 0 {
266 collecttest.TestMetricsHasAllChartsDims(t, collr.Charts(), mx)
267 assert.Equal(t, test.wantNumOfCharts, len(*collr.Charts()), "want number of charts")
268 }
269 })
270 }
271 }
272
273 func caseOk(t *testing.T) (*Collector, func()) {
274 t.Helper()
275 srv := httptest.NewServer(http.HandlerFunc(
276 func(w http.ResponseWriter, r *http.Request) {
277 if r.URL.Path != urlPathStatus || r.URL.RawQuery != urlQueryStatus {
278 w.WriteHeader(http.StatusNotFound)
279 return
280 }
281 _, _ = w.Write(dataStatus)
282 }))
283 collr := New()
284 collr.URL = srv.URL
285 require.NoError(t, collr.Init(context.Background()))
286
287 return collr, srv.Close
288 }
289
290 func caseUnexpectedXMLResponse(t *testing.T) (*Collector, func()) {
291 t.Helper()
292 data := `<?xml version="1.0" encoding="UTF-8"?>
293 <Response>
294 <Status>
295 <Code>200</Code>
296 <Message>Success</Message>
297 </Status>
298 <Data>
299 <User>
300 <ID>12345</ID>
301 <Name>John Doe</Name>
302 <Email>johndoe@example.com</Email>
303 <Roles>
304 <Role>Admin</Role>
305 <Role>User</Role>
306 </Roles>
307 </User>
308 <Order>
309 <OrderID>98765</OrderID>
310 <Date>2024-08-15</Date>
311 <Items>
312 <Item>
313 <Name>Widget A</Name>
314 <Quantity>2</Quantity>
315 <Price>19.99</Price>
316 </Item>
317 <Item>
318 <Name>Gadget B</Name>
319 <Quantity>1</Quantity>
320 <Price>99.99</Price>
321 </Item>
322 </Items>
323 <Total>139.97</Total>
324 </Order>
325 </Data>
326 </Response>
327 `
328 srv := httptest.NewServer(http.HandlerFunc(
329 func(w http.ResponseWriter, r *http.Request) {
330 _, _ = w.Write([]byte(data))
331 }))
332 collr := New()
333 collr.URL = srv.URL
334 require.NoError(t, collr.Init(context.Background()))
335
336 return collr, srv.Close
337 }
338
339 func caseInvalidDataResponse(t *testing.T) (*Collector, func()) {
340 t.Helper()
341 srv := httptest.NewServer(http.HandlerFunc(
342 func(w http.ResponseWriter, r *http.Request) {
343 _, _ = w.Write([]byte("hello and\n goodbye"))
344 }))
345 collr := New()
346 collr.URL = srv.URL
347 require.NoError(t, collr.Init(context.Background()))
348
349 return collr, srv.Close
350 }
351
352 func caseConnectionRefused(t *testing.T) (*Collector, func()) {
353 t.Helper()
354 collr := New()
355 collr.URL = "http://127.0.0.1:65001"
356 require.NoError(t, collr.Init(context.Background()))
357
358 return collr, func() {}
359 }
360
361 func case404(t *testing.T) (*Collector, func()) {
362 t.Helper()
363 srv := httptest.NewServer(http.HandlerFunc(
364 func(w http.ResponseWriter, r *http.Request) {
365 w.WriteHeader(http.StatusNotFound)
366 }))
367 collr := New()
368 collr.URL = srv.URL
369 require.NoError(t, collr.Init(context.Background()))
370
371 return collr, srv.Close
372 }