diff --git a/pkg/cmd/grafana-cli/commands/install_command.go b/pkg/cmd/grafana-cli/commands/install_command.go index 3e90a403347..a17a3769277 100644 --- a/pkg/cmd/grafana-cli/commands/install_command.go +++ b/pkg/cmd/grafana-cli/commands/install_command.go @@ -19,7 +19,14 @@ import ( "github.com/grafana/grafana/pkg/plugins/storage" ) -func validateInput(c utils.CommandLine, pluginFolder string) error { +const installArgsSize = 2 + +func validateInput(c utils.CommandLine) error { + if c.Args().Len() > installArgsSize { + logger.Info(color.RedString("Please specify the correct format. For example ./grafana cli () plugins install ()\n\n")) + return errors.New("install only supports 2 arguments: plugin and version") + } + arg := c.Args().First() if arg == "" { return errors.New("please specify plugin to install") @@ -50,8 +57,7 @@ func logRestartNotice() { } func installCommand(c utils.CommandLine) error { - pluginFolder := c.PluginDirectory() - if err := validateInput(c, pluginFolder); err != nil { + if err := validateInput(c); err != nil { return err } diff --git a/pkg/cmd/grafana-cli/commands/install_command_test.go b/pkg/cmd/grafana-cli/commands/install_command_test.go new file mode 100644 index 00000000000..5ecac4cb835 --- /dev/null +++ b/pkg/cmd/grafana-cli/commands/install_command_test.go @@ -0,0 +1,26 @@ +package commands + +import ( + "testing" + + "github.com/grafana/grafana/pkg/cmd/grafana-cli/utils" + "github.com/stretchr/testify/require" +) + +func TestValidateInput(t *testing.T) { + t.Run("should print message for ignored args", func(t *testing.T) { + mockCmdLine := &utils.MockCommandLine{} + defer mockCmdLine.AssertExpectations(t) + + cmdArgs := []string{"foo", "bar", "--bar=foo"} + + mockArgs := &utils.MockArgs{} + defer mockArgs.AssertExpectations(t) + + mockArgs.On("Len").Return(len(cmdArgs)) + mockCmdLine.On("Args").Return(mockArgs).Times(1) + + err := validateInput(mockCmdLine) + require.EqualError(t, err, "install only supports 2 arguments: plugin and version") + }) +} diff --git a/pkg/cmd/grafana-cli/utils/args_mock.go b/pkg/cmd/grafana-cli/utils/args_mock.go new file mode 100644 index 00000000000..7124000dc1a --- /dev/null +++ b/pkg/cmd/grafana-cli/utils/args_mock.go @@ -0,0 +1,112 @@ +// Code generated by mockery v2.31.4. DO NOT EDIT. + +package utils + +import mock "github.com/stretchr/testify/mock" + +// MockArgs is an autogenerated mock type for the Args type +type MockArgs struct { + mock.Mock +} + +// First provides a mock function with given fields: +func (_m *MockArgs) First() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// Get provides a mock function with given fields: n +func (_m *MockArgs) Get(n int) string { + ret := _m.Called(n) + + var r0 string + if rf, ok := ret.Get(0).(func(int) string); ok { + r0 = rf(n) + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// Len provides a mock function with given fields: +func (_m *MockArgs) Len() int { + ret := _m.Called() + + var r0 int + if rf, ok := ret.Get(0).(func() int); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int) + } + + return r0 +} + +// Present provides a mock function with given fields: +func (_m *MockArgs) Present() bool { + ret := _m.Called() + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// Slice provides a mock function with given fields: +func (_m *MockArgs) Slice() []string { + ret := _m.Called() + + var r0 []string + if rf, ok := ret.Get(0).(func() []string); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + return r0 +} + +// Tail provides a mock function with given fields: +func (_m *MockArgs) Tail() []string { + ret := _m.Called() + + var r0 []string + if rf, ok := ret.Get(0).(func() []string); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + return r0 +} + +// NewMockArgs creates a new instance of MockArgs. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockArgs(t interface { + mock.TestingT + Cleanup(func()) +}) *MockArgs { + mock := &MockArgs{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/cmd/grafana-cli/utils/command_line_mock.go b/pkg/cmd/grafana-cli/utils/command_line_mock.go new file mode 100644 index 00000000000..33096a40d3e --- /dev/null +++ b/pkg/cmd/grafana-cli/utils/command_line_mock.go @@ -0,0 +1,210 @@ +// Code generated by mockery v2.31.4. DO NOT EDIT. + +package utils + +import ( + mock "github.com/stretchr/testify/mock" + cli "github.com/urfave/cli/v2" +) + +// MockCommandLine is an autogenerated mock type for the CommandLine type +type MockCommandLine struct { + mock.Mock +} + +// Application provides a mock function with given fields: +func (_m *MockCommandLine) Application() *cli.App { + ret := _m.Called() + + var r0 *cli.App + if rf, ok := ret.Get(0).(func() *cli.App); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*cli.App) + } + } + + return r0 +} + +// Args provides a mock function with given fields: +func (_m *MockCommandLine) Args() cli.Args { + ret := _m.Called() + + var r0 cli.Args + if rf, ok := ret.Get(0).(func() cli.Args); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(cli.Args) + } + } + + return r0 +} + +// Bool provides a mock function with given fields: name +func (_m *MockCommandLine) Bool(name string) bool { + ret := _m.Called(name) + + var r0 bool + if rf, ok := ret.Get(0).(func(string) bool); ok { + r0 = rf(name) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// FlagNames provides a mock function with given fields: +func (_m *MockCommandLine) FlagNames() []string { + ret := _m.Called() + + var r0 []string + if rf, ok := ret.Get(0).(func() []string); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + return r0 +} + +// Generic provides a mock function with given fields: name +func (_m *MockCommandLine) Generic(name string) interface{} { + ret := _m.Called(name) + + var r0 interface{} + if rf, ok := ret.Get(0).(func(string) interface{}); ok { + r0 = rf(name) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(interface{}) + } + } + + return r0 +} + +// Int provides a mock function with given fields: name +func (_m *MockCommandLine) Int(name string) int { + ret := _m.Called(name) + + var r0 int + if rf, ok := ret.Get(0).(func(string) int); ok { + r0 = rf(name) + } else { + r0 = ret.Get(0).(int) + } + + return r0 +} + +// PluginDirectory provides a mock function with given fields: +func (_m *MockCommandLine) PluginDirectory() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// PluginRepoURL provides a mock function with given fields: +func (_m *MockCommandLine) PluginRepoURL() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// PluginURL provides a mock function with given fields: +func (_m *MockCommandLine) PluginURL() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// ShowHelp provides a mock function with given fields: +func (_m *MockCommandLine) ShowHelp() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ShowVersion provides a mock function with given fields: +func (_m *MockCommandLine) ShowVersion() { + _m.Called() +} + +// String provides a mock function with given fields: name +func (_m *MockCommandLine) String(name string) string { + ret := _m.Called(name) + + var r0 string + if rf, ok := ret.Get(0).(func(string) string); ok { + r0 = rf(name) + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// StringSlice provides a mock function with given fields: name +func (_m *MockCommandLine) StringSlice(name string) []string { + ret := _m.Called(name) + + var r0 []string + if rf, ok := ret.Get(0).(func(string) []string); ok { + r0 = rf(name) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + return r0 +} + +// NewMockCommandLine creates a new instance of MockCommandLine. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockCommandLine(t interface { + mock.TestingT + Cleanup(func()) +}) *MockCommandLine { + mock := &MockCommandLine{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +}