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

     1  package common_test
     2  
     3  import (
     4  	"errors"
     5  	"io/ioutil"
     6  	"os"
     7  
     8  	"code.cloudfoundry.org/cli/actor/actionerror"
     9  	"code.cloudfoundry.org/cli/api/plugin/pluginerror"
    10  	"code.cloudfoundry.org/cli/api/plugin/pluginfakes"
    11  	"code.cloudfoundry.org/cli/command/commandfakes"
    12  	. "code.cloudfoundry.org/cli/command/common"
    13  	"code.cloudfoundry.org/cli/command/common/commonfakes"
    14  	"code.cloudfoundry.org/cli/command/translatableerror"
    15  	"code.cloudfoundry.org/cli/util/configv3"
    16  	"code.cloudfoundry.org/cli/util/ui"
    17  	. "github.com/onsi/ginkgo"
    18  	. "github.com/onsi/gomega"
    19  	. "github.com/onsi/gomega/gbytes"
    20  )
    21  
    22  var _ = Describe("install-plugin command", func() {
    23  	var (
    24  		cmd             InstallPluginCommand
    25  		testUI          *ui.UI
    26  		input           *Buffer
    27  		fakeConfig      *commandfakes.FakeConfig
    28  		fakeActor       *commonfakes.FakeInstallPluginActor
    29  		fakeProgressBar *pluginfakes.FakeProxyReader
    30  		executeErr      error
    31  		expectedErr     error
    32  		pluginHome      string
    33  	)
    34  
    35  	BeforeEach(func() {
    36  		input = NewBuffer()
    37  		testUI = ui.NewTestUI(input, NewBuffer(), NewBuffer())
    38  		fakeConfig = new(commandfakes.FakeConfig)
    39  		fakeActor = new(commonfakes.FakeInstallPluginActor)
    40  		fakeProgressBar = new(pluginfakes.FakeProxyReader)
    41  
    42  		cmd = InstallPluginCommand{
    43  			UI:          testUI,
    44  			Config:      fakeConfig,
    45  			Actor:       fakeActor,
    46  			ProgressBar: fakeProgressBar,
    47  		}
    48  
    49  		var err error
    50  		pluginHome, err = ioutil.TempDir("", "some-pluginhome")
    51  		Expect(err).NotTo(HaveOccurred())
    52  
    53  		fakeConfig.PluginHomeReturns(pluginHome)
    54  		fakeConfig.BinaryNameReturns("faceman")
    55  	})
    56  
    57  	AfterEach(func() {
    58  		os.RemoveAll(pluginHome)
    59  	})
    60  
    61  	JustBeforeEach(func() {
    62  		executeErr = cmd.Execute(nil)
    63  	})
    64  
    65  	Describe("installing from a local file", func() {
    66  		BeforeEach(func() {
    67  			cmd.OptionalArgs.PluginNameOrLocation = "some-path"
    68  		})
    69  
    70  		When("the local file does not exist", func() {
    71  			BeforeEach(func() {
    72  				fakeActor.FileExistsReturns(false)
    73  			})
    74  
    75  			It("does not print installation messages and returns a FileNotFoundError", func() {
    76  				Expect(executeErr).To(MatchError(translatableerror.PluginNotFoundOnDiskOrInAnyRepositoryError{PluginName: "some-path", BinaryName: "faceman"}))
    77  
    78  				Expect(testUI.Out).ToNot(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
    79  				Expect(testUI.Out).ToNot(Say(`Installing plugin some-path\.\.\.`))
    80  			})
    81  		})
    82  
    83  		When("the file exists", func() {
    84  			BeforeEach(func() {
    85  				fakeActor.CreateExecutableCopyReturns("copy-path", nil)
    86  				fakeActor.FileExistsReturns(true)
    87  			})
    88  
    89  			When("the -f argument is given", func() {
    90  				BeforeEach(func() {
    91  					cmd.Force = true
    92  				})
    93  
    94  				When("the plugin is invalid", func() {
    95  					var returnedErr error
    96  
    97  					BeforeEach(func() {
    98  						returnedErr = actionerror.PluginInvalidError{}
    99  						fakeActor.GetAndValidatePluginReturns(configv3.Plugin{}, returnedErr)
   100  					})
   101  
   102  					It("returns an error", func() {
   103  						Expect(executeErr).To(MatchError(returnedErr))
   104  
   105  						Expect(testUI.Out).ToNot(Say("Installing plugin"))
   106  					})
   107  				})
   108  
   109  				When("the plugin is valid but generates an error when fetching metadata", func() {
   110  					var wrappedErr error
   111  
   112  					BeforeEach(func() {
   113  						wrappedErr = errors.New("some-error")
   114  						fakeActor.GetAndValidatePluginReturns(configv3.Plugin{}, actionerror.PluginInvalidError{Err: wrappedErr})
   115  					})
   116  
   117  					It("returns an error", func() {
   118  						Expect(executeErr).To(MatchError(actionerror.PluginInvalidError{Err: wrappedErr}))
   119  
   120  						Expect(testUI.Out).ToNot(Say("Installing plugin"))
   121  					})
   122  				})
   123  
   124  				When("the plugin is already installed", func() {
   125  					var (
   126  						plugin    configv3.Plugin
   127  						newPlugin configv3.Plugin
   128  					)
   129  					BeforeEach(func() {
   130  						plugin = configv3.Plugin{
   131  							Name: "some-plugin",
   132  							Version: configv3.PluginVersion{
   133  								Major: 1,
   134  								Minor: 2,
   135  								Build: 2,
   136  							},
   137  						}
   138  						newPlugin = configv3.Plugin{
   139  							Name: "some-plugin",
   140  							Version: configv3.PluginVersion{
   141  								Major: 1,
   142  								Minor: 2,
   143  								Build: 3,
   144  							},
   145  						}
   146  						fakeActor.GetAndValidatePluginReturns(newPlugin, nil)
   147  						fakeConfig.GetPluginCaseInsensitiveReturns(plugin, true)
   148  					})
   149  
   150  					When("an error is encountered uninstalling the existing plugin", func() {
   151  						BeforeEach(func() {
   152  							expectedErr = errors.New("uninstall plugin error")
   153  							fakeActor.UninstallPluginReturns(expectedErr)
   154  						})
   155  
   156  						It("returns the error", func() {
   157  							Expect(executeErr).To(MatchError(expectedErr))
   158  
   159  							Expect(testUI.Out).ToNot(Say(`Plugin some-plugin successfully uninstalled\.`))
   160  						})
   161  					})
   162  
   163  					When("no errors are encountered uninstalling the existing plugin", func() {
   164  						It("uninstalls the existing plugin and installs the current plugin", func() {
   165  							Expect(executeErr).ToNot(HaveOccurred())
   166  
   167  							Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   168  							Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   169  							Expect(testUI.Out).To(Say(`Plugin some-plugin 1\.2\.2 is already installed\. Uninstalling existing plugin\.\.\.`))
   170  							Expect(testUI.Out).To(Say("OK"))
   171  							Expect(testUI.Out).To(Say(`Plugin some-plugin successfully uninstalled\.`))
   172  							Expect(testUI.Out).To(Say(`Installing plugin some-plugin\.\.\.`))
   173  							Expect(testUI.Out).To(Say("OK"))
   174  							Expect(testUI.Out).To(Say(`Plugin some-plugin 1\.2\.3 successfully installed\.`))
   175  
   176  							Expect(fakeActor.FileExistsCallCount()).To(Equal(1))
   177  							Expect(fakeActor.FileExistsArgsForCall(0)).To(Equal("some-path"))
   178  
   179  							Expect(fakeActor.GetAndValidatePluginCallCount()).To(Equal(1))
   180  							_, _, path := fakeActor.GetAndValidatePluginArgsForCall(0)
   181  							Expect(path).To(Equal("copy-path"))
   182  
   183  							Expect(fakeConfig.GetPluginCaseInsensitiveCallCount()).To(Equal(1))
   184  							Expect(fakeConfig.GetPluginCaseInsensitiveArgsForCall(0)).To(Equal("some-plugin"))
   185  
   186  							Expect(fakeActor.UninstallPluginCallCount()).To(Equal(1))
   187  							_, pluginName := fakeActor.UninstallPluginArgsForCall(0)
   188  							Expect(pluginName).To(Equal("some-plugin"))
   189  
   190  							Expect(fakeActor.InstallPluginFromPathCallCount()).To(Equal(1))
   191  							path, installedPlugin := fakeActor.InstallPluginFromPathArgsForCall(0)
   192  							Expect(path).To(Equal("copy-path"))
   193  							Expect(installedPlugin).To(Equal(newPlugin))
   194  						})
   195  
   196  						When("an error is encountered installing the plugin", func() {
   197  							BeforeEach(func() {
   198  								expectedErr = errors.New("install plugin error")
   199  								fakeActor.InstallPluginFromPathReturns(expectedErr)
   200  							})
   201  
   202  							It("returns the error", func() {
   203  								Expect(executeErr).To(MatchError(expectedErr))
   204  
   205  								Expect(testUI.Out).ToNot(Say(`Plugin some-plugin 1\.2\.3 successfully installed\.`))
   206  							})
   207  						})
   208  					})
   209  				})
   210  
   211  				When("the plugin is not already installed", func() {
   212  					var plugin configv3.Plugin
   213  
   214  					BeforeEach(func() {
   215  						plugin = configv3.Plugin{
   216  							Name: "some-plugin",
   217  							Version: configv3.PluginVersion{
   218  								Major: 1,
   219  								Minor: 2,
   220  								Build: 3,
   221  							},
   222  						}
   223  						fakeActor.GetAndValidatePluginReturns(plugin, nil)
   224  					})
   225  
   226  					It("installs the plugin", func() {
   227  						Expect(executeErr).ToNot(HaveOccurred())
   228  
   229  						Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   230  						Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   231  						Expect(testUI.Out).To(Say(`Installing plugin some-plugin\.\.\.`))
   232  						Expect(testUI.Out).To(Say("OK"))
   233  						Expect(testUI.Out).To(Say(`Plugin some-plugin 1\.2\.3 successfully installed\.`))
   234  
   235  						Expect(fakeActor.FileExistsCallCount()).To(Equal(1))
   236  						Expect(fakeActor.FileExistsArgsForCall(0)).To(Equal("some-path"))
   237  
   238  						Expect(fakeActor.CreateExecutableCopyCallCount()).To(Equal(1))
   239  						pathArg, pluginDirArg := fakeActor.CreateExecutableCopyArgsForCall(0)
   240  						Expect(pathArg).To(Equal("some-path"))
   241  						Expect(pluginDirArg).To(ContainSubstring("some-pluginhome"))
   242  						Expect(pluginDirArg).To(ContainSubstring("temp"))
   243  
   244  						Expect(fakeActor.GetAndValidatePluginCallCount()).To(Equal(1))
   245  						_, _, path := fakeActor.GetAndValidatePluginArgsForCall(0)
   246  						Expect(path).To(Equal("copy-path"))
   247  
   248  						Expect(fakeConfig.GetPluginCaseInsensitiveCallCount()).To(Equal(1))
   249  						Expect(fakeConfig.GetPluginCaseInsensitiveArgsForCall(0)).To(Equal("some-plugin"))
   250  
   251  						Expect(fakeActor.InstallPluginFromPathCallCount()).To(Equal(1))
   252  						path, installedPlugin := fakeActor.InstallPluginFromPathArgsForCall(0)
   253  						Expect(path).To(Equal("copy-path"))
   254  						Expect(installedPlugin).To(Equal(plugin))
   255  
   256  						Expect(fakeActor.UninstallPluginCallCount()).To(Equal(0))
   257  					})
   258  
   259  					When("there is an error making an executable copy of the plugin binary", func() {
   260  						BeforeEach(func() {
   261  							expectedErr = errors.New("create executable copy error")
   262  							fakeActor.CreateExecutableCopyReturns("", expectedErr)
   263  						})
   264  
   265  						It("returns the error", func() {
   266  							Expect(executeErr).To(MatchError(expectedErr))
   267  						})
   268  					})
   269  
   270  					When("an error is encountered installing the plugin", func() {
   271  						BeforeEach(func() {
   272  							expectedErr = errors.New("install plugin error")
   273  							fakeActor.InstallPluginFromPathReturns(expectedErr)
   274  						})
   275  
   276  						It("returns the error", func() {
   277  							Expect(executeErr).To(MatchError(expectedErr))
   278  
   279  							Expect(testUI.Out).ToNot(Say(`Plugin some-plugin 1\.2\.3 successfully installed\.`))
   280  						})
   281  					})
   282  				})
   283  			})
   284  
   285  			When("the -f argument is not given (user is prompted for confirmation)", func() {
   286  				BeforeEach(func() {
   287  					cmd.Force = false
   288  				})
   289  
   290  				When("the user chooses no", func() {
   291  					BeforeEach(func() {
   292  						_, err := input.Write([]byte("n\n"))
   293  						Expect(err).ToNot(HaveOccurred())
   294  					})
   295  
   296  					It("cancels plugin installation", func() {
   297  						Expect(executeErr).ToNot(HaveOccurred())
   298  
   299  						Expect(testUI.Out).To(Say(`Plugin installation cancelled\.`))
   300  					})
   301  				})
   302  
   303  				When("the user chooses the default", func() {
   304  					BeforeEach(func() {
   305  						_, err := input.Write([]byte("\n"))
   306  						Expect(err).ToNot(HaveOccurred())
   307  					})
   308  
   309  					It("cancels plugin installation", func() {
   310  						Expect(executeErr).ToNot(HaveOccurred())
   311  
   312  						Expect(testUI.Out).To(Say(`Plugin installation cancelled\.`))
   313  					})
   314  				})
   315  
   316  				When("the user input is invalid", func() {
   317  					BeforeEach(func() {
   318  						_, err := input.Write([]byte("e\n"))
   319  						Expect(err).ToNot(HaveOccurred())
   320  					})
   321  
   322  					It("returns an error", func() {
   323  						Expect(executeErr).To(HaveOccurred())
   324  
   325  						Expect(testUI.Out).ToNot(Say("Installing plugin"))
   326  					})
   327  				})
   328  
   329  				When("the user chooses yes", func() {
   330  					BeforeEach(func() {
   331  						_, err := input.Write([]byte("y\n"))
   332  						Expect(err).ToNot(HaveOccurred())
   333  					})
   334  
   335  					When("the plugin is not already installed", func() {
   336  						var plugin configv3.Plugin
   337  
   338  						BeforeEach(func() {
   339  							plugin = configv3.Plugin{
   340  								Name: "some-plugin",
   341  								Version: configv3.PluginVersion{
   342  									Major: 1,
   343  									Minor: 2,
   344  									Build: 3,
   345  								},
   346  							}
   347  							fakeActor.GetAndValidatePluginReturns(plugin, nil)
   348  						})
   349  
   350  						It("installs the plugin", func() {
   351  							Expect(executeErr).ToNot(HaveOccurred())
   352  
   353  							Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   354  							Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   355  							Expect(testUI.Out).To(Say(`Do you want to install the plugin some-path\? \[yN\]`))
   356  							Expect(testUI.Out).To(Say(`Installing plugin some-plugin\.\.\.`))
   357  							Expect(testUI.Out).To(Say("OK"))
   358  							Expect(testUI.Out).To(Say(`Plugin some-plugin 1\.2\.3 successfully installed\.`))
   359  
   360  							Expect(fakeActor.FileExistsCallCount()).To(Equal(1))
   361  							Expect(fakeActor.FileExistsArgsForCall(0)).To(Equal("some-path"))
   362  
   363  							Expect(fakeActor.GetAndValidatePluginCallCount()).To(Equal(1))
   364  							_, _, path := fakeActor.GetAndValidatePluginArgsForCall(0)
   365  							Expect(path).To(Equal("copy-path"))
   366  
   367  							Expect(fakeConfig.GetPluginCaseInsensitiveCallCount()).To(Equal(1))
   368  							Expect(fakeConfig.GetPluginCaseInsensitiveArgsForCall(0)).To(Equal("some-plugin"))
   369  
   370  							Expect(fakeActor.InstallPluginFromPathCallCount()).To(Equal(1))
   371  							path, plugin := fakeActor.InstallPluginFromPathArgsForCall(0)
   372  							Expect(path).To(Equal("copy-path"))
   373  							Expect(plugin).To(Equal(plugin))
   374  
   375  							Expect(fakeActor.UninstallPluginCallCount()).To(Equal(0))
   376  						})
   377  					})
   378  
   379  					When("the plugin is already installed", func() {
   380  						BeforeEach(func() {
   381  							fakeConfig.GetPluginCaseInsensitiveReturns(configv3.Plugin{
   382  								Name: "some-plugin",
   383  								Version: configv3.PluginVersion{
   384  									Major: 1,
   385  									Minor: 2,
   386  									Build: 2,
   387  								},
   388  							}, true)
   389  							fakeActor.GetAndValidatePluginReturns(configv3.Plugin{
   390  								Name: "some-plugin",
   391  								Version: configv3.PluginVersion{
   392  									Major: 1,
   393  									Minor: 2,
   394  									Build: 3,
   395  								},
   396  							}, nil)
   397  						})
   398  
   399  						It("returns PluginAlreadyInstalledError", func() {
   400  							Expect(executeErr).To(MatchError(translatableerror.PluginAlreadyInstalledError{
   401  								BinaryName: "faceman",
   402  								Name:       "some-plugin",
   403  								Version:    "1.2.3",
   404  							}))
   405  						})
   406  					})
   407  				})
   408  			})
   409  		})
   410  	})
   411  
   412  	Describe("installing from an unsupported URL scheme", func() {
   413  		BeforeEach(func() {
   414  			cmd.OptionalArgs.PluginNameOrLocation = "ftp://some-url"
   415  		})
   416  
   417  		It("returns an error indicating an unsupported URL scheme", func() {
   418  			Expect(executeErr).To(MatchError(translatableerror.UnsupportedURLSchemeError{
   419  				UnsupportedURL: string(cmd.OptionalArgs.PluginNameOrLocation),
   420  			}))
   421  		})
   422  	})
   423  
   424  	Describe("installing from an HTTP URL", func() {
   425  		var (
   426  			plugin               configv3.Plugin
   427  			pluginName           string
   428  			executablePluginPath string
   429  		)
   430  
   431  		BeforeEach(func() {
   432  			cmd.OptionalArgs.PluginNameOrLocation = "http://some-url"
   433  			pluginName = "some-plugin"
   434  			executablePluginPath = "executable-path"
   435  		})
   436  
   437  		It("displays the plugin warning", func() {
   438  			Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   439  			Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   440  		})
   441  
   442  		When("the -f argument is given", func() {
   443  			BeforeEach(func() {
   444  				cmd.Force = true
   445  			})
   446  
   447  			It("begins downloading the plugin", func() {
   448  				Expect(testUI.Out).To(Say(`Starting download of plugin binary from URL\.\.\.`))
   449  
   450  				Expect(fakeActor.DownloadExecutableBinaryFromURLCallCount()).To(Equal(1))
   451  				url, tempPluginDir, proxyReader := fakeActor.DownloadExecutableBinaryFromURLArgsForCall(0)
   452  				Expect(url).To(Equal(cmd.OptionalArgs.PluginNameOrLocation.String()))
   453  				Expect(tempPluginDir).To(ContainSubstring("some-pluginhome"))
   454  				Expect(tempPluginDir).To(ContainSubstring("temp"))
   455  				Expect(proxyReader).To(Equal(fakeProgressBar))
   456  			})
   457  
   458  			When("getting the binary fails", func() {
   459  				BeforeEach(func() {
   460  					expectedErr = errors.New("some-error")
   461  					fakeActor.DownloadExecutableBinaryFromURLReturns("", expectedErr)
   462  				})
   463  
   464  				It("returns the error", func() {
   465  					Expect(executeErr).To(MatchError(expectedErr))
   466  
   467  					Expect(testUI.Out).ToNot(Say("downloaded"))
   468  					Expect(fakeActor.GetAndValidatePluginCallCount()).To(Equal(0))
   469  				})
   470  
   471  				When("a 4xx or 5xx status is encountered while downloading the plugin", func() {
   472  					BeforeEach(func() {
   473  						fakeActor.DownloadExecutableBinaryFromURLReturns("", pluginerror.RawHTTPStatusError{Status: "some-status"})
   474  					})
   475  
   476  					It("returns a DownloadPluginHTTPError", func() {
   477  						Expect(executeErr).To(MatchError(pluginerror.RawHTTPStatusError{Status: "some-status"}))
   478  					})
   479  				})
   480  
   481  				When("a SSL error is encountered while downloading the plugin", func() {
   482  					BeforeEach(func() {
   483  						fakeActor.DownloadExecutableBinaryFromURLReturns("", pluginerror.UnverifiedServerError{})
   484  					})
   485  
   486  					It("returns a DownloadPluginHTTPError", func() {
   487  						Expect(executeErr).To(MatchError(pluginerror.UnverifiedServerError{}))
   488  					})
   489  				})
   490  			})
   491  
   492  			When("getting the binary succeeds", func() {
   493  				BeforeEach(func() {
   494  					fakeActor.DownloadExecutableBinaryFromURLReturns("some-path", nil)
   495  					fakeActor.CreateExecutableCopyReturns(executablePluginPath, nil)
   496  				})
   497  
   498  				It("sets up the progress bar", func() {
   499  					Expect(fakeActor.GetAndValidatePluginCallCount()).To(Equal(1))
   500  					_, _, path := fakeActor.GetAndValidatePluginArgsForCall(0)
   501  					Expect(path).To(Equal(executablePluginPath))
   502  
   503  					Expect(fakeActor.DownloadExecutableBinaryFromURLCallCount()).To(Equal(1))
   504  					urlArg, pluginDirArg, proxyReader := fakeActor.DownloadExecutableBinaryFromURLArgsForCall(0)
   505  					Expect(urlArg).To(Equal("http://some-url"))
   506  					Expect(pluginDirArg).To(ContainSubstring("some-pluginhome"))
   507  					Expect(pluginDirArg).To(ContainSubstring("temp"))
   508  					Expect(proxyReader).To(Equal(fakeProgressBar))
   509  
   510  					Expect(fakeActor.CreateExecutableCopyCallCount()).To(Equal(1))
   511  					pathArg, pluginDirArg := fakeActor.CreateExecutableCopyArgsForCall(0)
   512  					Expect(pathArg).To(Equal("some-path"))
   513  					Expect(pluginDirArg).To(ContainSubstring("some-pluginhome"))
   514  					Expect(pluginDirArg).To(ContainSubstring("temp"))
   515  				})
   516  
   517  				When("the plugin is invalid", func() {
   518  					var returnedErr error
   519  
   520  					BeforeEach(func() {
   521  						returnedErr = actionerror.PluginInvalidError{}
   522  						fakeActor.GetAndValidatePluginReturns(configv3.Plugin{}, returnedErr)
   523  					})
   524  
   525  					It("returns an error", func() {
   526  						Expect(executeErr).To(MatchError(returnedErr))
   527  
   528  						Expect(fakeConfig.GetPluginCaseInsensitiveCallCount()).To(Equal(0))
   529  					})
   530  				})
   531  
   532  				When("the plugin is valid", func() {
   533  					var newPlugin configv3.Plugin
   534  
   535  					BeforeEach(func() {
   536  						plugin = configv3.Plugin{
   537  							Name: pluginName,
   538  							Version: configv3.PluginVersion{
   539  								Major: 1,
   540  								Minor: 2,
   541  								Build: 2,
   542  							},
   543  						}
   544  						newPlugin = configv3.Plugin{
   545  							Name: pluginName,
   546  							Version: configv3.PluginVersion{
   547  								Major: 1,
   548  								Minor: 2,
   549  								Build: 3,
   550  							},
   551  						}
   552  						fakeActor.GetAndValidatePluginReturns(newPlugin, nil)
   553  					})
   554  
   555  					When("the plugin is already installed", func() {
   556  						BeforeEach(func() {
   557  							fakeConfig.GetPluginCaseInsensitiveReturns(plugin, true)
   558  						})
   559  
   560  						It("displays uninstall message", func() {
   561  							Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.2 is already installed\. Uninstalling existing plugin\.\.\.`, pluginName))
   562  						})
   563  
   564  						When("an error is encountered uninstalling the existing plugin", func() {
   565  							BeforeEach(func() {
   566  								expectedErr = errors.New("uninstall plugin error")
   567  								fakeActor.UninstallPluginReturns(expectedErr)
   568  							})
   569  
   570  							It("returns the error", func() {
   571  								Expect(executeErr).To(MatchError(expectedErr))
   572  
   573  								Expect(testUI.Out).ToNot(Say(`Plugin some-plugin successfully uninstalled\.`))
   574  							})
   575  						})
   576  
   577  						When("no errors are encountered uninstalling the existing plugin", func() {
   578  							It("displays uninstall message", func() {
   579  								Expect(testUI.Out).To(Say(`Plugin %s successfully uninstalled\.`, pluginName))
   580  							})
   581  
   582  							When("no errors are encountered installing the plugin", func() {
   583  								It("uninstalls the existing plugin and installs the current plugin", func() {
   584  									Expect(executeErr).ToNot(HaveOccurred())
   585  
   586  									Expect(testUI.Out).To(Say(`Installing plugin %s\.\.\.`, pluginName))
   587  									Expect(testUI.Out).To(Say("OK"))
   588  									Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.3 successfully installed\.`, pluginName))
   589  								})
   590  							})
   591  
   592  							When("an error is encountered installing the plugin", func() {
   593  								BeforeEach(func() {
   594  									expectedErr = errors.New("install plugin error")
   595  									fakeActor.InstallPluginFromPathReturns(expectedErr)
   596  								})
   597  
   598  								It("returns the error", func() {
   599  									Expect(executeErr).To(MatchError(expectedErr))
   600  
   601  									Expect(testUI.Out).ToNot(Say(`Plugin some-plugin 1\.2\.3 successfully installed\.`))
   602  								})
   603  							})
   604  						})
   605  					})
   606  
   607  					When("the plugin is not already installed", func() {
   608  						It("installs the plugin", func() {
   609  							Expect(executeErr).ToNot(HaveOccurred())
   610  
   611  							Expect(testUI.Out).To(Say(`Installing plugin %s\.\.\.`, pluginName))
   612  							Expect(testUI.Out).To(Say("OK"))
   613  							Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.3 successfully installed\.`, pluginName))
   614  
   615  							Expect(fakeActor.UninstallPluginCallCount()).To(Equal(0))
   616  						})
   617  					})
   618  				})
   619  			})
   620  		})
   621  
   622  		When("the -f argument is not given (user is prompted for confirmation)", func() {
   623  			BeforeEach(func() {
   624  				plugin = configv3.Plugin{
   625  					Name: pluginName,
   626  					Version: configv3.PluginVersion{
   627  						Major: 1,
   628  						Minor: 2,
   629  						Build: 3,
   630  					},
   631  				}
   632  
   633  				cmd.Force = false
   634  				fakeActor.DownloadExecutableBinaryFromURLReturns("some-path", nil)
   635  				fakeActor.CreateExecutableCopyReturns("executable-path", nil)
   636  			})
   637  
   638  			When("the user chooses no", func() {
   639  				BeforeEach(func() {
   640  					_, err := input.Write([]byte("n\n"))
   641  					Expect(err).ToNot(HaveOccurred())
   642  				})
   643  
   644  				It("cancels plugin installation", func() {
   645  					Expect(executeErr).ToNot(HaveOccurred())
   646  
   647  					Expect(testUI.Out).To(Say(`Plugin installation cancelled\.`))
   648  				})
   649  			})
   650  
   651  			When("the user chooses the default", func() {
   652  				BeforeEach(func() {
   653  					_, err := input.Write([]byte("\n"))
   654  					Expect(err).ToNot(HaveOccurred())
   655  				})
   656  
   657  				It("cancels plugin installation", func() {
   658  					Expect(executeErr).ToNot(HaveOccurred())
   659  
   660  					Expect(testUI.Out).To(Say(`Plugin installation cancelled\.`))
   661  				})
   662  			})
   663  
   664  			When("the user input is invalid", func() {
   665  				BeforeEach(func() {
   666  					_, err := input.Write([]byte("e\n"))
   667  					Expect(err).ToNot(HaveOccurred())
   668  				})
   669  
   670  				It("returns an error", func() {
   671  					Expect(executeErr).To(HaveOccurred())
   672  
   673  					Expect(testUI.Out).ToNot(Say("Installing plugin"))
   674  				})
   675  			})
   676  
   677  			When("the user chooses yes", func() {
   678  				BeforeEach(func() {
   679  					_, err := input.Write([]byte("y\n"))
   680  					Expect(err).ToNot(HaveOccurred())
   681  				})
   682  
   683  				When("the plugin is not already installed", func() {
   684  					BeforeEach(func() {
   685  						fakeActor.GetAndValidatePluginReturns(plugin, nil)
   686  					})
   687  
   688  					It("installs the plugin", func() {
   689  						Expect(executeErr).ToNot(HaveOccurred())
   690  
   691  						Expect(testUI.Out).To(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`))
   692  						Expect(testUI.Out).To(Say(`Install and use plugins at your own risk\.`))
   693  						Expect(testUI.Out).To(Say(`Do you want to install the plugin %s\? \[yN\]`, cmd.OptionalArgs.PluginNameOrLocation))
   694  						Expect(testUI.Out).To(Say(`Starting download of plugin binary from URL\.\.\.`))
   695  						Expect(testUI.Out).To(Say(`Installing plugin %s\.\.\.`, pluginName))
   696  						Expect(testUI.Out).To(Say("OK"))
   697  						Expect(testUI.Out).To(Say(`Plugin %s 1\.2\.3 successfully installed\.`, pluginName))
   698  
   699  						Expect(fakeActor.DownloadExecutableBinaryFromURLCallCount()).To(Equal(1))
   700  						url, tempPluginDir, proxyReader := fakeActor.DownloadExecutableBinaryFromURLArgsForCall(0)
   701  						Expect(url).To(Equal(cmd.OptionalArgs.PluginNameOrLocation.String()))
   702  						Expect(tempPluginDir).To(ContainSubstring("some-pluginhome"))
   703  						Expect(tempPluginDir).To(ContainSubstring("temp"))
   704  						Expect(proxyReader).To(Equal(fakeProgressBar))
   705  
   706  						Expect(fakeActor.CreateExecutableCopyCallCount()).To(Equal(1))
   707  						path, tempPluginDir := fakeActor.CreateExecutableCopyArgsForCall(0)
   708  						Expect(path).To(Equal("some-path"))
   709  						Expect(tempPluginDir).To(ContainSubstring("some-pluginhome"))
   710  						Expect(tempPluginDir).To(ContainSubstring("temp"))
   711  
   712  						Expect(fakeActor.GetAndValidatePluginCallCount()).To(Equal(1))
   713  						_, _, path = fakeActor.GetAndValidatePluginArgsForCall(0)
   714  						Expect(path).To(Equal(executablePluginPath))
   715  
   716  						Expect(fakeConfig.GetPluginCaseInsensitiveCallCount()).To(Equal(1))
   717  						Expect(fakeConfig.GetPluginCaseInsensitiveArgsForCall(0)).To(Equal(pluginName))
   718  
   719  						Expect(fakeActor.InstallPluginFromPathCallCount()).To(Equal(1))
   720  						path, installedPlugin := fakeActor.InstallPluginFromPathArgsForCall(0)
   721  						Expect(path).To(Equal(executablePluginPath))
   722  						Expect(installedPlugin).To(Equal(plugin))
   723  
   724  						Expect(fakeActor.UninstallPluginCallCount()).To(Equal(0))
   725  					})
   726  				})
   727  
   728  				When("the plugin is already installed", func() {
   729  					BeforeEach(func() {
   730  						fakeConfig.GetPluginCaseInsensitiveReturns(configv3.Plugin{
   731  							Name: "some-plugin",
   732  							Version: configv3.PluginVersion{
   733  								Major: 1,
   734  								Minor: 2,
   735  								Build: 2,
   736  							},
   737  						}, true)
   738  						fakeActor.GetAndValidatePluginReturns(configv3.Plugin{
   739  							Name: "some-plugin",
   740  							Version: configv3.PluginVersion{
   741  								Major: 1,
   742  								Minor: 2,
   743  								Build: 3,
   744  							},
   745  						}, nil)
   746  					})
   747  
   748  					It("returns PluginAlreadyInstalledError", func() {
   749  						Expect(executeErr).To(MatchError(translatableerror.PluginAlreadyInstalledError{
   750  							BinaryName: "faceman",
   751  							Name:       pluginName,
   752  							Version:    "1.2.3",
   753  						}))
   754  					})
   755  				})
   756  			})
   757  		})
   758  	})
   759  })