github.com/netdata/go.d.plugin@v0.58.1/modules/upsd/upsd_test.go (about)

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