mirror of
https://github.com/grafana/grafana.git
synced 2024-11-29 20:24:18 -06:00
397 lines
9.5 KiB
Go
397 lines
9.5 KiB
Go
package influxdb
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"io/ioutil"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
|
"github.com/grafana/grafana/pkg/plugins"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func prepare(text string) io.ReadCloser {
|
|
return ioutil.NopCloser(strings.NewReader(text))
|
|
}
|
|
|
|
// nolint:staticcheck // plugins.DataQueryResult deprecated
|
|
func decodedFrames(t *testing.T, result plugins.DataQueryResult) data.Frames {
|
|
decoded, err := result.Dataframes.Decoded()
|
|
require.NoError(t, err)
|
|
return decoded
|
|
}
|
|
|
|
// nolint:staticcheck // plugins.DataQueryResult deprecated
|
|
func assertSeriesName(t *testing.T, result plugins.DataQueryResult, index int, name string) {
|
|
decoded := decodedFrames(t, result)
|
|
|
|
frame := decoded[index]
|
|
|
|
require.Equal(t, frame.Name, name)
|
|
|
|
// the current version of the alerting-code does not use the dataframe-name
|
|
// when generating the metric-names for the alerts.
|
|
// instead, it goes through multiple attributes on the Field.
|
|
// we use the `field.Config.DisplayNameFromDS` attribute.
|
|
|
|
valueFieldConfig := frame.Fields[1].Config
|
|
|
|
require.NotNil(t, valueFieldConfig)
|
|
require.Equal(t, valueFieldConfig.DisplayNameFromDS, name)
|
|
}
|
|
|
|
func TestInfluxdbResponseParser(t *testing.T) {
|
|
t.Run("Influxdb response parser should handle invalid JSON", func(t *testing.T) {
|
|
parser := &ResponseParser{}
|
|
|
|
response := `{ invalid }`
|
|
|
|
query := &Query{}
|
|
|
|
result := parser.Parse(prepare(response), query)
|
|
|
|
require.Nil(t, result.Dataframes)
|
|
require.Error(t, result.Error)
|
|
})
|
|
|
|
t.Run("Influxdb response parser should parse everything normally", func(t *testing.T) {
|
|
parser := &ResponseParser{}
|
|
|
|
response := `
|
|
{
|
|
"results": [
|
|
{
|
|
"series": [
|
|
{
|
|
"name": "cpu",
|
|
"columns": ["time","mean","sum"],
|
|
"tags": {"datacenter": "America"},
|
|
"values": [
|
|
[111,222,333],
|
|
[111,222,333],
|
|
[111,null,333]
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
`
|
|
|
|
query := &Query{}
|
|
|
|
result := parser.Parse(prepare(response), query)
|
|
|
|
decoded := decodedFrames(t, result)
|
|
require.Len(t, decoded, 2)
|
|
frame1 := decoded[0]
|
|
frame2 := decoded[1]
|
|
|
|
assertSeriesName(t, result, 0, "cpu.mean { datacenter: America }")
|
|
assertSeriesName(t, result, 1, "cpu.sum { datacenter: America }")
|
|
|
|
require.Len(t, frame1.Fields, 2)
|
|
require.Len(t, frame2.Fields, 2)
|
|
|
|
require.Equal(t, frame1.Fields[0].Len(), 3)
|
|
require.Equal(t, frame1.Fields[1].Len(), 3)
|
|
require.Equal(t, frame2.Fields[0].Len(), 3)
|
|
require.Equal(t, frame2.Fields[1].Len(), 3)
|
|
|
|
require.Equal(t, *frame1.Fields[1].At(1).(*float64), 222.0)
|
|
require.Equal(t, *frame2.Fields[1].At(1).(*float64), 333.0)
|
|
require.Nil(t, frame1.Fields[1].At(2))
|
|
})
|
|
|
|
t.Run("Influxdb response parser with invalid value-format", func(t *testing.T) {
|
|
parser := &ResponseParser{}
|
|
|
|
response := `
|
|
{
|
|
"results": [
|
|
{
|
|
"series": [
|
|
{
|
|
"name": "cpu",
|
|
"columns": ["time","mean"],
|
|
"values": [
|
|
[100,50],
|
|
[101,"hello"],
|
|
[102,52]
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
`
|
|
|
|
query := &Query{}
|
|
|
|
result := parser.Parse(prepare(response), query)
|
|
|
|
// the current behavior is that we do not report an error, we turn the invalid value into `nil`
|
|
require.Nil(t, result.Error)
|
|
require.Equal(t, result.ErrorString, "")
|
|
|
|
decoded := decodedFrames(t, result)
|
|
require.Len(t, decoded, 1)
|
|
|
|
frame := decoded[0]
|
|
|
|
require.Len(t, frame.Fields, 2)
|
|
|
|
field1 := frame.Fields[0]
|
|
field2 := frame.Fields[1]
|
|
|
|
require.Equal(t, field1.Len(), 3)
|
|
require.Equal(t, field2.Len(), 3)
|
|
|
|
require.Equal(t, *field2.At(0).(*float64), 50.0)
|
|
require.Nil(t, field2.At(1))
|
|
require.Equal(t, *field2.At(2).(*float64), 52.0)
|
|
})
|
|
|
|
t.Run("Influxdb response parser with invalid timestamp-format", func(t *testing.T) {
|
|
parser := &ResponseParser{}
|
|
|
|
response := `
|
|
{
|
|
"results": [
|
|
{
|
|
"series": [
|
|
{
|
|
"name": "cpu",
|
|
"columns": ["time","mean"],
|
|
"values": [
|
|
[100,50],
|
|
["hello",51],
|
|
["hello","hello"],
|
|
[102,52]
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
`
|
|
|
|
query := &Query{}
|
|
|
|
result := parser.Parse(prepare(response), query)
|
|
|
|
// the current behavior is that we do not report an error, we skip the item with the invalid timestamp
|
|
require.Nil(t, result.Error)
|
|
require.Equal(t, result.ErrorString, "")
|
|
|
|
decoded := decodedFrames(t, result)
|
|
require.Len(t, decoded, 1)
|
|
|
|
frame := decoded[0]
|
|
|
|
require.Len(t, frame.Fields, 2)
|
|
|
|
field1 := frame.Fields[0]
|
|
field2 := frame.Fields[1]
|
|
|
|
require.Equal(t, field1.Len(), 2)
|
|
require.Equal(t, field2.Len(), 2)
|
|
|
|
require.Equal(t, *field2.At(0).(*float64), 50.0)
|
|
require.Equal(t, *field2.At(1).(*float64), 52.0)
|
|
})
|
|
|
|
t.Run("Influxdb response parser with alias", func(t *testing.T) {
|
|
parser := &ResponseParser{}
|
|
|
|
response := `
|
|
{
|
|
"results": [
|
|
{
|
|
"series": [
|
|
{
|
|
"name": "cpu.upc",
|
|
"columns": ["time","mean","sum"],
|
|
"tags": {
|
|
"datacenter": "America",
|
|
"dc.region.name": "Northeast",
|
|
"cluster-name": "Cluster",
|
|
"/cluster/name/": "Cluster/",
|
|
"@cluster@name@": "Cluster@"
|
|
},
|
|
"values": [
|
|
[111,222,333]
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
`
|
|
|
|
query := &Query{Alias: "series alias"}
|
|
result := parser.Parse(prepare(response), query)
|
|
|
|
assertSeriesName(t, result, 0, "series alias")
|
|
|
|
query = &Query{Alias: "alias $m $measurement", Measurement: "10m"}
|
|
result = parser.Parse(prepare(response), query)
|
|
|
|
assertSeriesName(t, result, 0, "alias 10m 10m")
|
|
|
|
query = &Query{Alias: "alias $col", Measurement: "10m"}
|
|
result = parser.Parse(prepare(response), query)
|
|
|
|
assertSeriesName(t, result, 0, "alias mean")
|
|
assertSeriesName(t, result, 1, "alias sum")
|
|
|
|
query = &Query{Alias: "alias $tag_datacenter"}
|
|
result = parser.Parse(prepare(response), query)
|
|
|
|
assertSeriesName(t, result, 0, "alias America")
|
|
|
|
query = &Query{Alias: "alias $1"}
|
|
result = parser.Parse(prepare(response), query)
|
|
|
|
assertSeriesName(t, result, 0, "alias upc")
|
|
|
|
query = &Query{Alias: "alias $5"}
|
|
result = parser.Parse(prepare(response), query)
|
|
|
|
assertSeriesName(t, result, 0, "alias $5")
|
|
|
|
query = &Query{Alias: "series alias"}
|
|
result = parser.Parse(prepare(response), query)
|
|
|
|
assertSeriesName(t, result, 0, "series alias")
|
|
|
|
query = &Query{Alias: "alias [[m]] [[measurement]]", Measurement: "10m"}
|
|
result = parser.Parse(prepare(response), query)
|
|
|
|
assertSeriesName(t, result, 0, "alias 10m 10m")
|
|
|
|
query = &Query{Alias: "alias [[col]]", Measurement: "10m"}
|
|
result = parser.Parse(prepare(response), query)
|
|
|
|
assertSeriesName(t, result, 0, "alias mean")
|
|
assertSeriesName(t, result, 1, "alias sum")
|
|
|
|
query = &Query{Alias: "alias [[tag_datacenter]]"}
|
|
result = parser.Parse(prepare(response), query)
|
|
|
|
assertSeriesName(t, result, 0, "alias America")
|
|
|
|
query = &Query{Alias: "alias [[tag_dc.region.name]]"}
|
|
result = parser.Parse(prepare(response), query)
|
|
|
|
assertSeriesName(t, result, 0, "alias Northeast")
|
|
|
|
query = &Query{Alias: "alias [[tag_cluster-name]]"}
|
|
result = parser.Parse(prepare(response), query)
|
|
|
|
assertSeriesName(t, result, 0, "alias Cluster")
|
|
|
|
query = &Query{Alias: "alias [[tag_/cluster/name/]]"}
|
|
result = parser.Parse(prepare(response), query)
|
|
|
|
assertSeriesName(t, result, 0, "alias Cluster/")
|
|
|
|
query = &Query{Alias: "alias [[tag_@cluster@name@]]"}
|
|
result = parser.Parse(prepare(response), query)
|
|
|
|
assertSeriesName(t, result, 0, "alias Cluster@")
|
|
})
|
|
|
|
t.Run("Influxdb response parser with errors", func(t *testing.T) {
|
|
parser := &ResponseParser{}
|
|
|
|
response := `
|
|
{
|
|
"results": [
|
|
{
|
|
"series": [
|
|
{
|
|
"name": "cpu",
|
|
"columns": ["time","mean","sum"],
|
|
"tags": {"datacenter": "America"},
|
|
"values": [
|
|
[111,222,333],
|
|
[111,222,333],
|
|
[111,null,333]
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"error": "query-timeout limit exceeded"
|
|
}
|
|
]
|
|
}
|
|
`
|
|
|
|
query := &Query{}
|
|
|
|
result := parser.Parse(prepare(response), query)
|
|
|
|
decoded := decodedFrames(t, result)
|
|
|
|
require.Len(t, decoded, 2)
|
|
|
|
require.Equal(t, decoded[0].Fields[0].Len(), 3)
|
|
require.Equal(t, decoded[0].Fields[1].Len(), 3)
|
|
require.Equal(t, decoded[1].Fields[0].Len(), 3)
|
|
require.Equal(t, decoded[1].Fields[1].Len(), 3)
|
|
|
|
require.EqualError(t, result.Error, "query-timeout limit exceeded")
|
|
})
|
|
|
|
t.Run("Influxdb response parser with top-level error", func(t *testing.T) {
|
|
parser := &ResponseParser{}
|
|
|
|
response := `
|
|
{
|
|
"error": "error parsing query: found THING"
|
|
}
|
|
`
|
|
|
|
query := &Query{}
|
|
|
|
result := parser.Parse(prepare(response), query)
|
|
|
|
require.Nil(t, result.Dataframes)
|
|
|
|
require.EqualError(t, result.Error, "error parsing query: found THING")
|
|
})
|
|
|
|
t.Run("Influxdb response parser parseValue nil", func(t *testing.T) {
|
|
value := parseValue(nil)
|
|
require.Nil(t, value)
|
|
})
|
|
|
|
t.Run("Influxdb response parser parseValue valid JSON.number", func(t *testing.T) {
|
|
value := parseValue(json.Number("95.4"))
|
|
require.Equal(t, *value, 95.4)
|
|
})
|
|
|
|
t.Run("Influxdb response parser parseValue invalid type", func(t *testing.T) {
|
|
value := parseValue("95.4")
|
|
require.Nil(t, value)
|
|
})
|
|
|
|
t.Run("Influxdb response parser parseTimestamp valid JSON.number", func(t *testing.T) {
|
|
// currently we use seconds-precision with influxdb, so the test works with that.
|
|
// if we change this to for example milliseconds-precision, the tests will have to change.
|
|
timestamp, err := parseTimestamp(json.Number("1609556645"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, timestamp.Format(time.RFC3339), "2021-01-02T03:04:05Z")
|
|
})
|
|
|
|
t.Run("Influxdb response parser parseValue invalid type", func(t *testing.T) {
|
|
_, err := parseTimestamp("hello")
|
|
require.Error(t, err)
|
|
})
|
|
}
|