mirror of
https://github.com/grafana/grafana.git
synced 2025-08-01 01:41:50 +08:00

* Feature (quota service): Use ReplDB for quota service Gets This adds the replDB to the quota service, as well as some more test helper functions to simplify updating tests. My intent is that the helper functions can be removed when this is fully rolled out (or not) and we're consistently using the ReplDB interface (or not!) * test updates
259 lines
7.7 KiB
Go
259 lines
7.7 KiB
Go
package plugins
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/grafana/grafana/pkg/api/dtos"
|
|
"github.com/grafana/grafana/pkg/infra/db"
|
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
|
"github.com/grafana/grafana/pkg/plugins"
|
|
"github.com/grafana/grafana/pkg/services/org/orgimpl"
|
|
"github.com/grafana/grafana/pkg/services/quota/quotaimpl"
|
|
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
"github.com/grafana/grafana/pkg/services/user/userimpl"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
"github.com/grafana/grafana/pkg/tests/testinfra"
|
|
"github.com/grafana/grafana/pkg/tests/testsuite"
|
|
)
|
|
|
|
const (
|
|
usernameAdmin = "admin"
|
|
usernameNonAdmin = "nonAdmin"
|
|
defaultPassword = "password"
|
|
)
|
|
|
|
var updateSnapshotFlag = false
|
|
|
|
func TestMain(m *testing.M) {
|
|
testsuite.Run(m)
|
|
}
|
|
|
|
func TestIntegrationPlugins(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
|
|
dir, cfgPath := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
|
PluginAdminEnabled: true,
|
|
})
|
|
|
|
origBuildVersion := setting.BuildVersion
|
|
setting.BuildVersion = "0.0.0-test"
|
|
t.Cleanup(func() {
|
|
setting.BuildVersion = origBuildVersion
|
|
})
|
|
|
|
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, cfgPath)
|
|
store := env.SQLStore
|
|
|
|
type testCase struct {
|
|
desc string
|
|
url string
|
|
expStatus int
|
|
expRespPath string
|
|
}
|
|
|
|
t.Run("Install", func(t *testing.T) {
|
|
createUser(t, store, env.Cfg, user.CreateUserCommand{Login: usernameNonAdmin, Password: defaultPassword, IsAdmin: false})
|
|
createUser(t, store, env.Cfg, user.CreateUserCommand{Login: usernameAdmin, Password: defaultPassword, IsAdmin: true})
|
|
|
|
t.Run("Request is forbidden if not from an admin", func(t *testing.T) {
|
|
status, body := makePostRequest(t, grafanaAPIURL(usernameNonAdmin, grafanaListedAddr, "plugins/grafana-plugin/install"))
|
|
assert.Equal(t, 403, status)
|
|
assert.Equal(t, "You'll need additional permissions to perform this action. Permissions needed: plugins:install", body["message"])
|
|
|
|
status, body = makePostRequest(t, grafanaAPIURL(usernameNonAdmin, grafanaListedAddr, "plugins/grafana-plugin/uninstall"))
|
|
assert.Equal(t, 403, status)
|
|
assert.Equal(t, "You'll need additional permissions to perform this action. Permissions needed: plugins:install", body["message"])
|
|
})
|
|
|
|
t.Run("Request is not forbidden if from an admin", func(t *testing.T) {
|
|
statusCode, body := makePostRequest(t, grafanaAPIURL(usernameAdmin, grafanaListedAddr, "plugins/test/install"))
|
|
|
|
assert.Equal(t, 404, statusCode)
|
|
assert.Equal(t, "Plugin not found", body["message"])
|
|
|
|
statusCode, body = makePostRequest(t, grafanaAPIURL(usernameAdmin, grafanaListedAddr, "plugins/test/uninstall"))
|
|
assert.Equal(t, 404, statusCode)
|
|
assert.Equal(t, "Plugin not installed", body["message"])
|
|
})
|
|
})
|
|
|
|
//
|
|
// NOTE:
|
|
// If this test is failing due to changes in plugins just rerun with updateSnapshotFlag = true at the top.
|
|
//
|
|
t.Run("List", func(t *testing.T) {
|
|
testCases := []testCase{
|
|
{
|
|
desc: "should return all loaded core and bundled plugins",
|
|
url: "http://%s/api/plugins",
|
|
expStatus: http.StatusOK,
|
|
expRespPath: "expectedListResp.json",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
url := fmt.Sprintf(tc.url, grafanaListedAddr)
|
|
// nolint:gosec
|
|
resp, err := http.Get(url)
|
|
t.Cleanup(func() {
|
|
require.NoError(t, resp.Body.Close())
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, tc.expStatus, resp.StatusCode)
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
var result dtos.PluginList
|
|
err = json.Unmarshal(b, &result)
|
|
require.NoError(t, err)
|
|
|
|
expResp := expectedResp(t, tc.expRespPath)
|
|
|
|
if diff := cmp.Diff(expResp, result, cmpopts.IgnoreFields(plugins.Info{}, "Version")); diff != "" {
|
|
if updateSnapshotFlag {
|
|
t.Log("updating snapshot results")
|
|
var prettyJSON bytes.Buffer
|
|
if err := json.Indent(&prettyJSON, b, "", " "); err != nil {
|
|
t.FailNow()
|
|
}
|
|
updateRespSnapshot(t, tc.expRespPath, prettyJSON.String())
|
|
}
|
|
t.Errorf("unexpected response (-want +got):\n%s", diff)
|
|
t.FailNow()
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestIntegrationPluginAssets(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
|
|
type testCase struct {
|
|
desc string
|
|
url string
|
|
env string
|
|
expStatus int
|
|
expCacheControl string
|
|
}
|
|
t.Run("Assets", func(t *testing.T) {
|
|
testCases := []testCase{
|
|
{
|
|
desc: "should return no-cache settings for Dev env",
|
|
env: setting.Dev,
|
|
url: "http://%s/public/plugins/grafana-testdata-datasource/img/testdata.svg",
|
|
expStatus: http.StatusOK,
|
|
expCacheControl: "max-age=0, must-revalidate, no-cache",
|
|
},
|
|
{
|
|
desc: "should return cache settings for Prod env",
|
|
env: setting.Prod,
|
|
url: "http://%s/public/plugins/grafana-testdata-datasource/img/testdata.svg",
|
|
expStatus: http.StatusOK,
|
|
expCacheControl: "public, max-age=3600",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
dir, cfgPath := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
|
AppModeProduction: tc.env == setting.Prod,
|
|
})
|
|
|
|
grafanaListedAddr, _ := testinfra.StartGrafana(t, dir, cfgPath)
|
|
url := fmt.Sprintf(tc.url, grafanaListedAddr)
|
|
// nolint:gosec
|
|
resp, err := http.Get(url)
|
|
t.Cleanup(func() {
|
|
require.NoError(t, resp.Body.Close())
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, tc.expStatus, resp.StatusCode)
|
|
require.Equal(t, tc.expCacheControl, resp.Header.Get("Cache-Control"))
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
func createUser(t *testing.T, store db.DB, cfg *setting.Cfg, cmd user.CreateUserCommand) {
|
|
t.Helper()
|
|
|
|
cfg.AutoAssignOrg = true
|
|
cfg.AutoAssignOrgId = 1
|
|
|
|
quotaService := quotaimpl.ProvideService(db.FakeReplDBFromDB(store), cfg)
|
|
orgService, err := orgimpl.ProvideService(store, cfg, quotaService)
|
|
require.NoError(t, err)
|
|
usrSvc, err := userimpl.ProvideService(
|
|
store, orgService, cfg, nil, nil, tracing.InitializeTracerForTest(),
|
|
quotaService, supportbundlestest.NewFakeBundleService(),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
_, err = usrSvc.Create(context.Background(), &cmd)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func makePostRequest(t *testing.T, URL string) (int, map[string]interface{}) {
|
|
t.Helper()
|
|
|
|
// nolint:gosec
|
|
resp, err := http.Post(URL, "application/json", bytes.NewBufferString(""))
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
require.NoError(t, resp.Body.Close())
|
|
})
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
var body = make(map[string]interface{})
|
|
err = json.Unmarshal(b, &body)
|
|
require.NoError(t, err)
|
|
|
|
return resp.StatusCode, body
|
|
}
|
|
|
|
func grafanaAPIURL(username string, grafanaListedAddr string, path string) string {
|
|
return fmt.Sprintf("http://%s:%s@%s/api/%s", username, defaultPassword, grafanaListedAddr, path)
|
|
}
|
|
|
|
func expectedResp(t *testing.T, filename string) dtos.PluginList {
|
|
//nolint:GOSEC
|
|
contents, err := os.ReadFile(filepath.Join("data", filename))
|
|
if err != nil {
|
|
t.Errorf("failed to load %s: %v", filename, err)
|
|
}
|
|
|
|
var result dtos.PluginList
|
|
err = json.Unmarshal(contents, &result)
|
|
if err != nil {
|
|
t.Errorf("failed to unmarshal %s: %v", filename, err)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func updateRespSnapshot(t *testing.T, filename string, body string) {
|
|
err := os.WriteFile(filepath.Join("data", filename), []byte(body), 0600)
|
|
if err != nil {
|
|
t.Errorf("error writing snapshot %s: %v", filename, err)
|
|
}
|
|
}
|