2022-02-16 14:15:44 +01:00
|
|
|
package service
|
2018-02-19 11:12:56 +01:00
|
|
|
|
|
|
|
|
import (
|
2021-11-03 14:10:39 +01:00
|
|
|
"context"
|
2024-12-13 15:55:43 -07:00
|
|
|
"reflect"
|
2020-06-29 14:08:32 +02:00
|
|
|
"testing"
|
|
|
|
|
|
2024-12-13 15:55:43 -07:00
|
|
|
"github.com/stretchr/testify/assert"
|
2022-03-10 12:19:50 -05:00
|
|
|
"github.com/stretchr/testify/mock"
|
|
|
|
|
"github.com/stretchr/testify/require"
|
2024-12-13 15:55:43 -07:00
|
|
|
"k8s.io/client-go/dynamic"
|
2022-03-10 12:19:50 -05:00
|
|
|
|
2024-07-26 16:39:23 +03:00
|
|
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
2022-02-16 14:15:44 +01:00
|
|
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
|
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
2022-06-30 09:31:54 -04:00
|
|
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
2024-05-16 14:36:26 -03:00
|
|
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
2022-11-11 14:28:24 +01:00
|
|
|
"github.com/grafana/grafana/pkg/services/folder"
|
2023-02-01 15:43:21 +02:00
|
|
|
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
2019-04-10 13:29:10 +02:00
|
|
|
"github.com/grafana/grafana/pkg/services/guardian"
|
2022-08-10 11:56:48 +02:00
|
|
|
"github.com/grafana/grafana/pkg/services/user"
|
2022-02-16 14:15:44 +01:00
|
|
|
"github.com/grafana/grafana/pkg/setting"
|
2024-12-13 15:55:43 -07:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
2018-02-19 11:12:56 +01:00
|
|
|
)
|
|
|
|
|
|
2022-07-15 18:06:44 +02:00
|
|
|
func TestDashboardService(t *testing.T) {
|
2021-10-27 15:17:50 +02:00
|
|
|
t.Run("Dashboard service tests", func(t *testing.T) {
|
2022-06-30 09:31:54 -04:00
|
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
2022-02-16 14:15:44 +01:00
|
|
|
defer fakeStore.AssertExpectations(t)
|
2022-07-13 12:33:28 +03:00
|
|
|
|
2023-03-02 08:09:57 -05:00
|
|
|
folderSvc := foldertest.NewFakeService()
|
2023-01-19 18:38:07 +02:00
|
|
|
|
2022-02-16 14:15:44 +01:00
|
|
|
service := &DashboardServiceImpl{
|
2024-03-14 15:36:35 +01:00
|
|
|
cfg: setting.NewCfg(),
|
|
|
|
|
log: log.New("test.logger"),
|
|
|
|
|
dashboardStore: &fakeStore,
|
|
|
|
|
folderService: folderSvc,
|
2024-05-16 14:36:26 -03:00
|
|
|
features: featuremgmt.WithFeatures(),
|
2020-02-28 14:32:01 +01:00
|
|
|
}
|
2018-02-19 11:12:56 +01:00
|
|
|
|
|
|
|
|
origNewDashboardGuardian := guardian.New
|
2021-10-27 15:17:50 +02:00
|
|
|
defer func() { guardian.New = origNewDashboardGuardian }()
|
2018-02-20 18:08:19 +01:00
|
|
|
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true})
|
2018-02-19 11:12:56 +01:00
|
|
|
|
2021-10-27 15:17:50 +02:00
|
|
|
t.Run("Save dashboard validation", func(t *testing.T) {
|
2022-06-30 09:31:54 -04:00
|
|
|
dto := &dashboards.SaveDashboardDTO{}
|
2018-02-19 11:12:56 +01:00
|
|
|
|
2021-10-27 15:17:50 +02:00
|
|
|
t.Run("When saving a dashboard with empty title it should return error", func(t *testing.T) {
|
2018-02-19 11:12:56 +01:00
|
|
|
titles := []string{"", " ", " \t "}
|
|
|
|
|
|
|
|
|
|
for _, title := range titles {
|
2023-01-16 16:33:55 +01:00
|
|
|
dto.Dashboard = dashboards.NewDashboard(title)
|
2021-11-03 14:10:39 +01:00
|
|
|
_, err := service.SaveDashboard(context.Background(), dto, false)
|
2022-06-30 09:31:54 -04:00
|
|
|
require.Equal(t, err, dashboards.ErrDashboardTitleEmpty)
|
2018-02-19 11:12:56 +01:00
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2021-10-27 15:17:50 +02:00
|
|
|
t.Run("Should return validation error if folder is named General", func(t *testing.T) {
|
2023-01-16 16:33:55 +01:00
|
|
|
dto.Dashboard = dashboards.NewDashboardFolder("General")
|
2021-11-03 14:10:39 +01:00
|
|
|
_, err := service.SaveDashboard(context.Background(), dto, false)
|
2022-06-30 09:31:54 -04:00
|
|
|
require.Equal(t, err, dashboards.ErrDashboardFolderNameExists)
|
2018-02-19 11:12:56 +01:00
|
|
|
})
|
|
|
|
|
|
2021-10-27 15:17:50 +02:00
|
|
|
t.Run("When saving a dashboard should validate uid", func(t *testing.T) {
|
2018-02-19 11:12:56 +01:00
|
|
|
testCases := []struct {
|
|
|
|
|
Uid string
|
|
|
|
|
Error error
|
|
|
|
|
}{
|
|
|
|
|
{Uid: "", Error: nil},
|
|
|
|
|
{Uid: " ", Error: nil},
|
|
|
|
|
{Uid: " \t ", Error: nil},
|
|
|
|
|
{Uid: "asdf90_-", Error: nil},
|
2022-06-30 09:31:54 -04:00
|
|
|
{Uid: "asdf/90", Error: dashboards.ErrDashboardInvalidUid},
|
2018-02-19 11:12:56 +01:00
|
|
|
{Uid: " asdfghjklqwertyuiopzxcvbnmasdfghjklqwer ", Error: nil},
|
2022-06-30 09:31:54 -04:00
|
|
|
{Uid: "asdfghjklqwertyuiopzxcvbnmasdfghjklqwertyuiopzxcvbnmasdfghjklqwertyuiopzxcvbnm", Error: dashboards.ErrDashboardUidTooLong},
|
2018-02-19 11:12:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tc := range testCases {
|
2023-01-16 16:33:55 +01:00
|
|
|
dto.Dashboard = dashboards.NewDashboard("title")
|
|
|
|
|
dto.Dashboard.SetUID(tc.Uid)
|
2022-08-10 11:56:48 +02:00
|
|
|
dto.User = &user.SignedInUser{}
|
2018-02-19 11:12:56 +01:00
|
|
|
|
2022-02-16 14:15:44 +01:00
|
|
|
if tc.Error == nil {
|
2022-11-02 09:15:50 -04:00
|
|
|
fakeStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.Anything, mock.AnythingOfType("bool")).Return(true, nil).Once()
|
2022-02-16 14:15:44 +01:00
|
|
|
}
|
2024-03-14 15:36:35 +01:00
|
|
|
_, err := service.BuildSaveDashboardCommand(context.Background(), dto, false)
|
2021-10-27 15:17:50 +02:00
|
|
|
require.Equal(t, err, tc.Error)
|
2018-02-19 11:12:56 +01:00
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2023-11-16 11:11:35 +00:00
|
|
|
t.Run("Should return validation error if a folder that is specified can't be found", func(t *testing.T) {
|
|
|
|
|
dto.Dashboard = dashboards.NewDashboard("Dash")
|
|
|
|
|
dto.Dashboard.FolderUID = "non-existing-folder"
|
|
|
|
|
folderStore := foldertest.FakeFolderStore{}
|
|
|
|
|
folderStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(nil, dashboards.ErrFolderNotFound).Once()
|
|
|
|
|
service.folderStore = &folderStore
|
|
|
|
|
_, err := service.SaveDashboard(context.Background(), dto, false)
|
|
|
|
|
require.Equal(t, err, dashboards.ErrFolderNotFound)
|
|
|
|
|
})
|
|
|
|
|
|
2021-10-27 15:17:50 +02:00
|
|
|
t.Run("Should return validation error if dashboard is provisioned", func(t *testing.T) {
|
2022-11-02 09:15:50 -04:00
|
|
|
fakeStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.Anything, mock.AnythingOfType("bool")).Return(true, nil).Once()
|
2023-01-18 13:52:41 +01:00
|
|
|
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.AnythingOfType("int64")).Return(&dashboards.DashboardProvisioning{}, nil).Once()
|
2018-03-27 15:12:47 +02:00
|
|
|
|
2023-01-16 16:33:55 +01:00
|
|
|
dto.Dashboard = dashboards.NewDashboard("Dash")
|
|
|
|
|
dto.Dashboard.SetID(3)
|
2022-08-11 13:28:55 +02:00
|
|
|
dto.User = &user.SignedInUser{UserID: 1}
|
2021-11-03 14:10:39 +01:00
|
|
|
_, err := service.SaveDashboard(context.Background(), dto, false)
|
2022-06-30 09:31:54 -04:00
|
|
|
require.Equal(t, err, dashboards.ErrDashboardCannotSaveProvisionedDashboard)
|
2018-03-27 15:12:47 +02:00
|
|
|
})
|
|
|
|
|
|
2021-10-27 15:17:50 +02:00
|
|
|
t.Run("Should not return validation error if dashboard is provisioned but UI updates allowed", func(t *testing.T) {
|
2022-11-02 09:15:50 -04:00
|
|
|
fakeStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.Anything, mock.AnythingOfType("bool")).Return(true, nil).Once()
|
2023-01-16 16:33:55 +01:00
|
|
|
fakeStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(&dashboards.Dashboard{Data: simplejson.New()}, nil).Once()
|
2019-10-31 14:27:31 +01:00
|
|
|
|
2023-01-16 16:33:55 +01:00
|
|
|
dto.Dashboard = dashboards.NewDashboard("Dash")
|
|
|
|
|
dto.Dashboard.SetID(3)
|
2022-08-11 13:28:55 +02:00
|
|
|
dto.User = &user.SignedInUser{UserID: 1}
|
2021-11-03 14:10:39 +01:00
|
|
|
_, err := service.SaveDashboard(context.Background(), dto, true)
|
2021-10-27 15:17:50 +02:00
|
|
|
require.NoError(t, err)
|
2019-10-31 14:27:31 +01:00
|
|
|
})
|
2018-02-19 11:12:56 +01:00
|
|
|
})
|
|
|
|
|
|
2021-10-27 15:17:50 +02:00
|
|
|
t.Run("Save provisioned dashboard validation", func(t *testing.T) {
|
2022-06-30 09:31:54 -04:00
|
|
|
dto := &dashboards.SaveDashboardDTO{}
|
2018-04-13 17:45:32 +02:00
|
|
|
|
2021-10-27 15:17:50 +02:00
|
|
|
t.Run("Should not return validation error if dashboard is provisioned", func(t *testing.T) {
|
2022-11-02 09:15:50 -04:00
|
|
|
fakeStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.Anything, mock.AnythingOfType("bool")).Return(true, nil).Once()
|
2023-01-16 16:33:55 +01:00
|
|
|
fakeStore.On("SaveProvisionedDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand"), mock.AnythingOfType("*dashboards.DashboardProvisioning")).Return(&dashboards.Dashboard{Data: simplejson.New()}, nil).Once()
|
2018-04-13 17:45:32 +02:00
|
|
|
|
2023-01-16 16:33:55 +01:00
|
|
|
dto.Dashboard = dashboards.NewDashboard("Dash")
|
|
|
|
|
dto.Dashboard.SetID(3)
|
2022-08-11 13:28:55 +02:00
|
|
|
dto.User = &user.SignedInUser{UserID: 1}
|
2021-11-03 14:10:39 +01:00
|
|
|
_, err := service.SaveProvisionedDashboard(context.Background(), dto, nil)
|
2021-10-27 15:17:50 +02:00
|
|
|
require.NoError(t, err)
|
2018-04-13 17:45:32 +02:00
|
|
|
})
|
2020-02-28 14:32:01 +01:00
|
|
|
|
2021-10-27 15:17:50 +02:00
|
|
|
t.Run("Should override invalid refresh interval if dashboard is provisioned", func(t *testing.T) {
|
2022-11-02 09:15:50 -04:00
|
|
|
fakeStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.Anything, mock.AnythingOfType("bool")).Return(true, nil).Once()
|
2023-01-16 16:33:55 +01:00
|
|
|
fakeStore.On("SaveProvisionedDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand"), mock.AnythingOfType("*dashboards.DashboardProvisioning")).Return(&dashboards.Dashboard{Data: simplejson.New()}, nil).Once()
|
2022-02-16 14:15:44 +01:00
|
|
|
|
2024-01-23 12:36:22 +01:00
|
|
|
oldRefreshInterval := service.cfg.MinRefreshInterval
|
|
|
|
|
service.cfg.MinRefreshInterval = "5m"
|
|
|
|
|
defer func() { service.cfg.MinRefreshInterval = oldRefreshInterval }()
|
2020-02-28 14:32:01 +01:00
|
|
|
|
2023-01-16 16:33:55 +01:00
|
|
|
dto.Dashboard = dashboards.NewDashboard("Dash")
|
|
|
|
|
dto.Dashboard.SetID(3)
|
2022-08-11 13:28:55 +02:00
|
|
|
dto.User = &user.SignedInUser{UserID: 1}
|
2020-02-28 14:32:01 +01:00
|
|
|
dto.Dashboard.Data.Set("refresh", "1s")
|
2021-11-03 14:10:39 +01:00
|
|
|
_, err := service.SaveProvisionedDashboard(context.Background(), dto, nil)
|
2021-10-27 15:17:50 +02:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.Equal(t, dto.Dashboard.Data.Get("refresh").MustString(), "5m")
|
2020-02-28 14:32:01 +01:00
|
|
|
})
|
2018-04-13 17:45:32 +02:00
|
|
|
})
|
|
|
|
|
|
2021-10-27 15:17:50 +02:00
|
|
|
t.Run("Import dashboard validation", func(t *testing.T) {
|
2022-06-30 09:31:54 -04:00
|
|
|
dto := &dashboards.SaveDashboardDTO{}
|
2018-04-13 17:54:41 +02:00
|
|
|
|
2021-10-27 15:17:50 +02:00
|
|
|
t.Run("Should return validation error if dashboard is provisioned", func(t *testing.T) {
|
2022-11-02 09:15:50 -04:00
|
|
|
fakeStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.Anything, mock.AnythingOfType("bool")).Return(true, nil).Once()
|
2023-01-18 13:52:41 +01:00
|
|
|
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.AnythingOfType("int64")).Return(&dashboards.DashboardProvisioning{}, nil).Once()
|
2018-04-13 17:54:41 +02:00
|
|
|
|
2023-01-16 16:33:55 +01:00
|
|
|
dto.Dashboard = dashboards.NewDashboard("Dash")
|
|
|
|
|
dto.Dashboard.SetID(3)
|
2022-08-11 13:28:55 +02:00
|
|
|
dto.User = &user.SignedInUser{UserID: 1}
|
2021-11-12 14:35:38 +01:00
|
|
|
_, err := service.ImportDashboard(context.Background(), dto)
|
2022-06-30 09:31:54 -04:00
|
|
|
require.Equal(t, err, dashboards.ErrDashboardCannotSaveProvisionedDashboard)
|
2018-04-13 17:54:41 +02:00
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2021-10-27 15:17:50 +02:00
|
|
|
t.Run("Given provisioned dashboard", func(t *testing.T) {
|
|
|
|
|
t.Run("DeleteProvisionedDashboard should delete it", func(t *testing.T) {
|
2023-01-18 13:52:41 +01:00
|
|
|
args := &dashboards.DeleteDashboardCommand{OrgID: 1, ID: 1}
|
2022-03-22 14:36:50 +01:00
|
|
|
fakeStore.On("DeleteDashboard", mock.Anything, args).Return(nil).Once()
|
2021-11-19 14:32:14 +01:00
|
|
|
err := service.DeleteProvisionedDashboard(context.Background(), 1, 1)
|
2021-10-27 15:17:50 +02:00
|
|
|
require.NoError(t, err)
|
2019-04-10 13:29:10 +02:00
|
|
|
})
|
|
|
|
|
|
2022-03-22 14:36:50 +01:00
|
|
|
t.Run("DeleteDashboard should fail to delete it when provisioning information is missing", func(t *testing.T) {
|
2023-01-18 13:52:41 +01:00
|
|
|
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.AnythingOfType("int64")).Return(&dashboards.DashboardProvisioning{}, nil).Once()
|
2021-11-19 14:32:14 +01:00
|
|
|
err := service.DeleteDashboard(context.Background(), 1, 1)
|
2022-06-30 09:31:54 -04:00
|
|
|
require.Equal(t, err, dashboards.ErrDashboardCannotDeleteProvisionedDashboard)
|
2019-04-10 13:29:10 +02:00
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2021-10-27 15:17:50 +02:00
|
|
|
t.Run("Given non provisioned dashboard", func(t *testing.T) {
|
2022-03-22 14:36:50 +01:00
|
|
|
t.Run("DeleteProvisionedDashboard should delete the dashboard", func(t *testing.T) {
|
2023-12-04 10:34:38 +01:00
|
|
|
args := &dashboards.DeleteDashboardCommand{OrgID: 1, ID: 1}
|
2022-03-22 14:36:50 +01:00
|
|
|
fakeStore.On("DeleteDashboard", mock.Anything, args).Return(nil).Once()
|
2021-11-19 14:32:14 +01:00
|
|
|
err := service.DeleteProvisionedDashboard(context.Background(), 1, 1)
|
2021-10-27 15:17:50 +02:00
|
|
|
require.NoError(t, err)
|
2019-04-10 13:29:10 +02:00
|
|
|
})
|
|
|
|
|
|
2021-10-27 15:17:50 +02:00
|
|
|
t.Run("DeleteDashboard should delete it", func(t *testing.T) {
|
2023-01-18 13:52:41 +01:00
|
|
|
args := &dashboards.DeleteDashboardCommand{OrgID: 1, ID: 1}
|
2022-03-22 14:36:50 +01:00
|
|
|
fakeStore.On("DeleteDashboard", mock.Anything, args).Return(nil).Once()
|
2022-11-02 09:15:50 -04:00
|
|
|
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.AnythingOfType("int64")).Return(nil, nil).Once()
|
2021-11-19 14:32:14 +01:00
|
|
|
err := service.DeleteDashboard(context.Background(), 1, 1)
|
2021-10-27 15:17:50 +02:00
|
|
|
require.NoError(t, err)
|
2019-04-10 13:29:10 +02:00
|
|
|
})
|
|
|
|
|
})
|
2022-11-02 09:15:50 -04:00
|
|
|
|
|
|
|
|
t.Run("Count dashboards in folder", func(t *testing.T) {
|
2024-01-30 18:26:34 +02:00
|
|
|
fakeStore.On("CountDashboardsInFolders", mock.Anything, mock.AnythingOfType("*dashboards.CountDashboardsInFolderRequest")).Return(int64(3), nil)
|
2024-01-12 16:43:39 +01:00
|
|
|
folderSvc.ExpectedFolder = &folder.Folder{UID: "i am a folder"}
|
2022-11-02 09:15:50 -04:00
|
|
|
// set up a ctx with signed in user
|
|
|
|
|
usr := &user.SignedInUser{UserID: 1}
|
2024-07-26 16:39:23 +03:00
|
|
|
ctx := identity.WithRequester(context.Background(), usr)
|
2022-11-02 09:15:50 -04:00
|
|
|
|
2024-01-30 18:26:34 +02:00
|
|
|
count, err := service.CountInFolders(ctx, 1, []string{"i am a folder"}, usr)
|
2022-11-02 09:15:50 -04:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.Equal(t, int64(3), count)
|
|
|
|
|
})
|
2023-04-14 11:17:23 +02:00
|
|
|
|
|
|
|
|
t.Run("Delete dashboards in folder", func(t *testing.T) {
|
2024-01-30 18:26:34 +02:00
|
|
|
args := &dashboards.DeleteDashboardsInFolderRequest{OrgID: 1, FolderUIDs: []string{"uid"}}
|
|
|
|
|
fakeStore.On("DeleteDashboardsInFolders", mock.Anything, args).Return(nil).Once()
|
|
|
|
|
err := service.DeleteInFolders(context.Background(), 1, []string{"uid"}, nil)
|
2023-04-14 11:17:23 +02:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
})
|
2024-05-16 14:36:26 -03:00
|
|
|
|
|
|
|
|
t.Run("Soft Delete dashboards in folder", func(t *testing.T) {
|
|
|
|
|
service.features = featuremgmt.WithFeatures(featuremgmt.FlagDashboardRestore)
|
|
|
|
|
fakeStore.On("SoftDeleteDashboardsInFolders", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
|
|
|
|
err := service.DeleteInFolders(context.Background(), 1, []string{"uid"}, nil)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
})
|
2018-02-19 11:12:56 +01:00
|
|
|
})
|
|
|
|
|
}
|
2024-12-13 15:55:43 -07:00
|
|
|
|
|
|
|
|
type mockDashK8sCli struct {
|
|
|
|
|
mock.Mock
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *mockDashK8sCli) getClient(ctx context.Context, orgID int64) (dynamic.ResourceInterface, bool) {
|
|
|
|
|
args := m.Called(ctx, orgID)
|
|
|
|
|
return args.Get(0).(dynamic.ResourceInterface), args.Bool(1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type mockResourceInterface struct {
|
|
|
|
|
mock.Mock
|
|
|
|
|
dynamic.ResourceInterface
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *mockResourceInterface) Get(ctx context.Context, name string, options metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
|
|
|
|
args := m.Called(ctx, name, options, subresources)
|
|
|
|
|
if args.Get(0) == nil {
|
|
|
|
|
return nil, args.Error(1)
|
|
|
|
|
}
|
|
|
|
|
return args.Get(0).(*unstructured.Unstructured), args.Error(1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestGetDashboard(t *testing.T) {
|
|
|
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
|
|
|
defer fakeStore.AssertExpectations(t)
|
|
|
|
|
service := &DashboardServiceImpl{
|
|
|
|
|
cfg: setting.NewCfg(),
|
|
|
|
|
dashboardStore: &fakeStore,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
|
|
|
service.features = featuremgmt.WithFeatures()
|
|
|
|
|
query := &dashboards.GetDashboardQuery{
|
|
|
|
|
UID: "test-uid",
|
|
|
|
|
OrgID: 1,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fakeStore.On("GetDashboard", mock.Anything, query).Return(&dashboards.Dashboard{}, nil).Once()
|
|
|
|
|
dashboard, err := service.GetDashboard(context.Background(), query)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NotNil(t, dashboard)
|
|
|
|
|
fakeStore.AssertExpectations(t)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
|
|
|
|
k8sClientMock := new(mockDashK8sCli)
|
|
|
|
|
k8sResourceMock := new(mockResourceInterface)
|
|
|
|
|
service.k8sclient = k8sClientMock
|
|
|
|
|
service.features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesCliDashboards)
|
|
|
|
|
query := &dashboards.GetDashboardQuery{
|
|
|
|
|
UID: "test-uid",
|
|
|
|
|
OrgID: 1,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
|
|
|
|
"metadata": map[string]any{
|
|
|
|
|
"name": "uid",
|
|
|
|
|
},
|
|
|
|
|
"spec": map[string]any{
|
|
|
|
|
"test": "test",
|
|
|
|
|
"title": "testing slugify",
|
|
|
|
|
},
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
dashboardExpected := dashboards.Dashboard{
|
|
|
|
|
UID: "uid", // uid is the name of the k8s object
|
|
|
|
|
Title: "testing slugify",
|
|
|
|
|
Slug: "testing-slugify", // slug is taken from title
|
|
|
|
|
OrgID: 1, // orgID is populated from the query
|
|
|
|
|
Data: simplejson.NewFromAny(map[string]any{"test": "test", "title": "testing slugify"}),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
userCtx := &user.SignedInUser{UserID: 1}
|
|
|
|
|
ctx = identity.WithRequester(ctx, userCtx)
|
|
|
|
|
k8sClientMock.On("getClient", mock.Anything, int64(1)).Return(k8sResourceMock, true).Once()
|
|
|
|
|
k8sResourceMock.On("Get", mock.Anything, query.UID, mock.Anything, mock.Anything).Return(&dashboardUnstructured, nil).Once()
|
|
|
|
|
|
|
|
|
|
dashboard, err := service.GetDashboard(ctx, query)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NotNil(t, dashboard)
|
|
|
|
|
k8sClientMock.AssertExpectations(t)
|
|
|
|
|
// make sure the conversion is working
|
|
|
|
|
require.True(t, reflect.DeepEqual(dashboard, &dashboardExpected))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("Should return error when Kubernetes client fails", func(t *testing.T) {
|
|
|
|
|
k8sClientMock := new(mockDashK8sCli)
|
|
|
|
|
k8sResourceMock := new(mockResourceInterface)
|
|
|
|
|
service.k8sclient = k8sClientMock
|
|
|
|
|
service.features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesCliDashboards)
|
|
|
|
|
query := &dashboards.GetDashboardQuery{
|
|
|
|
|
UID: "test-uid",
|
|
|
|
|
OrgID: 1,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
userCtx := &user.SignedInUser{UserID: 1}
|
|
|
|
|
ctx = identity.WithRequester(ctx, userCtx)
|
|
|
|
|
k8sClientMock.On("getClient", mock.Anything, int64(1)).Return(k8sResourceMock, true).Once()
|
|
|
|
|
k8sResourceMock.On("Get", mock.Anything, query.UID, mock.Anything, mock.Anything).Return(nil, assert.AnError).Once()
|
|
|
|
|
|
|
|
|
|
dashboard, err := service.GetDashboard(ctx, query)
|
|
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Nil(t, dashboard)
|
|
|
|
|
k8sClientMock.AssertExpectations(t)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("Should return dashboard not found if Kubernetes client returns nil", func(t *testing.T) {
|
|
|
|
|
k8sClientMock := new(mockDashK8sCli)
|
|
|
|
|
k8sResourceMock := new(mockResourceInterface)
|
|
|
|
|
service.k8sclient = k8sClientMock
|
|
|
|
|
service.features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesCliDashboards)
|
|
|
|
|
query := &dashboards.GetDashboardQuery{
|
|
|
|
|
UID: "test-uid",
|
|
|
|
|
OrgID: 1,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
userCtx := &user.SignedInUser{UserID: 1}
|
|
|
|
|
ctx = identity.WithRequester(ctx, userCtx)
|
|
|
|
|
k8sClientMock.On("getClient", mock.Anything, int64(1)).Return(k8sResourceMock, true).Once()
|
|
|
|
|
k8sResourceMock.On("Get", mock.Anything, query.UID, mock.Anything, mock.Anything).Return(nil, nil).Once()
|
|
|
|
|
|
|
|
|
|
dashboard, err := service.GetDashboard(ctx, query)
|
|
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Equal(t, dashboards.ErrDashboardNotFound, err)
|
|
|
|
|
require.Nil(t, dashboard)
|
|
|
|
|
k8sClientMock.AssertExpectations(t)
|
|
|
|
|
})
|
|
|
|
|
}
|