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