2021-05-11 13:10:19 +08:00
package libraryelements
import (
2021-11-29 17:18:01 +08:00
"bytes"
2021-05-11 13:10:19 +08:00
"context"
"encoding/json"
2021-11-29 17:18:01 +08:00
"io"
2021-05-11 13:10:19 +08:00
"net/http"
"testing"
"time"
"github.com/google/go-cmp/cmp"
2023-04-20 17:24:41 +08:00
"github.com/stretchr/testify/mock"
2022-03-11 01:19:50 +08:00
"github.com/stretchr/testify/require"
2021-05-11 13:10:19 +08:00
"github.com/grafana/grafana/pkg/api/response"
2024-07-26 21:39:23 +08:00
"github.com/grafana/grafana/pkg/apimachinery/identity"
2022-10-18 21:31:56 +08:00
"github.com/grafana/grafana/pkg/bus"
2022-02-16 21:15:44 +08:00
"github.com/grafana/grafana/pkg/components/simplejson"
2022-10-19 21:02:15 +08:00
"github.com/grafana/grafana/pkg/infra/db"
2025-03-23 06:47:27 +08:00
"github.com/grafana/grafana/pkg/infra/kvstore"
2023-09-06 17:16:10 +08:00
"github.com/grafana/grafana/pkg/infra/log"
2025-03-23 06:47:27 +08:00
"github.com/grafana/grafana/pkg/infra/serverlock"
2022-10-18 21:31:56 +08:00
"github.com/grafana/grafana/pkg/infra/tracing"
2023-01-30 12:14:12 +08:00
"github.com/grafana/grafana/pkg/kinds/librarypanel"
2023-04-20 17:24:41 +08:00
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
2022-03-10 19:58:18 +08:00
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
2025-03-27 22:46:09 +08:00
"github.com/grafana/grafana/pkg/services/apiserver"
2025-02-12 03:14:25 +08:00
"github.com/grafana/grafana/pkg/services/apiserver/client"
2023-01-27 15:50:36 +08:00
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
2021-05-11 13:10:19 +08:00
"github.com/grafana/grafana/pkg/services/dashboards"
2022-02-16 21:15:44 +08:00
"github.com/grafana/grafana/pkg/services/dashboards/database"
2022-05-18 02:52:22 +08:00
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service"
2022-03-10 19:58:18 +08:00
"github.com/grafana/grafana/pkg/services/featuremgmt"
2022-11-10 17:41:03 +08:00
"github.com/grafana/grafana/pkg/services/folder"
2022-10-11 03:47:53 +08:00
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
2025-01-28 07:39:18 +08:00
"github.com/grafana/grafana/pkg/services/folder/foldertest"
2022-03-21 17:49:49 +08:00
"github.com/grafana/grafana/pkg/services/guardian"
2023-02-02 00:32:05 +08:00
"github.com/grafana/grafana/pkg/services/libraryelements/model"
2025-01-28 07:39:18 +08:00
ngstore "github.com/grafana/grafana/pkg/services/ngalert/store"
2022-08-10 17:56:48 +08:00
"github.com/grafana/grafana/pkg/services/org"
2022-12-08 00:03:22 +08:00
"github.com/grafana/grafana/pkg/services/org/orgimpl"
2025-01-28 07:39:18 +08:00
"github.com/grafana/grafana/pkg/services/publicdashboards"
2022-11-15 03:08:10 +08:00
"github.com/grafana/grafana/pkg/services/quota/quotatest"
2025-02-19 02:30:11 +08:00
"github.com/grafana/grafana/pkg/services/search/sort"
2023-02-07 00:50:03 +08:00
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
2022-09-21 20:04:01 +08:00
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
2022-06-28 20:32:25 +08:00
"github.com/grafana/grafana/pkg/services/user"
2022-12-08 00:03:22 +08:00
"github.com/grafana/grafana/pkg/services/user/userimpl"
2021-05-11 13:10:19 +08:00
"github.com/grafana/grafana/pkg/setting"
2025-02-19 22:50:39 +08:00
"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite"
2024-02-09 22:35:39 +08:00
"github.com/grafana/grafana/pkg/tests/testsuite"
2021-10-11 20:30:59 +08:00
"github.com/grafana/grafana/pkg/web"
2021-05-11 13:10:19 +08:00
)
const userInDbName = "user_in_db"
const userInDbAvatar = "/avatar/402d08de060496d6b6874495fe20f5ad"
2024-02-09 22:35:39 +08:00
func TestMain ( m * testing . M ) {
testsuite . Run ( m )
}
2021-05-11 13:10:19 +08:00
func TestDeleteLibraryPanelsInFolder ( t * testing . T ) {
scenarioWithPanel ( t , "When an admin tries to delete a folder that contains connected library elements, it should fail" ,
func ( t * testing . T , sc scenarioContext ) {
2023-08-30 23:46:47 +08:00
dashJSON := map [ string ] any {
"panels" : [ ] any {
map [ string ] any {
2021-05-11 13:10:19 +08:00
"id" : int64 ( 1 ) ,
2023-08-30 23:46:47 +08:00
"gridPos" : map [ string ] any {
2021-05-11 13:10:19 +08:00
"h" : 6 ,
"w" : 6 ,
"x" : 0 ,
"y" : 0 ,
} ,
} ,
2023-08-30 23:46:47 +08:00
map [ string ] any {
2021-05-11 13:10:19 +08:00
"id" : int64 ( 2 ) ,
2023-08-30 23:46:47 +08:00
"gridPos" : map [ string ] any {
2021-05-11 13:10:19 +08:00
"h" : 6 ,
"w" : 6 ,
"x" : 6 ,
"y" : 0 ,
} ,
2023-08-30 23:46:47 +08:00
"libraryPanel" : map [ string ] any {
2021-05-11 13:10:19 +08:00
"uid" : sc . initialResult . Result . UID ,
"name" : sc . initialResult . Result . Name ,
} ,
} ,
} ,
}
2023-01-16 23:33:55 +08:00
dash := dashboards . Dashboard {
2021-05-11 13:10:19 +08:00
Title : "Testing DeleteLibraryElementsInFolder" ,
Data : simplejson . NewFromAny ( dashJSON ) ,
}
2023-11-21 04:44:51 +08:00
// nolint:staticcheck
2024-09-26 07:21:39 +08:00
dashInDB := createDashboard ( t , sc . sqlStore , sc . user , & dash , sc . folder . ID , sc . folder . UID )
2023-01-16 23:33:55 +08:00
err := sc . service . ConnectElementsToDashboard ( sc . reqContext . Req . Context ( ) , sc . reqContext . SignedInUser , [ ] string { sc . initialResult . Result . UID } , dashInDB . ID )
2021-05-11 13:10:19 +08:00
require . NoError ( t , err )
2022-11-11 21:28:24 +08:00
err = sc . service . DeleteLibraryElementsInFolder ( sc . reqContext . Req . Context ( ) , sc . reqContext . SignedInUser , sc . folder . UID )
2023-02-02 00:32:05 +08:00
require . EqualError ( t , err , model . ErrFolderHasConnectedLibraryElements . Error ( ) )
2021-05-11 13:10:19 +08:00
} )
2021-11-30 21:07:04 +08:00
scenarioWithPanel ( t , "When an admin tries to delete a folder uid that doesn't exist, it should fail" ,
func ( t * testing . T , sc scenarioContext ) {
2022-11-11 21:28:24 +08:00
err := sc . service . DeleteLibraryElementsInFolder ( sc . reqContext . Req . Context ( ) , sc . reqContext . SignedInUser , sc . folder . UID + "xxxx" )
2025-01-24 00:30:14 +08:00
require . EqualError ( t , err , guardian . ErrGuardianFolderNotFound . Errorf ( "failed to get folder by UID: %w" , dashboards . ErrFolderNotFound ) . Error ( ) )
2021-11-30 21:07:04 +08:00
} )
2021-05-11 13:10:19 +08:00
scenarioWithPanel ( t , "When an admin tries to delete a folder that contains disconnected elements, it should delete all disconnected elements too" ,
func ( t * testing . T , sc scenarioContext ) {
2023-11-21 04:44:51 +08:00
// nolint:staticcheck
2024-04-09 18:27:43 +08:00
command := getCreateVariableCommand ( sc . folder . ID , sc . folder . UID , "query0" )
2021-11-29 17:18:01 +08:00
sc . reqContext . Req . Body = mockRequestBody ( command )
resp := sc . service . createHandler ( sc . reqContext )
2021-05-11 13:10:19 +08:00
require . Equal ( t , 200 , resp . Status ( ) )
resp = sc . service . getAllHandler ( sc . reqContext )
require . Equal ( t , 200 , resp . Status ( ) )
var result libraryElementsSearch
err := json . Unmarshal ( resp . Body ( ) , & result )
require . NoError ( t , err )
require . NotNil ( t , result . Result )
require . Equal ( t , 2 , len ( result . Result . Elements ) )
2022-11-11 21:28:24 +08:00
err = sc . service . DeleteLibraryElementsInFolder ( sc . reqContext . Req . Context ( ) , sc . reqContext . SignedInUser , sc . folder . UID )
2021-05-11 13:10:19 +08:00
require . NoError ( t , err )
resp = sc . service . getAllHandler ( sc . reqContext )
require . Equal ( t , 200 , resp . Status ( ) )
err = json . Unmarshal ( resp . Body ( ) , & result )
require . NoError ( t , err )
require . NotNil ( t , result . Result )
require . Equal ( t , 0 , len ( result . Result . Elements ) )
} )
}
2022-07-26 06:55:28 +08:00
func TestGetLibraryPanelConnections ( t * testing . T ) {
scenarioWithPanel ( t , "When an admin tries to get connections of library panel, it should succeed and return correct result" ,
func ( t * testing . T , sc scenarioContext ) {
2023-08-30 23:46:47 +08:00
dashJSON := map [ string ] any {
"panels" : [ ] any {
map [ string ] any {
2022-07-26 06:55:28 +08:00
"id" : int64 ( 1 ) ,
2023-08-30 23:46:47 +08:00
"gridPos" : map [ string ] any {
2022-07-26 06:55:28 +08:00
"h" : 6 ,
"w" : 6 ,
"x" : 0 ,
"y" : 0 ,
} ,
} ,
2023-08-30 23:46:47 +08:00
map [ string ] any {
2022-07-26 06:55:28 +08:00
"id" : int64 ( 2 ) ,
2023-08-30 23:46:47 +08:00
"gridPos" : map [ string ] any {
2022-07-26 06:55:28 +08:00
"h" : 6 ,
"w" : 6 ,
"x" : 6 ,
"y" : 0 ,
} ,
2023-08-30 23:46:47 +08:00
"libraryPanel" : map [ string ] any {
2022-07-26 06:55:28 +08:00
"uid" : sc . initialResult . Result . UID ,
"name" : sc . initialResult . Result . Name ,
} ,
} ,
} ,
}
2023-01-16 23:33:55 +08:00
dash := dashboards . Dashboard {
2022-07-26 06:55:28 +08:00
Title : "Testing GetLibraryPanelConnections" ,
Data : simplejson . NewFromAny ( dashJSON ) ,
}
2023-11-21 04:44:51 +08:00
// nolint:staticcheck
2024-09-26 07:21:39 +08:00
dashInDB := createDashboard ( t , sc . sqlStore , sc . user , & dash , sc . folder . ID , sc . folder . UID )
2023-01-16 23:33:55 +08:00
err := sc . service . ConnectElementsToDashboard ( sc . reqContext . Req . Context ( ) , sc . reqContext . SignedInUser , [ ] string { sc . initialResult . Result . UID } , dashInDB . ID )
2022-07-26 06:55:28 +08:00
require . NoError ( t , err )
2025-01-28 02:35:30 +08:00
// add a connection where the dashboard doesn't exist. Shouldn't be returned in the list
err = sc . service . ConnectElementsToDashboard ( sc . reqContext . Req . Context ( ) , sc . reqContext . SignedInUser , [ ] string { sc . initialResult . Result . UID } , 99999999 )
require . NoError ( t , err )
2023-02-02 00:32:05 +08:00
var expected = func ( res model . LibraryElementConnectionsResponse ) model . LibraryElementConnectionsResponse {
return model . LibraryElementConnectionsResponse {
Result : [ ] model . LibraryElementConnectionDTO {
2022-07-26 06:55:28 +08:00
{
2025-01-18 03:43:14 +08:00
ID : sc . initialResult . Result . ID ,
Kind : sc . initialResult . Result . Kind ,
ElementID : 1 ,
ConnectionID : dashInDB . ID ,
ConnectionUID : dashInDB . UID ,
Created : res . Result [ 0 ] . Created ,
2023-01-30 12:14:12 +08:00
CreatedBy : librarypanel . LibraryElementDTOMetaUser {
Id : 1 ,
2022-07-26 06:55:28 +08:00
Name : userInDbName ,
2023-01-30 12:14:12 +08:00
AvatarUrl : userInDbAvatar ,
2022-07-26 06:55:28 +08:00
} ,
} ,
} ,
}
}
sc . ctx . Req = web . SetURLParams ( sc . ctx . Req , map [ string ] string { ":uid" : sc . initialResult . Result . UID } )
resp := sc . service . getConnectionsHandler ( sc . reqContext )
var result = validateAndUnMarshalConnectionResponse ( t , resp )
if diff := cmp . Diff ( expected ( result ) , result , getCompareOptions ( ) ... ) ; diff != "" {
t . Fatalf ( "Result mismatch (-want +got):\n%s" , diff )
}
} )
2025-01-28 07:39:18 +08:00
scenarioWithPanel ( t , "When an admin tries to create a connection with an element that exists, but the original folder does not, it should still succeed" ,
func ( t * testing . T , sc scenarioContext ) {
b , err := json . Marshal ( map [ string ] string { "test" : "test" } )
require . NoError ( t , err )
newFolder := createFolder ( t , sc , "NewFolder" , nil )
2025-04-10 20:42:23 +08:00
sc . reqContext . Permissions [ sc . reqContext . OrgID ] [ dashboards . ActionFoldersRead ] = [ ] string { dashboards . ScopeFoldersAll }
sc . reqContext . Permissions [ sc . reqContext . OrgID ] [ dashboards . ActionFoldersDelete ] = [ ] string { dashboards . ScopeFoldersAll }
2025-01-28 07:39:18 +08:00
_ , err = sc . service . createLibraryElement ( sc . reqContext . Req . Context ( ) , sc . reqContext . SignedInUser , model . CreateLibraryElementCommand {
FolderID : newFolder . ID , // nolint:staticcheck
FolderUID : & newFolder . UID ,
Name : "Testing Library Panel With Deleted Folder" ,
Kind : 1 ,
Model : b ,
UID : "panel-with-deleted-folder" ,
} )
require . NoError ( t , err )
err = sc . service . folderService . Delete ( sc . reqContext . Req . Context ( ) , & folder . DeleteFolderCommand {
UID : newFolder . UID ,
OrgID : sc . reqContext . OrgID ,
SignedInUser : sc . reqContext . SignedInUser ,
} )
require . NoError ( t , err )
dash := dashboards . Dashboard {
Title : "Testing create element" ,
Data : simplejson . NewFromAny ( map [ string ] any { } ) ,
}
// nolint:staticcheck
dashInDB := createDashboard ( t , sc . sqlStore , sc . user , & dash , sc . folder . ID , sc . folder . UID )
err = sc . service . ConnectElementsToDashboard ( sc . reqContext . Req . Context ( ) , sc . reqContext . SignedInUser , [ ] string { sc . initialResult . Result . UID } , dashInDB . ID )
require . NoError ( t , err )
} )
2022-07-26 06:55:28 +08:00
}
2021-05-11 13:10:19 +08:00
type libraryElement struct {
2023-11-07 00:31:15 +08:00
ID int64 ` json:"id" `
OrgID int64 ` json:"orgId" `
// Deprecated: use FolderUID instead
2023-02-02 00:32:05 +08:00
FolderID int64 ` json:"folderId" `
2024-04-09 18:27:43 +08:00
FolderUID string ` json:"folderUid" `
2023-02-02 00:32:05 +08:00
UID string ` json:"uid" `
Name string ` json:"name" `
Kind int64 ` json:"kind" `
Type string ` json:"type" `
Description string ` json:"description" `
2023-08-30 23:46:47 +08:00
Model map [ string ] any ` json:"model" `
2023-02-02 00:32:05 +08:00
Version int64 ` json:"version" `
Meta model . LibraryElementDTOMeta ` json:"meta" `
2021-05-11 13:10:19 +08:00
}
type libraryElementResult struct {
Result libraryElement ` json:"result" `
}
2021-05-14 22:03:37 +08:00
type libraryElementArrayResult struct {
Result [ ] libraryElement ` json:"result" `
}
2021-05-11 13:10:19 +08:00
type libraryElementsSearch struct {
Result libraryElementsSearchResult ` json:"result" `
}
type libraryElementsSearchResult struct {
TotalCount int64 ` json:"totalCount" `
Elements [ ] libraryElement ` json:"elements" `
Page int ` json:"page" `
PerPage int ` json:"perPage" `
}
2024-04-09 18:27:43 +08:00
func getCreatePanelCommand ( folderID int64 , folderUID string , name string ) model . CreateLibraryElementCommand {
command := getCreateCommandWithModel ( folderID , folderUID , name , model . PanelElement , [ ] byte ( `
2021-05-11 13:10:19 +08:00
{
"datasource" : "${DS_GDEV-TESTDATA}" ,
"id" : 1 ,
"title" : "Text - Library Panel" ,
"type" : "text" ,
"description" : "A description"
}
` ) )
return command
}
2024-04-09 18:27:43 +08:00
func getCreateVariableCommand ( folderID int64 , folderUID , name string ) model . CreateLibraryElementCommand {
command := getCreateCommandWithModel ( folderID , folderUID , name , model . VariableElement , [ ] byte ( `
2021-05-11 13:10:19 +08:00
{
"datasource" : "${DS_GDEV-TESTDATA}" ,
"name" : "query0" ,
"type" : "query" ,
"description" : "A description"
}
` ) )
return command
}
2024-04-09 18:27:43 +08:00
func getCreateCommandWithModel ( folderID int64 , folderUID , name string , kind model . LibraryElementKind , byteModel [ ] byte ) model . CreateLibraryElementCommand {
2023-02-02 00:32:05 +08:00
command := model . CreateLibraryElementCommand {
2024-04-09 18:27:43 +08:00
FolderUID : & folderUID ,
Name : name ,
Model : byteModel ,
Kind : int64 ( kind ) ,
2021-05-11 13:10:19 +08:00
}
return command
}
type scenarioContext struct {
2021-10-11 20:30:59 +08:00
ctx * web . Context
2021-05-11 13:10:19 +08:00
service * LibraryElementService
2023-01-27 15:50:36 +08:00
reqContext * contextmodel . ReqContext
2022-08-10 17:56:48 +08:00
user user . SignedInUser
2022-11-11 21:28:24 +08:00
folder * folder . Folder
2021-05-11 13:10:19 +08:00
initialResult libraryElementResult
2022-10-15 03:33:06 +08:00
sqlStore db . DB
2023-09-06 17:16:10 +08:00
log log . Logger
2021-05-11 13:10:19 +08:00
}
2024-09-26 07:21:39 +08:00
func createDashboard ( t * testing . T , sqlStore db . DB , user user . SignedInUser , dash * dashboards . Dashboard , folderID int64 , folderUID string ) * dashboards . Dashboard {
2023-11-15 23:28:50 +08:00
// nolint:staticcheck
2023-01-16 23:33:55 +08:00
dash . FolderID = folderID
2024-04-09 18:27:43 +08:00
dash . FolderUID = folderUID
2021-05-11 13:10:19 +08:00
dashItem := & dashboards . SaveDashboardDTO {
Dashboard : dash ,
Message : "" ,
2023-01-16 23:33:55 +08:00
OrgID : user . OrgID ,
2021-05-11 13:10:19 +08:00
User : & user ,
Overwrite : false ,
}
2022-10-15 03:33:06 +08:00
features := featuremgmt . WithFeatures ( )
2023-11-13 23:55:15 +08:00
cfg := setting . NewCfg ( )
2022-11-15 03:08:10 +08:00
quotaService := quotatest . New ( false , nil )
2025-01-10 13:21:21 +08:00
dashboardStore , err := database . ProvideDashboardStore ( sqlStore , cfg , features , tagimpl . ProvideService ( sqlStore ) )
2022-11-15 03:08:10 +08:00
require . NoError ( t , err )
2023-04-20 17:24:41 +08:00
ac := actest . FakeAccessControl { ExpectedEvaluate : true }
2022-05-10 21:48:47 +08:00
folderPermissions := acmock . NewMockedPermissionsService ( )
2025-01-24 00:30:14 +08:00
folderPermissions . On ( "SetPermissions" , mock . Anything , mock . Anything , mock . Anything , mock . Anything ) . Return ( [ ] accesscontrol . ResourcePermission { } , nil )
2022-05-10 21:48:47 +08:00
dashboardPermissions := acmock . NewMockedPermissionsService ( )
2023-04-20 17:24:41 +08:00
dashboardPermissions . On ( "SetPermissions" , mock . Anything , mock . Anything , mock . Anything , mock . Anything ) . Return ( [ ] accesscontrol . ResourcePermission { } , nil )
2024-09-26 07:21:39 +08:00
folderStore := folderimpl . ProvideDashboardFolderStore ( sqlStore )
2025-01-24 00:30:14 +08:00
fStore := folderimpl . ProvideStore ( sqlStore )
2025-01-27 21:29:47 +08:00
folderSvc := folderimpl . ProvideService (
fStore , ac , bus . ProvideBus ( tracing . InitializeTracerForTest ( ) ) , dashboardStore , folderStore ,
2025-03-27 22:46:09 +08:00
nil , sqlStore , features , supportbundlestest . NewFakeBundleService ( ) , nil , cfg , nil , tracing . InitializeTracerForTest ( ) , nil , dualwrite . ProvideTestService ( ) , sort . ProvideService ( ) , apiserver . WithoutRestConfig )
2025-01-24 00:30:14 +08:00
_ , err = folderSvc . Create ( context . Background ( ) , & folder . CreateFolderCommand { UID : folderUID , SignedInUser : & user , Title : folderUID + "-title" } )
require . NoError ( t , err )
2023-04-14 17:17:23 +08:00
service , err := dashboardservice . ProvideDashboardServiceImpl (
2024-03-14 22:36:35 +08:00
cfg , dashboardStore , folderStore ,
2025-01-22 04:57:43 +08:00
features , folderPermissions , ac ,
2025-04-24 21:02:39 +08:00
actest . FakeService { } ,
2025-01-24 00:30:14 +08:00
folderSvc ,
2023-12-05 23:13:31 +08:00
nil ,
2025-02-12 03:14:25 +08:00
client . MockTestRestConfig { } ,
2025-01-02 23:39:45 +08:00
nil ,
2025-01-10 13:21:21 +08:00
quotaService ,
nil ,
2025-01-24 04:23:59 +08:00
nil ,
2025-02-14 19:34:52 +08:00
nil ,
2025-02-19 22:50:39 +08:00
dualwrite . ProvideTestService ( ) ,
2025-02-19 02:30:11 +08:00
sort . ProvideService ( ) ,
2025-03-23 06:47:27 +08:00
serverlock . ProvideService ( sqlStore , tracing . InitializeTracerForTest ( ) ) ,
kvstore . NewFakeKVStore ( ) ,
2022-03-10 19:58:18 +08:00
)
2023-04-14 17:17:23 +08:00
require . NoError ( t , err )
2025-01-22 04:57:43 +08:00
service . RegisterDashboardPermissions ( dashboardPermissions )
2022-03-10 19:58:18 +08:00
dashboard , err := service . SaveDashboard ( context . Background ( ) , dashItem , true )
2021-05-11 13:10:19 +08:00
require . NoError ( t , err )
return dashboard
}
2025-01-24 00:30:14 +08:00
func createFolder ( t * testing . T , sc scenarioContext , title string , folderSvc folder . Service ) * folder . Folder {
2021-05-11 13:10:19 +08:00
t . Helper ( )
2025-01-24 00:30:14 +08:00
if folderSvc == nil {
features := featuremgmt . WithFeatures ( )
cfg := setting . NewCfg ( )
ac := actest . FakeAccessControl { ExpectedEvaluate : true }
dashboardStore , err := database . ProvideDashboardStore ( sc . sqlStore , cfg , features , tagimpl . ProvideService ( sc . sqlStore ) )
require . NoError ( t , err )
2022-03-10 19:58:18 +08:00
2025-01-24 00:30:14 +08:00
folderStore := folderimpl . ProvideDashboardFolderStore ( sc . sqlStore )
store := folderimpl . ProvideStore ( sc . sqlStore )
2025-01-27 21:29:47 +08:00
folderSvc = folderimpl . ProvideService (
store , ac , bus . ProvideBus ( tracing . InitializeTracerForTest ( ) ) , dashboardStore , folderStore ,
2025-03-27 22:46:09 +08:00
nil , sc . sqlStore , features , supportbundlestest . NewFakeBundleService ( ) , nil , cfg , nil , tracing . InitializeTracerForTest ( ) , nil , dualwrite . ProvideTestService ( ) , sort . ProvideService ( ) , apiserver . WithoutRestConfig )
2025-01-27 17:51:41 +08:00
t . Logf ( "Creating folder with title %q and UID uid_for_%s" , title , title )
2025-01-24 00:30:14 +08:00
}
2024-07-26 21:39:23 +08:00
ctx := identity . WithRequester ( context . Background ( ) , & sc . user )
2025-01-24 00:30:14 +08:00
folder , err := folderSvc . Create ( ctx , & folder . CreateFolderCommand {
2025-01-27 17:51:41 +08:00
OrgID : sc . user . OrgID , Title : title , UID : "uid_for_" + title , SignedInUser : & sc . user ,
2022-11-10 17:41:03 +08:00
} )
2021-05-11 13:10:19 +08:00
require . NoError ( t , err )
2023-04-20 17:24:41 +08:00
// Set user permissions on the newly created folder so that they can interact with library elements stored in it
2025-04-10 20:42:23 +08:00
sc . reqContext . Permissions [ sc . user . OrgID ] [ dashboards . ActionFoldersWrite ] = append ( sc . reqContext . Permissions [ sc . user . OrgID ] [ dashboards . ActionFoldersWrite ] , dashboards . ScopeFoldersProvider . GetResourceScopeUID ( folder . UID ) )
sc . reqContext . Permissions [ sc . user . OrgID ] [ dashboards . ActionFoldersRead ] = append ( sc . reqContext . Permissions [ sc . user . OrgID ] [ dashboards . ActionFoldersRead ] , dashboards . ScopeFoldersProvider . GetResourceScopeUID ( folder . UID ) )
sc . reqContext . Permissions [ sc . user . OrgID ] [ dashboards . ActionDashboardsCreate ] = append ( sc . reqContext . Permissions [ sc . user . OrgID ] [ dashboards . ActionDashboardsCreate ] , dashboards . ScopeFoldersProvider . GetResourceScopeUID ( folder . UID ) )
2021-05-11 13:10:19 +08:00
return folder
}
func validateAndUnMarshalResponse ( t * testing . T , resp response . Response ) libraryElementResult {
t . Helper ( )
require . Equal ( t , 200 , resp . Status ( ) )
var result = libraryElementResult { }
err := json . Unmarshal ( resp . Body ( ) , & result )
require . NoError ( t , err )
2021-05-14 22:03:37 +08:00
return result
}
2023-02-02 00:32:05 +08:00
func validateAndUnMarshalConnectionResponse ( t * testing . T , resp response . Response ) model . LibraryElementConnectionsResponse {
2022-07-26 06:55:28 +08:00
t . Helper ( )
require . Equal ( t , 200 , resp . Status ( ) )
2023-02-02 00:32:05 +08:00
var result = model . LibraryElementConnectionsResponse { }
2022-07-26 06:55:28 +08:00
err := json . Unmarshal ( resp . Body ( ) , & result )
require . NoError ( t , err )
return result
}
2021-05-14 22:03:37 +08:00
func validateAndUnMarshalArrayResponse ( t * testing . T , resp response . Response ) libraryElementArrayResult {
t . Helper ( )
require . Equal ( t , 200 , resp . Status ( ) )
var result = libraryElementArrayResult { }
err := json . Unmarshal ( resp . Body ( ) , & result )
require . NoError ( t , err )
2021-05-11 13:10:19 +08:00
return result
}
func scenarioWithPanel ( t * testing . T , desc string , fn func ( t * testing . T , sc scenarioContext ) ) {
t . Helper ( )
2023-04-20 17:24:41 +08:00
2023-11-13 23:55:15 +08:00
features := featuremgmt . WithFeatures ( )
2024-09-26 07:21:39 +08:00
sqlStore , cfg := db . InitTestDBWithCfg ( t )
2023-04-20 17:24:41 +08:00
ac := actest . FakeAccessControl { }
quotaService := quotatest . New ( false , nil )
2025-01-10 13:21:21 +08:00
dashboardStore , err := database . ProvideDashboardStore ( sqlStore , cfg , features , tagimpl . ProvideService ( sqlStore ) )
2023-04-20 17:24:41 +08:00
require . NoError ( t , err )
folderPermissions := acmock . NewMockedPermissionsService ( )
dashboardPermissions := acmock . NewMockedPermissionsService ( )
folderStore := folderimpl . ProvideDashboardFolderStore ( sqlStore )
2025-01-24 00:30:14 +08:00
fStore := folderimpl . ProvideStore ( sqlStore )
2025-01-27 21:29:47 +08:00
folderSvc := folderimpl . ProvideService (
fStore , ac , bus . ProvideBus ( tracing . InitializeTracerForTest ( ) ) , dashboardStore , folderStore ,
2025-03-27 22:46:09 +08:00
nil , sqlStore , features , supportbundlestest . NewFakeBundleService ( ) , nil , cfg , nil , tracing . InitializeTracerForTest ( ) , nil , dualwrite . ProvideTestService ( ) , sort . ProvideService ( ) , apiserver . WithoutRestConfig )
2023-04-20 17:24:41 +08:00
dashboardService , svcErr := dashboardservice . ProvideDashboardServiceImpl (
2024-04-24 16:38:40 +08:00
cfg , dashboardStore , folderStore ,
2025-04-24 21:02:39 +08:00
features , folderPermissions , ac , actest . FakeService { } , folderSvc ,
2025-02-19 22:50:39 +08:00
nil , client . MockTestRestConfig { } , nil , quotaService , nil , nil , nil , dualwrite . ProvideTestService ( ) , sort . ProvideService ( ) ,
2025-03-23 06:47:27 +08:00
serverlock . ProvideService ( sqlStore , tracing . InitializeTracerForTest ( ) ) ,
kvstore . NewFakeKVStore ( ) ,
2023-04-20 17:24:41 +08:00
)
require . NoError ( t , svcErr )
2025-01-22 04:57:43 +08:00
dashboardService . RegisterDashboardPermissions ( dashboardPermissions )
2025-01-24 00:30:14 +08:00
guardian . InitAccessControlGuardian ( cfg , ac , dashboardService , folderSvc , log . NewNopLogger ( ) )
2021-05-11 13:10:19 +08:00
testScenario ( t , desc , func ( t * testing . T , sc scenarioContext ) {
2023-11-21 04:44:51 +08:00
// nolint:staticcheck
2024-04-09 18:27:43 +08:00
command := getCreatePanelCommand ( sc . folder . ID , sc . folder . UID , "Text - Library Panel" )
2021-11-29 17:18:01 +08:00
sc . reqContext . Req . Body = mockRequestBody ( command )
resp := sc . service . createHandler ( sc . reqContext )
2021-05-11 13:10:19 +08:00
sc . initialResult = validateAndUnMarshalResponse ( t , resp )
2023-09-06 17:16:10 +08:00
sc . log = log . New ( "libraryelements-test" )
2021-05-11 13:10:19 +08:00
fn ( t , sc )
} )
}
// testScenario is a wrapper around t.Run performing common setup for library panel tests.
// It takes your real test function as a callback.
func testScenario ( t * testing . T , desc string , fn func ( t * testing . T , sc scenarioContext ) ) {
t . Helper ( )
t . Run ( desc , func ( t * testing . T ) {
2022-11-11 21:28:24 +08:00
orgID := int64 ( 1 )
role := org . RoleAdmin
usr := user . SignedInUser {
UserID : 1 ,
Name : "Signed In User" ,
Login : "signed_in_user" ,
Email : "signed.in.user@test.com" ,
OrgID : orgID ,
OrgRole : role ,
LastSeenAt : time . Now ( ) ,
2023-04-20 17:24:41 +08:00
// Allow user to create folders
Permissions : map [ int64 ] map [ string ] [ ] string {
2024-08-01 23:20:38 +08:00
1 : { dashboards . ActionFoldersCreate : { dashboards . ScopeFoldersAll } } ,
2023-04-20 17:24:41 +08:00
} ,
2022-11-11 21:28:24 +08:00
}
req := & http . Request {
2022-02-09 20:44:38 +08:00
Header : http . Header {
"Content-Type" : [ ] string { "application/json" } ,
} ,
2022-11-11 21:28:24 +08:00
}
2024-07-26 21:39:23 +08:00
ctx := identity . WithRequester ( context . Background ( ) , & usr )
2022-11-11 21:28:24 +08:00
req = req . WithContext ( ctx )
webCtx := web . Context { Req : req }
2023-11-13 23:55:15 +08:00
features := featuremgmt . WithFeatures ( )
2024-04-30 19:15:56 +08:00
tracer := tracing . InitializeTracerForTest ( )
2024-09-26 07:21:39 +08:00
sqlStore , cfg := db . InitTestDBWithCfg ( t )
2022-11-15 03:08:10 +08:00
quotaService := quotatest . New ( false , nil )
2025-01-10 13:21:21 +08:00
dashboardStore , err := database . ProvideDashboardStore ( sqlStore , cfg , features , tagimpl . ProvideService ( sqlStore ) )
2022-11-15 03:08:10 +08:00
require . NoError ( t , err )
2025-01-14 17:26:15 +08:00
ac := acimpl . ProvideAccessControl ( features )
2022-05-10 21:48:47 +08:00
folderPermissions := acmock . NewMockedPermissionsService ( )
2023-04-20 17:24:41 +08:00
folderPermissions . On ( "SetPermissions" , mock . Anything , mock . Anything , mock . Anything , mock . Anything ) . Return ( [ ] accesscontrol . ResourcePermission { } , nil )
2022-05-10 21:48:47 +08:00
dashboardPermissions := acmock . NewMockedPermissionsService ( )
2023-02-01 21:43:21 +08:00
folderStore := folderimpl . ProvideDashboardFolderStore ( sqlStore )
2025-01-24 00:30:14 +08:00
fStore := folderimpl . ProvideStore ( sqlStore )
2025-01-28 07:39:18 +08:00
publicDash := & publicdashboards . FakePublicDashboardServiceWrapper { }
publicDash . On ( "DeleteByDashboardUIDs" , mock . Anything , mock . Anything , mock . Anything ) . Return ( nil )
2025-01-27 21:29:47 +08:00
folderSvc := folderimpl . ProvideService (
fStore , ac , bus . ProvideBus ( tracing . InitializeTracerForTest ( ) ) , dashboardStore , folderStore ,
2025-03-27 22:46:09 +08:00
nil , sqlStore , features , supportbundlestest . NewFakeBundleService ( ) , publicDash , cfg , nil , tracing . InitializeTracerForTest ( ) , nil , dualwrite . ProvideTestService ( ) , sort . ProvideService ( ) , apiserver . WithoutRestConfig )
2025-01-28 07:39:18 +08:00
alertStore , err := ngstore . ProvideDBStore ( cfg , features , sqlStore , & foldertest . FakeService { } , & dashboards . FakeDashboardService { } , ac , bus . ProvideBus ( tracing . InitializeTracerForTest ( ) ) )
require . NoError ( t , err )
err = folderSvc . RegisterService ( alertStore )
require . NoError ( t , err )
2023-04-20 17:24:41 +08:00
dashService , dashSvcErr := dashboardservice . ProvideDashboardServiceImpl (
2024-04-24 16:38:40 +08:00
cfg , dashboardStore , folderStore ,
2025-04-24 21:02:39 +08:00
features , folderPermissions , ac , actest . FakeService { } , folderSvc ,
2025-02-19 22:50:39 +08:00
nil , client . MockTestRestConfig { } , nil , quotaService , nil , nil , nil , dualwrite . ProvideTestService ( ) , sort . ProvideService ( ) ,
2025-03-23 06:47:27 +08:00
serverlock . ProvideService ( sqlStore , tracing . InitializeTracerForTest ( ) ) ,
kvstore . NewFakeKVStore ( ) ,
2022-03-10 19:58:18 +08:00
)
2023-04-20 17:24:41 +08:00
require . NoError ( t , dashSvcErr )
2025-01-22 04:57:43 +08:00
dashService . RegisterDashboardPermissions ( dashboardPermissions )
2025-01-24 00:30:14 +08:00
guardian . InitAccessControlGuardian ( cfg , ac , dashService , folderSvc , log . NewNopLogger ( ) )
2021-05-12 14:48:17 +08:00
service := LibraryElementService {
2025-01-27 21:42:57 +08:00
Cfg : cfg ,
features : featuremgmt . WithFeatures ( ) ,
SQLStore : sqlStore ,
folderService : folderSvc ,
dashboardsService : dashService ,
2021-05-12 14:48:17 +08:00
}
2021-05-11 13:10:19 +08:00
// deliberate difference between signed in user and user in db to make it crystal clear
// what to expect in the tests
// In the real world these are identical
2022-06-28 20:32:25 +08:00
cmd := user . CreateUserCommand {
2021-05-11 13:10:19 +08:00
Email : "user.in.db@test.com" ,
Name : "User In DB" ,
Login : userInDbName ,
}
2024-04-24 16:38:40 +08:00
orgSvc , err := orgimpl . ProvideService ( sqlStore , cfg , quotaService )
2022-12-08 00:03:22 +08:00
require . NoError ( t , err )
2024-04-30 19:15:56 +08:00
usrSvc , err := userimpl . ProvideService (
sqlStore , orgSvc , cfg , nil , nil , tracer ,
quotaService , supportbundlestest . NewFakeBundleService ( ) ,
)
2022-12-08 00:03:22 +08:00
require . NoError ( t , err )
_ , err = usrSvc . Create ( context . Background ( ) , & cmd )
2021-05-11 13:10:19 +08:00
require . NoError ( t , err )
sc := scenarioContext {
2024-09-26 07:21:39 +08:00
user : usr ,
ctx : & webCtx ,
service : & service ,
sqlStore : sqlStore ,
2023-01-27 15:50:36 +08:00
reqContext : & contextmodel . ReqContext {
2022-11-11 21:28:24 +08:00
Context : & webCtx ,
2022-06-28 20:32:25 +08:00
SignedInUser : & usr ,
2021-05-11 13:10:19 +08:00
} ,
}
2025-01-24 00:30:14 +08:00
sc . folder = createFolder ( t , sc , "ScenarioFolder" , folderSvc )
2021-05-11 13:10:19 +08:00
fn ( t , sc )
} )
}
func getCompareOptions ( ) [ ] cmp . Option {
return [ ] cmp . Option {
cmp . Transformer ( "Time" , func ( in time . Time ) int64 {
return in . UTC ( ) . Unix ( )
} ) ,
}
}
2021-11-29 17:18:01 +08:00
2023-08-30 23:46:47 +08:00
func mockRequestBody ( v any ) io . ReadCloser {
2021-11-29 17:18:01 +08:00
b , _ := json . Marshal ( v )
return io . NopCloser ( bytes . NewReader ( b ) )
}