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/services/ngalert/api/api_testing_test.go

279 lines
8.4 KiB

package api
import (
"net/http"
"testing"
"time"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
models2 "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
acMock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/datasources"
fakes "github.com/grafana/grafana/pkg/services/datasources/fakes"
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/ngalert/eval"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/web"
)
func TestRouteTestGrafanaRuleConfig(t *testing.T) {
t.Run("when fine-grained access is enabled", func(t *testing.T) {
rc := &models2.ReqContext{
Context: &web.Context{
Req: &http.Request{},
},
SignedInUser: &models2.SignedInUser{
OrgId: 1,
},
}
t.Run("should return 401 if user cannot query a data source", func(t *testing.T) {
data1 := models.GenerateAlertQuery()
data2 := models.GenerateAlertQuery()
ac := acMock.New().WithPermissions([]*accesscontrol.Permission{
{Action: datasources.ActionQuery, Scope: datasources.ScopeProvider.GetResourceScopeUID(data1.DatasourceUID)},
})
srv := createTestingApiSrv(nil, ac, nil)
response := srv.RouteTestGrafanaRuleConfig(rc, definitions.TestRulePayload{
Expr: "",
GrafanaManagedCondition: &models.EvalAlertConditionCommand{
Condition: data1.RefID,
Data: []models.AlertQuery{data1, data2},
Now: time.Time{},
},
})
require.Equal(t, http.StatusUnauthorized, response.Status())
})
t.Run("should return 200 if user can query all data sources", func(t *testing.T) {
data1 := models.GenerateAlertQuery()
data2 := models.GenerateAlertQuery()
ac := acMock.New().WithPermissions([]*accesscontrol.Permission{
{Action: datasources.ActionQuery, Scope: datasources.ScopeProvider.GetResourceScopeUID(data1.DatasourceUID)},
{Action: datasources.ActionQuery, Scope: datasources.ScopeProvider.GetResourceScopeUID(data2.DatasourceUID)},
})
ds := &fakes.FakeCacheService{DataSources: []*models2.DataSource{
{Uid: data1.DatasourceUID},
{Uid: data2.DatasourceUID},
}}
evaluator := &eval.FakeEvaluator{}
var result []eval.Result
evaluator.EXPECT().ConditionEval(mock.Anything, mock.Anything, mock.Anything).Return(result, nil)
srv := createTestingApiSrv(ds, ac, evaluator)
response := srv.RouteTestGrafanaRuleConfig(rc, definitions.TestRulePayload{
Expr: "",
GrafanaManagedCondition: &models.EvalAlertConditionCommand{
Condition: data1.RefID,
Data: []models.AlertQuery{data1, data2},
Now: time.Time{},
},
})
require.Equal(t, http.StatusOK, response.Status())
evaluator.AssertCalled(t, "ConditionEval", mock.Anything, mock.Anything, mock.Anything)
})
})
t.Run("when fine-grained access is disabled", func(t *testing.T) {
rc := &models2.ReqContext{
Context: &web.Context{
Req: &http.Request{},
},
IsSignedIn: false,
SignedInUser: &models2.SignedInUser{
OrgId: 1,
},
}
ac := acMock.New().WithDisabled()
t.Run("should require user to be signed in", func(t *testing.T) {
data1 := models.GenerateAlertQuery()
ds := &fakes.FakeCacheService{DataSources: []*models2.DataSource{
{Uid: data1.DatasourceUID},
}}
evaluator := &eval.FakeEvaluator{}
var result []eval.Result
evaluator.EXPECT().ConditionEval(mock.Anything, mock.Anything, mock.Anything).Return(result, nil)
srv := createTestingApiSrv(ds, ac, evaluator)
response := srv.RouteTestGrafanaRuleConfig(rc, definitions.TestRulePayload{
Expr: "",
GrafanaManagedCondition: &models.EvalAlertConditionCommand{
Condition: data1.RefID,
Data: []models.AlertQuery{data1},
Now: time.Time{},
},
})
require.Equal(t, http.StatusUnauthorized, response.Status())
evaluator.AssertNotCalled(t, "ConditionEval", mock.Anything, mock.Anything, mock.Anything)
rc.IsSignedIn = true
response = srv.RouteTestGrafanaRuleConfig(rc, definitions.TestRulePayload{
Expr: "",
GrafanaManagedCondition: &models.EvalAlertConditionCommand{
Condition: data1.RefID,
Data: []models.AlertQuery{data1},
Now: time.Time{},
},
})
require.Equal(t, http.StatusOK, response.Status())
evaluator.AssertCalled(t, "ConditionEval", mock.Anything, mock.Anything, mock.Anything)
})
})
}
func TestRouteEvalQueries(t *testing.T) {
t.Run("when fine-grained access is enabled", func(t *testing.T) {
rc := &models2.ReqContext{
Context: &web.Context{
Req: &http.Request{},
},
SignedInUser: &models2.SignedInUser{
OrgId: 1,
},
}
t.Run("should return 401 if user cannot query a data source", func(t *testing.T) {
data1 := models.GenerateAlertQuery()
data2 := models.GenerateAlertQuery()
ac := acMock.New().WithPermissions([]*accesscontrol.Permission{
{Action: datasources.ActionQuery, Scope: datasources.ScopeProvider.GetResourceScopeUID(data1.DatasourceUID)},
})
srv := &TestingApiSrv{
accessControl: ac,
}
response := srv.RouteEvalQueries(rc, definitions.EvalQueriesPayload{
Data: []models.AlertQuery{data1, data2},
Now: time.Time{},
})
require.Equal(t, http.StatusUnauthorized, response.Status())
})
t.Run("should return 200 if user can query all data sources", func(t *testing.T) {
data1 := models.GenerateAlertQuery()
data2 := models.GenerateAlertQuery()
ac := acMock.New().WithPermissions([]*accesscontrol.Permission{
{Action: datasources.ActionQuery, Scope: datasources.ScopeProvider.GetResourceScopeUID(data1.DatasourceUID)},
{Action: datasources.ActionQuery, Scope: datasources.ScopeProvider.GetResourceScopeUID(data2.DatasourceUID)},
})
ds := &fakes.FakeCacheService{DataSources: []*models2.DataSource{
{Uid: data1.DatasourceUID},
{Uid: data2.DatasourceUID},
}}
evaluator := &eval.FakeEvaluator{}
result := &backend.QueryDataResponse{
Responses: map[string]backend.DataResponse{
"test": {
Frames: nil,
Error: nil,
},
},
}
evaluator.EXPECT().QueriesAndExpressionsEval(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(result, nil)
srv := createTestingApiSrv(ds, ac, evaluator)
response := srv.RouteEvalQueries(rc, definitions.EvalQueriesPayload{
Data: []models.AlertQuery{data1, data2},
Now: time.Time{},
})
require.Equal(t, http.StatusOK, response.Status())
evaluator.AssertCalled(t, "QueriesAndExpressionsEval", mock.Anything, mock.Anything, mock.Anything, mock.Anything)
})
})
t.Run("when fine-grained access is disabled", func(t *testing.T) {
rc := &models2.ReqContext{
Context: &web.Context{
Req: &http.Request{},
},
IsSignedIn: false,
SignedInUser: &models2.SignedInUser{
OrgId: 1,
},
}
ac := acMock.New().WithDisabled()
t.Run("should require user to be signed in", func(t *testing.T) {
data1 := models.GenerateAlertQuery()
ds := &fakes.FakeCacheService{DataSources: []*models2.DataSource{
{Uid: data1.DatasourceUID},
}}
evaluator := &eval.FakeEvaluator{}
result := &backend.QueryDataResponse{
Responses: map[string]backend.DataResponse{
"test": {
Frames: nil,
Error: nil,
},
},
}
evaluator.EXPECT().QueriesAndExpressionsEval(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(result, nil)
srv := createTestingApiSrv(ds, ac, evaluator)
response := srv.RouteEvalQueries(rc, definitions.EvalQueriesPayload{
Data: []models.AlertQuery{data1},
Now: time.Time{},
})
require.Equal(t, http.StatusUnauthorized, response.Status())
evaluator.AssertNotCalled(t, "QueriesAndExpressionsEval", mock.Anything, mock.Anything, mock.Anything, mock.Anything)
rc.IsSignedIn = true
response = srv.RouteEvalQueries(rc, definitions.EvalQueriesPayload{
Data: []models.AlertQuery{data1},
Now: time.Time{},
})
require.Equal(t, http.StatusOK, response.Status())
evaluator.AssertCalled(t, "QueriesAndExpressionsEval", mock.Anything, mock.Anything, mock.Anything, mock.Anything)
})
})
}
func createTestingApiSrv(ds *fakes.FakeCacheService, ac *acMock.Mock, evaluator *eval.FakeEvaluator) *TestingApiSrv {
if ac == nil {
ac = acMock.New().WithDisabled()
}
return &TestingApiSrv{
DatasourceCache: ds,
accessControl: ac,
evaluator: evaluator,
}
}