Skip to main content
Glama
grafana

Grafana

Official
by grafana
prometheus_test.go9.65 kB
// Requires a Grafana instance running on localhost:3000, // with a Prometheus datasource provisioned. // Run with `go test -tags integration`. //go:build integration package tools import ( "fmt" "testing" "time" "github.com/prometheus/common/model" "github.com/prometheus/prometheus/model/labels" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestPrometheusTools(t *testing.T) { t.Run("list prometheus metric metadata", func(t *testing.T) { ctx := newTestContext() result, err := listPrometheusMetricMetadata(ctx, ListPrometheusMetricMetadataParams{ DatasourceUID: "prometheus", }) require.NoError(t, err) assert.Len(t, result, 10) }) t.Run("list prometheus metric names", func(t *testing.T) { ctx := newTestContext() result, err := listPrometheusMetricNames(ctx, ListPrometheusMetricNamesParams{ DatasourceUID: "prometheus", Regex: ".*", Limit: 10, }) require.NoError(t, err) assert.Len(t, result, 10) }) t.Run("list prometheus label names", func(t *testing.T) { ctx := newTestContext() result, err := listPrometheusLabelNames(ctx, ListPrometheusLabelNamesParams{ DatasourceUID: "prometheus", Matches: []Selector{ { Filters: []LabelMatcher{ {Name: "job", Value: "prometheus"}, }, }, }, Limit: 10, }) require.NoError(t, err) assert.Len(t, result, 10) }) t.Run("list prometheus label values", func(t *testing.T) { ctx := newTestContext() result, err := listPrometheusLabelValues(ctx, ListPrometheusLabelValuesParams{ DatasourceUID: "prometheus", LabelName: "job", Matches: []Selector{ { Filters: []LabelMatcher{ {Name: "job", Value: "prometheus"}, }, }, }, }) require.NoError(t, err) assert.Len(t, result, 1) }) } func TestSelectorMatches(t *testing.T) { testCases := []struct { name string selector Selector labels map[string]string expected bool expectErr bool }{ { name: "Equal match", selector: Selector{ Filters: []LabelMatcher{ {Name: "job", Type: "=", Value: "prometheus"}, }, }, labels: map[string]string{"job": "prometheus"}, expected: true, }, { name: "Equal no match", selector: Selector{ Filters: []LabelMatcher{ {Name: "job", Type: "=", Value: "prometheus"}, }, }, labels: map[string]string{"job": "node-exporter"}, expected: false, }, { name: "Not equal match", selector: Selector{ Filters: []LabelMatcher{ {Name: "job", Type: "!=", Value: "prometheus"}, }, }, labels: map[string]string{"job": "node-exporter"}, expected: true, }, { name: "Not equal no match", selector: Selector{ Filters: []LabelMatcher{ {Name: "job", Type: "!=", Value: "prometheus"}, }, }, labels: map[string]string{"job": "prometheus"}, expected: false, }, { name: "Regex match", selector: Selector{ Filters: []LabelMatcher{ {Name: "job", Type: "=~", Value: "prom.*"}, }, }, labels: map[string]string{"job": "prometheus"}, expected: true, }, { name: "Regex no match", selector: Selector{ Filters: []LabelMatcher{ {Name: "job", Type: "=~", Value: "node.*"}, }, }, labels: map[string]string{"job": "prometheus"}, expected: false, }, { name: "Not regex match", selector: Selector{ Filters: []LabelMatcher{ {Name: "job", Type: "!~", Value: "node.*"}, }, }, labels: map[string]string{"job": "prometheus"}, expected: true, }, { name: "Not regex no match", selector: Selector{ Filters: []LabelMatcher{ {Name: "job", Type: "!~", Value: "prom.*"}, }, }, labels: map[string]string{"job": "prometheus"}, expected: false, }, { name: "Multiple filters all match", selector: Selector{ Filters: []LabelMatcher{ {Name: "job", Type: "=", Value: "prometheus"}, {Name: "instance", Type: "=~", Value: "localhost.*"}, }, }, labels: map[string]string{"job": "prometheus", "instance": "localhost:9090"}, expected: true, }, { name: "Multiple filters one doesn't match", selector: Selector{ Filters: []LabelMatcher{ {Name: "job", Type: "=", Value: "prometheus"}, {Name: "instance", Type: "=~", Value: "remote.*"}, }, }, labels: map[string]string{"job": "prometheus", "instance": "localhost:9090"}, expected: false, }, { name: "Label doesn't exist with = operator", selector: Selector{ Filters: []LabelMatcher{ {Name: "missing", Type: "=", Value: "value"}, }, }, labels: map[string]string{"job": "prometheus"}, expected: false, }, { name: "Label doesn't exist with != operator", selector: Selector{ Filters: []LabelMatcher{ {Name: "missing", Type: "!=", Value: "value"}, }, }, labels: map[string]string{"job": "prometheus"}, expected: true, }, { name: "Invalid matcher type", selector: Selector{ Filters: []LabelMatcher{ {Name: "job", Type: "<>", Value: "prometheus"}, }, }, labels: map[string]string{"job": "prometheus"}, expected: false, expectErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { lbls := labels.FromMap(tc.labels) result, err := tc.selector.Matches(lbls) if tc.expectErr { assert.Error(t, err) return } assert.NoError(t, err) assert.Equal(t, tc.expected, result) }) } } func TestPrometheusQueries(t *testing.T) { t.Run("query prometheus range", func(t *testing.T) { end := time.Now() start := end.Add(-10 * time.Minute) for _, step := range []int{15, 60, 300} { t.Run(fmt.Sprintf("step=%d", step), func(t *testing.T) { ctx := newTestContext() result, err := queryPrometheus(ctx, QueryPrometheusParams{ DatasourceUID: "prometheus", Expr: "test", StartTime: start.Format(time.RFC3339), EndTime: end.Format(time.RFC3339), StepSeconds: step, QueryType: "range", }) require.NoError(t, err) matrix := result.(model.Matrix) require.Len(t, matrix, 1) expectedLen := int(end.Sub(start).Seconds()/float64(step)) + 1 assert.Len(t, matrix[0].Values, expectedLen) assert.Less(t, matrix[0].Values[0].Timestamp.Sub(model.TimeFromUnix(start.Unix())), time.Duration(step)*time.Second) assert.Equal(t, matrix[0].Metric["__name__"], model.LabelValue("test")) }) } }) t.Run("query prometheus instant", func(t *testing.T) { ctx := newTestContext() result, err := queryPrometheus(ctx, QueryPrometheusParams{ DatasourceUID: "prometheus", Expr: "up", StartTime: time.Now().Format(time.RFC3339), QueryType: "instant", }) require.NoError(t, err) scalar := result.(model.Vector) assert.Equal(t, scalar[0].Value, model.SampleValue(1)) assert.Equal(t, scalar[0].Timestamp, model.TimeFromUnix(time.Now().Unix())) assert.Equal(t, scalar[0].Metric["__name__"], model.LabelValue("up")) }) t.Run("query prometheus instant with relative timestamps", func(t *testing.T) { ctx := newTestContext() beforeQuery := model.TimeFromUnix(time.Now().Unix()) result, err := queryPrometheus(ctx, QueryPrometheusParams{ DatasourceUID: "prometheus", Expr: "up", StartTime: "now", QueryType: "instant", }) afterQuery := model.TimeFromUnix(time.Now().Unix()) require.NoError(t, err) scalar := result.(model.Vector) assert.Equal(t, scalar[0].Value, model.SampleValue(1)) // Check that the timestamp is within the expected range buffer := 5 * time.Second assert.True(t, scalar[0].Timestamp >= beforeQuery, "Result timestamp should be after or equal to the time before the query") assert.True(t, scalar[0].Timestamp <= afterQuery.Add(buffer), "Result timestamp should be before or equal to the time after the query (with 5s buffer)") assert.Equal(t, scalar[0].Metric["__name__"], model.LabelValue("up")) }) t.Run("query prometheus range with relative timestamps", func(t *testing.T) { ctx := newTestContext() beforeQuery := model.TimeFromUnix(time.Now().Unix()) result, err := queryPrometheus(ctx, QueryPrometheusParams{ DatasourceUID: "prometheus", Expr: "test", StartTime: "now-1h", EndTime: "now", StepSeconds: 60, QueryType: "range", }) afterQuery := model.TimeFromUnix(time.Now().Unix()) require.NoError(t, err) matrix := result.(model.Matrix) require.Len(t, matrix, 1) // Should have approximately 60 samples (one per minute for an hour) assert.InDelta(t, 60, len(matrix[0].Values), 2) buffer := 5 * time.Second oneHour := time.Hour firstSampleTime := matrix[0].Values[0].Timestamp // Check that the start timestamp is within the expected range assert.True(t, firstSampleTime >= beforeQuery.Add(-oneHour), "First timestamp should be after or equal to the time before the query minus one hour") assert.True(t, firstSampleTime <= afterQuery.Add(buffer).Add(-oneHour), "First timestamp should be before or equal to the time after the query minus one hour (with 5s buffer)") // Check that the end timestamp is is within the expected range lastSampleTime := matrix[0].Values[len(matrix[0].Values)-1].Timestamp assert.True(t, lastSampleTime >= beforeQuery, "Last timestamp should be after or equal to the time before the query") assert.True(t, lastSampleTime <= afterQuery.Add(buffer), "Last timestamp should be before or equal to the time after the query (with 5s buffer)") assert.Equal(t, matrix[0].Metric["__name__"], model.LabelValue("test")) }) }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/grafana/mcp-grafana'

If you have feedback or need assistance with the MCP directory API, please join our Discord server