mirror of https://github.com/grafana/grafana.git
				
				
				
			
		
			
				
	
	
		
			254 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			254 lines
		
	
	
		
			7.6 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/plugins"
 | |
| 	"github.com/grafana/grafana/pkg/services/org/orgimpl"
 | |
| 	"github.com/grafana/grafana/pkg/services/quota/quotaimpl"
 | |
| 	"github.com/grafana/grafana/pkg/services/sqlstore"
 | |
| 	"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, store := testinfra.StartGrafana(t, dir, cfgPath)
 | |
| 
 | |
| 	type testCase struct {
 | |
| 		desc        string
 | |
| 		url         string
 | |
| 		expStatus   int
 | |
| 		expRespPath string
 | |
| 	}
 | |
| 
 | |
| 	t.Run("Install", func(t *testing.T) {
 | |
| 		createUser(t, store, user.CreateUserCommand{Login: usernameNonAdmin, Password: defaultPassword, IsAdmin: false})
 | |
| 		createUser(t, store, 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 *sqlstore.SQLStore, cmd user.CreateUserCommand) {
 | |
| 	t.Helper()
 | |
| 
 | |
| 	store.Cfg.AutoAssignOrg = true
 | |
| 	store.Cfg.AutoAssignOrgId = 1
 | |
| 
 | |
| 	quotaService := quotaimpl.ProvideService(store, store.Cfg)
 | |
| 	orgService, err := orgimpl.ProvideService(store, store.Cfg, quotaService)
 | |
| 	require.NoError(t, err)
 | |
| 	usrSvc, err := userimpl.ProvideService(store, orgService, store.Cfg, nil, nil, 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)
 | |
| 	}
 | |
| }
 |