github.com/netdata/go.d.plugin@v0.58.1/modules/energid/energid_test.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package energid
     4  
     5  import (
     6  	"encoding/json"
     7  	"io"
     8  	"net/http"
     9  	"net/http/httptest"
    10  	"os"
    11  	"testing"
    12  
    13  	"github.com/netdata/go.d.plugin/pkg/tlscfg"
    14  	"github.com/netdata/go.d.plugin/pkg/web"
    15  
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  var (
    21  	v241GetBlockchainInfo, _ = os.ReadFile("testdata/v2.4.1/getblockchaininfo.json")
    22  	v241GetMemPoolInfo, _    = os.ReadFile("testdata/v2.4.1/getmempoolinfo.json")
    23  	v241GetNetworkInfo, _    = os.ReadFile("testdata/v2.4.1/getnetworkinfo.json")
    24  	v241GetTXOutSetInfo, _   = os.ReadFile("testdata/v2.4.1/gettxoutsetinfo.json")
    25  	v241GetMemoryInfo, _     = os.ReadFile("testdata/v2.4.1/getmemoryinfo.json")
    26  )
    27  
    28  func Test_Testdata(t *testing.T) {
    29  	for name, data := range map[string][]byte{
    30  		"v241GetBlockchainInfo": v241GetBlockchainInfo,
    31  		"v241GetMemPoolInfo":    v241GetMemPoolInfo,
    32  		"v241GetNetworkInfo":    v241GetNetworkInfo,
    33  		"v241GetTXOutSetInfo":   v241GetTXOutSetInfo,
    34  		"v241GetMemoryInfo":     v241GetMemoryInfo,
    35  	} {
    36  		require.NotNilf(t, data, name)
    37  	}
    38  }
    39  
    40  func TestNew(t *testing.T) {
    41  	assert.IsType(t, (*Energid)(nil), New())
    42  }
    43  
    44  func Test_Init(t *testing.T) {
    45  	tests := map[string]struct {
    46  		config   Config
    47  		wantFail bool
    48  	}{
    49  		"success on default config": {
    50  			config: New().Config,
    51  		},
    52  		"fails on unset URL": {
    53  			wantFail: true,
    54  			config: Config{
    55  				HTTP: web.HTTP{
    56  					Request: web.Request{URL: ""},
    57  				},
    58  			},
    59  		},
    60  		"fails on invalid TLSCA": {
    61  			wantFail: true,
    62  			config: Config{
    63  				HTTP: web.HTTP{
    64  					Request: web.Request{
    65  						URL: "http://127.0.0.1:38001",
    66  					},
    67  					Client: web.Client{
    68  						TLSConfig: tlscfg.TLSConfig{TLSCA: "testdata/tls"},
    69  					},
    70  				},
    71  			},
    72  		},
    73  	}
    74  
    75  	for name, test := range tests {
    76  		t.Run(name, func(t *testing.T) {
    77  			energid := New()
    78  			energid.Config = test.config
    79  
    80  			if test.wantFail {
    81  				assert.False(t, energid.Init())
    82  			} else {
    83  				assert.True(t, energid.Init())
    84  			}
    85  		})
    86  	}
    87  }
    88  
    89  func Test_Charts(t *testing.T) {
    90  	energid := New()
    91  	require.True(t, energid.Init())
    92  	assert.NotNil(t, energid.Charts())
    93  }
    94  
    95  func Test_Cleanup(t *testing.T) {
    96  	assert.NotPanics(t, New().Cleanup)
    97  }
    98  
    99  func Test_Check(t *testing.T) {
   100  	tests := map[string]struct {
   101  		prepare  func() (energid *Energid, cleanup func())
   102  		wantFail bool
   103  	}{
   104  		"success on valid v2.4.1 response": {
   105  			prepare: prepareEnergidV241,
   106  		},
   107  		"fails on 404 response": {
   108  			wantFail: true,
   109  			prepare:  prepareEnergid404,
   110  		},
   111  		"fails on connection refused": {
   112  			wantFail: true,
   113  			prepare:  prepareEnergidConnectionRefused,
   114  		},
   115  		"fails on response with invalid data": {
   116  			wantFail: true,
   117  			prepare:  prepareEnergidInvalidData,
   118  		},
   119  	}
   120  
   121  	for name, test := range tests {
   122  		t.Run(name, func(t *testing.T) {
   123  			energid, cleanup := test.prepare()
   124  			defer cleanup()
   125  
   126  			require.True(t, energid.Init())
   127  
   128  			if test.wantFail {
   129  				assert.False(t, energid.Check())
   130  			} else {
   131  				assert.True(t, energid.Check())
   132  			}
   133  		})
   134  	}
   135  }
   136  
   137  func Test_Collect(t *testing.T) {
   138  	tests := map[string]struct {
   139  		prepare       func() (energid *Energid, cleanup func())
   140  		wantCollected map[string]int64
   141  	}{
   142  		"success on valid v2.4.1 response": {
   143  			prepare: prepareEnergidV241,
   144  			wantCollected: map[string]int64{
   145  				"blockchain_blocks":        1,
   146  				"blockchain_difficulty":    0,
   147  				"blockchain_headers":       1,
   148  				"mempool_current":          1,
   149  				"mempool_max":              300000000,
   150  				"mempool_txsize":           1,
   151  				"network_connections":      1,
   152  				"network_timeoffset":       1,
   153  				"secmem_free":              65248,
   154  				"secmem_locked":            65536,
   155  				"secmem_total":             65536,
   156  				"secmem_used":              288,
   157  				"utxo_output_transactions": 1,
   158  				"utxo_transactions":        1,
   159  			},
   160  		},
   161  		"fails on 404 response": {
   162  			prepare: prepareEnergid404,
   163  		},
   164  		"fails on connection refused": {
   165  			prepare: prepareEnergidConnectionRefused,
   166  		},
   167  		"fails on response with invalid data": {
   168  			prepare: prepareEnergidInvalidData,
   169  		},
   170  	}
   171  
   172  	for name, test := range tests {
   173  		t.Run(name, func(t *testing.T) {
   174  			energid, cleanup := test.prepare()
   175  			defer cleanup()
   176  			require.True(t, energid.Init())
   177  
   178  			collected := energid.Collect()
   179  
   180  			assert.Equal(t, test.wantCollected, collected)
   181  			if len(test.wantCollected) > 0 {
   182  				ensureCollectedHasAllChartsDimsVarsIDs(t, energid, collected)
   183  			}
   184  		})
   185  	}
   186  }
   187  
   188  func ensureCollectedHasAllChartsDimsVarsIDs(t *testing.T, energid *Energid, ms map[string]int64) {
   189  	for _, chart := range *energid.Charts() {
   190  		if chart.Obsolete {
   191  			continue
   192  		}
   193  		for _, dim := range chart.Dims {
   194  			_, ok := ms[dim.ID]
   195  			assert.Truef(t, ok, "chart '%s' dim '%s': no dim in collected", dim.ID, chart.ID)
   196  		}
   197  		for _, v := range chart.Vars {
   198  			_, ok := ms[v.ID]
   199  			assert.Truef(t, ok, "chart '%s' dim '%s': no dim in collected", v.ID, chart.ID)
   200  		}
   201  	}
   202  }
   203  
   204  func prepareEnergidV241() (*Energid, func()) {
   205  	srv := prepareEnergidEndPoint()
   206  	energid := New()
   207  	energid.URL = srv.URL
   208  
   209  	return energid, srv.Close
   210  }
   211  
   212  func prepareEnergidInvalidData() (*Energid, func()) {
   213  	srv := httptest.NewServer(http.HandlerFunc(
   214  		func(w http.ResponseWriter, r *http.Request) {
   215  			_, _ = w.Write([]byte("Hello world!"))
   216  		}))
   217  	energid := New()
   218  	energid.URL = srv.URL
   219  
   220  	return energid, srv.Close
   221  }
   222  
   223  func prepareEnergid404() (*Energid, func()) {
   224  	srv := httptest.NewServer(http.HandlerFunc(
   225  		func(w http.ResponseWriter, r *http.Request) {
   226  			w.WriteHeader(http.StatusNotFound)
   227  		}))
   228  	energid := New()
   229  	energid.URL = srv.URL
   230  
   231  	return energid, srv.Close
   232  }
   233  
   234  func prepareEnergidConnectionRefused() (*Energid, func()) {
   235  	energid := New()
   236  	energid.URL = "http://127.0.0.1:38001"
   237  
   238  	return energid, func() {}
   239  }
   240  
   241  func prepareEnergidEndPoint() *httptest.Server {
   242  	return httptest.NewServer(http.HandlerFunc(
   243  		func(w http.ResponseWriter, r *http.Request) {
   244  			if r.Method != http.MethodPost {
   245  				w.WriteHeader(http.StatusMethodNotAllowed)
   246  				return
   247  			}
   248  
   249  			body, _ := io.ReadAll(r.Body)
   250  			var requests rpcRequests
   251  			if err := json.Unmarshal(body, &requests); err != nil || len(requests) == 0 {
   252  				w.WriteHeader(http.StatusInternalServerError)
   253  				return
   254  			}
   255  
   256  			var responses rpcResponses
   257  			for _, req := range requests {
   258  				resp := rpcResponse{JSONRPC: jsonRPCVersion, ID: req.ID}
   259  				switch req.Method {
   260  				case methodGetBlockchainInfo:
   261  					resp.Result = prepareResult(v241GetBlockchainInfo)
   262  				case methodGetMemPoolInfo:
   263  					resp.Result = prepareResult(v241GetMemPoolInfo)
   264  				case methodGetNetworkInfo:
   265  					resp.Result = prepareResult(v241GetNetworkInfo)
   266  				case methodGetTXOutSetInfo:
   267  					resp.Result = prepareResult(v241GetTXOutSetInfo)
   268  				case methodGetMemoryInfo:
   269  					resp.Result = prepareResult(v241GetMemoryInfo)
   270  				default:
   271  					resp.Error = &rpcError{Code: -32601, Message: "Method not found"}
   272  				}
   273  				responses = append(responses, resp)
   274  			}
   275  
   276  			bs, _ := json.Marshal(responses)
   277  			_, _ = w.Write(bs)
   278  		}))
   279  }
   280  
   281  func prepareResult(resp []byte) json.RawMessage {
   282  	var r rpcResponse
   283  	_ = json.Unmarshal(resp, &r)
   284  	return r.Result
   285  }