github.com/jenspinney/cli@v6.42.1-0.20190207184520-7450c600020e+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  								input.Write([]byte("n\n"))
   553  							})
   554  
   555  							It("cancels plugin installation", func() {
   556  								Expect(executeErr).ToNot(HaveOccurred())
   557  
   558  								Expect(testUI.Out).To(Say(`Searching %s for plugin %s\.\.\.`, repoName, pluginName))
   559  								Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repoName))
   560  								Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.2 is already installed\.`, pluginName))
   561  								Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   562  								Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   563  								Expect(testUI.Out).To(Say(`Do you want to uninstall the existing plugin and install %s %s\? \[yN\]`, pluginName, downloadedVersionString))
   564  								Expect(testUI.Out).To(Say(`Plugin installation cancelled\.`))
   565  							})
   566  						})
   567  
   568  						When("the user chooses the default", func() {
   569  							BeforeEach(func() {
   570  								input.Write([]byte("\n"))
   571  							})
   572  
   573  							It("cancels plugin installation", func() {
   574  								Expect(executeErr).ToNot(HaveOccurred())
   575  
   576  								Expect(testUI.Out).To(Say(`Do you want to uninstall the existing plugin and install %s %s\? \[yN\]`, pluginName, downloadedVersionString))
   577  								Expect(testUI.Out).To(Say(`Plugin installation cancelled\.`))
   578  							})
   579  						})
   580  
   581  						When("the user input is invalid", func() {
   582  							BeforeEach(func() {
   583  								input.Write([]byte("e\n"))
   584  							})
   585  
   586  							It("returns an error", func() {
   587  								Expect(executeErr).To(HaveOccurred())
   588  
   589  								Expect(testUI.Out).To(Say(`Do you want to uninstall the existing plugin and install %s %s\? \[yN\]`, pluginName, downloadedVersionString))
   590  								Expect(testUI.Out).ToNot(Say(`Plugin installation cancelled\.`))
   591  								Expect(testUI.Out).ToNot(Say("Installing plugin"))
   592  							})
   593  						})
   594  
   595  						When("the user chooses yes", func() {
   596  							BeforeEach(func() {
   597  								input.Write([]byte("y\n"))
   598  								fakeActor.DownloadExecutableBinaryFromURLReturns("some-path", nil)
   599  								fakeActor.CreateExecutableCopyReturns("copy-path", nil)
   600  								fakeActor.GetAndValidatePluginReturns(configv3.Plugin{
   601  									Name:    pluginName,
   602  									Version: configv3.PluginVersion{Major: 1, Minor: 2, Build: 3},
   603  								}, nil)
   604  							})
   605  
   606  							It("installs the plugin", func() {
   607  								Expect(testUI.Out).To(Say(`Searching %s for plugin %s\.\.\.`, repoName, pluginName))
   608  								Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repoName))
   609  								Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.2 is already installed\.`, pluginName))
   610  								Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   611  								Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   612  								Expect(testUI.Out).To(Say(`Do you want to uninstall the existing plugin and install %s %s\? \[yN\]`, pluginName, downloadedVersionString))
   613  								Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repoName))
   614  								Expect(testUI.Out).To(Say(`Uninstalling existing plugin\.\.\.`))
   615  								Expect(testUI.Out).To(Say("OK"))
   616  								Expect(testUI.Out).To(Say(`Plugin %s successfully uninstalled\.`, pluginName))
   617  								Expect(testUI.Out).To(Say(`Installing plugin %s\.\.\.`, pluginName))
   618  								Expect(testUI.Out).To(Say("OK"))
   619  								Expect(testUI.Out).To(Say(`%s 1\.2\.3 successfully installed`, pluginName))
   620  							})
   621  						})
   622  					})
   623  
   624  					When("the plugin is NOT already installed", func() {
   625  						When("the user chooses no", func() {
   626  							BeforeEach(func() {
   627  								input.Write([]byte("n\n"))
   628  							})
   629  
   630  							It("cancels plugin installation", func() {
   631  								Expect(executeErr).ToNot(HaveOccurred())
   632  
   633  								Expect(testUI.Out).To(Say(`Searching %s for plugin %s\.\.\.`, repoName, pluginName))
   634  								Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repoName))
   635  								Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   636  								Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   637  								Expect(testUI.Out).To(Say(`Do you want to install the plugin %s\? \[yN\]`, pluginName))
   638  								Expect(testUI.Out).To(Say(`Plugin installation cancelled\.`))
   639  							})
   640  						})
   641  
   642  						When("the user chooses the default", func() {
   643  							BeforeEach(func() {
   644  								input.Write([]byte("\n"))
   645  							})
   646  
   647  							It("cancels plugin installation", func() {
   648  								Expect(executeErr).ToNot(HaveOccurred())
   649  
   650  								Expect(testUI.Out).To(Say(`Do you want to install the plugin %s\? \[yN\]`, pluginName))
   651  								Expect(testUI.Out).To(Say(`Plugin installation cancelled\.`))
   652  							})
   653  						})
   654  
   655  						When("the user input is invalid", func() {
   656  							BeforeEach(func() {
   657  								input.Write([]byte("e\n"))
   658  							})
   659  
   660  							It("returns an error", func() {
   661  								Expect(executeErr).To(HaveOccurred())
   662  
   663  								Expect(testUI.Out).To(Say(`Do you want to install the plugin %s\? \[yN\]`, pluginName))
   664  								Expect(testUI.Out).ToNot(Say(`Plugin installation cancelled\.`))
   665  								Expect(testUI.Out).ToNot(Say("Installing plugin"))
   666  							})
   667  						})
   668  
   669  						When("the user chooses yes", func() {
   670  							var execPath string
   671  
   672  							BeforeEach(func() {
   673  								input.Write([]byte("y\n"))
   674  								execPath = helpers.PrefixedRandomName("some-path")
   675  								fakeActor.DownloadExecutableBinaryFromURLReturns(execPath, nil)
   676  								fakeActor.CreateExecutableCopyReturns("copy-path", nil)
   677  								fakeActor.GetAndValidatePluginReturns(configv3.Plugin{
   678  									Name:    pluginName,
   679  									Version: configv3.PluginVersion{Major: 1, Minor: 2, Build: 3},
   680  								}, nil)
   681  							})
   682  
   683  							It("installs the plugin", func() {
   684  								Expect(executeErr).ToNot(HaveOccurred())
   685  
   686  								Expect(testUI.Out).To(Say(`Searching %s for plugin %s\.\.\.`, repoName, pluginName))
   687  								Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repoName))
   688  								Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   689  								Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   690  								Expect(testUI.Out).To(Say(`Do you want to install the plugin %s\? \[yN\]`, pluginName))
   691  								Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repoName))
   692  								Expect(testUI.Out).To(Say(`Installing plugin %s\.\.\.`, pluginName))
   693  								Expect(testUI.Out).To(Say("OK"))
   694  								Expect(testUI.Out).To(Say(`%s 1\.2\.3 successfully installed`, pluginName))
   695  							})
   696  						})
   697  					})
   698  				})
   699  			})
   700  		})
   701  	})
   702  
   703  	Describe("installing from any registered repo", func() {
   704  		var (
   705  			pluginName string
   706  			pluginURL  string
   707  			repoName   string
   708  			repoURL    string
   709  			repo2Name  string
   710  			repo2URL   string
   711  			repo3Name  string
   712  			repo3URL   string
   713  			platform   string
   714  		)
   715  
   716  		BeforeEach(func() {
   717  			pluginName = helpers.PrefixedRandomName("plugin")
   718  			pluginURL = helpers.PrefixedRandomName("http://")
   719  			repoName = helpers.PrefixedRandomName("repoA")
   720  			repoURL = helpers.PrefixedRandomName("http://")
   721  			repo2Name = helpers.PrefixedRandomName("repoB")
   722  			repo2URL = helpers.PrefixedRandomName("http://")
   723  			repo3Name = helpers.PrefixedRandomName("repoC")
   724  			repo3URL = helpers.PrefixedRandomName("http://")
   725  			cmd.OptionalArgs.PluginNameOrLocation = flag.Path(pluginName)
   726  
   727  			platform = helpers.PrefixedRandomName("platform")
   728  			fakeActor.GetPlatformStringReturns(platform)
   729  		})
   730  
   731  		When("there are no registered repos", func() {
   732  			BeforeEach(func() {
   733  				fakeConfig.PluginRepositoriesReturns([]configv3.PluginRepository{})
   734  			})
   735  
   736  			It("returns PluginNotFoundOnDiskOrInAnyRepositoryError", func() {
   737  				Expect(executeErr).To(MatchError(translatableerror.PluginNotFoundOnDiskOrInAnyRepositoryError{PluginName: pluginName, BinaryName: binaryName}))
   738  			})
   739  		})
   740  
   741  		When("there is one registered repo", func() {
   742  			BeforeEach(func() {
   743  				fakeConfig.PluginRepositoriesReturns([]configv3.PluginRepository{{Name: repoName, URL: repoURL}})
   744  			})
   745  
   746  			When("there is an error getting the plugin", func() {
   747  				BeforeEach(func() {
   748  					fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(pluginaction.PluginInfo{}, nil, errors.New("some-error"))
   749  				})
   750  
   751  				It("returns the error", func() {
   752  					Expect(executeErr).To(MatchError(errors.New("some-error")))
   753  				})
   754  			})
   755  
   756  			When("the plugin is not found", func() {
   757  				BeforeEach(func() {
   758  					fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(pluginaction.PluginInfo{}, nil, actionerror.PluginNotFoundInAnyRepositoryError{PluginName: pluginName})
   759  				})
   760  
   761  				It("returns the plugin not found error", func() {
   762  					Expect(executeErr).To(MatchError(translatableerror.PluginNotFoundOnDiskOrInAnyRepositoryError{PluginName: pluginName, BinaryName: binaryName}))
   763  				})
   764  			})
   765  
   766  			When("the plugin is found", func() {
   767  				var (
   768  					checksum                string
   769  					downloadedVersionString string
   770  					execPath                string
   771  				)
   772  
   773  				BeforeEach(func() {
   774  					fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(pluginaction.PluginInfo{Name: pluginName, Version: downloadedVersionString, URL: pluginURL, Checksum: checksum}, []string{repoName}, nil)
   775  
   776  					plugin := configv3.Plugin{
   777  						Name:    pluginName,
   778  						Version: configv3.PluginVersion{Major: 1, Minor: 2, Build: 2},
   779  					}
   780  					fakeConfig.GetPluginReturns(plugin, true)
   781  					fakeConfig.GetPluginCaseInsensitiveReturns(plugin, true)
   782  
   783  					execPath = helpers.PrefixedRandomName("some-path")
   784  					fakeActor.DownloadExecutableBinaryFromURLReturns(execPath, nil)
   785  				})
   786  
   787  				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() {
   788  					var (
   789  						pluginVersion      configv3.PluginVersion
   790  						pluginVersionRegex string
   791  					)
   792  
   793  					BeforeEach(func() {
   794  						cmd.Force = true
   795  
   796  						fakeActor.ValidateFileChecksumReturns(true)
   797  						checksum = helpers.PrefixedRandomName("checksum")
   798  
   799  						fakeActor.CreateExecutableCopyReturns("copy-path", nil)
   800  
   801  						major := rand.Int()
   802  						minor := rand.Int()
   803  						build := rand.Int()
   804  						pluginVersion = configv3.PluginVersion{Major: major, Minor: minor, Build: build}
   805  						pluginVersionRegex = fmt.Sprintf(`%d\.%d\.%d`, major, minor, build)
   806  
   807  						fakeActor.GetAndValidatePluginReturns(configv3.Plugin{
   808  							Name:    pluginName,
   809  							Version: pluginVersion,
   810  						}, nil)
   811  					})
   812  
   813  					It("uninstalls the existing plugin and installs the new one", func() {
   814  						Expect(executeErr).ToNot(HaveOccurred())
   815  
   816  						Expect(testUI.Out).To(Say(`Searching %s for plugin %s\.\.\.`, repoName, pluginName))
   817  						Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repoName))
   818  						Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.2 is already installed\.`, pluginName))
   819  						Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   820  						Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   821  						Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repoName))
   822  						Expect(testUI.Out).To(Say(`Uninstalling existing plugin\.\.\.`))
   823  						Expect(testUI.Out).To(Say("OK"))
   824  						Expect(testUI.Out).To(Say(`Plugin %s successfully uninstalled\.`, pluginName))
   825  						Expect(testUI.Out).To(Say(`Installing plugin %s\.\.\.`, pluginName))
   826  						Expect(testUI.Out).To(Say("OK"))
   827  						Expect(testUI.Out).To(Say(`%s %s successfully installed\.`, pluginName, pluginVersionRegex))
   828  					})
   829  				})
   830  
   831  				When("the -f flag is not provided, the plugin has already been installed, getting the binary succeeds, but validating the checksum fails", func() {
   832  					BeforeEach(func() {
   833  						fakeActor.DownloadExecutableBinaryFromURLReturns("some-path", nil)
   834  					})
   835  
   836  					When("the checksum fails", func() {
   837  						BeforeEach(func() {
   838  							cmd.Force = false
   839  							fakeActor.ValidateFileChecksumReturns(false)
   840  							input.Write([]byte("y\n"))
   841  						})
   842  
   843  						It("returns the checksum error", func() {
   844  							Expect(executeErr).To(MatchError(translatableerror.InvalidChecksumError{}))
   845  
   846  							Expect(testUI.Out).To(Say(`Searching %s for plugin %s\.\.\.`, repoName, pluginName))
   847  							Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repoName))
   848  							Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.2 is already installed\.`, pluginName))
   849  							Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   850  							Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   851  							Expect(testUI.Out).To(Say(`Do you want to uninstall the existing plugin and install %s %s\? \[yN\]`, pluginName, downloadedVersionString))
   852  							Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repoName))
   853  						})
   854  					})
   855  				})
   856  			})
   857  		})
   858  
   859  		When("there are many registered repos", func() {
   860  			BeforeEach(func() {
   861  				fakeConfig.PluginRepositoriesReturns([]configv3.PluginRepository{{Name: repoName, URL: repoURL}, {Name: repo2Name, URL: repo2URL}, {Name: repo3Name, URL: repo3URL}})
   862  			})
   863  
   864  			When("getting the repository information errors", func() {
   865  				DescribeTable("properly propagates errors",
   866  					func(clientErr error, expectedErr error) {
   867  						fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(
   868  							pluginaction.PluginInfo{},
   869  							nil,
   870  							clientErr)
   871  
   872  						executeErr = cmd.Execute(nil)
   873  
   874  						Expect(executeErr).To(MatchError(expectedErr))
   875  					},
   876  
   877  					Entry("when the error is a RawHTTPStatusError",
   878  						actionerror.FetchingPluginInfoFromRepositoryError{
   879  							RepositoryName: "some-repo",
   880  							Err:            pluginerror.RawHTTPStatusError{Status: "some-status"},
   881  						},
   882  						translatableerror.FetchingPluginInfoFromRepositoriesError{Message: "some-status", RepositoryName: "some-repo"},
   883  					),
   884  
   885  					Entry("when the error is a SSLValidationHostnameError",
   886  						actionerror.FetchingPluginInfoFromRepositoryError{
   887  							RepositoryName: "some-repo",
   888  							Err:            pluginerror.SSLValidationHostnameError{Message: "some-status"},
   889  						},
   890  
   891  						translatableerror.FetchingPluginInfoFromRepositoriesError{Message: "Hostname does not match SSL Certificate (some-status)", RepositoryName: "some-repo"},
   892  					),
   893  
   894  					Entry("when the error is an UnverifiedServerError",
   895  						actionerror.FetchingPluginInfoFromRepositoryError{
   896  							RepositoryName: "some-repo",
   897  							Err:            pluginerror.UnverifiedServerError{URL: "some-url"},
   898  						},
   899  						translatableerror.FetchingPluginInfoFromRepositoriesError{Message: "x509: certificate signed by unknown authority", RepositoryName: "some-repo"},
   900  					),
   901  
   902  					Entry("when the error is generic",
   903  						actionerror.FetchingPluginInfoFromRepositoryError{
   904  							RepositoryName: "some-repo",
   905  							Err:            errors.New("generic-error"),
   906  						},
   907  						errors.New("generic-error"),
   908  					),
   909  				)
   910  			})
   911  
   912  			When("the plugin can't be found in any repos", func() {
   913  				BeforeEach(func() {
   914  					fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(pluginaction.PluginInfo{}, nil, actionerror.PluginNotFoundInAnyRepositoryError{PluginName: pluginName})
   915  				})
   916  
   917  				It("returns PluginNotFoundOnDiskOrInAnyRepositoryError", func() {
   918  					Expect(executeErr).To(MatchError(translatableerror.PluginNotFoundOnDiskOrInAnyRepositoryError{PluginName: pluginName, BinaryName: binaryName}))
   919  				})
   920  			})
   921  
   922  			When("the plugin is found in one repo", func() {
   923  				var (
   924  					checksum                string
   925  					downloadedVersionString string
   926  					execPath                string
   927  				)
   928  
   929  				BeforeEach(func() {
   930  					fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(pluginaction.PluginInfo{Name: pluginName, Version: downloadedVersionString, URL: pluginURL, Checksum: checksum}, []string{repo2Name}, nil)
   931  
   932  					plugin := configv3.Plugin{
   933  						Name:    pluginName,
   934  						Version: configv3.PluginVersion{Major: 1, Minor: 2, Build: 2},
   935  					}
   936  					fakeConfig.GetPluginReturns(plugin, true)
   937  					fakeConfig.GetPluginCaseInsensitiveReturns(plugin, true)
   938  
   939  					execPath = helpers.PrefixedRandomName("some-path")
   940  				})
   941  
   942  				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() {
   943  					var pluginVersion configv3.PluginVersion
   944  
   945  					BeforeEach(func() {
   946  						cmd.Force = true
   947  
   948  						fakeActor.DownloadExecutableBinaryFromURLReturns(execPath, nil)
   949  
   950  						fakeActor.ValidateFileChecksumReturns(true)
   951  						checksum = helpers.PrefixedRandomName("checksum")
   952  
   953  						fakeActor.CreateExecutableCopyReturns("copy-path", nil)
   954  
   955  						major := rand.Int()
   956  						minor := rand.Int()
   957  						build := rand.Int()
   958  
   959  						pluginVersion = configv3.PluginVersion{Major: major, Minor: minor, Build: build}
   960  
   961  						fakeActor.GetAndValidatePluginReturns(configv3.Plugin{
   962  							Name:    pluginName,
   963  							Version: pluginVersion,
   964  						}, nil)
   965  					})
   966  
   967  					It("uninstalls the existing plugin and installs the new one", func() {
   968  						Expect(executeErr).ToNot(HaveOccurred())
   969  
   970  						Expect(testUI.Out).To(Say(`Searching %s, %s, %s for plugin %s\.\.\.`, repoName, repo2Name, repo3Name, pluginName))
   971  						Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repo2Name))
   972  					})
   973  				})
   974  
   975  				When("the -f flag is not provided, the plugin has already been installed, getting the binary succeeds fails", func() {
   976  
   977  					BeforeEach(func() {
   978  						cmd.Force = false
   979  						fakeActor.DownloadExecutableBinaryFromURLReturns("", errors.New("some-error"))
   980  						input.Write([]byte("y\n"))
   981  					})
   982  
   983  					It("returns the checksum error", func() {
   984  						Expect(executeErr).To(MatchError(errors.New("some-error")))
   985  
   986  						Expect(testUI.Out).To(Say(`Searching %s, %s, %s for plugin %s\.\.\.`, repoName, repo2Name, repo3Name, pluginName))
   987  						Expect(testUI.Out).To(Say("Plugin %s %s found in: %s", pluginName, downloadedVersionString, repo2Name))
   988  						Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.2 is already installed\.`, pluginName))
   989  						Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   990  						Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   991  						Expect(testUI.Out).To(Say(`Do you want to uninstall the existing plugin and install %s %s\? \[yN\]`, pluginName, downloadedVersionString))
   992  						Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repo2Name))
   993  					})
   994  				})
   995  			})
   996  
   997  			When("the plugin is found in multiple repos", func() {
   998  				var (
   999  					checksum                string
  1000  					downloadedVersionString string
  1001  					execPath                string
  1002  				)
  1003  
  1004  				BeforeEach(func() {
  1005  					downloadedVersionString = helpers.PrefixedRandomName("version")
  1006  
  1007  					fakeActor.GetPluginInfoFromRepositoriesForPlatformReturns(pluginaction.PluginInfo{Name: pluginName, Version: downloadedVersionString, URL: pluginURL, Checksum: checksum}, []string{repo2Name, repo3Name}, nil)
  1008  
  1009  					plugin := configv3.Plugin{
  1010  						Name:    pluginName,
  1011  						Version: configv3.PluginVersion{Major: 1, Minor: 2, Build: 2},
  1012  					}
  1013  					fakeConfig.GetPluginReturns(plugin, true)
  1014  					fakeConfig.GetPluginCaseInsensitiveReturns(plugin, true)
  1015  
  1016  					execPath = helpers.PrefixedRandomName("some-path")
  1017  					fakeActor.DownloadExecutableBinaryFromURLReturns(execPath, nil)
  1018  				})
  1019  
  1020  				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() {
  1021  					var (
  1022  						pluginVersion      configv3.PluginVersion
  1023  						pluginVersionRegex string
  1024  					)
  1025  
  1026  					BeforeEach(func() {
  1027  						cmd.Force = true
  1028  
  1029  						fakeActor.ValidateFileChecksumReturns(true)
  1030  						checksum = helpers.PrefixedRandomName("checksum")
  1031  
  1032  						fakeActor.CreateExecutableCopyReturns("copy-path", nil)
  1033  
  1034  						major := rand.Int()
  1035  						minor := rand.Int()
  1036  						build := rand.Int()
  1037  						pluginVersion = configv3.PluginVersion{Major: major, Minor: minor, Build: build}
  1038  						pluginVersionRegex = fmt.Sprintf(`%d\.%d\.%d`, major, minor, build)
  1039  
  1040  						fakeActor.GetAndValidatePluginReturns(configv3.Plugin{
  1041  							Name:    pluginName,
  1042  							Version: pluginVersion,
  1043  						}, nil)
  1044  					})
  1045  
  1046  					It("uninstalls the existing plugin and installs the new one", func() {
  1047  						Expect(executeErr).ToNot(HaveOccurred())
  1048  
  1049  						Expect(testUI.Out).To(Say(`Searching %s, %s, %s for plugin %s\.\.\.`, repoName, repo2Name, repo3Name, pluginName))
  1050  						Expect(testUI.Out).To(Say("Plugin %s %s found in: %s, %s", pluginName, downloadedVersionString, repo2Name, repo3Name))
  1051  						Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.2 is already installed\.`, pluginName))
  1052  						Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
  1053  						Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
  1054  						Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repo2Name))
  1055  						Expect(testUI.Out).To(Say(`Uninstalling existing plugin\.\.\.`))
  1056  						Expect(testUI.Out).To(Say("OK"))
  1057  						Expect(testUI.Out).To(Say(`Plugin %s successfully uninstalled\.`, pluginName))
  1058  						Expect(testUI.Out).To(Say(`Installing plugin %s\.\.\.`, pluginName))
  1059  						Expect(testUI.Out).To(Say("OK"))
  1060  						Expect(testUI.Out).To(Say(`%s %s successfully installed\.`, pluginName, pluginVersionRegex))
  1061  					})
  1062  				})
  1063  
  1064  				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() {
  1065  
  1066  					BeforeEach(func() {
  1067  						cmd.Force = false
  1068  						fakeActor.ValidateFileChecksumReturns(true)
  1069  						checksum = helpers.PrefixedRandomName("checksum")
  1070  
  1071  						fakeActor.CreateExecutableCopyReturns("", errors.New("some-error"))
  1072  						input.Write([]byte("y\n"))
  1073  					})
  1074  
  1075  					It("returns the error", func() {
  1076  						Expect(executeErr).To(MatchError(errors.New("some-error")))
  1077  
  1078  						Expect(testUI.Out).To(Say(`Searching %s, %s, %s for plugin %s\.\.\.`, repoName, repo2Name, repo3Name, pluginName))
  1079  						Expect(testUI.Out).To(Say("Plugin %s %s found in: %s, %s", pluginName, downloadedVersionString, repo2Name, repo3Name))
  1080  						Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.2 is already installed\.`, pluginName))
  1081  						Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
  1082  						Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
  1083  						Expect(testUI.Out).To(Say(`Do you want to uninstall the existing plugin and install %s %s\? \[yN\]`, pluginName, downloadedVersionString))
  1084  						Expect(testUI.Out).To(Say(`Starting download of plugin binary from repository %s\.\.\.`, repo2Name))
  1085  					})
  1086  				})
  1087  			})
  1088  		})
  1089  	})
  1090  })