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