The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/tsdb/azuremonitor/loganalytics/azure-response-table-frame_...

200 lines
5.9 KiB

package loganalytics
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/kinds/dataquery"
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/testdata"
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestLogTableToFrame(t *testing.T) {
tests := []struct {
name string
testFile string
expectedFrame func() *data.Frame
}{
{
name: "single series",
testFile: "loganalytics/1-log-analytics-response-metrics-single-series.json",
},
{
name: "response table",
testFile: "loganalytics/6-log-analytics-response-table.json",
},
{
name: "all supported field types",
testFile: "loganalytics/7-log-analytics-all-types-table.json",
},
{
name: "nan and infinity in real response",
testFile: "loganalytics/8-log-analytics-response-nan-inf.json",
},
{
name: "data and error in real response",
testFile: "loganalytics/9-log-analytics-response-error.json",
},
{
name: "data and warning in real response",
testFile: "loganalytics/10-log-analytics-response-warning.json",
},
{
name: "empty data response",
testFile: "loganalytics/11-log-analytics-response-empty.json",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
res := loadTestFileWithNumber(t, tt.testFile)
frame, err := ResponseTableToFrame(&res.Tables[0], "A", "query", dataquery.AzureQueryTypeAzureLogAnalytics, dataquery.ResultFormatTable, false)
appendErrorNotice(frame, res.Error)
require.NoError(t, err)
testdata.CheckGoldenFrame(t, "../testdata", tt.testFile, frame)
})
}
}
func TestTraceTableToFrame(t *testing.T) {
tests := []struct {
name string
testFile string
expectedFrame func() *data.Frame
resultFormat dataquery.ResultFormat
queryType dataquery.AzureQueryType
}{
{
name: "multi trace",
testFile: "traces/1-traces-multiple-table.json",
resultFormat: dataquery.ResultFormatTable,
queryType: dataquery.AzureQueryTypeAzureTraces,
},
{
name: "multi trace as trace format",
testFile: "traces/1-traces-multiple-table.json",
resultFormat: dataquery.ResultFormatTrace,
queryType: dataquery.AzureQueryTypeAzureTraces,
},
{
name: "single trace",
testFile: "traces/2-traces-single-table.json",
resultFormat: dataquery.ResultFormatTable,
queryType: dataquery.AzureQueryTypeAzureTraces,
},
{
name: "single trace as trace format",
testFile: "traces/2-traces-single-table.json",
resultFormat: dataquery.ResultFormatTrace,
queryType: dataquery.AzureQueryTypeAzureTraces,
},
{
name: "single trace with empty serviceTags and tags",
testFile: "traces/3-traces-empty-dynamics.json",
resultFormat: dataquery.ResultFormatTrace,
queryType: dataquery.AzureQueryTypeAzureTraces,
},
{
name: "single trace as trace format from exemplars query",
testFile: "traces/2-traces-single-table.json",
resultFormat: dataquery.ResultFormatTrace,
queryType: dataquery.AzureQueryTypeTraceql,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
res := loadTestFileWithNumber(t, tt.testFile)
frame, err := ResponseTableToFrame(&res.Tables[0], "A", "query", tt.queryType, tt.resultFormat, false)
appendErrorNotice(frame, res.Error)
require.NoError(t, err)
testdata.CheckGoldenFrame(t, "../testdata", fmt.Sprintf("%s.%s", tt.testFile, strings.ReplaceAll(tt.name, " ", "-")), frame)
})
}
}
func TestLargeLogsResponse(t *testing.T) {
t.Run("large logs response with limit enabled", func(t *testing.T) {
res := AzureLogAnalyticsResponse{
Tables: []types.AzureResponseTable{
{Name: "PrimaryResult",
Columns: []struct {
Name string `json:"name"`
Type string `json:"type"`
}{
{Name: "value", Type: "int"},
}},
},
}
rows := [][]any{}
for i := 0; i <= 30000; i++ {
rows = append(rows, []any{json.Number(strconv.Itoa(i))})
}
res.Tables[0].Rows = rows
resultFormat := dataquery.ResultFormatLogs
frame, err := ResponseTableToFrame(&res.Tables[0], "A", "query", dataquery.AzureQueryTypeAzureLogAnalytics, resultFormat, false)
appendErrorNotice(frame, res.Error)
require.NoError(t, err)
require.Equal(t, frame.Rows(), 30000)
require.Len(t, frame.Meta.Notices, 1)
require.Equal(t, frame.Meta.Notices[0], data.Notice{
Severity: data.NoticeSeverityWarning,
Text: "The number of results in the result set has been limited to 30,000.",
})
})
t.Run("large logs response with limit disabled", func(t *testing.T) {
res := AzureLogAnalyticsResponse{
Tables: []types.AzureResponseTable{
{Name: "PrimaryResult",
Columns: []struct {
Name string `json:"name"`
Type string `json:"type"`
}{
{Name: "value", Type: "int"},
}},
},
}
rows := [][]any{}
for i := 0; i < 40000; i++ {
rows = append(rows, []any{json.Number(strconv.Itoa(i))})
}
res.Tables[0].Rows = rows
resultFormat := dataquery.ResultFormatLogs
frame, err := ResponseTableToFrame(&res.Tables[0], "A", "query", dataquery.AzureQueryTypeAzureLogAnalytics, resultFormat, true)
appendErrorNotice(frame, res.Error)
require.NoError(t, err)
require.Equal(t, frame.Rows(), 40000)
require.Nil(t, frame.Meta.Notices)
})
}
func loadTestFileWithNumber(t *testing.T, name string) AzureLogAnalyticsResponse {
t.Helper()
path := filepath.Join("../testdata", name)
// Ignore gosec warning G304 since it's a test
// nolint:gosec
f, err := os.Open(path)
require.NoError(t, err)
defer func() {
err := f.Close()
assert.NoError(t, err)
}()
d := json.NewDecoder(f)
d.UseNumber()
var data AzureLogAnalyticsResponse
err = d.Decode(&data)
require.NoError(t, err)
return data
}