package azuremonitor import ( "encoding/json" "math" "os" "path/filepath" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/xorcare/pointer" ) 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", expectedFrame: func() *data.Frame { frame := data.NewFrame("", data.NewField("TimeGenerated", nil, []*time.Time{ pointer.Time(time.Date(2020, 4, 19, 19, 16, 6, 5e8, time.UTC)), pointer.Time(time.Date(2020, 4, 19, 19, 16, 16, 5e8, time.UTC)), pointer.Time(time.Date(2020, 4, 19, 19, 16, 26, 5e8, time.UTC)), }), data.NewField("Computer", nil, []*string{ pointer.String("grafana-vm"), pointer.String("grafana-vm"), pointer.String("grafana-vm"), }), data.NewField("avg_CounterValue", nil, []*float64{ pointer.Float64(1.1), pointer.Float64(2.2), pointer.Float64(3.3), }), ) frame.Meta = &data.FrameMeta{ Custom: &LogAnalyticsMeta{ColumnTypes: []string{"datetime", "string", "real"}}, } return frame }, }, { name: "response table", testFile: "loganalytics/6-log-analytics-response-table.json", expectedFrame: func() *data.Frame { frame := data.NewFrame("", data.NewField("TenantId", nil, []*string{ pointer.String("a2c1b44e-3e57-4410-b027-6cc0ae6dee67"), pointer.String("a2c1b44e-3e57-4410-b027-6cc0ae6dee67"), pointer.String("a2c1b44e-3e57-4410-b027-6cc0ae6dee67"), }), data.NewField("Computer", nil, []*string{ pointer.String("grafana-vm"), pointer.String("grafana-vm"), pointer.String("grafana-vm"), }), data.NewField("ObjectName", nil, []*string{ pointer.String("Memory"), pointer.String("Memory"), pointer.String("Memory"), }), data.NewField("CounterName", nil, []*string{ pointer.String("Available MBytes Memory"), pointer.String("Available MBytes Memory"), pointer.String("Available MBytes Memory"), }), data.NewField("InstanceName", nil, []*string{ pointer.String("Memory"), pointer.String("Memory"), pointer.String("Memory"), }), data.NewField("Min", nil, []*float64{nil, nil, nil}), data.NewField("Max", nil, []*float64{nil, nil, nil}), data.NewField("SampleCount", nil, []*int32{nil, nil, nil}), data.NewField("CounterValue", nil, []*float64{ pointer.Float64(2040), pointer.Float64(2066), pointer.Float64(2066), }), data.NewField("TimeGenerated", nil, []*time.Time{ pointer.Time(time.Date(2020, 4, 23, 11, 46, 3, 857e6, time.UTC)), pointer.Time(time.Date(2020, 4, 23, 11, 46, 13, 857e6, time.UTC)), pointer.Time(time.Date(2020, 4, 23, 11, 46, 23, 857e6, time.UTC)), }), ) frame.Meta = &data.FrameMeta{ Custom: &LogAnalyticsMeta{ColumnTypes: []string{"string", "string", "string", "string", "string", "real", "real", "int", "real", "datetime"}}, } return frame }, }, { name: "all supported field types", testFile: "loganalytics/7-log-analytics-all-types-table.json", expectedFrame: func() *data.Frame { frame := data.NewFrame("", data.NewField("XBool", nil, []*bool{pointer.Bool(true)}), data.NewField("XString", nil, []*string{pointer.String("Grafana")}), data.NewField("XDateTime", nil, []*time.Time{pointer.Time(time.Date(2006, 1, 2, 22, 4, 5, 1*1e8, time.UTC))}), data.NewField("XDynamic", nil, []*string{pointer.String(`[{"person":"Daniel"},{"cats":23},{"diagnosis":"cat problem"}]`)}), data.NewField("XGuid", nil, []*string{pointer.String("74be27de-1e4e-49d9-b579-fe0b331d3642")}), data.NewField("XInt", nil, []*int32{pointer.Int32(2147483647)}), data.NewField("XLong", nil, []*int64{pointer.Int64(9223372036854775807)}), data.NewField("XReal", nil, []*float64{pointer.Float64(1.797693134862315708145274237317043567981e+308)}), data.NewField("XTimeSpan", nil, []*string{pointer.String("00:00:00.0000001")}), data.NewField("XDecimal", nil, []*float64{pointer.Float64(79228162514264337593543950335)}), ) frame.Meta = &data.FrameMeta{ Custom: &LogAnalyticsMeta{ColumnTypes: []string{"bool", "string", "datetime", "dynamic", "guid", "int", "long", "real", "timespan", "decimal"}}, } return frame }, }, { name: "nan and infinity in real response", testFile: "loganalytics/8-log-analytics-response-nan-inf.json", expectedFrame: func() *data.Frame { frame := data.NewFrame("", data.NewField("XInf", nil, []*float64{pointer.Float64(math.Inf(0))}), data.NewField("XInfNeg", nil, []*float64{pointer.Float64(math.Inf(-2))}), data.NewField("XNan", nil, []*float64{pointer.Float64(math.NaN())}), ) frame.Meta = &data.FrameMeta{ Custom: &LogAnalyticsMeta{ColumnTypes: []string{"real", "real", "real"}}, } return frame }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { res := loadLogAnalyticsTestFileWithNumber(t, tt.testFile) frame, err := LogTableToFrame(&res.Tables[0]) require.NoError(t, err) if diff := cmp.Diff(tt.expectedFrame(), frame, data.FrameTestCompareOptions()...); diff != "" { t.Errorf("Result mismatch (-want +got):\n%s", diff) } }) } } func loadLogAnalyticsTestFileWithNumber(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 }