master
go 496 lines 16 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package upsd
4
5 import (
6 "context"
7 "errors"
8 "fmt"
9 "os"
10 "testing"
11
12 "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/collecttest"
13
14 "github.com/stretchr/testify/assert"
15 "github.com/stretchr/testify/require"
16 )
17
18 var (
19 dataConfigJSON, _ = os.ReadFile("testdata/config.json")
20 dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
21 )
22
23 func Test_testDataIsValid(t *testing.T) {
24 for name, data := range map[string][]byte{
25 "dataConfigJSON": dataConfigJSON,
26 "dataConfigYAML": dataConfigYAML,
27 } {
28 require.NotNil(t, data, name)
29 }
30 }
31
32 func TestCollector_ConfigurationSerialize(t *testing.T) {
33 collecttest.TestConfigurationSerialize(t, &Collector{}, dataConfigJSON, dataConfigYAML)
34 }
35
36 func TestCollector_Cleanup(t *testing.T) {
37 collr := New()
38
39 require.NotPanics(t, func() { collr.Cleanup(context.Background()) })
40
41 mock := prepareMockConnOK()
42 collr.newUpsdConn = func(Config) upsdConn { return mock }
43
44 require.NoError(t, collr.Init(context.Background()))
45 _ = collr.Collect(context.Background())
46 require.NotPanics(t, func() { collr.Cleanup(context.Background()) })
47 assert.True(t, mock.calledDisconnect)
48 }
49
50 func TestCollector_Init(t *testing.T) {
51 tests := map[string]struct {
52 config Config
53 wantFail bool
54 }{
55 "success on default config": {
56 wantFail: false,
57 config: New().Config,
58 },
59 "fails when 'address' option not set": {
60 wantFail: true,
61 config: Config{Address: ""},
62 },
63 }
64
65 for name, test := range tests {
66 t.Run(name, func(t *testing.T) {
67 collr := New()
68 collr.Config = test.config
69
70 if test.wantFail {
71 assert.Error(t, collr.Init(context.Background()))
72 } else {
73 assert.NoError(t, collr.Init(context.Background()))
74 }
75 })
76 }
77 }
78
79 func TestCollector_Check(t *testing.T) {
80 tests := map[string]struct {
81 prepareUpsd func() *Collector
82 prepareMock func() *mockUpsdConn
83 wantFail bool
84 }{
85 "successful data collection": {
86 wantFail: false,
87 prepareUpsd: New,
88 prepareMock: prepareMockConnOK,
89 },
90 "error on connect()": {
91 wantFail: true,
92 prepareUpsd: New,
93 prepareMock: prepareMockConnErrOnConnect,
94 },
95 "error on authenticate()": {
96 wantFail: true,
97 prepareUpsd: func() *Collector {
98 collr := New()
99 collr.Username = "user"
100 collr.Password = "pass"
101 return collr
102 },
103 prepareMock: prepareMockConnErrOnAuthenticate,
104 },
105 "error on upsList()": {
106 wantFail: true,
107 prepareUpsd: New,
108 prepareMock: prepareMockConnErrOnUpsUnits,
109 },
110 }
111
112 for name, test := range tests {
113 t.Run(name, func(t *testing.T) {
114 collr := test.prepareUpsd()
115 collr.newUpsdConn = func(Config) upsdConn { return test.prepareMock() }
116
117 require.NoError(t, collr.Init(context.Background()))
118
119 if test.wantFail {
120 assert.Error(t, collr.Check(context.Background()))
121 } else {
122 assert.NoError(t, collr.Check(context.Background()))
123 }
124 })
125 }
126 }
127
128 func TestCollector_Charts(t *testing.T) {
129 collr := New()
130 require.NoError(t, collr.Init(context.Background()))
131 assert.NotNil(t, collr.Charts())
132 }
133
134 func TestCollector_Collect(t *testing.T) {
135 tests := map[string]struct {
136 prepareUpsd func() *Collector
137 prepareMock func() *mockUpsdConn
138 wantCollected map[string]int64
139 wantCharts int
140 wantConnConnect bool
141 wantConnDisconnect bool
142 wantConnAuthenticate bool
143 }{
144 "successful data collection": {
145 prepareUpsd: New,
146 prepareMock: prepareMockConnOK,
147 wantCollected: map[string]int64{
148 "ups_cp1500_battery.charge": 10000,
149 "ups_cp1500_battery.runtime": 489000,
150 "ups_cp1500_battery.voltage": 2400,
151 "ups_cp1500_battery.voltage.nominal": 2400,
152 "ups_cp1500_input.voltage": 22700,
153 "ups_cp1500_input.voltage.nominal": 23000,
154 "ups_cp1500_output.voltage": 26000,
155 "ups_cp1500_ups.load": 800,
156 "ups_cp1500_ups.load.usage": 4300,
157 "ups_cp1500_ups.realpower.nominal": 90000,
158 "ups_cp1500_ups.status.BOOST": 0,
159 "ups_cp1500_ups.status.BYPASS": 0,
160 "ups_cp1500_ups.status.CAL": 0,
161 "ups_cp1500_ups.status.CHRG": 0,
162 "ups_cp1500_ups.status.DISCHRG": 0,
163 "ups_cp1500_ups.status.FSD": 0,
164 "ups_cp1500_ups.status.HB": 0,
165 "ups_cp1500_ups.status.LB": 0,
166 "ups_cp1500_ups.status.OB": 0,
167 "ups_cp1500_ups.status.OFF": 0,
168 "ups_cp1500_ups.status.OL": 1,
169 "ups_cp1500_ups.status.OVER": 0,
170 "ups_cp1500_ups.status.RB": 0,
171 "ups_cp1500_ups.status.TRIM": 0,
172 "ups_cp1500_ups.status.other": 0,
173 "ups_cp1600_battery.charge": 10000,
174 "ups_cp1600_battery.runtime": 197000,
175 "ups_cp1600_battery.voltage": 5480,
176 "ups_cp1600_battery.voltage.nominal": 4800,
177 "ups_cp1600_ups.status.BOOST": 0,
178 "ups_cp1600_ups.status.BYPASS": 0,
179 "ups_cp1600_ups.status.CAL": 0,
180 "ups_cp1600_ups.status.CHRG": 0,
181 "ups_cp1600_ups.status.DISCHRG": 0,
182 "ups_cp1600_ups.status.FSD": 0,
183 "ups_cp1600_ups.status.HB": 0,
184 "ups_cp1600_ups.status.LB": 0,
185 "ups_cp1600_ups.status.OB": 0,
186 "ups_cp1600_ups.status.OFF": 0,
187 "ups_cp1600_ups.status.OL": 1,
188 "ups_cp1600_ups.status.OVER": 0,
189 "ups_cp1600_ups.status.RB": 0,
190 "ups_cp1600_ups.status.TRIM": 0,
191 "ups_cp1600_ups.status.other": 0,
192 "ups_pr3000_battery.charge": 10000,
193 "ups_pr3000_battery.runtime": 110800,
194 "ups_pr3000_battery.voltage": 5990,
195 "ups_pr3000_battery.voltage.nominal": 4800,
196 "ups_pr3000_input.voltage": 22500,
197 "ups_pr3000_input.voltage.nominal": 23000,
198 "ups_pr3000_output.voltage": 22500,
199 "ups_pr3000_ups.load": 2800,
200 "ups_pr3000_ups.load.usage": 84000,
201 "ups_pr3000_ups.realpower.nominal": 300000,
202 "ups_pr3000_ups.status.BOOST": 0,
203 "ups_pr3000_ups.status.BYPASS": 0,
204 "ups_pr3000_ups.status.CAL": 0,
205 "ups_pr3000_ups.status.CHRG": 0,
206 "ups_pr3000_ups.status.DISCHRG": 0,
207 "ups_pr3000_ups.status.FSD": 0,
208 "ups_pr3000_ups.status.HB": 0,
209 "ups_pr3000_ups.status.LB": 0,
210 "ups_pr3000_ups.status.OB": 0,
211 "ups_pr3000_ups.status.OFF": 0,
212 "ups_pr3000_ups.status.OL": 1,
213 "ups_pr3000_ups.status.OVER": 0,
214 "ups_pr3000_ups.status.RB": 0,
215 "ups_pr3000_ups.status.TRIM": 0,
216 "ups_pr3000_ups.status.other": 0,
217 },
218 wantCharts: 25,
219 wantConnConnect: true,
220 wantConnDisconnect: false,
221 wantConnAuthenticate: false,
222 },
223 "error on connect()": {
224 prepareUpsd: New,
225 prepareMock: prepareMockConnErrOnConnect,
226 wantCollected: nil,
227 wantCharts: 0,
228 wantConnConnect: true,
229 wantConnDisconnect: false,
230 wantConnAuthenticate: false,
231 },
232 "error on authenticate()": {
233 prepareUpsd: func() *Collector {
234 collr := New()
235 collr.Username = "user"
236 collr.Password = "pass"
237 return collr
238 },
239 prepareMock: prepareMockConnErrOnAuthenticate,
240 wantCollected: nil,
241 wantCharts: 0,
242 wantConnConnect: true,
243 wantConnDisconnect: true,
244 wantConnAuthenticate: true,
245 },
246 "err on upsList()": {
247 prepareUpsd: New,
248 prepareMock: prepareMockConnErrOnUpsUnits,
249 wantCollected: nil,
250 wantCharts: 0,
251 wantConnConnect: true,
252 wantConnDisconnect: true,
253 wantConnAuthenticate: false,
254 },
255 "command err on upsList() (unknown ups)": {
256 prepareUpsd: New,
257 prepareMock: prepareMockConnCommandErrOnUpsUnits,
258 wantCollected: nil,
259 wantCharts: 0,
260 wantConnConnect: true,
261 wantConnDisconnect: false,
262 wantConnAuthenticate: false,
263 },
264 }
265
266 for name, test := range tests {
267 t.Run(name, func(t *testing.T) {
268 collr := test.prepareUpsd()
269 require.NoError(t, collr.Init(context.Background()))
270
271 mock := test.prepareMock()
272 collr.newUpsdConn = func(Config) upsdConn { return mock }
273
274 mx := collr.Collect(context.Background())
275
276 assert.Equal(t, test.wantCollected, mx)
277
278 assert.Equalf(t, test.wantCharts, len(*collr.Charts()), "number of charts")
279 if len(test.wantCollected) > 0 {
280 collecttest.TestMetricsHasAllChartsDims(t, collr.Charts(), mx)
281 }
282
283 assert.Equalf(t, test.wantConnConnect, mock.calledConnect, "calledConnect")
284 assert.Equalf(t, test.wantConnDisconnect, mock.calledDisconnect, "calledDisconnect")
285 assert.Equal(t, test.wantConnAuthenticate, mock.calledAuthenticate, "calledAuthenticate")
286 })
287 }
288 }
289
290 func prepareMockConnOK() *mockUpsdConn {
291 return &mockUpsdConn{}
292 }
293
294 func prepareMockConnErrOnConnect() *mockUpsdConn {
295 return &mockUpsdConn{errOnConnect: true}
296 }
297
298 func prepareMockConnErrOnAuthenticate() *mockUpsdConn {
299 return &mockUpsdConn{errOnAuthenticate: true}
300 }
301
302 func prepareMockConnErrOnUpsUnits() *mockUpsdConn {
303 return &mockUpsdConn{errOnUpsUnits: true}
304 }
305
306 func prepareMockConnCommandErrOnUpsUnits() *mockUpsdConn {
307 return &mockUpsdConn{commandErrOnUpsUnits: true}
308 }
309
310 type mockUpsdConn struct {
311 errOnConnect bool
312 errOnDisconnect bool
313 errOnAuthenticate bool
314 errOnUpsUnits bool
315 commandErrOnUpsUnits bool
316
317 calledConnect bool
318 calledDisconnect bool
319 calledAuthenticate bool
320 }
321
322 func (m *mockUpsdConn) connect() error {
323 m.calledConnect = true
324 if m.errOnConnect {
325 return errors.New("mock error on connect()")
326 }
327 return nil
328 }
329
330 func (m *mockUpsdConn) disconnect() error {
331 m.calledDisconnect = true
332 if m.errOnDisconnect {
333 return errors.New("mock error on disconnect()")
334 }
335 return nil
336 }
337
338 func (m *mockUpsdConn) authenticate(_, _ string) error {
339 m.calledAuthenticate = true
340 if m.errOnAuthenticate {
341 return errors.New("mock error on authenticate()")
342 }
343 return nil
344 }
345
346 func (m *mockUpsdConn) upsUnits() ([]upsUnit, error) {
347 if m.errOnUpsUnits {
348 return nil, errors.New("mock error on upsUnits()")
349 }
350 if m.commandErrOnUpsUnits {
351 return nil, fmt.Errorf("%w: mock command error on upsUnits()", errUpsdCommand)
352 }
353
354 upsUnits := []upsUnit{
355 {
356 name: "pr3000",
357 vars: map[string]string{
358 "battery.charge": "100",
359 "battery.charge.warning": "35",
360 "battery.mfr.date": "CPS",
361 "battery.runtime": "1108",
362 "battery.runtime.low": "300",
363 "battery.type": "PbAcid",
364 "battery.voltage": "59.9",
365 "battery.voltage.nominal": "48",
366 "device.mfr": "CPS",
367 "device.model": "PR3000ERT2U",
368 "device.serial": "P11MQ2000041",
369 "device.type": "ups",
370 "driver.name": "usbhid-ups",
371 "driver.parameter.pollfreq": "30",
372 "driver.parameter.pollinterval": "2",
373 "driver.parameter.port": "auto",
374 "driver.parameter.synchronous": "no",
375 "driver.version": "2.7.4",
376 "driver.version.data": "CyberPower HID 0.4",
377 "driver.version.internal": "0.41",
378 "input.voltage": "225.0",
379 "input.voltage.nominal": "230",
380 "output.voltage": "225.0",
381 "ups.beeper.status": "enabled",
382 "ups.delay.shutdown": "20",
383 "ups.delay.start": "30",
384 "ups.load": "28",
385 "ups.mfr": "CPS",
386 "ups.model": "PR3000ERT2U",
387 "ups.productid": "0601",
388 "ups.realpower.nominal": "3000",
389 "ups.serial": "P11MQ2000041",
390 "ups.status": "OL",
391 "ups.test.result": "No test initiated",
392 "ups.timer.shutdown": "0",
393 "ups.timer.start": "0",
394 "ups.vendorid": "0764",
395 },
396 },
397 {
398 name: "cp1500",
399 vars: map[string]string{
400 "battery.charge": "100",
401 "battery.charge.low": "10",
402 "battery.charge.warning": "20",
403 "battery.mfr.date": "CPS",
404 "battery.runtime": "4890",
405 "battery.runtime.low": "300",
406 "battery.type": "PbAcid",
407 "battery.voltage": "24.0",
408 "battery.voltage.nominal": "24",
409 "device.mfr": "CPS",
410 "device.model": "CP1500EPFCLCD",
411 "device.serial": "CRMNO2000312",
412 "device.type": "ups",
413 "driver.name": "usbhid-ups",
414 "driver.parameter.bus": "001",
415 "driver.parameter.pollfreq": "30",
416 "driver.parameter.pollinterval": "2",
417 "driver.parameter.port": "auto",
418 "driver.parameter.product": "CP1500EPFCLCD",
419 "driver.parameter.productid": "0501",
420 "driver.parameter.serial": "CRMNO2000312",
421 "driver.parameter.synchronous": "no",
422 "driver.parameter.vendor": "CPS",
423 "driver.parameter.vendorid": "0764",
424 "driver.version": "2.7.4",
425 "driver.version.data": "CyberPower HID 0.4",
426 "driver.version.internal": "0.41",
427 "input.transfer.high": "260",
428 "input.transfer.low": "170",
429 "input.voltage": "227.0",
430 "input.voltage.nominal": "230",
431 "output.voltage": "260.0",
432 "ups.beeper.status": "enabled",
433 "ups.delay.shutdown": "20",
434 "ups.delay.start": "30",
435 "ups.load": "8",
436 "ups.mfr": "CPS",
437 "ups.model": "CP1500EPFCLCD",
438 "ups.productid": "0501",
439 "ups.realpower": "43",
440 "ups.realpower.nominal": "900",
441 "ups.serial": "CRMNO2000312",
442 "ups.status": "OL",
443 "ups.test.result": "No test initiated",
444 "ups.timer.shutdown": "-60",
445 "ups.timer.start": "-60",
446 "ups.vendorid": "0764",
447 },
448 },
449 {
450 name: "cp1600",
451 vars: map[string]string{
452 "battery.charge": "100",
453 "battery.charge.low": "10",
454 "battery.charge.warning": "50",
455 "battery.runtime": "1970",
456 "battery.runtime.low": "150",
457 "battery.type": "PbAc",
458 "battery.voltage": "54.8",
459 "battery.voltage.nominal": "48.0",
460 "device.mfr": "American Power Conversion",
461 "device.model": "Smart-UPS X 1500",
462 "device.serial": "****************",
463 "device.type": "ups",
464 "driver.name": "usbhid-ups",
465 "driver.parameter.bus": "001",
466 "driver.parameter.pollfreq": "30",
467 "driver.parameter.pollinterval": "2",
468 "driver.parameter.port": "auto",
469 "driver.parameter.product": "Smart-UPS X 1500 FW:UPS 09.8 / ID=20",
470 "driver.parameter.productid": "0003",
471 "driver.parameter.serial": "****************",
472 "driver.parameter.synchronous": "auto",
473 "driver.parameter.vendor": "American Power Conversion",
474 "driver.parameter.vendorid": "051D",
475 "driver.version": "2.8.0",
476 "driver.version.data": "APC HID 0.98",
477 "driver.version.internal": "0.47",
478 "driver.version.usb": "libusb-1.0.26 (API: 0x1000109)",
479 "ups.beeper.status": "enabled",
480 "ups.delay.shutdown": "20",
481 "ups.firmware": "UPS 09.8 / ID=20",
482 "ups.mfr": "American Power Conversion",
483 "ups.mfr.date": "2019/01/12",
484 "ups.model": "Smart-UPS X 1500",
485 "ups.productid": "0003",
486 "ups.serial": "****************",
487 "ups.status": "OL",
488 "ups.timer.reboot": "-1",
489 "ups.timer.shutdown": "-1",
490 "ups.vendorid": "051d",
491 },
492 },
493 }
494
495 return upsUnits, nil
496 }