github.com/cloudfoundry/cli@v7.1.0+incompatible/command/common/install_plugin_from_repo_command_test.go (about)

     1  package common_test
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"math/rand"
     9  	"os"
    10  	"runtime"
    11  
    12  	"code.cloudfoundry.org/cli/actor/actionerror"
    13  	"code.cloudfoundry.org/cli/actor/pluginaction"
    14  	"code.cloudfoundry.org/cli/api/plugin/pluginerror"
    15  	"code.cloudfoundry.org/cli/api/plugin/pluginfakes"
    16  	"code.cloudfoundry.org/cli/command/commandfakes"
    17  	. "code.cloudfoundry.org/cli/command/common"
    18  	"code.cloudfoundry.org/cli/command/common/commonfakes"
    19  	"code.cloudfoundry.org/cli/command/flag"
    20  	"code.cloudfoundry.org/cli/command/translatableerror"
    21  	"code.cloudfoundry.org/cli/integration/helpers"
    22  	"code.cloudfoundry.org/cli/util/configv3"
    23  	"code.cloudfoundry.org/cli/util/ui"
    24  	. "github.com/onsi/ginkgo"
    25  	. "github.com/onsi/ginkgo/extensions/table"
    26  	. "github.com/onsi/gomega"
    27  	. "github.com/onsi/gomega/gbytes"
    28  )
    29  
    30  var _ = Describe("install-plugin command", func() {
    31  	var (
    32  		cmd             InstallPluginCommand
    33  		testUI          *ui.UI
    34  		input           *Buffer
    35  		fakeConfig      *commandfakes.FakeConfig
    36  		fakeActor       *commonfakes.FakeInstallPluginActor
    37  		fakeProgressBar *pluginfakes.FakeProxyReader
    38  		executeErr      error
    39  		expectedErr     error
    40  		pluginHome      string
    41  		binaryName      string
    42  	)
    43  
    44  	BeforeEach(func() {
    45  		input = NewBuffer()
    46  		testUI = ui.NewTestUI(input, NewBuffer(), NewBuffer())
    47  		fakeConfig = new(commandfakes.FakeConfig)
    48  		fakeActor = new(commonfakes.FakeInstallPluginActor)
    49  		fakeProgressBar = new(pluginfakes.FakeProxyReader)
    50  
    51  		cmd = InstallPluginCommand{
    52  			UI:          testUI,
    53  			Config:      fakeConfig,
    54  			Actor:       fakeActor,
    55  			ProgressBar: fakeProgressBar,
    56  		}
    57  
    58  		var err error
    59  		pluginHome, err = ioutil.TempDir("", "some-pluginhome")
    60  		Expect(err).ToNot(HaveOccurred())
    61  		fakeConfig.PluginHomeReturns(pluginHome)
    62  		binaryName = helpers.PrefixedRandomName("bin")
    63  		fakeConfig.BinaryNameReturns(binaryName)
    64  	})
    65  
    66  	AfterEach(func() {
    67  		os.RemoveAll(pluginHome)
    68  	})
    69  
    70  	JustBeforeEach(func() {
    71  		executeErr = cmd.Execute(nil)
    72  	})
    73  
    74  	Describe("installing from a specific repo", func() {
    75  		var (
    76  			pluginName string
    77  			pluginURL  string
    78  			repoName   string
    79  			repoURL    string
    80  		)
    81  
    82  		BeforeEach(func() {
    83  			pluginName = helpers.PrefixedRandomName("plugin")
    84  			pluginURL = helpers.PrefixedRandomName("http://")
    85  			repoName = helpers.PrefixedRandomName("repo")
    86  			repoURL = helpers.PrefixedRandomName("http://")
    87  			cmd.OptionalArgs.PluginNameOrLocation = flag.Path(pluginName)
    88  			cmd.RegisteredRepository = repoName
    89  		})
    90  
    91  		When("the repo is not registered", func() {
    92  			BeforeEach(func() {
    93  				fakeActor.GetPluginRepositoryReturns(configv3.PluginRepository{}, actionerror.RepositoryNotRegisteredError{Name: repoName})
    94  			})
    95  
    96  			It("returns a RepositoryNotRegisteredError", func() {
    97  				Expect(executeErr).To(MatchError(actionerror.RepositoryNotRegisteredError{Name: repoName}))
    98  
    99  				Expect(fakeActor.GetPluginRepositoryCallCount()).To(Equal(1))
   100  				repositoryNameArg := fakeActor.GetPluginRepositoryArgsForCall(0)
   101  				Expect(repositoryNameArg).To(Equal(repoName))
   102  			})
   103  		})
   104  
   105  		When("the repository is registered", func() {
   106  			var platform string
   107  
   108  			BeforeEach(func() {
   109  				platform = helpers.PrefixedRandomName("platform")
   110  				fakeActor.GetPlatformStringReturns(platform)
   111  				fakeActor.GetPluginRepositoryReturns(configv3.PluginRepository{Name: repoName, URL: repoURL}, nil)
   112  			})
   113  
   114  			When("getting repository information returns a json syntax error", func() {
   115  				var jsonErr error
   116  				BeforeEach(func() {
   117  					jsonErr = &json.SyntaxError{}
   118  					fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(pluginaction.PluginInfo{}, nil, jsonErr)
   119  				})
   120  
   121  				It("returns a JSONSyntaxError", func() {
   122  					Expect(executeErr).To(MatchError(jsonErr))
   123  				})
   124  			})
   125  
   126  			When("getting the repository information errors", func() {
   127  				Context("with a generic error", func() {
   128  					BeforeEach(func() {
   129  						expectedErr = errors.New("some-client-error")
   130  						fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(pluginaction.PluginInfo{}, nil, actionerror.FetchingPluginInfoFromRepositoryError{
   131  							RepositoryName: "some-repo",
   132  							Err:            expectedErr,
   133  						})
   134  					})
   135  
   136  					It("returns the wrapped client(request/http status) error", func() {
   137  						Expect(executeErr).To(MatchError(expectedErr))
   138  					})
   139  				})
   140  
   141  				Context("with a RawHTTPStatusError error", func() {
   142  					var returnedErr pluginerror.RawHTTPStatusError
   143  
   144  					BeforeEach(func() {
   145  						returnedErr = pluginerror.RawHTTPStatusError{Status: "some-status"}
   146  						fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(pluginaction.PluginInfo{}, nil, actionerror.FetchingPluginInfoFromRepositoryError{
   147  							RepositoryName: "some-repo",
   148  							Err:            returnedErr,
   149  						})
   150  					})
   151  
   152  					It("returns the wrapped client(request/http status) error", func() {
   153  						Expect(executeErr).To(MatchError(returnedErr))
   154  					})
   155  				})
   156  			})
   157  
   158  			When("the plugin can't be found in the repository", func() {
   159  				BeforeEach(func() {
   160  					fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(pluginaction.PluginInfo{}, nil, actionerror.PluginNotFoundInAnyRepositoryError{PluginName: pluginName})
   161  				})
   162  
   163  				It("returns the PluginNotFoundInRepositoryError", func() {
   164  					Expect(executeErr).To(MatchError(translatableerror.PluginNotFoundInRepositoryError{BinaryName: binaryName, PluginName: pluginName, RepositoryName: repoName}))
   165  
   166  					Expect(fakeActor.GetPlatformStringCallCount()).To(Equal(1))
   167  					platformGOOS, platformGOARCH := fakeActor.GetPlatformStringArgsForCall(0)
   168  					Expect(platformGOOS).To(Equal(runtime.GOOS))
   169  					Expect(platformGOARCH).To(Equal(runtime.GOARCH))
   170  
   171  					Expect(fakeActor.GetPluginInfoFromRepositoriesForPlatformCallCount()).To(Equal(1))
   172  					pluginNameArg, pluginRepositoriesArg, pluginPlatform := fakeActor.GetPluginInfoFromRepositoriesForPlatformArgsForCall(0)
   173  					Expect(pluginNameArg).To(Equal(pluginName))
   174  					Expect(pluginRepositoriesArg).To(Equal([]configv3.PluginRepository{{Name: repoName, URL: repoURL}}))
   175  					Expect(pluginPlatform).To(Equal(platform))
   176  				})
   177  			})
   178  
   179  			When("a compatible binary can't be found in the repository", func() {
   180  				BeforeEach(func() {
   181  					fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(pluginaction.PluginInfo{}, nil, actionerror.NoCompatibleBinaryError{})
   182  				})
   183  
   184  				It("returns the NoCompatibleBinaryError", func() {
   185  					Expect(executeErr).To(MatchError(actionerror.NoCompatibleBinaryError{}))
   186  				})
   187  			})
   188  
   189  			When("the plugin is found", func() {
   190  				var (
   191  					checksum                string
   192  					downloadedVersionString string
   193  				)
   194  
   195  				BeforeEach(func() {
   196  					checksum = helpers.PrefixedRandomName("checksum")
   197  					downloadedVersionString = helpers.PrefixedRandomName("version")
   198  
   199  					fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(pluginaction.PluginInfo{Name: pluginName, Version: downloadedVersionString, URL: pluginURL, Checksum: checksum}, []string{repoName}, nil)
   200  				})
   201  
   202  				When("the -f argument is given", func() {
   203  					BeforeEach(func() {
   204  						cmd.Force = true
   205  					})
   206  
   207  					When("the plugin is already installed", func() {
   208  						BeforeEach(func() {
   209  							plugin := configv3.Plugin{
   210  								Name:    pluginName,
   211  								Version: configv3.PluginVersion{Major: 1, Minor: 2, Build: 2},
   212  							}
   213  							fakeConfig.GetPluginReturns(plugin, true)
   214  							fakeConfig.GetPluginCaseInsensitiveReturns(plugin, true)
   215  						})
   216  
   217  						When("getting the binary errors", func() {
   218  							BeforeEach(func() {
   219  								expectedErr = errors.New("some-error")
   220  								fakeActor.DownloadExecutableBinaryFromURLReturns("", expectedErr)
   221  							})
   222  
   223  							It("returns the error", func() {
   224  								Expect(executeErr).To(MatchError(expectedErr))
   225  
   226  								Expect(testUI.Out).To(Say(`Searching %s for plugin %s\.\.\.`, repoName, pluginName))
   227  								Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repoName))
   228  								Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.2 is already installed\.`, pluginName))
   229  								Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   230  								Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   231  								Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repoName))
   232  
   233  								Expect(testUI.Out).ToNot(Say("downloaded"))
   234  								Expect(fakeActor.GetAndValidatePluginCallCount()).To(Equal(0))
   235  
   236  								Expect(fakeConfig.GetPluginCallCount()).To(Equal(1))
   237  								Expect(fakeConfig.GetPluginArgsForCall(0)).To(Equal(pluginName))
   238  
   239  								Expect(fakeActor.GetPlatformStringCallCount()).To(Equal(1))
   240  								platformGOOS, platformGOARCH := fakeActor.GetPlatformStringArgsForCall(0)
   241  								Expect(platformGOOS).To(Equal(runtime.GOOS))
   242  								Expect(platformGOARCH).To(Equal(runtime.GOARCH))
   243  
   244  								Expect(fakeActor.GetPluginInfoFromRepositoriesForPlatformCallCount()).To(Equal(1))
   245  								pluginNameArg, pluginRepositoriesArg, pluginPlatform := fakeActor.GetPluginInfoFromRepositoriesForPlatformArgsForCall(0)
   246  								Expect(pluginNameArg).To(Equal(pluginName))
   247  								Expect(pluginRepositoriesArg).To(Equal([]configv3.PluginRepository{{Name: repoName, URL: repoURL}}))
   248  								Expect(pluginPlatform).To(Equal(platform))
   249  
   250  								Expect(fakeActor.DownloadExecutableBinaryFromURLCallCount()).To(Equal(1))
   251  								urlArg, dirArg, proxyReader := fakeActor.DownloadExecutableBinaryFromURLArgsForCall(0)
   252  								Expect(urlArg).To(Equal(pluginURL))
   253  								Expect(dirArg).To(ContainSubstring("temp"))
   254  								Expect(proxyReader).To(Equal(fakeProgressBar))
   255  							})
   256  						})
   257  
   258  						When("getting the binary succeeds", func() {
   259  							var execPath string
   260  
   261  							BeforeEach(func() {
   262  								execPath = helpers.PrefixedRandomName("some-path")
   263  								fakeActor.DownloadExecutableBinaryFromURLReturns(execPath, nil)
   264  							})
   265  
   266  							When("the checksum fails", func() {
   267  								BeforeEach(func() {
   268  									fakeActor.ValidateFileChecksumReturns(false)
   269  								})
   270  
   271  								It("returns the checksum error", func() {
   272  									Expect(executeErr).To(MatchError(translatableerror.InvalidChecksumError{}))
   273  
   274  									Expect(testUI.Out).To(Say(`Searching %s for plugin %s\.\.\.`, repoName, pluginName))
   275  									Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repoName))
   276  									Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.2 is already installed\.`, pluginName))
   277  									Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   278  									Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   279  									Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repoName))
   280  									Expect(testUI.Out).ToNot(Say("Installing plugin"))
   281  
   282  									Expect(fakeActor.ValidateFileChecksumCallCount()).To(Equal(1))
   283  									pathArg, checksumArg := fakeActor.ValidateFileChecksumArgsForCall(0)
   284  									Expect(pathArg).To(Equal(execPath))
   285  									Expect(checksumArg).To(Equal(checksum))
   286  								})
   287  							})
   288  
   289  							When("the checksum succeeds", func() {
   290  								BeforeEach(func() {
   291  									fakeActor.ValidateFileChecksumReturns(true)
   292  								})
   293  
   294  								When("creating an executable copy errors", func() {
   295  									BeforeEach(func() {
   296  										fakeActor.CreateExecutableCopyReturns("", errors.New("some-error"))
   297  									})
   298  
   299  									It("returns the error", func() {
   300  										Expect(executeErr).To(MatchError(errors.New("some-error")))
   301  										Expect(testUI.Out).ToNot(Say("Installing plugin"))
   302  
   303  										Expect(fakeActor.CreateExecutableCopyCallCount()).To(Equal(1))
   304  										pathArg, tempDirArg := fakeActor.CreateExecutableCopyArgsForCall(0)
   305  										Expect(pathArg).To(Equal(execPath))
   306  										Expect(tempDirArg).To(ContainSubstring("temp"))
   307  									})
   308  								})
   309  
   310  								When("creating an exectuable copy succeeds", func() {
   311  									BeforeEach(func() {
   312  										fakeActor.CreateExecutableCopyReturns("copy-path", nil)
   313  									})
   314  
   315  									When("validating the new plugin errors", func() {
   316  										BeforeEach(func() {
   317  											fakeActor.GetAndValidatePluginReturns(configv3.Plugin{}, actionerror.PluginInvalidError{})
   318  										})
   319  
   320  										It("returns the error", func() {
   321  											Expect(executeErr).To(MatchError(actionerror.PluginInvalidError{}))
   322  											Expect(testUI.Out).ToNot(Say("Installing plugin"))
   323  
   324  											Expect(fakeActor.GetAndValidatePluginCallCount()).To(Equal(1))
   325  											_, commandsArg, tempDirArg := fakeActor.GetAndValidatePluginArgsForCall(0)
   326  											Expect(commandsArg).To(Equal(Commands))
   327  											Expect(tempDirArg).To(Equal("copy-path"))
   328  										})
   329  									})
   330  
   331  									When("validating the new plugin succeeds", func() {
   332  										var (
   333  											pluginVersion      configv3.PluginVersion
   334  											pluginVersionRegex string
   335  										)
   336  
   337  										BeforeEach(func() {
   338  											major := rand.Int()
   339  											minor := rand.Int()
   340  											build := rand.Int()
   341  											pluginVersion = configv3.PluginVersion{Major: major, Minor: minor, Build: build}
   342  											pluginVersionRegex = fmt.Sprintf(`%d\.%d\.%d`, major, minor, build)
   343  
   344  											fakeActor.GetAndValidatePluginReturns(configv3.Plugin{
   345  												Name:    pluginName,
   346  												Version: pluginVersion,
   347  											}, nil)
   348  										})
   349  
   350  										When("uninstalling the existing errors", func() {
   351  											BeforeEach(func() {
   352  												expectedErr = errors.New("uninstall plugin error")
   353  												fakeActor.UninstallPluginReturns(expectedErr)
   354  											})
   355  
   356  											It("returns the error", func() {
   357  												Expect(executeErr).To(MatchError(expectedErr))
   358  
   359  												Expect(testUI.Out).To(Say(`Uninstalling existing plugin\.\.\.`))
   360  												Expect(testUI.Out).ToNot(Say(`Plugin %s successfully uninstalled\.`, pluginName))
   361  
   362  												Expect(fakeActor.UninstallPluginCallCount()).To(Equal(1))
   363  												_, pluginNameArg := fakeActor.UninstallPluginArgsForCall(0)
   364  												Expect(pluginNameArg).To(Equal(pluginName))
   365  											})
   366  										})
   367  
   368  										When("uninstalling the existing plugin succeeds", func() {
   369  											When("installing the new plugin errors", func() {
   370  												BeforeEach(func() {
   371  													expectedErr = errors.New("install plugin error")
   372  													fakeActor.InstallPluginFromPathReturns(expectedErr)
   373  												})
   374  
   375  												It("returns the error", func() {
   376  													Expect(executeErr).To(MatchError(expectedErr))
   377  
   378  													Expect(testUI.Out).To(Say(`Plugin %s successfully uninstalled\.`, pluginName))
   379  													Expect(testUI.Out).To(Say(`Installing plugin %s\.\.\.`, pluginName))
   380  													Expect(testUI.Out).ToNot(Say("successfully installed"))
   381  
   382  													Expect(fakeActor.InstallPluginFromPathCallCount()).To(Equal(1))
   383  													pathArg, pluginArg := fakeActor.InstallPluginFromPathArgsForCall(0)
   384  													Expect(pathArg).To(Equal("copy-path"))
   385  													Expect(pluginArg).To(Equal(configv3.Plugin{
   386  														Name:    pluginName,
   387  														Version: pluginVersion,
   388  													}))
   389  												})
   390  											})
   391  
   392  											When("installing the new plugin succeeds", func() {
   393  												It("uninstalls the existing plugin and installs the new one", func() {
   394  													Expect(executeErr).ToNot(HaveOccurred())
   395  
   396  													Expect(testUI.Out).To(Say(`Searching %s for plugin %s\.\.\.`, repoName, pluginName))
   397  													Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repoName))
   398  													Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.2 is already installed\.`, pluginName))
   399  													Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   400  													Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   401  													Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repoName))
   402  													Expect(testUI.Out).To(Say(`Uninstalling existing plugin\.\.\.`))
   403  													Expect(testUI.Out).To(Say("OK"))
   404  													Expect(testUI.Out).To(Say(`Plugin %s successfully uninstalled\.`, pluginName))
   405  													Expect(testUI.Out).To(Say(`Installing plugin %s\.\.\.`, pluginName))
   406  													Expect(testUI.Out).To(Say("OK"))
   407  													Expect(testUI.Out).To(Say(`%s %s successfully installed\.`, pluginName, pluginVersionRegex))
   408  												})
   409  											})
   410  										})
   411  									})
   412  								})
   413  							})
   414  						})
   415  					})
   416  
   417  					When("the plugin is NOT already installed", func() {
   418  						When("getting the binary errors", func() {
   419  							BeforeEach(func() {
   420  								expectedErr = errors.New("some-error")
   421  								fakeActor.DownloadExecutableBinaryFromURLReturns("", expectedErr)
   422  							})
   423  
   424  							It("returns the error", func() {
   425  								Expect(executeErr).To(MatchError(expectedErr))
   426  
   427  								Expect(testUI.Out).To(Say(`Searching %s for plugin %s\.\.\.`, repoName, pluginName))
   428  								Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repoName))
   429  								Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   430  								Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   431  								Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repoName))
   432  
   433  								Expect(testUI.Out).ToNot(Say("downloaded"))
   434  								Expect(fakeActor.GetAndValidatePluginCallCount()).To(Equal(0))
   435  							})
   436  						})
   437  
   438  						When("getting the binary succeeds", func() {
   439  							BeforeEach(func() {
   440  								fakeActor.DownloadExecutableBinaryFromURLReturns("some-path", nil)
   441  							})
   442  
   443  							When("the checksum fails", func() {
   444  								BeforeEach(func() {
   445  									fakeActor.ValidateFileChecksumReturns(false)
   446  								})
   447  
   448  								It("returns the checksum error", func() {
   449  									Expect(executeErr).To(MatchError(translatableerror.InvalidChecksumError{}))
   450  
   451  									Expect(testUI.Out).To(Say(`Searching %s for plugin %s\.\.\.`, repoName, pluginName))
   452  									Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repoName))
   453  									Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   454  									Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   455  									Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repoName))
   456  									Expect(testUI.Out).ToNot(Say("Installing plugin"))
   457  								})
   458  							})
   459  
   460  							When("the checksum succeeds", func() {
   461  								BeforeEach(func() {
   462  									fakeActor.ValidateFileChecksumReturns(true)
   463  								})
   464  
   465  								When("creating an executable copy errors", func() {
   466  									BeforeEach(func() {
   467  										fakeActor.CreateExecutableCopyReturns("", errors.New("some-error"))
   468  									})
   469  
   470  									It("returns the error", func() {
   471  										Expect(executeErr).To(MatchError(errors.New("some-error")))
   472  										Expect(testUI.Out).ToNot(Say("Installing plugin"))
   473  									})
   474  								})
   475  
   476  								When("creating an executable copy succeeds", func() {
   477  									BeforeEach(func() {
   478  										fakeActor.CreateExecutableCopyReturns("copy-path", nil)
   479  									})
   480  
   481  									When("validating the plugin errors", func() {
   482  										BeforeEach(func() {
   483  											fakeActor.GetAndValidatePluginReturns(configv3.Plugin{}, actionerror.PluginInvalidError{})
   484  										})
   485  
   486  										It("returns the error", func() {
   487  											Expect(executeErr).To(MatchError(actionerror.PluginInvalidError{}))
   488  											Expect(testUI.Out).ToNot(Say("Installing plugin"))
   489  										})
   490  									})
   491  
   492  									When("validating the plugin succeeds", func() {
   493  										BeforeEach(func() {
   494  											fakeActor.GetAndValidatePluginReturns(configv3.Plugin{
   495  												Name:    pluginName,
   496  												Version: configv3.PluginVersion{Major: 1, Minor: 2, Build: 3},
   497  											}, nil)
   498  										})
   499  
   500  										When("installing the plugin errors", func() {
   501  											BeforeEach(func() {
   502  												expectedErr = errors.New("install plugin error")
   503  												fakeActor.InstallPluginFromPathReturns(expectedErr)
   504  											})
   505  
   506  											It("returns the error", func() {
   507  												Expect(executeErr).To(MatchError(expectedErr))
   508  
   509  												Expect(testUI.Out).To(Say(`Installing plugin %s\.\.\.`, pluginName))
   510  												Expect(testUI.Out).ToNot(Say("successfully installed"))
   511  											})
   512  										})
   513  
   514  										When("installing the plugin succeeds", func() {
   515  											It("uninstalls the existing plugin and installs the new one", func() {
   516  												Expect(executeErr).ToNot(HaveOccurred())
   517  
   518  												Expect(testUI.Out).To(Say(`Searching %s for plugin %s\.\.\.`, repoName, pluginName))
   519  												Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repoName))
   520  												Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   521  												Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   522  												Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repoName))
   523  												Expect(testUI.Out).To(Say(`Installing plugin %s\.\.\.`, pluginName))
   524  												Expect(testUI.Out).To(Say("OK"))
   525  												Expect(testUI.Out).To(Say(`%s 1\.2\.3 successfully installed`, pluginName))
   526  											})
   527  										})
   528  									})
   529  								})
   530  							})
   531  						})
   532  					})
   533  				})
   534  
   535  				When("the -f argument is not given (user is prompted for confirmation)", func() {
   536  					BeforeEach(func() {
   537  						fakeActor.ValidateFileChecksumReturns(true)
   538  					})
   539  
   540  					When("the plugin is already installed", func() {
   541  						BeforeEach(func() {
   542  							plugin := configv3.Plugin{
   543  								Name:    pluginName,
   544  								Version: configv3.PluginVersion{Major: 1, Minor: 2, Build: 2},
   545  							}
   546  							fakeConfig.GetPluginReturns(plugin, true)
   547  							fakeConfig.GetPluginCaseInsensitiveReturns(plugin, true)
   548  						})
   549  
   550  						When("the user chooses no", func() {
   551  							BeforeEach(func() {
   552  								_, err := input.Write([]byte("n\n"))
   553  								Expect(err).ToNot(HaveOccurred())
   554  							})
   555  
   556  							It("cancels plugin installation", func() {
   557  								Expect(executeErr).ToNot(HaveOccurred())
   558  
   559  								Expect(testUI.Out).To(Say(`Searching %s for plugin %s\.\.\.`, repoName, pluginName))
   560  								Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repoName))
   561  								Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.2 is already installed\.`, pluginName))
   562  								Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   563  								Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   564  								Expect(testUI.Out).To(Say(`Do you want to uninstall the existing plugin and install %s %s\? \[yN\]`, pluginName, downloadedVersionString))
   565  								Expect(testUI.Out).To(Say(`Plugin installation cancelled\.`))
   566  							})
   567  						})
   568  
   569  						When("the user chooses the default", func() {
   570  							BeforeEach(func() {
   571  								_, err := input.Write([]byte("\n"))
   572  								Expect(err).ToNot(HaveOccurred())
   573  							})
   574  
   575  							It("cancels plugin installation", func() {
   576  								Expect(executeErr).ToNot(HaveOccurred())
   577  
   578  								Expect(testUI.Out).To(Say(`Do you want to uninstall the existing plugin and install %s %s\? \[yN\]`, pluginName, downloadedVersionString))
   579  								Expect(testUI.Out).To(Say(`Plugin installation cancelled\.`))
   580  							})
   581  						})
   582  
   583  						When("the user input is invalid", func() {
   584  							BeforeEach(func() {
   585  								_, err := input.Write([]byte("e\n"))
   586  								Expect(err).ToNot(HaveOccurred())
   587  							})
   588  
   589  							It("returns an error", func() {
   590  								Expect(executeErr).To(HaveOccurred())
   591  
   592  								Expect(testUI.Out).To(Say(`Do you want to uninstall the existing plugin and install %s %s\? \[yN\]`, pluginName, downloadedVersionString))
   593  								Expect(testUI.Out).ToNot(Say(`Plugin installation cancelled\.`))
   594  								Expect(testUI.Out).ToNot(Say("Installing plugin"))
   595  							})
   596  						})
   597  
   598  						When("the user chooses yes", func() {
   599  							BeforeEach(func() {
   600  								_, err := input.Write([]byte("y\n"))
   601  								Expect(err).ToNot(HaveOccurred())
   602  								fakeActor.DownloadExecutableBinaryFromURLReturns("some-path", nil)
   603  								fakeActor.CreateExecutableCopyReturns("copy-path", nil)
   604  								fakeActor.GetAndValidatePluginReturns(configv3.Plugin{
   605  									Name:    pluginName,
   606  									Version: configv3.PluginVersion{Major: 1, Minor: 2, Build: 3},
   607  								}, nil)
   608  							})
   609  
   610  							It("installs the plugin", func() {
   611  								Expect(testUI.Out).To(Say(`Searching %s for plugin %s\.\.\.`, repoName, pluginName))
   612  								Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repoName))
   613  								Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.2 is already installed\.`, pluginName))
   614  								Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   615  								Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   616  								Expect(testUI.Out).To(Say(`Do you want to uninstall the existing plugin and install %s %s\? \[yN\]`, pluginName, downloadedVersionString))
   617  								Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repoName))
   618  								Expect(testUI.Out).To(Say(`Uninstalling existing plugin\.\.\.`))
   619  								Expect(testUI.Out).To(Say("OK"))
   620  								Expect(testUI.Out).To(Say(`Plugin %s successfully uninstalled\.`, pluginName))
   621  								Expect(testUI.Out).To(Say(`Installing plugin %s\.\.\.`, pluginName))
   622  								Expect(testUI.Out).To(Say("OK"))
   623  								Expect(testUI.Out).To(Say(`%s 1\.2\.3 successfully installed`, pluginName))
   624  							})
   625  						})
   626  					})
   627  
   628  					When("the plugin is NOT already installed", func() {
   629  						When("the user chooses no", func() {
   630  							BeforeEach(func() {
   631  								_, err := input.Write([]byte("n\n"))
   632  								Expect(err).ToNot(HaveOccurred())
   633  							})
   634  
   635  							It("cancels plugin installation", func() {
   636  								Expect(executeErr).ToNot(HaveOccurred())
   637  
   638  								Expect(testUI.Out).To(Say(`Searching %s for plugin %s\.\.\.`, repoName, pluginName))
   639  								Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repoName))
   640  								Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   641  								Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   642  								Expect(testUI.Out).To(Say(`Do you want to install the plugin %s\? \[yN\]`, pluginName))
   643  								Expect(testUI.Out).To(Say(`Plugin installation cancelled\.`))
   644  							})
   645  						})
   646  
   647  						When("the user chooses the default", func() {
   648  							BeforeEach(func() {
   649  								_, err := input.Write([]byte("\n"))
   650  								Expect(err).ToNot(HaveOccurred())
   651  							})
   652  
   653  							It("cancels plugin installation", func() {
   654  								Expect(executeErr).ToNot(HaveOccurred())
   655  
   656  								Expect(testUI.Out).To(Say(`Do you want to install the plugin %s\? \[yN\]`, pluginName))
   657  								Expect(testUI.Out).To(Say(`Plugin installation cancelled\.`))
   658  							})
   659  						})
   660  
   661  						When("the user input is invalid", func() {
   662  							BeforeEach(func() {
   663  								_, err := input.Write([]byte("e\n"))
   664  								Expect(err).ToNot(HaveOccurred())
   665  							})
   666  
   667  							It("returns an error", func() {
   668  								Expect(executeErr).To(HaveOccurred())
   669  
   670  								Expect(testUI.Out).To(Say(`Do you want to install the plugin %s\? \[yN\]`, pluginName))
   671  								Expect(testUI.Out).ToNot(Say(`Plugin installation cancelled\.`))
   672  								Expect(testUI.Out).ToNot(Say("Installing plugin"))
   673  							})
   674  						})
   675  
   676  						When("the user chooses yes", func() {
   677  							var execPath string
   678  
   679  							BeforeEach(func() {
   680  								_, err := input.Write([]byte("y\n"))
   681  								Expect(err).ToNot(HaveOccurred())
   682  								execPath = helpers.PrefixedRandomName("some-path")
   683  								fakeActor.DownloadExecutableBinaryFromURLReturns(execPath, nil)
   684  								fakeActor.CreateExecutableCopyReturns("copy-path", nil)
   685  								fakeActor.GetAndValidatePluginReturns(configv3.Plugin{
   686  									Name:    pluginName,
   687  									Version: configv3.PluginVersion{Major: 1, Minor: 2, Build: 3},
   688  								}, nil)
   689  							})
   690  
   691  							It("installs the plugin", func() {
   692  								Expect(executeErr).ToNot(HaveOccurred())
   693  
   694  								Expect(testUI.Out).To(Say(`Searching %s for plugin %s\.\.\.`, repoName, pluginName))
   695  								Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repoName))
   696  								Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   697  								Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   698  								Expect(testUI.Out).To(Say(`Do you want to install the plugin %s\? \[yN\]`, pluginName))
   699  								Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repoName))
   700  								Expect(testUI.Out).To(Say(`Installing plugin %s\.\.\.`, pluginName))
   701  								Expect(testUI.Out).To(Say("OK"))
   702  								Expect(testUI.Out).To(Say(`%s 1\.2\.3 successfully installed`, pluginName))
   703  							})
   704  						})
   705  					})
   706  				})
   707  			})
   708  		})
   709  	})
   710  
   711  	Describe("installing from any registered repo", func() {
   712  		var (
   713  			pluginName string
   714  			pluginURL  string
   715  			repoName   string
   716  			repoURL    string
   717  			repo2Name  string
   718  			repo2URL   string
   719  			repo3Name  string
   720  			repo3URL   string
   721  			platform   string
   722  		)
   723  
   724  		BeforeEach(func() {
   725  			pluginName = helpers.PrefixedRandomName("plugin")
   726  			pluginURL = helpers.PrefixedRandomName("http://")
   727  			repoName = helpers.PrefixedRandomName("repoA")
   728  			repoURL = helpers.PrefixedRandomName("http://")
   729  			repo2Name = helpers.PrefixedRandomName("repoB")
   730  			repo2URL = helpers.PrefixedRandomName("http://")
   731  			repo3Name = helpers.PrefixedRandomName("repoC")
   732  			repo3URL = helpers.PrefixedRandomName("http://")
   733  			cmd.OptionalArgs.PluginNameOrLocation = flag.Path(pluginName)
   734  
   735  			platform = helpers.PrefixedRandomName("platform")
   736  			fakeActor.GetPlatformStringReturns(platform)
   737  		})
   738  
   739  		When("there are no registered repos", func() {
   740  			BeforeEach(func() {
   741  				fakeConfig.PluginRepositoriesReturns([]configv3.PluginRepository{})
   742  			})
   743  
   744  			It("returns PluginNotFoundOnDiskOrInAnyRepositoryError", func() {
   745  				Expect(executeErr).To(MatchError(translatableerror.PluginNotFoundOnDiskOrInAnyRepositoryError{PluginName: pluginName, BinaryName: binaryName}))
   746  			})
   747  		})
   748  
   749  		When("there is one registered repo", func() {
   750  			BeforeEach(func() {
   751  				fakeConfig.PluginRepositoriesReturns([]configv3.PluginRepository{{Name: repoName, URL: repoURL}})
   752  			})
   753  
   754  			When("there is an error getting the plugin", func() {
   755  				BeforeEach(func() {
   756  					fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(pluginaction.PluginInfo{}, nil, errors.New("some-error"))
   757  				})
   758  
   759  				It("returns the error", func() {
   760  					Expect(executeErr).To(MatchError(errors.New("some-error")))
   761  				})
   762  			})
   763  
   764  			When("the plugin is not found", func() {
   765  				BeforeEach(func() {
   766  					fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(pluginaction.PluginInfo{}, nil, actionerror.PluginNotFoundInAnyRepositoryError{PluginName: pluginName})
   767  				})
   768  
   769  				It("returns the plugin not found error", func() {
   770  					Expect(executeErr).To(MatchError(translatableerror.PluginNotFoundOnDiskOrInAnyRepositoryError{PluginName: pluginName, BinaryName: binaryName}))
   771  				})
   772  			})
   773  
   774  			When("the plugin is found", func() {
   775  				var (
   776  					checksum                string
   777  					downloadedVersionString string
   778  					execPath                string
   779  				)
   780  
   781  				BeforeEach(func() {
   782  					fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(pluginaction.PluginInfo{Name: pluginName, Version: downloadedVersionString, URL: pluginURL, Checksum: checksum}, []string{repoName}, nil)
   783  
   784  					plugin := configv3.Plugin{
   785  						Name:    pluginName,
   786  						Version: configv3.PluginVersion{Major: 1, Minor: 2, Build: 2},
   787  					}
   788  					fakeConfig.GetPluginReturns(plugin, true)
   789  					fakeConfig.GetPluginCaseInsensitiveReturns(plugin, true)
   790  
   791  					execPath = helpers.PrefixedRandomName("some-path")
   792  					fakeActor.DownloadExecutableBinaryFromURLReturns(execPath, nil)
   793  				})
   794  
   795  				When("the -f flag is provided, the plugin has already been installed, getting the binary succeeds, validating the checksum succeeds, creating an executable copy succeeds, validating the new plugin succeeds, uninstalling the existing plugin succeeds, and installing the plugin is succeeds", func() {
   796  					var (
   797  						pluginVersion      configv3.PluginVersion
   798  						pluginVersionRegex string
   799  					)
   800  
   801  					BeforeEach(func() {
   802  						cmd.Force = true
   803  
   804  						fakeActor.ValidateFileChecksumReturns(true)
   805  						checksum = helpers.PrefixedRandomName("checksum")
   806  
   807  						fakeActor.CreateExecutableCopyReturns("copy-path", nil)
   808  
   809  						major := rand.Int()
   810  						minor := rand.Int()
   811  						build := rand.Int()
   812  						pluginVersion = configv3.PluginVersion{Major: major, Minor: minor, Build: build}
   813  						pluginVersionRegex = fmt.Sprintf(`%d\.%d\.%d`, major, minor, build)
   814  
   815  						fakeActor.GetAndValidatePluginReturns(configv3.Plugin{
   816  							Name:    pluginName,
   817  							Version: pluginVersion,
   818  						}, nil)
   819  					})
   820  
   821  					It("uninstalls the existing plugin and installs the new one", func() {
   822  						Expect(executeErr).ToNot(HaveOccurred())
   823  
   824  						Expect(testUI.Out).To(Say(`Searching %s for plugin %s\.\.\.`, repoName, pluginName))
   825  						Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repoName))
   826  						Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.2 is already installed\.`, pluginName))
   827  						Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   828  						Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   829  						Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repoName))
   830  						Expect(testUI.Out).To(Say(`Uninstalling existing plugin\.\.\.`))
   831  						Expect(testUI.Out).To(Say("OK"))
   832  						Expect(testUI.Out).To(Say(`Plugin %s successfully uninstalled\.`, pluginName))
   833  						Expect(testUI.Out).To(Say(`Installing plugin %s\.\.\.`, pluginName))
   834  						Expect(testUI.Out).To(Say("OK"))
   835  						Expect(testUI.Out).To(Say(`%s %s successfully installed\.`, pluginName, pluginVersionRegex))
   836  					})
   837  				})
   838  
   839  				When("the -f flag is not provided, the plugin has already been installed, getting the binary succeeds, but validating the checksum fails", func() {
   840  					BeforeEach(func() {
   841  						fakeActor.DownloadExecutableBinaryFromURLReturns("some-path", nil)
   842  					})
   843  
   844  					When("the checksum fails", func() {
   845  						BeforeEach(func() {
   846  							cmd.Force = false
   847  							fakeActor.ValidateFileChecksumReturns(false)
   848  							_, err := input.Write([]byte("y\n"))
   849  							Expect(err).ToNot(HaveOccurred())
   850  						})
   851  
   852  						It("returns the checksum error", func() {
   853  							Expect(executeErr).To(MatchError(translatableerror.InvalidChecksumError{}))
   854  
   855  							Expect(testUI.Out).To(Say(`Searching %s for plugin %s\.\.\.`, repoName, pluginName))
   856  							Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repoName))
   857  							Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.2 is already installed\.`, pluginName))
   858  							Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   859  							Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   860  							Expect(testUI.Out).To(Say(`Do you want to uninstall the existing plugin and install %s %s\? \[yN\]`, pluginName, downloadedVersionString))
   861  							Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repoName))
   862  						})
   863  					})
   864  				})
   865  			})
   866  		})
   867  
   868  		When("there are many registered repos", func() {
   869  			BeforeEach(func() {
   870  				fakeConfig.PluginRepositoriesReturns([]configv3.PluginRepository{{Name: repoName, URL: repoURL}, {Name: repo2Name, URL: repo2URL}, {Name: repo3Name, URL: repo3URL}})
   871  			})
   872  
   873  			When("getting the repository information errors", func() {
   874  				DescribeTable("properly propagates errors",
   875  					func(clientErr error, expectedErr error) {
   876  						fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(
   877  							pluginaction.PluginInfo{},
   878  							nil,
   879  							clientErr)
   880  
   881  						executeErr = cmd.Execute(nil)
   882  
   883  						Expect(executeErr).To(MatchError(expectedErr))
   884  					},
   885  
   886  					Entry("when the error is a RawHTTPStatusError",
   887  						actionerror.FetchingPluginInfoFromRepositoryError{
   888  							RepositoryName: "some-repo",
   889  							Err:            pluginerror.RawHTTPStatusError{Status: "some-status"},
   890  						},
   891  						translatableerror.FetchingPluginInfoFromRepositoriesError{Message: "some-status", RepositoryName: "some-repo"},
   892  					),
   893  
   894  					Entry("when the error is a SSLValidationHostnameError",
   895  						actionerror.FetchingPluginInfoFromRepositoryError{
   896  							RepositoryName: "some-repo",
   897  							Err:            pluginerror.SSLValidationHostnameError{Message: "some-status"},
   898  						},
   899  
   900  						translatableerror.FetchingPluginInfoFromRepositoriesError{Message: "Hostname does not match SSL Certificate (some-status)", RepositoryName: "some-repo"},
   901  					),
   902  
   903  					Entry("when the error is an UnverifiedServerError",
   904  						actionerror.FetchingPluginInfoFromRepositoryError{
   905  							RepositoryName: "some-repo",
   906  							Err:            pluginerror.UnverifiedServerError{URL: "some-url"},
   907  						},
   908  						translatableerror.FetchingPluginInfoFromRepositoriesError{Message: "x509: certificate signed by unknown authority", RepositoryName: "some-repo"},
   909  					),
   910  
   911  					Entry("when the error is generic",
   912  						actionerror.FetchingPluginInfoFromRepositoryError{
   913  							RepositoryName: "some-repo",
   914  							Err:            errors.New("generic-error"),
   915  						},
   916  						errors.New("generic-error"),
   917  					),
   918  				)
   919  			})
   920  
   921  			When("the plugin can't be found in any repos", func() {
   922  				BeforeEach(func() {
   923  					fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(pluginaction.PluginInfo{}, nil, actionerror.PluginNotFoundInAnyRepositoryError{PluginName: pluginName})
   924  				})
   925  
   926  				It("returns PluginNotFoundOnDiskOrInAnyRepositoryError", func() {
   927  					Expect(executeErr).To(MatchError(translatableerror.PluginNotFoundOnDiskOrInAnyRepositoryError{PluginName: pluginName, BinaryName: binaryName}))
   928  				})
   929  			})
   930  
   931  			When("the plugin is found in one repo", func() {
   932  				var (
   933  					checksum                string
   934  					downloadedVersionString string
   935  					execPath                string
   936  				)
   937  
   938  				BeforeEach(func() {
   939  					fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(pluginaction.PluginInfo{Name: pluginName, Version: downloadedVersionString, URL: pluginURL, Checksum: checksum}, []string{repo2Name}, nil)
   940  
   941  					plugin := configv3.Plugin{
   942  						Name:    pluginName,
   943  						Version: configv3.PluginVersion{Major: 1, Minor: 2, Build: 2},
   944  					}
   945  					fakeConfig.GetPluginReturns(plugin, true)
   946  					fakeConfig.GetPluginCaseInsensitiveReturns(plugin, true)
   947  
   948  					execPath = helpers.PrefixedRandomName("some-path")
   949  				})
   950  
   951  				When("the -f flag is provided, the plugin has already been installed, getting the binary succeeds, validating the checksum succeeds, creating an executable copy succeeds, validating the new plugin succeeds, uninstalling the existing plugin succeeds, and installing the plugin is succeeds", func() {
   952  					var pluginVersion configv3.PluginVersion
   953  
   954  					BeforeEach(func() {
   955  						cmd.Force = true
   956  
   957  						fakeActor.DownloadExecutableBinaryFromURLReturns(execPath, nil)
   958  
   959  						fakeActor.ValidateFileChecksumReturns(true)
   960  						checksum = helpers.PrefixedRandomName("checksum")
   961  
   962  						fakeActor.CreateExecutableCopyReturns("copy-path", nil)
   963  
   964  						major := rand.Int()
   965  						minor := rand.Int()
   966  						build := rand.Int()
   967  
   968  						pluginVersion = configv3.PluginVersion{Major: major, Minor: minor, Build: build}
   969  
   970  						fakeActor.GetAndValidatePluginReturns(configv3.Plugin{
   971  							Name:    pluginName,
   972  							Version: pluginVersion,
   973  						}, nil)
   974  					})
   975  
   976  					It("uninstalls the existing plugin and installs the new one", func() {
   977  						Expect(executeErr).ToNot(HaveOccurred())
   978  
   979  						Expect(testUI.Out).To(Say(`Searching %s, %s, %s for plugin %s\.\.\.`, repoName, repo2Name, repo3Name, pluginName))
   980  						Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repo2Name))
   981  					})
   982  				})
   983  
   984  				When("the -f flag is not provided, the plugin has already been installed, getting the binary succeeds fails", func() {
   985  
   986  					BeforeEach(func() {
   987  						cmd.Force = false
   988  						fakeActor.DownloadExecutableBinaryFromURLReturns("", errors.New("some-error"))
   989  						_, err := input.Write([]byte("y\n"))
   990  						Expect(err).ToNot(HaveOccurred())
   991  					})
   992  
   993  					It("returns the checksum error", func() {
   994  						Expect(executeErr).To(MatchError(errors.New("some-error")))
   995  
   996  						Expect(testUI.Out).To(Say(`Searching %s, %s, %s for plugin %s\.\.\.`, repoName, repo2Name, repo3Name, pluginName))
   997  						Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repo2Name))
   998  						Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.2 is already installed\.`, pluginName))
   999  						Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
  1000  						Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
  1001  						Expect(testUI.Out).To(Say(`Do you want to uninstall the existing plugin and install %s %s\? \[yN\]`, pluginName, downloadedVersionString))
  1002  						Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repo2Name))
  1003  					})
  1004  				})
  1005  			})
  1006  
  1007  			When("the plugin is found in multiple repos", func() {
  1008  				var (
  1009  					checksum                string
  1010  					downloadedVersionString string
  1011  					execPath                string
  1012  				)
  1013  
  1014  				BeforeEach(func() {
  1015  					downloadedVersionString = helpers.PrefixedRandomName("version")
  1016  
  1017  					fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(pluginaction.PluginInfo{Name: pluginName, Version: downloadedVersionString, URL: pluginURL, Checksum: checksum}, []string{repo2Name, repo3Name}, nil)
  1018  
  1019  					plugin := configv3.Plugin{
  1020  						Name:    pluginName,
  1021  						Version: configv3.PluginVersion{Major: 1, Minor: 2, Build: 2},
  1022  					}
  1023  					fakeConfig.GetPluginReturns(plugin, true)
  1024  					fakeConfig.GetPluginCaseInsensitiveReturns(plugin, true)
  1025  
  1026  					execPath = helpers.PrefixedRandomName("some-path")
  1027  					fakeActor.DownloadExecutableBinaryFromURLReturns(execPath, nil)
  1028  				})
  1029  
  1030  				When("the -f flag is provided, the plugin has already been installed, getting the binary succeeds, validating the checksum succeeds, creating an executable copy succeeds, validating the new plugin succeeds, uninstalling the existing plugin succeeds, and installing the plugin is succeeds", func() {
  1031  					var (
  1032  						pluginVersion      configv3.PluginVersion
  1033  						pluginVersionRegex string
  1034  					)
  1035  
  1036  					BeforeEach(func() {
  1037  						cmd.Force = true
  1038  
  1039  						fakeActor.ValidateFileChecksumReturns(true)
  1040  						checksum = helpers.PrefixedRandomName("checksum")
  1041  
  1042  						fakeActor.CreateExecutableCopyReturns("copy-path", nil)
  1043  
  1044  						major := rand.Int()
  1045  						minor := rand.Int()
  1046  						build := rand.Int()
  1047  						pluginVersion = configv3.PluginVersion{Major: major, Minor: minor, Build: build}
  1048  						pluginVersionRegex = fmt.Sprintf(`%d\.%d\.%d`, major, minor, build)
  1049  
  1050  						fakeActor.GetAndValidatePluginReturns(configv3.Plugin{
  1051  							Name:    pluginName,
  1052  							Version: pluginVersion,
  1053  						}, nil)
  1054  					})
  1055  
  1056  					It("uninstalls the existing plugin and installs the new one", func() {
  1057  						Expect(executeErr).ToNot(HaveOccurred())
  1058  
  1059  						Expect(testUI.Out).To(Say(`Searching %s, %s, %s for plugin %s\.\.\.`, repoName, repo2Name, repo3Name, pluginName))
  1060  						Expect(testUI.Out).To(Say("Plugin %s %s found in: %s, %s", pluginName, downloadedVersionString, repo2Name, repo3Name))
  1061  						Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.2 is already installed\.`, pluginName))
  1062  						Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
  1063  						Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
  1064  						Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repo2Name))
  1065  						Expect(testUI.Out).To(Say(`Uninstalling existing plugin\.\.\.`))
  1066  						Expect(testUI.Out).To(Say("OK"))
  1067  						Expect(testUI.Out).To(Say(`Plugin %s successfully uninstalled\.`, pluginName))
  1068  						Expect(testUI.Out).To(Say(`Installing plugin %s\.\.\.`, pluginName))
  1069  						Expect(testUI.Out).To(Say("OK"))
  1070  						Expect(testUI.Out).To(Say(`%s %s successfully installed\.`, pluginName, pluginVersionRegex))
  1071  					})
  1072  				})
  1073  
  1074  				When("the -f flag is not provided, the plugin has already been installed, getting the binary succeeds, validating the checksum succeeds, but creating an executable copy fails", func() {
  1075  
  1076  					BeforeEach(func() {
  1077  						cmd.Force = false
  1078  						fakeActor.ValidateFileChecksumReturns(true)
  1079  						checksum = helpers.PrefixedRandomName("checksum")
  1080  
  1081  						fakeActor.CreateExecutableCopyReturns("", errors.New("some-error"))
  1082  						_, err := input.Write([]byte("y\n"))
  1083  						Expect(err).ToNot(HaveOccurred())
  1084  					})
  1085  
  1086  					It("returns the error", func() {
  1087  						Expect(executeErr).To(MatchError(errors.New("some-error")))
  1088  
  1089  						Expect(testUI.Out).To(Say(`Searching %s, %s, %s for plugin %s\.\.\.`, repoName, repo2Name, repo3Name, pluginName))
  1090  						Expect(testUI.Out).To(Say("Plugin %s %s found in: %s, %s", pluginName, downloadedVersionString, repo2Name, repo3Name))
  1091  						Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.2 is already installed\.`, pluginName))
  1092  						Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
  1093  						Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
  1094  						Expect(testUI.Out).To(Say(`Do you want to uninstall the existing plugin and install %s %s\? \[yN\]`, pluginName, downloadedVersionString))
  1095  						Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repo2Name))
  1096  					})
  1097  				})
  1098  			})
  1099  		})
  1100  	})
  1101  })