master
go 284 lines 7.36 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package apcupsd
4
5 import (
6 "context"
7 "errors"
8 "os"
9 "strings"
10 "testing"
11
12 "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi"
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/status.txt")
24 dataStatusCommlost, _ = os.ReadFile("testdata/status_commlost.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 "dataStatus": dataStatus,
32 "dataStatusCommlost": dataStatusCommlost,
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_Cleanup(t *testing.T) {
43 collr := New()
44 require.NotPanics(t, func() { collr.Cleanup(context.Background()) })
45
46 mock := prepareMockOk()
47 collr.newConn = func(Config) apcupsdConn { return mock }
48
49 require.NoError(t, collr.Init(context.Background()))
50 _ = collr.Collect(context.Background())
51 require.NotPanics(t, func() { collr.Cleanup(context.Background()) })
52 assert.True(t, mock.calledDisconnect)
53 }
54
55 func TestCollector_Init(t *testing.T) {
56 tests := map[string]struct {
57 config Config
58 wantFail bool
59 }{
60 "success on default config": {
61 wantFail: false,
62 config: New().Config,
63 },
64 "fails when 'address' option not set": {
65 wantFail: true,
66 config: Config{Address: ""},
67 },
68 }
69
70 for name, test := range tests {
71 t.Run(name, func(t *testing.T) {
72 collr := New()
73 collr.Config = test.config
74
75 if test.wantFail {
76 assert.Error(t, collr.Init(context.Background()))
77 } else {
78 assert.NoError(t, collr.Init(context.Background()))
79 }
80 })
81 }
82 }
83
84 func TestCollector_Check(t *testing.T) {
85 tests := map[string]struct {
86 prepareMock func() *mockApcupsdConn
87 wantFail bool
88 }{
89 "case ok": {
90 wantFail: false,
91 prepareMock: prepareMockOk,
92 },
93 "case commlost": {
94 wantFail: false,
95 prepareMock: prepareMockOkCommlost,
96 },
97 "error on connect()": {
98 wantFail: true,
99 prepareMock: prepareMockErrOnConnect,
100 },
101 "error on status()": {
102 wantFail: true,
103 prepareMock: prepareMockErrOnStatus,
104 },
105 }
106
107 for name, test := range tests {
108 t.Run(name, func(t *testing.T) {
109 collr := New()
110 collr.newConn = func(Config) apcupsdConn { return test.prepareMock() }
111
112 require.NoError(t, collr.Init(context.Background()))
113
114 if test.wantFail {
115 assert.Error(t, collr.Check(context.Background()))
116 } else {
117 assert.NoError(t, collr.Check(context.Background()))
118 }
119 })
120 }
121 }
122
123 func TestCollector_Charts(t *testing.T) {
124 collr := New()
125 require.NoError(t, collr.Init(context.Background()))
126 assert.NotNil(t, collr.Charts())
127 }
128
129 func TestCollector_Collect(t *testing.T) {
130 tests := map[string]struct {
131 prepareMock func() *mockApcupsdConn
132 wantCollected map[string]int64
133 wantCharts int
134 wantConnDisconnect bool
135 }{
136 "case ok": {
137 prepareMock: prepareMockOk,
138 wantCollected: map[string]int64{
139 "battery_charge": 10000,
140 "battery_seconds_since_replacement": 86400,
141 "battery_voltage": 2790,
142 "battery_voltage_nominal": 2400,
143 "input_frequency": 5000,
144 "input_voltage": 23530,
145 "input_voltage_max": 23920,
146 "input_voltage_min": 23400,
147 "itemp": 3279,
148 "load": 5580,
149 "load_percent": 930,
150 "output_voltage": 23660,
151 "output_voltage_nominal": 23000,
152 "selftest_BT": 0,
153 "selftest_IP": 0,
154 "selftest_NG": 0,
155 "selftest_NO": 1,
156 "selftest_OK": 0,
157 "selftest_UNK": 0,
158 "selftest_WN": 0,
159 "status_BOOST": 0,
160 "status_CAL": 0,
161 "status_COMMLOST": 0,
162 "status_LOWBATT": 0,
163 "status_NOBATT": 0,
164 "status_ONBATT": 0,
165 "status_ONLINE": 1,
166 "status_OVERLOAD": 0,
167 "status_REPLACEBATT": 0,
168 "status_SHUTTING_DOWN": 0,
169 "status_SLAVE": 0,
170 "status_SLAVEDOWN": 0,
171 "status_TRIM": 0,
172 "timeleft": 780000,
173 },
174 wantConnDisconnect: false,
175 },
176 "case commlost": {
177 prepareMock: prepareMockOkCommlost,
178 wantCollected: map[string]int64{
179 "status_BOOST": 0,
180 "status_CAL": 0,
181 "status_COMMLOST": 1,
182 "status_LOWBATT": 0,
183 "status_NOBATT": 0,
184 "status_ONBATT": 0,
185 "status_ONLINE": 0,
186 "status_OVERLOAD": 0,
187 "status_REPLACEBATT": 0,
188 "status_SHUTTING_DOWN": 0,
189 "status_SLAVE": 0,
190 "status_SLAVEDOWN": 0,
191 "status_TRIM": 0,
192 },
193 wantConnDisconnect: false,
194 },
195 "error on connect()": {
196 prepareMock: prepareMockErrOnConnect,
197 wantCollected: nil,
198 wantConnDisconnect: false,
199 },
200 "error on status()": {
201 prepareMock: prepareMockErrOnStatus,
202 wantCollected: nil,
203 wantConnDisconnect: true,
204 },
205 }
206
207 for name, test := range tests {
208 t.Run(name, func(t *testing.T) {
209 collr := New()
210 require.NoError(t, collr.Init(context.Background()))
211
212 mock := test.prepareMock()
213 collr.newConn = func(Config) apcupsdConn { return mock }
214
215 mx := collr.Collect(context.Background())
216
217 if _, ok := mx["battery_seconds_since_replacement"]; ok {
218 mx["battery_seconds_since_replacement"] = 86400
219 }
220
221 assert.Equal(t, test.wantCollected, mx)
222
223 if len(test.wantCollected) > 0 {
224 if strings.Contains(name, "commlost") {
225 collecttest.TestMetricsHasAllChartsDimsSkip(t, collr.Charts(), mx, func(chart *collectorapi.Chart, _ *collectorapi.Dim) bool {
226 return chart.ID != statusChart.ID
227 })
228 } else {
229 collecttest.TestMetricsHasAllChartsDims(t, collr.Charts(), mx)
230 }
231 }
232
233 assert.Equalf(t, test.wantConnDisconnect, mock.calledDisconnect, "calledDisconnect")
234 })
235 }
236 }
237
238 func prepareMockOk() *mockApcupsdConn {
239 return &mockApcupsdConn{
240 dataStatus: dataStatus,
241 }
242 }
243
244 func prepareMockOkCommlost() *mockApcupsdConn {
245 return &mockApcupsdConn{
246 dataStatus: dataStatusCommlost,
247 }
248 }
249
250 func prepareMockErrOnConnect() *mockApcupsdConn {
251 return &mockApcupsdConn{errOnConnect: true}
252 }
253
254 func prepareMockErrOnStatus() *mockApcupsdConn {
255 return &mockApcupsdConn{errOnStatus: true}
256 }
257
258 type mockApcupsdConn struct {
259 errOnConnect bool
260 errOnStatus bool
261 calledDisconnect bool
262
263 dataStatus []byte
264 }
265
266 func (m *mockApcupsdConn) connect() error {
267 if m.errOnConnect {
268 return errors.New("mock error on connect()")
269 }
270 return nil
271 }
272
273 func (m *mockApcupsdConn) disconnect() error {
274 m.calledDisconnect = true
275 return nil
276 }
277
278 func (m *mockApcupsdConn) status() ([]byte, error) {
279 if m.errOnStatus {
280 return nil, errors.New("mock error on status()")
281 }
282
283 return m.dataStatus, nil
284 }