reorganize packages and add tests for zabbix datasource instance methods

This commit is contained in:
Alexander Zobnin
2020-06-04 11:43:20 +03:00
parent 4bebeb4919
commit 7990613e2c
13 changed files with 231 additions and 531 deletions

4
go.mod
View File

@@ -7,8 +7,8 @@ require (
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
github.com/google/go-cmp v0.3.1 // indirect
github.com/grafana/grafana-plugin-sdk-go v0.65.0
github.com/grafana/grafana_plugin_model v0.0.0-20180518082423-84176c64269d
github.com/hashicorp/go-hclog v0.9.2
github.com/grafana/grafana_plugin_model v0.0.0-20180518082423-84176c64269d // indirect
github.com/hashicorp/go-hclog v0.9.2 // indirect
github.com/hashicorp/go-plugin v1.2.2 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible

View File

@@ -1,4 +1,4 @@
package main
package datasource
import (
"context"
@@ -31,6 +31,13 @@ type ZabbixDatasourceInstance struct {
logger log.Logger
}
func NewZabbixDatasource() *ZabbixDatasource {
return &ZabbixDatasource{
datasourceCache: cache.NewCache(10*time.Minute, 10*time.Minute),
logger: log.New(),
}
}
// NewZabbixDatasourceInstance returns an initialized zabbix datasource instance
func NewZabbixDatasourceInstance(dsInfo *backend.DataSourceInstanceSettings) (*ZabbixDatasourceInstance, error) {
zabbixAPI, err := zabbixapi.New(dsInfo.URL)

View File

@@ -1,4 +1,4 @@
package main
package datasource
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package main
package datasource
import (
"encoding/json"
@@ -39,17 +39,17 @@ type ZabbixAPIRequest struct {
Params ZabbixAPIParams `json:"params,omitempty"`
}
func (r *ZabbixAPIRequest) String() string {
jsonRequest, _ := json.Marshal(r.Params)
return r.Method + string(jsonRequest)
}
type ZabbixAPIParams = map[string]interface{}
type ZabbixAPIResourceResponse struct {
Result interface{} `json:"result,omitempty"`
}
func (r *ZabbixAPIRequest) String() string {
jsonRequest, _ := json.Marshal(r.Params)
return r.Method + string(jsonRequest)
}
// QueryModel model
type QueryModel struct {
Mode int64 `json:"mode"`

View File

@@ -1,4 +1,4 @@
package main
package datasource
import (
"encoding/json"
@@ -8,14 +8,14 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter"
)
func (ds *ZabbixDatasource) rootHandler(rw http.ResponseWriter, req *http.Request) {
func (ds *ZabbixDatasource) RootHandler(rw http.ResponseWriter, req *http.Request) {
ds.logger.Debug("Received resource call", "url", req.URL.String(), "method", req.Method)
rw.Write([]byte("Hello from Zabbix data source!"))
rw.WriteHeader(http.StatusOK)
}
func (ds *ZabbixDatasource) zabbixAPIHandler(rw http.ResponseWriter, req *http.Request) {
func (ds *ZabbixDatasource) ZabbixAPIHandler(rw http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodPost {
return
}
@@ -23,7 +23,7 @@ func (ds *ZabbixDatasource) zabbixAPIHandler(rw http.ResponseWriter, req *http.R
body, err := ioutil.ReadAll(req.Body)
defer req.Body.Close()
if err != nil || len(body) == 0 {
WriteError(rw, http.StatusBadRequest, err)
writeError(rw, http.StatusBadRequest, err)
return
}
@@ -31,7 +31,7 @@ func (ds *ZabbixDatasource) zabbixAPIHandler(rw http.ResponseWriter, req *http.R
err = json.Unmarshal(body, &reqData)
if err != nil {
ds.logger.Error("Cannot unmarshal request", "error", err.Error())
WriteError(rw, http.StatusInternalServerError, err)
writeError(rw, http.StatusInternalServerError, err)
return
}
@@ -39,7 +39,7 @@ func (ds *ZabbixDatasource) zabbixAPIHandler(rw http.ResponseWriter, req *http.R
dsInstance, err := ds.GetDatasource(pluginCxt)
if err != nil {
ds.logger.Error("Error loading datasource", "error", err)
WriteError(rw, http.StatusInternalServerError, err)
writeError(rw, http.StatusInternalServerError, err)
return
}
@@ -48,17 +48,17 @@ func (ds *ZabbixDatasource) zabbixAPIHandler(rw http.ResponseWriter, req *http.R
result, err := dsInstance.ZabbixAPIQuery(req.Context(), apiReq)
if err != nil {
ds.logger.Error("Zabbix API request error", "error", err)
WriteError(rw, http.StatusInternalServerError, err)
writeError(rw, http.StatusInternalServerError, err)
return
}
WriteResponse(rw, result)
writeResponse(rw, result)
}
func WriteResponse(rw http.ResponseWriter, result *ZabbixAPIResourceResponse) {
func writeResponse(rw http.ResponseWriter, result *ZabbixAPIResourceResponse) {
resultJson, err := json.Marshal(*result)
if err != nil {
WriteError(rw, http.StatusInternalServerError, err)
writeError(rw, http.StatusInternalServerError, err)
}
rw.Header().Add("Content-Type", "application/json")
@@ -66,7 +66,7 @@ func WriteResponse(rw http.ResponseWriter, result *ZabbixAPIResourceResponse) {
rw.Write(resultJson)
}
func WriteError(rw http.ResponseWriter, statusCode int, err error) {
func writeError(rw http.ResponseWriter, statusCode int, err error) {
data := make(map[string]interface{})
data["error"] = "Internal Server Error"

View File

@@ -1,4 +1,4 @@
package zabbix
package datasource
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package main
package datasource
import (
"encoding/json"
@@ -7,7 +7,6 @@ import (
"time"
"github.com/alexanderzobnin/grafana-zabbix/pkg/cache"
"github.com/alexanderzobnin/grafana-zabbix/pkg/zabbix"
"github.com/alexanderzobnin/grafana-zabbix/pkg/zabbixapi"
simplejson "github.com/bitly/go-simplejson"
"github.com/grafana/grafana-plugin-sdk-go/backend"
@@ -156,7 +155,7 @@ func (ds *ZabbixDatasourceInstance) queryNumericItems(ctx context.Context, query
return frames, nil
}
func (ds *ZabbixDatasourceInstance) getItems(ctx context.Context, groupFilter string, hostFilter string, appFilter string, itemFilter string, itemType string) (zabbix.Items, error) {
func (ds *ZabbixDatasourceInstance) getItems(ctx context.Context, groupFilter string, hostFilter string, appFilter string, itemFilter string, itemType string) (Items, error) {
hosts, err := ds.getHosts(ctx, groupFilter, hostFilter)
if err != nil {
return nil, err
@@ -182,10 +181,10 @@ func (ds *ZabbixDatasourceInstance) getItems(ctx context.Context, groupFilter st
allItems, err = ds.getAllItems(ctx, nil, appids, itemType)
}
var items zabbix.Items
var items Items
if allItems == nil {
items = zabbix.Items{}
items = Items{}
} else {
itemsJSON, err := allItems.MarshalJSON()
if err != nil {
@@ -203,7 +202,7 @@ func (ds *ZabbixDatasourceInstance) getItems(ctx context.Context, groupFilter st
return nil, err
}
filteredItems := zabbix.Items{}
filteredItems := Items{}
for _, item := range items {
itemName := item.ExpandItem()
if item.Status == "0" {
@@ -361,7 +360,7 @@ func (ds *ZabbixDatasourceInstance) getAllGroups(ctx context.Context) (*simplejs
return ds.ZabbixQuery(ctx, &ZabbixAPIRequest{Method: "hostgroup.get", Params: params})
}
func (ds *ZabbixDatasourceInstance) queryNumericDataForItems(ctx context.Context, query *QueryModel, items zabbix.Items) (*data.Frame, error) {
func (ds *ZabbixDatasourceInstance) queryNumericDataForItems(ctx context.Context, query *QueryModel, items Items) (*data.Frame, error) {
valueType := ds.getTrendValueType(query)
consolidateBy := ds.getConsolidateBy(query)
@@ -400,12 +399,12 @@ func (ds *ZabbixDatasourceInstance) getConsolidateBy(query *QueryModel) string {
return consolidateBy
}
func (ds *ZabbixDatasourceInstance) getHistotyOrTrend(ctx context.Context, query *QueryModel, items zabbix.Items) (zabbix.History, error) {
func (ds *ZabbixDatasourceInstance) getHistotyOrTrend(ctx context.Context, query *QueryModel, items Items) (History, error) {
timeRange := query.TimeRange
useTrend := ds.isUseTrend(timeRange)
allHistory := zabbix.History{}
allHistory := History{}
groupedItems := map[int]zabbix.Items{}
groupedItems := map[int]Items{}
for _, j := range items {
groupedItems[j.ValueType] = append(groupedItems[j.ValueType], j)
@@ -444,7 +443,7 @@ func (ds *ZabbixDatasourceInstance) getHistotyOrTrend(ctx context.Context, query
return nil, fmt.Errorf("Internal error parsing response JSON: %w", err)
}
history := zabbix.History{}
history := History{}
err = json.Unmarshal(pointJSON, &history)
if err != nil {
ds.logger.Warn(fmt.Sprintf("Could not map Zabbix response to History: %s", err.Error()))
@@ -472,7 +471,7 @@ func (ds *ZabbixDatasourceInstance) isUseTrend(timeRange backend.TimeRange) bool
return false
}
func convertHistory(history zabbix.History, items zabbix.Items) *data.Frame {
func convertHistory(history History, items Items) *data.Frame {
timeFileld := data.NewFieldFromFieldType(data.FieldTypeTime, 0)
timeFileld.Name = "time"
frame := data.NewFrame("History", timeFileld)

View File

@@ -0,0 +1,134 @@
package datasource
import (
"context"
"net/http"
"testing"
"time"
"github.com/alexanderzobnin/grafana-zabbix/pkg/cache"
"github.com/alexanderzobnin/grafana-zabbix/pkg/zabbixapi"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/stretchr/testify/assert"
)
var emptyParams = map[string]interface{}{}
type RoundTripFunc func(req *http.Request) *http.Response
func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return f(req), nil
}
//NewTestClient returns *http.Client with Transport replaced to avoid making real calls
func NewTestClient(fn RoundTripFunc) *http.Client {
return &http.Client{
Transport: RoundTripFunc(fn),
}
}
var basicDatasourceInfo = &backend.DataSourceInstanceSettings{
ID: 1,
Name: "TestDatasource",
URL: "http://zabbix.org/zabbix",
JSONData: []byte(`{"username":"username", "password":"password"}}`),
}
func mockZabbixQuery(method string, params ZabbixAPIParams) *ZabbixAPIRequest {
return &ZabbixAPIRequest{
Method: method,
Params: params,
}
}
func MockZabbixDataSource(body string, statusCode int) *ZabbixDatasourceInstance {
zabbixAPI, _ := zabbixapi.MockZabbixAPI(body, statusCode)
zabbixSettings, _ := readZabbixSettings(basicDatasourceInfo)
return &ZabbixDatasourceInstance{
dsInfo: basicDatasourceInfo,
zabbixAPI: zabbixAPI,
Settings: zabbixSettings,
queryCache: cache.NewCache(cache.NoExpiration, 10*time.Minute),
logger: log.New(),
}
}
func MockZabbixDataSourceResponse(dsInstance *ZabbixDatasourceInstance, body string, statusCode int) *ZabbixDatasourceInstance {
zabbixAPI, _ := zabbixapi.MockZabbixAPI(body, statusCode)
dsInstance.zabbixAPI = zabbixAPI
return dsInstance
}
func TestLogin(t *testing.T) {
dsInstance := MockZabbixDataSource(`{"result":"secretauth"}`, 200)
err := dsInstance.login(context.Background())
assert.Nil(t, err)
assert.Equal(t, "secretauth", dsInstance.zabbixAPI.GetAuth())
}
func TestLoginError(t *testing.T) {
dsInstance := MockZabbixDataSource(`{"result":""}`, 500)
err := dsInstance.login(context.Background())
assert.NotNil(t, err)
assert.Equal(t, "", dsInstance.zabbixAPI.GetAuth())
}
func TestZabbixAPIQuery(t *testing.T) {
dsInstance := MockZabbixDataSource(`{"result":"test"}`, 200)
resp, err := dsInstance.ZabbixAPIQuery(context.Background(), mockZabbixQuery("test.get", emptyParams))
assert.Nil(t, err)
result, ok := resp.Result.(string)
assert.True(t, ok)
assert.Equal(t, "test", result)
}
func TestCachedQuery(t *testing.T) {
// Using methods with caching enabled
query := mockZabbixQuery("host.get", emptyParams)
dsInstance := MockZabbixDataSource(`{"result":"testOld"}`, 200)
// Run query first time
resp, err := dsInstance.ZabbixAPIQuery(context.Background(), query)
assert.Nil(t, err)
result, _ := resp.Result.(string)
assert.Equal(t, "testOld", result)
// Mock request with new value
dsInstance = MockZabbixDataSourceResponse(dsInstance, `{"result":"testNew"}`, 200)
// Should not run actual API query and return first result
resp, err = dsInstance.ZabbixAPIQuery(context.Background(), query)
assert.Nil(t, err)
result, _ = resp.Result.(string)
assert.Equal(t, "testOld", result)
}
func TestNonCachedQuery(t *testing.T) {
// Using methods with caching disabled
query := mockZabbixQuery("history.get", emptyParams)
dsInstance := MockZabbixDataSource(`{"result":"testOld"}`, 200)
// Run query first time
resp, err := dsInstance.ZabbixAPIQuery(context.Background(), query)
assert.Nil(t, err)
result, _ := resp.Result.(string)
assert.Equal(t, "testOld", result)
// Mock request with new value
dsInstance = MockZabbixDataSourceResponse(dsInstance, `{"result":"testNew"}`, 200)
// Should not run actual API query and return first result
resp, err = dsInstance.ZabbixAPIQuery(context.Background(), query)
assert.Nil(t, err)
result, _ = resp.Result.(string)
assert.Equal(t, "testNew", result)
}

View File

@@ -3,9 +3,8 @@ package main
import (
"net/http"
"os"
"time"
"github.com/alexanderzobnin/grafana-zabbix/pkg/cache"
"github.com/alexanderzobnin/grafana-zabbix/pkg/datasource"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter"
@@ -33,7 +32,7 @@ func main() {
}
}
func Init(logger log.Logger, mux *http.ServeMux) *ZabbixDatasource {
func Init(logger log.Logger, mux *http.ServeMux) *datasource.ZabbixDatasource {
variableName := "GFX_ZABBIX_DATA_PATH"
path, exist := os.LookupEnv(variableName)
if !exist {
@@ -42,13 +41,10 @@ func Init(logger log.Logger, mux *http.ServeMux) *ZabbixDatasource {
logger.Debug("environment variable for storage found", "variable", variableName, "value", path)
}
ds := &ZabbixDatasource{
logger: logger,
datasourceCache: cache.NewCache(10*time.Minute, 10*time.Minute),
}
ds := datasource.NewZabbixDatasource()
mux.HandleFunc("/", ds.rootHandler)
mux.HandleFunc("/zabbix-api", ds.zabbixAPIHandler)
mux.HandleFunc("/", ds.RootHandler)
mux.HandleFunc("/zabbix-api", ds.ZabbixAPIHandler)
// mux.Handle("/scenarios", getScenariosHandler(logger))
return ds

View File

@@ -1,446 +0,0 @@
package main
import (
"bytes"
"io/ioutil"
"net/http"
"net/url"
"regexp"
"testing"
"time"
simplejson "github.com/bitly/go-simplejson"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana_plugin_model/go/datasource"
hclog "github.com/hashicorp/go-hclog"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
)
type RoundTripFunc func(req *http.Request) *http.Response
func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return f(req), nil
}
//NewTestClient returns *http.Client with Transport replaced to avoid making real calls
func NewTestClient(fn RoundTripFunc) *http.Client {
return &http.Client{
Transport: RoundTripFunc(fn),
}
}
var basicDatasourceInfo = &backend.DataSourceInstanceSettings{
ID: 1,
Name: "TestDatasource",
URL: "http://zabbix.org/zabbix",
JSONData: []byte(`{"username":"username", "password":"password"}}`),
}
func mockDataSourceRequest(modelJSON string) *datasource.DatasourceRequest {
return &datasource.DatasourceRequest{
Datasource: basicDatasourceInfo,
Queries: []*datasource.Query{
{
ModelJson: modelJSON,
},
},
}
}
func mockZabbixDataSource(body string, statusCode int) ZabbixDatasourceInstance {
apiUrl, _ := url.Parse(basicDatasourceInfo.Url)
return ZabbixDatasourceInstance{
url: apiUrl,
dsInfo: basicDatasourceInfo,
queryCache: NewCache(10*time.Minute, 10*time.Minute),
httpClient: NewTestClient(func(req *http.Request) *http.Response {
return &http.Response{
StatusCode: statusCode,
Body: ioutil.NopCloser(bytes.NewBufferString(body)),
Header: make(http.Header),
}
}),
authToken: "sampleAuthToken",
logger: hclog.Default(),
}
}
func TestZabbixAPIQuery(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":"sampleResult"}`, 200)
resp, err := zabbixDatasource.ZabbixAPIQuery(context.Background(), mockDataSourceRequest(`{"target":{"method":"Method","params":{"param1" : "Param1"}}}`))
assert.Equal(t, "\"sampleResult\"", resp.GetResults()[0].GetMetaJson())
assert.Equal(t, "zabbixAPI", resp.GetResults()[0].GetRefId())
assert.Nil(t, err)
}
func TestZabbixAPIQueryEmptyQuery(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":"sampleResult"}`, 200)
resp, err := zabbixDatasource.ZabbixAPIQuery(context.Background(), mockDataSourceRequest(``))
assert.Nil(t, resp)
assert.NotNil(t, err)
}
func TestZabbixAPIQueryNoQueries(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":"sampleResult"}`, 200)
basicDatasourceRequest := &datasource.DatasourceRequest{
Datasource: &datasource.DatasourceInfo{
Id: 1,
Name: "TestDatasource",
},
}
resp, err := zabbixDatasource.ZabbixAPIQuery(context.Background(), basicDatasourceRequest)
assert.Nil(t, resp)
assert.Equal(t, "At least one query should be provided", err.Error())
}
func TestZabbixAPIQueryError(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":"sampleResult"}`, 500)
resp, err := zabbixDatasource.ZabbixAPIQuery(context.Background(), mockDataSourceRequest(`{"target":{"method":"Method","params":{"param1" : "Param1"}}}`))
assert.Nil(t, resp)
assert.Equal(t, "ZabbixAPIQuery is not implemented yet", err.Error())
}
func TestLogin(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":"sampleResult"}`, 200)
resp, err := zabbixDatasource.login(context.Background(), "username", "password")
assert.Equal(t, "sampleResult", resp)
assert.Nil(t, err)
}
func TestLoginError(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":"sampleResult"}`, 500)
resp, err := zabbixDatasource.login(context.Background(), "username", "password")
assert.Equal(t, "", resp)
assert.NotNil(t, err)
}
func TestLoginWithDs(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":"sampleResult"}`, 200)
err := zabbixDatasource.login(context.Background())
assert.Equal(t, "sampleResult", zabbixDatasource.authToken)
assert.Nil(t, err)
}
func TestLoginWithDsError(t *testing.T) {
errResponse := `{"error":{"code":-32500,"message":"Application error.","data":"Login name or password is incorrect."}}`
zabbixDatasource := mockZabbixDataSource(errResponse, 200)
err := zabbixDatasource.login(context.Background())
assert.Equal(t, "", zabbixDatasource.authToken)
assert.NotNil(t, err)
}
func TestZabbixRequest(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":"sampleResult"}`, 200)
resp, err := zabbixDatasource.ZabbixRequest(context.Background(), "method", ZabbixAPIParams{})
assert.Equal(t, "sampleResult", resp.MustString())
assert.Nil(t, err)
}
func TestZabbixRequestWithNoAuthToken(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":"auth"}`, 200)
resp, err := zabbixDatasource.ZabbixRequest(context.Background(), "method", ZabbixAPIParams{})
assert.Equal(t, "auth", resp.MustString())
assert.Nil(t, err)
}
func TestZabbixRequestError(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":"sampleResult"}`, 500)
resp, err := zabbixDatasource.ZabbixRequest(context.Background(), "method", ZabbixAPIParams{})
assert.Nil(t, resp)
assert.NotNil(t, err)
}
func TestZabbixAPIRequest(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":"sampleResult"}`, 200)
resp, err := zabbixDatasource.ZabbixAPIRequest(context.Background(), "item.get", ZabbixAPIParams{}, "auth")
assert.Equal(t, "sampleResult", resp.MustString())
assert.Nil(t, err)
}
func TestZabbixAPIRequestError(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":"sampleResult"}`, 500)
resp, err := zabbixDatasource.ZabbixAPIRequest(context.Background(), "item.get", ZabbixAPIParams{}, "auth")
assert.Nil(t, resp)
assert.NotNil(t, err)
}
func TestTestConnection(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":"sampleResult"}`, 200)
resp, err := zabbixDatasource.TestConnection(context.Background(), mockDataSourceRequest(``))
assert.Equal(t, "{\"zabbixVersion\":\"sampleResult\",\"dbConnectorStatus\":null}", resp.Results[0].GetMetaJson())
assert.Nil(t, err)
}
func TestTestConnectionError(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":"sampleResult"}`, 500)
resp, err := zabbixDatasource.TestConnection(context.Background(), mockDataSourceRequest(``))
assert.Equal(t, "", resp.Results[0].GetMetaJson())
assert.NotNil(t, resp.Results[0].GetError())
assert.Nil(t, err)
}
func TestIsNotAuthorized(t *testing.T) {
testPositive := isNotAuthorized("Not authorised.")
assert.True(t, testPositive)
testNegative := isNotAuthorized("testNegative")
assert.False(t, testNegative)
}
func TestHandleAPIResult(t *testing.T) {
expectedResponse, err := handleAPIResult([]byte(`{"result":"sampleResult"}`))
assert.Equal(t, "sampleResult", expectedResponse.MustString())
assert.Nil(t, err)
}
func TestHandleAPIResultFormatError(t *testing.T) {
expectedResponse, err := handleAPIResult([]byte(`{"result"::"sampleResult"}`))
assert.NotNil(t, err)
assert.Nil(t, expectedResponse)
}
func TestHandleAPIResultError(t *testing.T) {
expectedResponse, err := handleAPIResult([]byte(`{"result":"sampleResult", "error":{"message":"Message", "data":"Data"}}`))
assert.Equal(t, "Message Data", err.Error())
assert.Nil(t, expectedResponse)
}
func TestGetAllGroups(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":[{"groupid": "46489126", "name": "name1"},{"groupid": "46489127", "name":"name2"}]}`, 200)
resp, err := zabbixDatasource.getAllGroups(context.Background(), basicDatasourceInfo)
assert.Equal(t, "46489126", resp.MustArray()[0].(map[string]interface{})["groupid"])
assert.Equal(t, "46489127", resp.MustArray()[1].(map[string]interface{})["groupid"])
assert.Nil(t, err)
}
func TestGetAllHosts(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":[{"hostid": "46489126", "name": "hostname1"},{"hostid": "46489127", "name":"hostname2"}]}`, 200)
resp, err := zabbixDatasource.getAllHosts(context.Background(), basicDatasourceInfo, []string{"46489127", "46489127"})
assert.Equal(t, "46489126", resp.MustArray()[0].(map[string]interface{})["hostid"])
assert.Equal(t, "46489127", resp.MustArray()[1].(map[string]interface{})["hostid"])
assert.Nil(t, err)
}
func TestGetAllApps(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":[{"applicationid": "46489126", "name": "hostname1"},{"applicationid": "46489127", "name":"hostname2"}]}`, 200)
resp, err := zabbixDatasource.getAllApps(context.Background(), basicDatasourceInfo, []string{"46489127", "46489127"})
assert.Equal(t, "46489126", resp.MustArray()[0].(map[string]interface{})["applicationid"])
assert.Equal(t, "46489127", resp.MustArray()[1].(map[string]interface{})["applicationid"])
assert.Nil(t, err)
}
func TestGetAllItems(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":[{"itemid": "46489126", "name": "hostname1"},{"itemid": "46489127", "name":"hostname2"}]}`, 200)
resp, err := zabbixDatasource.getAllItems(context.Background(), basicDatasourceInfo, []string{"46489127", "46489127"}, []string{"7947934", "9182763"}, "num")
assert.Equal(t, "46489126", resp.MustArray()[0].(map[string]interface{})["itemid"])
assert.Equal(t, "46489127", resp.MustArray()[1].(map[string]interface{})["itemid"])
assert.Nil(t, err)
}
func TestGetGroups(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":[{"groupid": "46489126", "name": "name1"},{"groupid": "46489127", "name":"name2"}]}`, 200)
resp, err := zabbixDatasource.getGroups(context.Background(), basicDatasourceInfo, "name1")
assert.Equal(t, "46489126", resp[0]["groupid"])
assert.Equal(t, "name1", resp[0]["name"])
assert.Nil(t, err)
}
func TestGetGroupsError(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":[{"groupid": "46489126", "name": "name1"},{"groupid": "46489127", "name":"name2"}]}`, 500)
resp, err := zabbixDatasource.getGroups(context.Background(), basicDatasourceInfo, "name1")
assert.Nil(t, resp)
assert.NotNil(t, err)
}
func TestGetHosts(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":[{"groupid": "46489126", "hostid": "7468763", "name": "hostname1"},{"groupid": "46489127","hostid": "846586", "name":"hostname2"}]}`, 200)
resp, err := zabbixDatasource.getHosts(context.Background(), basicDatasourceInfo, "nam", "hostname1")
assert.Equal(t, "7468763", resp[0]["hostid"])
assert.Equal(t, "hostname1", resp[0]["name"])
assert.Nil(t, err)
}
func TestGetHostsError(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":[{"groupid": "46489126", "hostid": "7468763", "name": "hostname1"},{"groupid": "46489127","hostid": "846586", "name":"hostname2"}]}`, 500)
resp, err := zabbixDatasource.getHosts(context.Background(), basicDatasourceInfo, "nam", "hostna")
assert.Nil(t, resp)
assert.NotNil(t, err)
}
func TestGetApps(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":[{"groupid": "46489126", "hostid": "7468763", "applicationid": "7947934", "name": "appname1"},
{"groupid": "46489127","hostid": "846586", "applicationid": "9182763", "name": "appname2"}]}`, 200)
resp, err := zabbixDatasource.getApps(context.Background(), basicDatasourceInfo, "nam", "hostnam", "appname1")
assert.Equal(t, "7947934", resp[0]["applicationid"])
assert.Equal(t, "appname1", resp[0]["name"])
assert.Nil(t, err)
}
func TestGetAppsError(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":[{"groupid": "46489126", "hostid": "7468763", "applicationid": "7947934", "name": "appname1"},
{"groupid": "46489127","hostid": "846586", "applicationid": "9182763", "name": "appname2"}]}`, 500)
resp, err := zabbixDatasource.getApps(context.Background(), basicDatasourceInfo, "nam", "hostnam", "appname1")
assert.Nil(t, resp)
assert.NotNil(t, err)
}
func TestGetItems(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":[{"groupid": "46489126", "hostid": "7468763", "applicationid": "7947934", "itemid": "837465", "name": "itemname1", "status": "0"},
{"groupid": "46489127","hostid": "846586", "applicationid": "9182763", "itemid" : "0288374", "name": "itemname2", "status": "0"}]}`, 200)
resp, err := zabbixDatasource.getItems(context.Background(), basicDatasourceInfo, "itemname1", "itemname1", "itemname1", "itemname1", "num")
assert.Equal(t, "837465", resp[0].ID)
assert.Equal(t, "itemname1", resp[0].Name)
assert.Nil(t, err)
}
func TestGetItemsError(t *testing.T) {
zabbixDatasource := mockZabbixDataSource(`{"result":[{"groupid": "46489126", "hostid": "7468763", "applicationid": "7947934", "itemid": "837465", "name": "itemname1", "status": "0"},
{"groupid": "46489127","hostid": "846586", "applicationid": "9182763", "itemid" : "0288374", "name": "itemname2", "status": "0"}]}`, 500)
resp, err := zabbixDatasource.getItems(context.Background(), basicDatasourceInfo, "name", "name", "name", "name", "num")
assert.Nil(t, resp)
assert.NotNil(t, err)
}
func TestGetTrendValueType(t *testing.T) {
json1, _ := simplejson.NewJson([]byte(`{"functions":[{"def":{"name":"name1"}},{"def":{"name":"name2"}}]}`))
json2, _ := simplejson.NewJson([]byte(`{"functions":[{"def":{"name":"name1"}},{"def":{"name":"name2"}}]}`))
jsonQueries := []*simplejson.Json{json1, json2}
zabbixDatasource := mockZabbixDataSource(`{"result":"sampleResult"}`, 200)
resp := zabbixDatasource.getTrendValueType(jsonQueries)
assert.Equal(t, "avg", resp)
}
func TestGetConsolidateBy(t *testing.T) {
json1, _ := simplejson.NewJson([]byte(`{"functions":[{"def":{"name":"consolidateBy", "params":["sum"]}},{"def":{"name":"name2"}}]}`))
json2, _ := simplejson.NewJson([]byte(`{"functions":[{"def":{"name":"name1"}},{"def":{"name":"name2"}}]}`))
jsonQueries := []*simplejson.Json{json1, json2}
zabbixDatasource := mockZabbixDataSource(`{"result":"sampleResult"}`, 200)
resp := zabbixDatasource.getConsolidateBy(jsonQueries)
assert.Equal(t, "sum", resp)
}
func Test_isUseTrend(t *testing.T) {
tests := []struct {
name string
timeRange *datasource.TimeRange
want bool
}{
{
name: "History time",
timeRange: &datasource.TimeRange{
FromEpochMs: time.Now().Add(-time.Hour*48).Unix() * 1000,
ToEpochMs: time.Now().Add(-time.Hour*12).Unix() * 1000,
},
want: false,
},
{
name: "Trend time (past 7 days)",
timeRange: &datasource.TimeRange{
FromEpochMs: time.Now().Add(-time.Hour*24*14).Unix() * 1000,
ToEpochMs: time.Now().Add(-time.Hour*24*13).Unix() * 1000,
},
want: true,
},
{
name: "Trend time (longer than 4 days)",
timeRange: &datasource.TimeRange{
FromEpochMs: time.Now().Add(-time.Hour*24*8).Unix() * 1000,
ToEpochMs: time.Now().Add(-time.Hour*24*1).Unix() * 1000,
},
want: true,
},
}
for _, tt := range tests {
got := isUseTrend(tt.timeRange)
assert.Equal(t, tt.want, got, tt.name, tt.timeRange)
}
}
func Test_parseFilter(t *testing.T) {
tests := []struct {
name string
filter string
want *regexp.Regexp
wantErr string
}{
{
name: "Non-regex filter",
filter: "foobar",
want: nil,
},
{
name: "Non-regex filter (would-be invalid regex)",
filter: "fooba(r",
want: nil,
},
{
name: "Regex filter",
filter: "/^foo.+/",
want: regexp.MustCompile("^foo.+"),
},
{
name: "Regex filter with flags",
filter: "/^foo.+/s",
want: regexp.MustCompile("(?s)^foo.+"),
},
{
name: "Invalid regex",
filter: "/fooba(r/",
wantErr: "error parsing regexp: missing closing ): `fooba(r`",
},
{
name: "Unsupported flag",
filter: "/foo.+/z",
wantErr: "error parsing regexp: unsupported flags `z` (expected [imsU])",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseFilter(tt.filter)
if tt.wantErr != "" {
assert.Error(t, err)
assert.EqualError(t, err, tt.wantErr)
assert.Nil(t, got)
return
}
assert.NoError(t, err)
assert.Equal(t, tt.want, got)
})
}
}

44
pkg/zabbixapi/testing.go Normal file
View File

@@ -0,0 +1,44 @@
package zabbixapi
import (
"bytes"
"io/ioutil"
"net/http"
"net/url"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
)
type RoundTripFunc func(req *http.Request) *http.Response
func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return f(req), nil
}
//NewTestClient returns *http.Client with Transport replaced to avoid making real calls
func NewTestClient(fn RoundTripFunc) *http.Client {
return &http.Client{
Transport: RoundTripFunc(fn),
}
}
func MockZabbixAPI(body string, statusCode int) (*ZabbixAPI, error) {
apiLogger := log.New()
zabbixURL, err := url.Parse("http://zabbix.org/zabbix")
if err != nil {
return nil, err
}
return &ZabbixAPI{
url: zabbixURL,
logger: apiLogger,
httpClient: NewTestClient(func(req *http.Request) *http.Response {
return &http.Response{
StatusCode: statusCode,
Body: ioutil.NopCloser(bytes.NewBufferString(body)),
Header: make(http.Header),
}
}),
}, nil
}

View File

@@ -81,6 +81,11 @@ func (api *ZabbixAPI) SetUrl(api_url string) error {
return nil
}
// GetAuth returns API authentication token
func (api *ZabbixAPI) GetAuth() string {
return api.auth
}
// SetAuth sets API authentication token
func (api *ZabbixAPI) SetAuth(auth string) {
api.auth = auth

View File

@@ -1,53 +1,14 @@
package zabbixapi
import (
"bytes"
"context"
"io/ioutil"
"net/http"
"net/url"
"testing"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/stretchr/testify/assert"
)
type RoundTripFunc func(req *http.Request) *http.Response
func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return f(req), nil
}
//NewTestClient returns *http.Client with Transport replaced to avoid making real calls
func NewTestClient(fn RoundTripFunc) *http.Client {
return &http.Client{
Transport: RoundTripFunc(fn),
}
}
func mockZabbixAPI(body string, statusCode int) (*ZabbixAPI, error) {
apiLogger := log.New()
zabbixURL, err := url.Parse("http://zabbix.org/zabbix")
if err != nil {
return nil, err
}
return &ZabbixAPI{
url: zabbixURL,
logger: apiLogger,
httpClient: NewTestClient(func(req *http.Request) *http.Response {
return &http.Response{
StatusCode: statusCode,
Body: ioutil.NopCloser(bytes.NewBufferString(body)),
Header: make(http.Header),
}
}),
}, nil
}
func TestZabbixAPIUnauthenticatedQuery(t *testing.T) {
zabbixApi, _ := mockZabbixAPI(`{"result":"sampleResult"}`, 200)
zabbixApi, _ := MockZabbixAPI(`{"result":"sampleResult"}`, 200)
resp, err := zabbixApi.RequestUnauthenticated(context.Background(), "test.get", map[string]interface{}{})
assert.Equal(t, "sampleResult", resp.MustString())
@@ -55,7 +16,7 @@ func TestZabbixAPIUnauthenticatedQuery(t *testing.T) {
}
func TestLogin(t *testing.T) {
zabbixApi, _ := mockZabbixAPI(`{"result":"secretauth"}`, 200)
zabbixApi, _ := MockZabbixAPI(`{"result":"secretauth"}`, 200)
err := zabbixApi.Authenticate(context.Background(), "user", "password")
assert.Nil(t, err)
@@ -91,7 +52,7 @@ func TestZabbixAPI(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
zabbixApi, _ := mockZabbixAPI(tt.mockApiResponse, tt.mockApiResponseCode)
zabbixApi, _ := MockZabbixAPI(tt.mockApiResponse, tt.mockApiResponseCode)
zabbixApi.auth = tt.auth
resp, err := zabbixApi.Request(context.Background(), "test.get", map[string]interface{}{})