github.com/cloudfoundry-attic/cli-with-i18n@v6.32.1-0.20171002233121-7401370d3b85+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/pluginaction"
     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  		Context("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  		Context("when the file exists", func() {
    84  			BeforeEach(func() {
    85  				fakeActor.CreateExecutableCopyReturns("copy-path", nil)
    86  				fakeActor.FileExistsReturns(true)
    87  			})
    88  
    89  			Context("when the -f argument is given", func() {
    90  				BeforeEach(func() {
    91  					cmd.Force = true
    92  				})
    93  
    94  				Context("when the plugin is invalid", func() {
    95  					var returnedErr error
    96  
    97  					BeforeEach(func() {
    98  						returnedErr = pluginaction.PluginInvalidError{}
    99  						fakeActor.GetAndValidatePluginReturns(configv3.Plugin{}, returnedErr)
   100  					})
   101  
   102  					It("returns an error", func() {
   103  						Expect(executeErr).To(MatchError(translatableerror.PluginInvalidError{}))
   104  
   105  						Expect(testUI.Out).ToNot(Say("Installing plugin"))
   106  					})
   107  				})
   108  
   109  				Context("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{}, pluginaction.PluginInvalidError{Err: wrappedErr})
   115  					})
   116  
   117  					It("returns an error", func() {
   118  						Expect(executeErr).To(MatchError(translatableerror.PluginInvalidError{Err: wrappedErr}))
   119  
   120  						Expect(testUI.Out).ToNot(Say("Installing plugin"))
   121  					})
   122  				})
   123  
   124  				Context("when the plugin is already installed", func() {
   125  					var plugin configv3.Plugin
   126  
   127  					BeforeEach(func() {
   128  						plugin = configv3.Plugin{
   129  							Name: "some-plugin",
   130  							Version: configv3.PluginVersion{
   131  								Major: 1,
   132  								Minor: 2,
   133  								Build: 3,
   134  							},
   135  						}
   136  						fakeActor.GetAndValidatePluginReturns(plugin, nil)
   137  						fakeActor.IsPluginInstalledReturns(true)
   138  					})
   139  
   140  					Context("when an error is encountered uninstalling the existing plugin", func() {
   141  						BeforeEach(func() {
   142  							expectedErr = errors.New("uninstall plugin error")
   143  							fakeActor.UninstallPluginReturns(expectedErr)
   144  						})
   145  
   146  						It("returns the error", func() {
   147  							Expect(executeErr).To(MatchError(expectedErr))
   148  
   149  							Expect(testUI.Out).ToNot(Say("Plugin some-plugin successfully uninstalled\\."))
   150  						})
   151  					})
   152  
   153  					Context("when no errors are encountered uninstalling the existing plugin", func() {
   154  						It("uninstalls the existing plugin and installs the current plugin", func() {
   155  							Expect(executeErr).ToNot(HaveOccurred())
   156  
   157  							Expect(testUI.Out).To(Say("Attention: Plugins are binaries written by potentially untrusted authors\\."))
   158  							Expect(testUI.Out).To(Say("Install and use plugins at your own risk\\."))
   159  							Expect(testUI.Out).To(Say("Plugin some-plugin 1\\.2\\.3 is already installed\\. Uninstalling existing plugin\\.\\.\\."))
   160  							Expect(testUI.Out).To(Say("OK"))
   161  							Expect(testUI.Out).To(Say("Plugin some-plugin successfully uninstalled\\."))
   162  							Expect(testUI.Out).To(Say("Installing plugin some-plugin\\.\\.\\."))
   163  							Expect(testUI.Out).To(Say("OK"))
   164  							Expect(testUI.Out).To(Say("Plugin some-plugin 1\\.2\\.3 successfully installed\\."))
   165  
   166  							Expect(fakeActor.FileExistsCallCount()).To(Equal(1))
   167  							Expect(fakeActor.FileExistsArgsForCall(0)).To(Equal("some-path"))
   168  
   169  							Expect(fakeActor.GetAndValidatePluginCallCount()).To(Equal(1))
   170  							_, _, path := fakeActor.GetAndValidatePluginArgsForCall(0)
   171  							Expect(path).To(Equal("copy-path"))
   172  
   173  							Expect(fakeActor.IsPluginInstalledCallCount()).To(Equal(1))
   174  							Expect(fakeActor.IsPluginInstalledArgsForCall(0)).To(Equal("some-plugin"))
   175  
   176  							Expect(fakeActor.UninstallPluginCallCount()).To(Equal(1))
   177  							_, pluginName := fakeActor.UninstallPluginArgsForCall(0)
   178  							Expect(pluginName).To(Equal("some-plugin"))
   179  
   180  							Expect(fakeActor.InstallPluginFromPathCallCount()).To(Equal(1))
   181  							path, installedPlugin := fakeActor.InstallPluginFromPathArgsForCall(0)
   182  							Expect(path).To(Equal("copy-path"))
   183  							Expect(installedPlugin).To(Equal(plugin))
   184  						})
   185  
   186  						Context("when an error is encountered installing the plugin", func() {
   187  							BeforeEach(func() {
   188  								expectedErr = errors.New("install plugin error")
   189  								fakeActor.InstallPluginFromPathReturns(expectedErr)
   190  							})
   191  
   192  							It("returns the error", func() {
   193  								Expect(executeErr).To(MatchError(expectedErr))
   194  
   195  								Expect(testUI.Out).ToNot(Say("Plugin some-plugin 1\\.2\\.3 successfully installed\\."))
   196  							})
   197  						})
   198  					})
   199  				})
   200  
   201  				Context("when the plugin is not already installed", func() {
   202  					var plugin configv3.Plugin
   203  
   204  					BeforeEach(func() {
   205  						plugin = configv3.Plugin{
   206  							Name: "some-plugin",
   207  							Version: configv3.PluginVersion{
   208  								Major: 1,
   209  								Minor: 2,
   210  								Build: 3,
   211  							},
   212  						}
   213  						fakeActor.GetAndValidatePluginReturns(plugin, nil)
   214  					})
   215  
   216  					It("installs the plugin", func() {
   217  						Expect(executeErr).ToNot(HaveOccurred())
   218  
   219  						Expect(testUI.Out).To(Say("Attention: Plugins are binaries written by potentially untrusted authors\\."))
   220  						Expect(testUI.Out).To(Say("Install and use plugins at your own risk\\."))
   221  						Expect(testUI.Out).To(Say("Installing plugin some-plugin\\.\\.\\."))
   222  						Expect(testUI.Out).To(Say("OK"))
   223  						Expect(testUI.Out).To(Say("Plugin some-plugin 1\\.2\\.3 successfully installed\\."))
   224  
   225  						Expect(fakeActor.FileExistsCallCount()).To(Equal(1))
   226  						Expect(fakeActor.FileExistsArgsForCall(0)).To(Equal("some-path"))
   227  
   228  						Expect(fakeActor.CreateExecutableCopyCallCount()).To(Equal(1))
   229  						pathArg, pluginDirArg := fakeActor.CreateExecutableCopyArgsForCall(0)
   230  						Expect(pathArg).To(Equal("some-path"))
   231  						Expect(pluginDirArg).To(ContainSubstring("some-pluginhome"))
   232  						Expect(pluginDirArg).To(ContainSubstring("temp"))
   233  
   234  						Expect(fakeActor.GetAndValidatePluginCallCount()).To(Equal(1))
   235  						_, _, path := fakeActor.GetAndValidatePluginArgsForCall(0)
   236  						Expect(path).To(Equal("copy-path"))
   237  
   238  						Expect(fakeActor.IsPluginInstalledCallCount()).To(Equal(1))
   239  						Expect(fakeActor.IsPluginInstalledArgsForCall(0)).To(Equal("some-plugin"))
   240  
   241  						Expect(fakeActor.InstallPluginFromPathCallCount()).To(Equal(1))
   242  						path, installedPlugin := fakeActor.InstallPluginFromPathArgsForCall(0)
   243  						Expect(path).To(Equal("copy-path"))
   244  						Expect(installedPlugin).To(Equal(plugin))
   245  
   246  						Expect(fakeActor.UninstallPluginCallCount()).To(Equal(0))
   247  					})
   248  
   249  					Context("when there is an error making an executable copy of the plugin binary", func() {
   250  						BeforeEach(func() {
   251  							expectedErr = errors.New("create executable copy error")
   252  							fakeActor.CreateExecutableCopyReturns("", expectedErr)
   253  						})
   254  
   255  						It("returns the error", func() {
   256  							Expect(executeErr).To(MatchError(expectedErr))
   257  						})
   258  					})
   259  
   260  					Context("when an error is encountered installing the plugin", func() {
   261  						BeforeEach(func() {
   262  							expectedErr = errors.New("install plugin error")
   263  							fakeActor.InstallPluginFromPathReturns(expectedErr)
   264  						})
   265  
   266  						It("returns the error", func() {
   267  							Expect(executeErr).To(MatchError(expectedErr))
   268  
   269  							Expect(testUI.Out).ToNot(Say("Plugin some-plugin 1\\.2\\.3 successfully installed\\."))
   270  						})
   271  					})
   272  				})
   273  			})
   274  
   275  			Context("when the -f argument is not given (user is prompted for confirmation)", func() {
   276  				BeforeEach(func() {
   277  					cmd.Force = false
   278  				})
   279  
   280  				Context("when the user chooses no", func() {
   281  					BeforeEach(func() {
   282  						input.Write([]byte("n\n"))
   283  					})
   284  
   285  					It("cancels plugin installation", func() {
   286  						Expect(executeErr).ToNot(HaveOccurred())
   287  
   288  						Expect(testUI.Out).To(Say("Plugin installation cancelled\\."))
   289  					})
   290  				})
   291  
   292  				Context("when the user chooses the default", func() {
   293  					BeforeEach(func() {
   294  						input.Write([]byte("\n"))
   295  					})
   296  
   297  					It("cancels plugin installation", func() {
   298  						Expect(executeErr).ToNot(HaveOccurred())
   299  
   300  						Expect(testUI.Out).To(Say("Plugin installation cancelled\\."))
   301  					})
   302  				})
   303  
   304  				Context("when the user input is invalid", func() {
   305  					BeforeEach(func() {
   306  						input.Write([]byte("e\n"))
   307  					})
   308  
   309  					It("returns an error", func() {
   310  						Expect(executeErr).To(HaveOccurred())
   311  
   312  						Expect(testUI.Out).ToNot(Say("Installing plugin"))
   313  					})
   314  				})
   315  
   316  				Context("when the user chooses yes", func() {
   317  					BeforeEach(func() {
   318  						input.Write([]byte("y\n"))
   319  					})
   320  
   321  					Context("when the plugin is not already installed", func() {
   322  						var plugin configv3.Plugin
   323  
   324  						BeforeEach(func() {
   325  							plugin = configv3.Plugin{
   326  								Name: "some-plugin",
   327  								Version: configv3.PluginVersion{
   328  									Major: 1,
   329  									Minor: 2,
   330  									Build: 3,
   331  								},
   332  							}
   333  							fakeActor.GetAndValidatePluginReturns(plugin, nil)
   334  						})
   335  
   336  						It("installs the plugin", func() {
   337  							Expect(executeErr).ToNot(HaveOccurred())
   338  
   339  							Expect(testUI.Out).To(Say("Attention: Plugins are binaries written by potentially untrusted authors\\."))
   340  							Expect(testUI.Out).To(Say("Install and use plugins at your own risk\\."))
   341  							Expect(testUI.Out).To(Say("Do you want to install the plugin some-path\\? \\[yN\\]"))
   342  							Expect(testUI.Out).To(Say("Installing plugin some-plugin\\.\\.\\."))
   343  							Expect(testUI.Out).To(Say("OK"))
   344  							Expect(testUI.Out).To(Say("Plugin some-plugin 1\\.2\\.3 successfully installed\\."))
   345  
   346  							Expect(fakeActor.FileExistsCallCount()).To(Equal(1))
   347  							Expect(fakeActor.FileExistsArgsForCall(0)).To(Equal("some-path"))
   348  
   349  							Expect(fakeActor.GetAndValidatePluginCallCount()).To(Equal(1))
   350  							_, _, path := fakeActor.GetAndValidatePluginArgsForCall(0)
   351  							Expect(path).To(Equal("copy-path"))
   352  
   353  							Expect(fakeActor.IsPluginInstalledCallCount()).To(Equal(1))
   354  							Expect(fakeActor.IsPluginInstalledArgsForCall(0)).To(Equal("some-plugin"))
   355  
   356  							Expect(fakeActor.InstallPluginFromPathCallCount()).To(Equal(1))
   357  							path, plugin := fakeActor.InstallPluginFromPathArgsForCall(0)
   358  							Expect(path).To(Equal("copy-path"))
   359  							Expect(plugin).To(Equal(plugin))
   360  
   361  							Expect(fakeActor.UninstallPluginCallCount()).To(Equal(0))
   362  						})
   363  					})
   364  
   365  					Context("when the plugin is already installed", func() {
   366  						BeforeEach(func() {
   367  							plugin := configv3.Plugin{
   368  								Name: "some-plugin",
   369  								Version: configv3.PluginVersion{
   370  									Major: 1,
   371  									Minor: 2,
   372  									Build: 3,
   373  								},
   374  							}
   375  							fakeActor.GetAndValidatePluginReturns(plugin, nil)
   376  							fakeActor.IsPluginInstalledReturns(true)
   377  						})
   378  
   379  						It("returns PluginAlreadyInstalledError", func() {
   380  							Expect(executeErr).To(MatchError(translatableerror.PluginAlreadyInstalledError{
   381  								BinaryName: "faceman",
   382  								Name:       "some-plugin",
   383  								Version:    "1.2.3",
   384  							}))
   385  						})
   386  					})
   387  				})
   388  			})
   389  		})
   390  	})
   391  
   392  	Describe("installing from an unsupported URL scheme", func() {
   393  		BeforeEach(func() {
   394  			cmd.OptionalArgs.PluginNameOrLocation = "ftp://some-url"
   395  		})
   396  
   397  		It("returns an error indicating an unsupported URL scheme", func() {
   398  			Expect(executeErr).To(MatchError(translatableerror.UnsupportedURLSchemeError{
   399  				UnsupportedURL: string(cmd.OptionalArgs.PluginNameOrLocation),
   400  			}))
   401  		})
   402  	})
   403  
   404  	Describe("installing from an HTTP URL", func() {
   405  		var (
   406  			plugin               configv3.Plugin
   407  			pluginName           string
   408  			executablePluginPath string
   409  		)
   410  
   411  		BeforeEach(func() {
   412  			cmd.OptionalArgs.PluginNameOrLocation = "http://some-url"
   413  			pluginName = "some-plugin"
   414  			executablePluginPath = "executable-path"
   415  		})
   416  
   417  		It("displays the plugin warning", func() {
   418  			Expect(testUI.Out).To(Say("Attention: Plugins are binaries written by potentially untrusted authors\\."))
   419  			Expect(testUI.Out).To(Say("Install and use plugins at your own risk\\."))
   420  		})
   421  
   422  		Context("when the -f argument is given", func() {
   423  			BeforeEach(func() {
   424  				cmd.Force = true
   425  			})
   426  
   427  			It("begins downloading the plugin", func() {
   428  				Expect(testUI.Out).To(Say("Starting download of plugin binary from URL\\.\\.\\."))
   429  
   430  				Expect(fakeActor.DownloadExecutableBinaryFromURLCallCount()).To(Equal(1))
   431  				url, tempPluginDir, proxyReader := fakeActor.DownloadExecutableBinaryFromURLArgsForCall(0)
   432  				Expect(url).To(Equal(cmd.OptionalArgs.PluginNameOrLocation.String()))
   433  				Expect(tempPluginDir).To(ContainSubstring("some-pluginhome"))
   434  				Expect(tempPluginDir).To(ContainSubstring("temp"))
   435  				Expect(proxyReader).To(Equal(fakeProgressBar))
   436  			})
   437  
   438  			Context("When getting the binary fails", func() {
   439  				BeforeEach(func() {
   440  					expectedErr = errors.New("some-error")
   441  					fakeActor.DownloadExecutableBinaryFromURLReturns("", expectedErr)
   442  				})
   443  
   444  				It("returns the error", func() {
   445  					Expect(executeErr).To(MatchError(expectedErr))
   446  
   447  					Expect(testUI.Out).ToNot(Say("downloaded"))
   448  					Expect(fakeActor.GetAndValidatePluginCallCount()).To(Equal(0))
   449  				})
   450  
   451  				Context("when a 4xx or 5xx status is encountered while downloading the plugin", func() {
   452  					BeforeEach(func() {
   453  						fakeActor.DownloadExecutableBinaryFromURLReturns("", pluginerror.RawHTTPStatusError{Status: "some-status"})
   454  					})
   455  
   456  					It("returns a DownloadPluginHTTPError", func() {
   457  						Expect(executeErr).To(MatchError(translatableerror.DownloadPluginHTTPError{Message: "some-status"}))
   458  					})
   459  				})
   460  
   461  				Context("when a SSL error is encountered while downloading the plugin", func() {
   462  					BeforeEach(func() {
   463  						fakeActor.DownloadExecutableBinaryFromURLReturns("", pluginerror.UnverifiedServerError{})
   464  					})
   465  
   466  					It("returns a DownloadPluginHTTPError", func() {
   467  						Expect(executeErr).To(MatchError(translatableerror.DownloadPluginHTTPError{Message: "x509: certificate signed by unknown authority"}))
   468  					})
   469  				})
   470  			})
   471  
   472  			Context("when getting the binary succeeds", func() {
   473  				BeforeEach(func() {
   474  					fakeActor.DownloadExecutableBinaryFromURLReturns("some-path", nil)
   475  					fakeActor.CreateExecutableCopyReturns(executablePluginPath, nil)
   476  				})
   477  
   478  				It("sets up the progress bar", func() {
   479  					Expect(fakeActor.GetAndValidatePluginCallCount()).To(Equal(1))
   480  					_, _, path := fakeActor.GetAndValidatePluginArgsForCall(0)
   481  					Expect(path).To(Equal(executablePluginPath))
   482  
   483  					Expect(fakeActor.DownloadExecutableBinaryFromURLCallCount()).To(Equal(1))
   484  					urlArg, pluginDirArg, proxyReader := fakeActor.DownloadExecutableBinaryFromURLArgsForCall(0)
   485  					Expect(urlArg).To(Equal("http://some-url"))
   486  					Expect(pluginDirArg).To(ContainSubstring("some-pluginhome"))
   487  					Expect(pluginDirArg).To(ContainSubstring("temp"))
   488  					Expect(proxyReader).To(Equal(fakeProgressBar))
   489  
   490  					Expect(fakeActor.CreateExecutableCopyCallCount()).To(Equal(1))
   491  					pathArg, pluginDirArg := fakeActor.CreateExecutableCopyArgsForCall(0)
   492  					Expect(pathArg).To(Equal("some-path"))
   493  					Expect(pluginDirArg).To(ContainSubstring("some-pluginhome"))
   494  					Expect(pluginDirArg).To(ContainSubstring("temp"))
   495  				})
   496  
   497  				Context("when the plugin is invalid", func() {
   498  					var returnedErr error
   499  
   500  					BeforeEach(func() {
   501  						returnedErr = pluginaction.PluginInvalidError{}
   502  						fakeActor.GetAndValidatePluginReturns(configv3.Plugin{}, returnedErr)
   503  					})
   504  
   505  					It("returns an error", func() {
   506  						Expect(executeErr).To(MatchError(translatableerror.PluginInvalidError{}))
   507  
   508  						Expect(fakeActor.IsPluginInstalledCallCount()).To(Equal(0))
   509  					})
   510  				})
   511  
   512  				Context("when the plugin is valid", func() {
   513  					BeforeEach(func() {
   514  						plugin = configv3.Plugin{
   515  							Name: pluginName,
   516  							Version: configv3.PluginVersion{
   517  								Major: 1,
   518  								Minor: 2,
   519  								Build: 3,
   520  							},
   521  						}
   522  						fakeActor.GetAndValidatePluginReturns(plugin, nil)
   523  					})
   524  
   525  					Context("when the plugin is already installed", func() {
   526  						BeforeEach(func() {
   527  							fakeActor.IsPluginInstalledReturns(true)
   528  						})
   529  
   530  						It("displays uninstall message", func() {
   531  							Expect(testUI.Out).To(Say("Plugin %s 1\\.2\\.3 is already installed\\. Uninstalling existing plugin\\.\\.\\.", pluginName))
   532  						})
   533  
   534  						Context("when an error is encountered uninstalling the existing plugin", func() {
   535  							BeforeEach(func() {
   536  								expectedErr = errors.New("uninstall plugin error")
   537  								fakeActor.UninstallPluginReturns(expectedErr)
   538  							})
   539  
   540  							It("returns the error", func() {
   541  								Expect(executeErr).To(MatchError(expectedErr))
   542  
   543  								Expect(testUI.Out).ToNot(Say("Plugin some-plugin successfully uninstalled\\."))
   544  							})
   545  						})
   546  
   547  						Context("when no errors are encountered uninstalling the existing plugin", func() {
   548  							It("displays uninstall message", func() {
   549  								Expect(testUI.Out).To(Say("Plugin %s successfully uninstalled\\.", pluginName))
   550  							})
   551  
   552  							Context("when no errors are encountered installing the plugin", func() {
   553  								It("uninstalls the existing plugin and installs the current plugin", func() {
   554  									Expect(executeErr).ToNot(HaveOccurred())
   555  
   556  									Expect(testUI.Out).To(Say("Installing plugin %s\\.\\.\\.", pluginName))
   557  									Expect(testUI.Out).To(Say("OK"))
   558  									Expect(testUI.Out).To(Say("Plugin %s 1\\.2\\.3 successfully installed\\.", pluginName))
   559  								})
   560  							})
   561  
   562  							Context("when an error is encountered installing the plugin", func() {
   563  								BeforeEach(func() {
   564  									expectedErr = errors.New("install plugin error")
   565  									fakeActor.InstallPluginFromPathReturns(expectedErr)
   566  								})
   567  
   568  								It("returns the error", func() {
   569  									Expect(executeErr).To(MatchError(expectedErr))
   570  
   571  									Expect(testUI.Out).ToNot(Say("Plugin some-plugin 1\\.2\\.3 successfully installed\\."))
   572  								})
   573  							})
   574  						})
   575  					})
   576  
   577  					Context("when the plugin is not already installed", func() {
   578  						It("installs the plugin", func() {
   579  							Expect(executeErr).ToNot(HaveOccurred())
   580  
   581  							Expect(testUI.Out).To(Say("Installing plugin %s\\.\\.\\.", pluginName))
   582  							Expect(testUI.Out).To(Say("OK"))
   583  							Expect(testUI.Out).To(Say("Plugin %s 1\\.2\\.3 successfully installed\\.", pluginName))
   584  
   585  							Expect(fakeActor.UninstallPluginCallCount()).To(Equal(0))
   586  						})
   587  					})
   588  				})
   589  			})
   590  		})
   591  
   592  		Context("when the -f argument is not given (user is prompted for confirmation)", func() {
   593  			BeforeEach(func() {
   594  				plugin = configv3.Plugin{
   595  					Name: pluginName,
   596  					Version: configv3.PluginVersion{
   597  						Major: 1,
   598  						Minor: 2,
   599  						Build: 3,
   600  					},
   601  				}
   602  
   603  				cmd.Force = false
   604  				fakeActor.DownloadExecutableBinaryFromURLReturns("some-path", nil)
   605  				fakeActor.CreateExecutableCopyReturns("executable-path", nil)
   606  			})
   607  
   608  			Context("when the user chooses no", func() {
   609  				BeforeEach(func() {
   610  					input.Write([]byte("n\n"))
   611  				})
   612  
   613  				It("cancels plugin installation", func() {
   614  					Expect(executeErr).ToNot(HaveOccurred())
   615  
   616  					Expect(testUI.Out).To(Say("Plugin installation cancelled\\."))
   617  				})
   618  			})
   619  
   620  			Context("when the user chooses the default", func() {
   621  				BeforeEach(func() {
   622  					input.Write([]byte("\n"))
   623  				})
   624  
   625  				It("cancels plugin installation", func() {
   626  					Expect(executeErr).ToNot(HaveOccurred())
   627  
   628  					Expect(testUI.Out).To(Say("Plugin installation cancelled\\."))
   629  				})
   630  			})
   631  
   632  			Context("when the user input is invalid", func() {
   633  				BeforeEach(func() {
   634  					input.Write([]byte("e\n"))
   635  				})
   636  
   637  				It("returns an error", func() {
   638  					Expect(executeErr).To(HaveOccurred())
   639  
   640  					Expect(testUI.Out).ToNot(Say("Installing plugin"))
   641  				})
   642  			})
   643  
   644  			Context("when the user chooses yes", func() {
   645  				BeforeEach(func() {
   646  					input.Write([]byte("y\n"))
   647  				})
   648  
   649  				Context("when the plugin is not already installed", func() {
   650  					BeforeEach(func() {
   651  						fakeActor.GetAndValidatePluginReturns(plugin, nil)
   652  					})
   653  
   654  					It("installs the plugin", func() {
   655  						Expect(executeErr).ToNot(HaveOccurred())
   656  
   657  						Expect(testUI.Out).To(Say("Attention: Plugins are binaries written by potentially untrusted authors\\."))
   658  						Expect(testUI.Out).To(Say("Install and use plugins at your own risk\\."))
   659  						Expect(testUI.Out).To(Say("Do you want to install the plugin %s\\? \\[yN\\]", cmd.OptionalArgs.PluginNameOrLocation))
   660  						Expect(testUI.Out).To(Say("Starting download of plugin binary from URL\\.\\.\\."))
   661  						Expect(testUI.Out).To(Say("Installing plugin %s\\.\\.\\.", pluginName))
   662  						Expect(testUI.Out).To(Say("OK"))
   663  						Expect(testUI.Out).To(Say("Plugin %s 1\\.2\\.3 successfully installed\\.", pluginName))
   664  
   665  						Expect(fakeActor.DownloadExecutableBinaryFromURLCallCount()).To(Equal(1))
   666  						url, tempPluginDir, proxyReader := fakeActor.DownloadExecutableBinaryFromURLArgsForCall(0)
   667  						Expect(url).To(Equal(cmd.OptionalArgs.PluginNameOrLocation.String()))
   668  						Expect(tempPluginDir).To(ContainSubstring("some-pluginhome"))
   669  						Expect(tempPluginDir).To(ContainSubstring("temp"))
   670  						Expect(proxyReader).To(Equal(fakeProgressBar))
   671  
   672  						Expect(fakeActor.CreateExecutableCopyCallCount()).To(Equal(1))
   673  						path, tempPluginDir := fakeActor.CreateExecutableCopyArgsForCall(0)
   674  						Expect(path).To(Equal("some-path"))
   675  						Expect(tempPluginDir).To(ContainSubstring("some-pluginhome"))
   676  						Expect(tempPluginDir).To(ContainSubstring("temp"))
   677  
   678  						Expect(fakeActor.GetAndValidatePluginCallCount()).To(Equal(1))
   679  						_, _, path = fakeActor.GetAndValidatePluginArgsForCall(0)
   680  						Expect(path).To(Equal(executablePluginPath))
   681  
   682  						Expect(fakeActor.IsPluginInstalledCallCount()).To(Equal(1))
   683  						Expect(fakeActor.IsPluginInstalledArgsForCall(0)).To(Equal(pluginName))
   684  
   685  						Expect(fakeActor.InstallPluginFromPathCallCount()).To(Equal(1))
   686  						path, installedPlugin := fakeActor.InstallPluginFromPathArgsForCall(0)
   687  						Expect(path).To(Equal(executablePluginPath))
   688  						Expect(installedPlugin).To(Equal(plugin))
   689  
   690  						Expect(fakeActor.UninstallPluginCallCount()).To(Equal(0))
   691  					})
   692  				})
   693  
   694  				Context("when the plugin is already installed", func() {
   695  					BeforeEach(func() {
   696  						fakeActor.GetAndValidatePluginReturns(plugin, nil)
   697  						fakeActor.IsPluginInstalledReturns(true)
   698  					})
   699  
   700  					It("returns PluginAlreadyInstalledError", func() {
   701  						Expect(executeErr).To(MatchError(translatableerror.PluginAlreadyInstalledError{
   702  							BinaryName: "faceman",
   703  							Name:       pluginName,
   704  							Version:    "1.2.3",
   705  						}))
   706  					})
   707  				})
   708  			})
   709  		})
   710  	})
   711  })