2021-11-16 17:26:46 +08:00
package dashboards
import (
"bytes"
"context"
"encoding/json"
"fmt"
2022-08-10 21:37:51 +08:00
"io"
2021-11-16 17:26:46 +08:00
"net/http"
2021-12-17 23:31:52 +08:00
"net/url"
"os"
"path/filepath"
2021-11-16 17:26:46 +08:00
"testing"
2024-04-08 15:47:34 +08:00
"time"
2021-11-16 17:26:46 +08:00
2022-06-30 21:31:54 +08:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
2023-11-01 23:01:54 +08:00
"github.com/grafana/grafana/pkg/api/dtos"
2021-11-16 17:26:46 +08:00
"github.com/grafana/grafana/pkg/components/simplejson"
2024-04-04 21:04:47 +08:00
"github.com/grafana/grafana/pkg/infra/db"
2024-04-30 19:15:56 +08:00
"github.com/grafana/grafana/pkg/infra/tracing"
2022-01-28 17:28:33 +08:00
"github.com/grafana/grafana/pkg/services/dashboardimport"
2022-06-30 21:31:54 +08:00
"github.com/grafana/grafana/pkg/services/dashboards"
2025-03-31 18:02:19 +08:00
"github.com/grafana/grafana/pkg/services/featuremgmt"
2023-11-01 23:01:54 +08:00
"github.com/grafana/grafana/pkg/services/folder"
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"
2022-03-11 01:38:04 +08:00
"github.com/grafana/grafana/pkg/services/plugindashboards"
2022-12-08 00:03:22 +08:00
"github.com/grafana/grafana/pkg/services/quota/quotaimpl"
2023-01-31 18:04:55 +08:00
"github.com/grafana/grafana/pkg/services/search/model"
2023-02-07 00:50:03 +08:00
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
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"
2024-04-04 21:04:47 +08:00
"github.com/grafana/grafana/pkg/setting"
2021-11-16 17:26:46 +08:00
"github.com/grafana/grafana/pkg/tests/testinfra"
2024-02-09 22:35:39 +08:00
"github.com/grafana/grafana/pkg/tests/testsuite"
2023-11-01 23:01:54 +08:00
"github.com/grafana/grafana/pkg/util"
2021-11-16 17:26:46 +08:00
)
2024-02-09 22:35:39 +08:00
func TestMain ( m * testing . M ) {
testsuite . Run ( m )
}
2022-12-09 15:11:56 +08:00
func TestIntegrationDashboardQuota ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
2025-03-31 21:34:39 +08:00
testDashboardQuota ( t , [ ] string { } )
}
func TestIntegrationDashboardQuotaK8s ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
testDashboardQuota ( t , [ ] string { featuremgmt . FlagKubernetesClientDashboardsFolders } )
}
2022-12-09 15:11:56 +08:00
2025-03-31 21:34:39 +08:00
func testDashboardQuota ( t * testing . T , featureToggles [ ] string ) {
2021-11-16 17:26:46 +08:00
// enable quota and set low dashboard quota
// Setup Grafana and its Database
dashboardQuota := int64 ( 1 )
dir , path := testinfra . CreateGrafDir ( t , testinfra . GrafanaOpts {
2025-03-31 21:34:39 +08:00
DisableAnonymous : true ,
EnableQuota : true ,
DashboardOrgQuota : & dashboardQuota ,
EnableFeatureToggles : featureToggles ,
2021-11-16 17:26:46 +08:00
} )
2022-01-20 18:10:12 +08:00
2024-04-04 21:04:47 +08:00
grafanaListedAddr , env := testinfra . StartGrafanaEnv ( t , dir , path )
store , cfg := env . SQLStore , env . Cfg
2021-11-16 17:26:46 +08:00
// Create user
2024-04-04 21:04:47 +08:00
createUser ( t , store , cfg , user . CreateUserCommand {
2022-08-10 17:56:48 +08:00
DefaultOrgRole : string ( org . RoleAdmin ) ,
2021-11-16 17:26:46 +08:00
Password : "admin" ,
Login : "admin" ,
} )
t . Run ( "when quota limit doesn't exceed, importing a dashboard should succeed" , func ( t * testing . T ) {
// Import dashboard
dashboardDataOne , err := simplejson . NewJson ( [ ] byte ( ` { "title":"just testing"} ` ) )
require . NoError ( t , err )
buf1 := & bytes . Buffer { }
2022-01-28 17:28:33 +08:00
err = json . NewEncoder ( buf1 ) . Encode ( dashboardimport . ImportDashboardRequest {
2021-11-16 17:26:46 +08:00
Dashboard : dashboardDataOne ,
} )
require . NoError ( t , err )
u := fmt . Sprintf ( "http://admin:admin@%s/api/dashboards/import" , grafanaListedAddr )
// nolint:gosec
resp , err := http . Post ( u , "application/json" , buf1 )
require . NoError ( t , err )
assert . Equal ( t , http . StatusOK , resp . StatusCode )
t . Cleanup ( func ( ) {
err := resp . Body . Close ( )
require . NoError ( t , err )
} )
2022-08-10 21:37:51 +08:00
b , err := io . ReadAll ( resp . Body )
2021-11-16 17:26:46 +08:00
require . NoError ( t , err )
2022-03-11 01:38:04 +08:00
dashboardDTO := & plugindashboards . PluginDashboard { }
2021-11-16 17:26:46 +08:00
err = json . Unmarshal ( b , dashboardDTO )
require . NoError ( t , err )
require . EqualValues ( t , 1 , dashboardDTO . DashboardId )
} )
t . Run ( "when quota limit exceeds importing a dashboard should fail" , func ( t * testing . T ) {
dashboardDataOne , err := simplejson . NewJson ( [ ] byte ( ` { "title":"just testing"} ` ) )
require . NoError ( t , err )
buf1 := & bytes . Buffer { }
2022-01-28 17:28:33 +08:00
err = json . NewEncoder ( buf1 ) . Encode ( dashboardimport . ImportDashboardRequest {
2021-11-16 17:26:46 +08:00
Dashboard : dashboardDataOne ,
} )
require . NoError ( t , err )
u := fmt . Sprintf ( "http://admin:admin@%s/api/dashboards/import" , grafanaListedAddr )
// nolint:gosec
resp , err := http . Post ( u , "application/json" , buf1 )
require . NoError ( t , err )
assert . Equal ( t , http . StatusForbidden , resp . StatusCode )
t . Cleanup ( func ( ) {
err := resp . Body . Close ( )
require . NoError ( t , err )
} )
2022-08-10 21:37:51 +08:00
b , err := io . ReadAll ( resp . Body )
2021-11-16 17:26:46 +08:00
require . NoError ( t , err )
assert . Equal ( t , http . StatusForbidden , resp . StatusCode )
require . JSONEq ( t , ` { "message":"Quota reached"} ` , string ( b ) )
} )
}
2024-09-26 07:21:39 +08:00
func createUser ( t * testing . T , db db . DB , cfg * setting . Cfg , cmd user . CreateUserCommand ) int64 {
2021-11-16 17:26:46 +08:00
t . Helper ( )
2022-06-07 21:49:18 +08:00
2024-04-04 21:04:47 +08:00
cfg . AutoAssignOrg = true
cfg . AutoAssignOrgId = 1
2022-06-07 21:49:18 +08:00
2024-09-26 07:21:39 +08:00
quotaService := quotaimpl . ProvideService ( db , cfg )
orgService , err := orgimpl . ProvideService ( db , 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 (
2024-09-26 07:21:39 +08:00
db , orgService , cfg , nil , nil , tracing . InitializeTracerForTest ( ) ,
2024-04-30 19:15:56 +08:00
quotaService , supportbundlestest . NewFakeBundleService ( ) ,
)
2022-12-08 00:03:22 +08:00
require . NoError ( t , err )
2023-03-04 00:01:23 +08:00
u , err := usrSvc . Create ( context . Background ( ) , & cmd )
2021-11-16 17:26:46 +08:00
require . NoError ( t , err )
2022-06-28 20:32:25 +08:00
return u . ID
2021-11-16 17:26:46 +08:00
}
2021-12-17 23:31:52 +08:00
2022-12-09 15:11:56 +08:00
func TestIntegrationUpdatingProvisionionedDashboards ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
2025-03-31 21:34:54 +08:00
testUpdatingProvisionionedDashboards ( t , [ ] string { } )
}
func TestIntegrationUpdatingProvisionionedDashboardsK8s ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
// will be the default in g12
testUpdatingProvisionionedDashboards ( t , [ ] string { featuremgmt . FlagKubernetesClientDashboardsFolders } )
}
func testUpdatingProvisionionedDashboards ( t * testing . T , featureToggles [ ] string ) {
2021-12-17 23:31:52 +08:00
// Setup Grafana and its Database
dir , path := testinfra . CreateGrafDir ( t , testinfra . GrafanaOpts {
2025-03-31 21:34:54 +08:00
DisableAnonymous : true ,
EnableFeatureToggles : featureToggles ,
2021-12-17 23:31:52 +08:00
} )
provDashboardsDir := filepath . Join ( dir , "conf" , "provisioning" , "dashboards" )
provDashboardsCfg := filepath . Join ( provDashboardsDir , "dev.yaml" )
blob := [ ] byte ( fmt . Sprintf ( `
apiVersion : 1
providers :
- name : ' provisioned dashboards '
type : file
allowUiUpdates : false
options :
path : % s ` , provDashboardsDir ) )
err := os . WriteFile ( provDashboardsCfg , blob , 0644 )
require . NoError ( t , err )
2022-08-10 21:37:51 +08:00
input , err := os . ReadFile ( filepath . Join ( "./home.json" ) )
2021-12-17 23:31:52 +08:00
require . NoError ( t , err )
provDashboardFile := filepath . Join ( provDashboardsDir , "home.json" )
2022-08-10 21:37:51 +08:00
err = os . WriteFile ( provDashboardFile , input , 0644 )
2021-12-17 23:31:52 +08:00
require . NoError ( t , err )
2024-04-04 21:04:47 +08:00
grafanaListedAddr , env := testinfra . StartGrafanaEnv ( t , dir , path )
store , cfg := env . SQLStore , env . Cfg
2021-12-17 23:31:52 +08:00
// Create user
2024-04-04 21:04:47 +08:00
createUser ( t , store , cfg , user . CreateUserCommand {
2022-08-10 17:56:48 +08:00
DefaultOrgRole : string ( org . RoleAdmin ) ,
2021-12-17 23:31:52 +08:00
Password : "admin" ,
Login : "admin" ,
} )
2024-04-08 23:42:12 +08:00
// give provisioner some time since we don't have a way to know when provisioning is complete
// TODO https://github.com/grafana/grafana/issues/85617
time . Sleep ( 1 * time . Second )
2021-12-17 23:31:52 +08:00
type errorResponseBody struct {
Message string ` json:"message" `
}
t . Run ( "when provisioned directory is not empty, dashboard should be created" , func ( t * testing . T ) {
title := "Grafana Dev Overview & Home"
2023-01-31 18:04:55 +08:00
dashboardList := & model . HitList { }
2024-04-08 15:47:34 +08:00
2025-04-07 10:44:28 +08:00
require . EventuallyWithT ( t , func ( collect * assert . CollectT ) {
2024-04-08 15:47:34 +08:00
u := fmt . Sprintf ( "http://admin:admin@%s/api/search?query=%s" , grafanaListedAddr , url . QueryEscape ( title ) )
// nolint:gosec
resp , err := http . Get ( u )
require . NoError ( t , err )
assert . Equal ( t , http . StatusOK , resp . StatusCode )
2025-04-07 10:44:28 +08:00
2024-04-08 15:47:34 +08:00
b , err := io . ReadAll ( resp . Body )
require . NoError ( t , err )
2025-04-07 10:44:28 +08:00
err = resp . Body . Close ( )
require . NoError ( t , err )
2024-04-08 15:47:34 +08:00
err = json . Unmarshal ( b , dashboardList )
require . NoError ( t , err )
2025-04-07 10:44:28 +08:00
assert . Greater ( collect , dashboardList . Len ( ) , 0 , "Dashboard should be ready" )
} , 10 * time . Second , 25 * time . Millisecond )
2024-04-08 15:47:34 +08:00
2021-12-17 23:31:52 +08:00
var dashboardUID string
var dashboardID int64
for _ , d := range * dashboardList {
dashboardUID = d . UID
dashboardID = d . ID
}
assert . Equal ( t , int64 ( 1 ) , dashboardID )
testCases := [ ] struct {
desc string
dashboardData string
2022-01-12 00:39:53 +08:00
expStatus int
expErrReason string
2021-12-17 23:31:52 +08:00
} {
{
desc : "when updating provisioned dashboard using ID it should fail" ,
dashboardData : fmt . Sprintf ( ` { "title":"just testing", "id": %d, "version": 1} ` , dashboardID ) ,
2022-01-12 00:39:53 +08:00
expStatus : http . StatusBadRequest ,
2022-06-30 21:31:54 +08:00
expErrReason : dashboards . ErrDashboardCannotSaveProvisionedDashboard . Reason ,
2021-12-17 23:31:52 +08:00
} ,
{
2022-01-12 00:39:53 +08:00
desc : "when updating provisioned dashboard using UID it should fail" ,
2021-12-17 23:31:52 +08:00
dashboardData : fmt . Sprintf ( ` { "title":"just testing", "uid": %q, "version": 1} ` , dashboardUID ) ,
2022-01-12 00:39:53 +08:00
expStatus : http . StatusBadRequest ,
2022-06-30 21:31:54 +08:00
expErrReason : dashboards . ErrDashboardCannotSaveProvisionedDashboard . Reason ,
2022-01-12 00:39:53 +08:00
} ,
{
desc : "when updating dashboard using unknown ID, it should fail" ,
dashboardData : ` { "title":"just testing", "id": 42, "version": 1} ` ,
expStatus : http . StatusNotFound ,
2022-06-30 21:31:54 +08:00
expErrReason : dashboards . ErrDashboardNotFound . Reason ,
2022-01-12 00:39:53 +08:00
} ,
{
desc : "when updating dashboard using unknown UID, it should succeed" ,
dashboardData : ` { "title":"just testing", "uid": "unknown", "version": 1} ` ,
expStatus : http . StatusOK ,
2021-12-17 23:31:52 +08:00
} ,
}
for _ , tc := range testCases {
t . Run ( tc . desc , func ( t * testing . T ) {
u := fmt . Sprintf ( "http://admin:admin@%s/api/dashboards/db" , grafanaListedAddr )
// nolint:gosec
dashboardData , err := simplejson . NewJson ( [ ] byte ( tc . dashboardData ) )
require . NoError ( t , err )
buf := & bytes . Buffer { }
2023-01-18 20:52:41 +08:00
err = json . NewEncoder ( buf ) . Encode ( dashboards . SaveDashboardCommand {
2021-12-17 23:31:52 +08:00
Dashboard : dashboardData ,
} )
require . NoError ( t , err )
// nolint:gosec
resp , err := http . Post ( u , "application/json" , buf )
require . NoError ( t , err )
2022-01-12 00:39:53 +08:00
assert . Equal ( t , tc . expStatus , resp . StatusCode )
2021-12-17 23:31:52 +08:00
t . Cleanup ( func ( ) {
err := resp . Body . Close ( )
require . NoError ( t , err )
} )
2022-01-12 00:39:53 +08:00
if tc . expErrReason == "" {
return
}
2022-08-10 21:37:51 +08:00
b , err := io . ReadAll ( resp . Body )
2021-12-17 23:31:52 +08:00
require . NoError ( t , err )
dashboardErr := & errorResponseBody { }
err = json . Unmarshal ( b , dashboardErr )
require . NoError ( t , err )
2022-01-12 00:39:53 +08:00
assert . Equal ( t , tc . expErrReason , dashboardErr . Message )
2021-12-17 23:31:52 +08:00
} )
}
t . Run ( "deleting provisioned dashboard should fail" , func ( t * testing . T ) {
u := fmt . Sprintf ( "http://admin:admin@%s/api/dashboards/uid/%s" , grafanaListedAddr , dashboardUID )
req , err := http . NewRequest ( "DELETE" , u , nil )
if err != nil {
fmt . Println ( err )
return
}
client := & http . Client { }
resp , err := client . Do ( req )
require . NoError ( t , err )
t . Cleanup ( func ( ) {
err := resp . Body . Close ( )
require . NoError ( t , err )
} )
assert . Equal ( t , http . StatusBadRequest , resp . StatusCode )
2022-08-10 21:37:51 +08:00
b , err := io . ReadAll ( resp . Body )
2021-12-17 23:31:52 +08:00
require . NoError ( t , err )
dashboardErr := & errorResponseBody { }
err = json . Unmarshal ( b , dashboardErr )
require . NoError ( t , err )
2022-06-30 21:31:54 +08:00
assert . Equal ( t , dashboards . ErrDashboardCannotDeleteProvisionedDashboard . Reason , dashboardErr . Message )
2021-12-17 23:31:52 +08:00
} )
} )
}
2023-11-01 23:01:54 +08:00
func TestIntegrationCreate ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
2025-03-31 18:02:19 +08:00
testCreate ( t , [ ] string { } )
}
func TestIntegrationCreateK8s ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
testCreate ( t , [ ] string { featuremgmt . FlagKubernetesClientDashboardsFolders } )
}
2025-04-21 23:51:28 +08:00
func TestIntegrationPreserveSchemaVersion ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
testPreserveSchemaVersion ( t , [ ] string { featuremgmt . FlagKubernetesClientDashboardsFolders } )
}
2025-03-31 18:02:19 +08:00
func testCreate ( t * testing . T , featureToggles [ ] string ) {
2023-11-01 23:01:54 +08:00
// Setup Grafana and its Database
dir , path := testinfra . CreateGrafDir ( t , testinfra . GrafanaOpts {
2025-03-31 18:02:19 +08:00
DisableAnonymous : true ,
EnableFeatureToggles : featureToggles ,
2023-11-01 23:01:54 +08:00
} )
2024-04-04 21:04:47 +08:00
grafanaListedAddr , env := testinfra . StartGrafanaEnv ( t , dir , path )
store , cfg := env . SQLStore , env . Cfg
2023-11-01 23:01:54 +08:00
// Create user
2024-04-04 21:04:47 +08:00
createUser ( t , store , cfg , user . CreateUserCommand {
2023-11-01 23:01:54 +08:00
DefaultOrgRole : string ( org . RoleAdmin ) ,
Password : "admin" ,
Login : "admin" ,
} )
t . Run ( "create dashboard should succeed" , func ( t * testing . T ) {
dashboardDataOne , err := simplejson . NewJson ( [ ] byte ( ` { "title":"just testing"} ` ) )
require . NoError ( t , err )
buf1 := & bytes . Buffer { }
err = json . NewEncoder ( buf1 ) . Encode ( dashboards . SaveDashboardCommand {
Dashboard : dashboardDataOne ,
} )
require . NoError ( t , err )
u := fmt . Sprintf ( "http://admin:admin@%s/api/dashboards/db" , grafanaListedAddr )
// nolint:gosec
resp , err := http . Post ( u , "application/json" , buf1 )
require . NoError ( t , err )
assert . Equal ( t , http . StatusOK , resp . StatusCode )
t . Cleanup ( func ( ) {
err := resp . Body . Close ( )
require . NoError ( t , err )
} )
b , err := io . ReadAll ( resp . Body )
require . NoError ( t , err )
var m util . DynMap
err = json . Unmarshal ( b , & m )
require . NoError ( t , err )
assert . NotEmpty ( t , m [ "id" ] )
assert . NotEmpty ( t , m [ "uid" ] )
} )
t . Run ( "create dashboard under folder should succeed" , func ( t * testing . T ) {
folder := createFolder ( t , grafanaListedAddr , "test folder" )
dashboardDataOne , err := simplejson . NewJson ( [ ] byte ( ` { "title":"just testing"} ` ) )
require . NoError ( t , err )
buf1 := & bytes . Buffer { }
err = json . NewEncoder ( buf1 ) . Encode ( dashboards . SaveDashboardCommand {
Dashboard : dashboardDataOne ,
2023-12-07 20:56:04 +08:00
OrgID : 0 ,
2023-12-07 20:15:58 +08:00
FolderUID : folder . UID ,
2023-11-01 23:01:54 +08:00
} )
require . NoError ( t , err )
u := fmt . Sprintf ( "http://admin:admin@%s/api/dashboards/db" , grafanaListedAddr )
// nolint:gosec
resp , err := http . Post ( u , "application/json" , buf1 )
require . NoError ( t , err )
require . Equal ( t , http . StatusOK , resp . StatusCode )
t . Cleanup ( func ( ) {
err := resp . Body . Close ( )
require . NoError ( t , err )
} )
b , err := io . ReadAll ( resp . Body )
require . NoError ( t , err )
var m util . DynMap
err = json . Unmarshal ( b , & m )
require . NoError ( t , err )
assert . NotEmpty ( t , m [ "id" ] )
assert . NotEmpty ( t , m [ "uid" ] )
2023-12-07 20:15:58 +08:00
assert . Equal ( t , folder . UID , m [ "folderUid" ] )
2023-11-01 23:01:54 +08:00
} )
t . Run ( "create dashboard under folder (using deprecated folder sequential ID) should succeed" , func ( t * testing . T ) {
folder := createFolder ( t , grafanaListedAddr , "test folder 2" )
dashboardDataOne , err := simplejson . NewJson ( [ ] byte ( ` { "title":"just testing"} ` ) )
require . NoError ( t , err )
buf1 := & bytes . Buffer { }
err = json . NewEncoder ( buf1 ) . Encode ( dashboards . SaveDashboardCommand {
Dashboard : dashboardDataOne ,
2023-12-07 20:56:04 +08:00
OrgID : 0 ,
FolderUID : folder . UID ,
2023-11-01 23:01:54 +08:00
} )
require . NoError ( t , err )
u := fmt . Sprintf ( "http://admin:admin@%s/api/dashboards/db" , grafanaListedAddr )
// nolint:gosec
resp , err := http . Post ( u , "application/json" , buf1 )
require . NoError ( t , err )
require . Equal ( t , http . StatusOK , resp . StatusCode )
t . Cleanup ( func ( ) {
err := resp . Body . Close ( )
require . NoError ( t , err )
} )
b , err := io . ReadAll ( resp . Body )
require . NoError ( t , err )
var m util . DynMap
err = json . Unmarshal ( b , & m )
require . NoError ( t , err )
assert . NotEmpty ( t , m [ "id" ] )
assert . NotEmpty ( t , m [ "uid" ] )
2023-12-07 20:15:58 +08:00
assert . Equal ( t , folder . UID , m [ "folderUid" ] )
2023-11-01 23:01:54 +08:00
} )
t . Run ( "create dashboard under unknow folder should fail" , func ( t * testing . T ) {
folderUID := "unknown"
// Import dashboard
dashboardDataOne , err := simplejson . NewJson ( [ ] byte ( ` { "title":"just testing"} ` ) )
require . NoError ( t , err )
buf1 := & bytes . Buffer { }
err = json . NewEncoder ( buf1 ) . Encode ( dashboards . SaveDashboardCommand {
Dashboard : dashboardDataOne ,
FolderUID : folderUID ,
} )
require . NoError ( t , err )
u := fmt . Sprintf ( "http://admin:admin@%s/api/dashboards/db" , grafanaListedAddr )
// nolint:gosec
resp , err := http . Post ( u , "application/json" , buf1 )
require . NoError ( t , err )
assert . Equal ( t , http . StatusBadRequest , resp . StatusCode )
t . Cleanup ( func ( ) {
err := resp . Body . Close ( )
require . NoError ( t , err )
} )
b , err := io . ReadAll ( resp . Body )
require . NoError ( t , err )
var m util . DynMap
err = json . Unmarshal ( b , & m )
require . NoError ( t , err )
2023-11-16 19:11:35 +08:00
assert . Equal ( t , dashboards . ErrFolderNotFound . Error ( ) , m [ "message" ] )
2023-11-01 23:01:54 +08:00
} )
}
func createFolder ( t * testing . T , grafanaListedAddr string , title string ) * dtos . Folder {
t . Helper ( )
buf1 := & bytes . Buffer { }
err := json . NewEncoder ( buf1 ) . Encode ( folder . CreateFolderCommand {
Title : title ,
} )
require . NoError ( t , err )
u := fmt . Sprintf ( "http://admin:admin@%s/api/folders" , grafanaListedAddr )
// nolint:gosec
resp , err := http . Post ( u , "application/json" , buf1 )
require . NoError ( t , err )
assert . Equal ( t , http . StatusOK , resp . StatusCode )
t . Cleanup ( func ( ) {
err := resp . Body . Close ( )
require . NoError ( t , err )
} )
b , err := io . ReadAll ( resp . Body )
require . NoError ( t , err )
require . Equal ( t , http . StatusOK , resp . StatusCode )
var f * dtos . Folder
err = json . Unmarshal ( b , & f )
require . NoError ( t , err )
return f
}
2025-04-21 23:51:28 +08:00
func intPtr ( n int ) * int {
return & n
}
func testPreserveSchemaVersion ( t * testing . T , featureToggles [ ] string ) {
dir , path := testinfra . CreateGrafDir ( t , testinfra . GrafanaOpts {
DisableAnonymous : true ,
EnableFeatureToggles : featureToggles ,
} )
grafanaListedAddr , env := testinfra . StartGrafanaEnv ( t , dir , path )
store , cfg := env . SQLStore , env . Cfg
createUser ( t , store , cfg , user . CreateUserCommand {
DefaultOrgRole : string ( org . RoleAdmin ) ,
Password : "admin" ,
Login : "admin" ,
} )
schemaVersions := [ ] * int { intPtr ( 1 ) , intPtr ( 36 ) , intPtr ( 40 ) , nil }
for _ , schemaVersion := range schemaVersions {
var title string
if schemaVersion == nil {
title = "save dashboard with no schemaVersion"
} else {
title = fmt . Sprintf ( "save dashboard with schemaVersion %d" , * schemaVersion )
}
t . Run ( title , func ( t * testing . T ) {
// Create dashboard JSON with specified schema version
var dashboardJSON string
if schemaVersion != nil {
dashboardJSON = fmt . Sprintf ( ` { "title":"Schema Version Test", "schemaVersion": %d} ` , * schemaVersion )
} else {
dashboardJSON = ` { "title":"Schema Version Test"} `
}
dashboardData , err := simplejson . NewJson ( [ ] byte ( dashboardJSON ) )
require . NoError ( t , err )
// Save the dashboard via API
buf := & bytes . Buffer { }
err = json . NewEncoder ( buf ) . Encode ( dashboards . SaveDashboardCommand {
Dashboard : dashboardData ,
} )
require . NoError ( t , err )
url := fmt . Sprintf ( "http://admin:admin@%s/api/dashboards/db" , grafanaListedAddr )
// nolint:gosec
resp , err := http . Post ( url , "application/json" , buf )
require . NoError ( t , err )
require . Equal ( t , http . StatusOK , resp . StatusCode )
t . Cleanup ( func ( ) {
err := resp . Body . Close ( )
require . NoError ( t , err )
} )
// Get dashboard UID from response
b , err := io . ReadAll ( resp . Body )
require . NoError ( t , err )
var saveResp struct {
UID string ` json:"uid" `
}
err = json . Unmarshal ( b , & saveResp )
require . NoError ( t , err )
require . NotEmpty ( t , saveResp . UID )
getDashURL := fmt . Sprintf ( "http://admin:admin@%s/api/dashboards/uid/%s" , grafanaListedAddr , saveResp . UID )
// nolint:gosec
getResp , err := http . Get ( getDashURL )
require . NoError ( t , err )
require . Equal ( t , http . StatusOK , getResp . StatusCode )
t . Cleanup ( func ( ) {
err := getResp . Body . Close ( )
require . NoError ( t , err )
} )
// Parse response and check if schema version is preserved
dashBody , err := io . ReadAll ( getResp . Body )
require . NoError ( t , err )
var dashResp struct {
Dashboard * simplejson . Json ` json:"dashboard" `
}
err = json . Unmarshal ( dashBody , & dashResp )
require . NoError ( t , err )
actualSchemaVersion := dashResp . Dashboard . Get ( "schemaVersion" )
if schemaVersion != nil {
// Check if schemaVersion is preserved (not migrated to latest)
actualVersion := actualSchemaVersion . MustInt ( )
require . Equal ( t , * schemaVersion , actualVersion ,
"Dashboard schemaVersion should not be automatically changed when saved through /api/dashboards/db" )
} else {
actualVersion , err := actualSchemaVersion . Int ( )
s , _ := dashResp . Dashboard . EncodePretty ( )
require . Error ( t , err , fmt . Sprintf ( "Dashboard schemaVersion should not be automatically populated when saved through /api/dashboards/db, was %d. %s" , actualVersion , string ( s ) ) )
}
} )
}
}