github.com/wanddynosios/cli/v8@v8.7.9-0.20240221182337-1a92e3a7017f/actor/pluginaction/install_test.go (about)

     1  package pluginaction_test
     2  
     3  import (
     4  	"errors"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  
     9  	"code.cloudfoundry.org/cli/actor/actionerror"
    10  	. "code.cloudfoundry.org/cli/actor/pluginaction"
    11  	"code.cloudfoundry.org/cli/actor/pluginaction/pluginactionfakes"
    12  	"code.cloudfoundry.org/cli/api/plugin"
    13  	"code.cloudfoundry.org/cli/api/plugin/pluginfakes"
    14  	"code.cloudfoundry.org/cli/util/configv3"
    15  	"code.cloudfoundry.org/cli/util/generic"
    16  	. "github.com/onsi/ginkgo"
    17  	. "github.com/onsi/gomega"
    18  )
    19  
    20  var _ = Describe("install actions", func() {
    21  	var (
    22  		actor         *Actor
    23  		fakeConfig    *pluginactionfakes.FakeConfig
    24  		fakeClient    *pluginactionfakes.FakePluginClient
    25  		tempPluginDir string
    26  	)
    27  
    28  	BeforeEach(func() {
    29  		fakeConfig = new(pluginactionfakes.FakeConfig)
    30  		fakeConfig.BinaryVersionReturns("6.0.0")
    31  		fakeClient = new(pluginactionfakes.FakePluginClient)
    32  		actor = NewActor(fakeConfig, fakeClient)
    33  
    34  		var err error
    35  		tempPluginDir, err = ioutil.TempDir("", "")
    36  		Expect(err).ToNot(HaveOccurred())
    37  	})
    38  
    39  	AfterEach(func() {
    40  		err := os.RemoveAll(tempPluginDir)
    41  		Expect(err).ToNot(HaveOccurred())
    42  	})
    43  
    44  	Describe("CreateExecutableCopy", func() {
    45  		When("the file exists", func() {
    46  			var pluginPath string
    47  
    48  			BeforeEach(func() {
    49  				tempFile, err := ioutil.TempFile("", "")
    50  				Expect(err).ToNot(HaveOccurred())
    51  
    52  				_, err = tempFile.WriteString("cthulhu")
    53  				Expect(err).ToNot(HaveOccurred())
    54  				err = tempFile.Close()
    55  				Expect(err).ToNot(HaveOccurred())
    56  
    57  				pluginPath = tempFile.Name()
    58  			})
    59  
    60  			AfterEach(func() {
    61  				err := os.Remove(pluginPath)
    62  				Expect(err).ToNot(HaveOccurred())
    63  			})
    64  
    65  			It("creates a copy of a file in plugin home", func() {
    66  				copyPath, err := actor.CreateExecutableCopy(pluginPath, tempPluginDir)
    67  				Expect(err).ToNot(HaveOccurred())
    68  
    69  				contents, err := ioutil.ReadFile(copyPath)
    70  				Expect(err).ToNot(HaveOccurred())
    71  				Expect(contents).To(BeEquivalentTo("cthulhu"))
    72  			})
    73  		})
    74  
    75  		When("the file does not exist", func() {
    76  			It("returns an os.PathError", func() {
    77  				_, err := actor.CreateExecutableCopy("i-don't-exist", tempPluginDir)
    78  				_, isPathError := err.(*os.PathError)
    79  				Expect(isPathError).To(BeTrue())
    80  			})
    81  		})
    82  	})
    83  
    84  	Describe("DownloadExecutableBinaryFromURL", func() {
    85  		var (
    86  			path            string
    87  			downloadErr     error
    88  			fakeProxyReader *pluginfakes.FakeProxyReader
    89  		)
    90  
    91  		JustBeforeEach(func() {
    92  			fakeProxyReader = new(pluginfakes.FakeProxyReader)
    93  			path, downloadErr = actor.DownloadExecutableBinaryFromURL("some-plugin-url.com", tempPluginDir, fakeProxyReader)
    94  		})
    95  
    96  		When("the downloaded is successful", func() {
    97  			var (
    98  				data []byte
    99  			)
   100  
   101  			BeforeEach(func() {
   102  				data = []byte("some test data")
   103  				fakeClient.DownloadPluginStub = func(_ string, path string, _ plugin.ProxyReader) error {
   104  					err := ioutil.WriteFile(path, data, 0700)
   105  					Expect(err).ToNot(HaveOccurred())
   106  					return nil
   107  				}
   108  			})
   109  			It("returns the path to the file and the size", func() {
   110  				Expect(downloadErr).ToNot(HaveOccurred())
   111  				fileData, err := ioutil.ReadFile(path)
   112  				Expect(err).ToNot(HaveOccurred())
   113  				Expect(fileData).To(Equal(data))
   114  
   115  				Expect(fakeClient.DownloadPluginCallCount()).To(Equal(1))
   116  				pluginURL, downloadPath, proxyReader := fakeClient.DownloadPluginArgsForCall(0)
   117  				Expect(pluginURL).To(Equal("some-plugin-url.com"))
   118  				Expect(downloadPath).To(Equal(path))
   119  				Expect(proxyReader).To(Equal(fakeProxyReader))
   120  			})
   121  		})
   122  
   123  		When("there is an error downloading file", func() {
   124  			var expectedErr error
   125  
   126  			BeforeEach(func() {
   127  				expectedErr = errors.New("some error")
   128  				fakeClient.DownloadPluginReturns(expectedErr)
   129  			})
   130  
   131  			It("returns the error", func() {
   132  				Expect(downloadErr).To(MatchError(expectedErr))
   133  			})
   134  		})
   135  	})
   136  
   137  	Describe("FileExists", func() {
   138  		var pluginPath string
   139  
   140  		When("the file exists", func() {
   141  			BeforeEach(func() {
   142  				pluginFile, err := ioutil.TempFile("", "")
   143  				Expect(err).NotTo(HaveOccurred())
   144  				err = pluginFile.Close()
   145  				Expect(err).NotTo(HaveOccurred())
   146  
   147  				pluginPath = pluginFile.Name()
   148  			})
   149  
   150  			AfterEach(func() {
   151  				err := os.Remove(pluginPath)
   152  				Expect(err).NotTo(HaveOccurred())
   153  			})
   154  
   155  			It("returns true", func() {
   156  				Expect(actor.FileExists(pluginPath)).To(BeTrue())
   157  			})
   158  		})
   159  
   160  		When("the file does not exist", func() {
   161  			It("returns false", func() {
   162  				Expect(actor.FileExists("/some/path/that/does/not/exist")).To(BeFalse())
   163  			})
   164  		})
   165  	})
   166  
   167  	Describe("GetAndValidatePlugin", func() {
   168  		var (
   169  			fakePluginMetadata *pluginactionfakes.FakePluginMetadata
   170  			fakeCommandList    *pluginactionfakes.FakeCommandList
   171  			plugin             configv3.Plugin
   172  			validateErr        error
   173  		)
   174  
   175  		BeforeEach(func() {
   176  			fakePluginMetadata = new(pluginactionfakes.FakePluginMetadata)
   177  			fakeCommandList = new(pluginactionfakes.FakeCommandList)
   178  		})
   179  
   180  		JustBeforeEach(func() {
   181  			plugin, validateErr = actor.GetAndValidatePlugin(fakePluginMetadata, fakeCommandList, "some-plugin-path")
   182  		})
   183  
   184  		When("getting the plugin metadata returns an error", func() {
   185  			var expectedErr error
   186  
   187  			BeforeEach(func() {
   188  				expectedErr = errors.New("error getting metadata")
   189  				fakePluginMetadata.GetMetadataReturns(configv3.Plugin{}, expectedErr)
   190  			})
   191  
   192  			It("returns a PluginInvalidError", func() {
   193  				Expect(validateErr).To(MatchError(actionerror.PluginInvalidError{Err: expectedErr}))
   194  			})
   195  		})
   196  
   197  		When("the plugin name is missing", func() {
   198  			BeforeEach(func() {
   199  				fakePluginMetadata.GetMetadataReturns(configv3.Plugin{}, nil)
   200  			})
   201  
   202  			It("returns a PluginInvalidError", func() {
   203  				Expect(validateErr).To(MatchError(actionerror.PluginInvalidError{}))
   204  			})
   205  		})
   206  
   207  		When("the plugin does not have any commands", func() {
   208  			BeforeEach(func() {
   209  				fakePluginMetadata.GetMetadataReturns(configv3.Plugin{Name: "some-plugin"}, nil)
   210  			})
   211  
   212  			It("returns a PluginInvalidError", func() {
   213  				Expect(validateErr).To(MatchError(actionerror.PluginInvalidError{}))
   214  			})
   215  		})
   216  
   217  		When("there are command conflicts", func() {
   218  			BeforeEach(func() {
   219  				fakePluginMetadata.GetMetadataReturns(configv3.Plugin{
   220  					Name: "some-plugin",
   221  					Version: configv3.PluginVersion{
   222  						Major: 1,
   223  						Minor: 1,
   224  						Build: 1,
   225  					},
   226  					Commands: []configv3.PluginCommand{
   227  						{Name: "some-other-command", Alias: "soc"},
   228  						{Name: "some-command", Alias: "sc"},
   229  						{Name: "version", Alias: "v"},
   230  						{Name: "p", Alias: "push"},
   231  					},
   232  				}, nil)
   233  			})
   234  
   235  			When("the plugin has command names that conflict with native command names", func() {
   236  				BeforeEach(func() {
   237  					fakeCommandList.HasCommandStub = func(commandName string) bool {
   238  						switch commandName {
   239  						case "version":
   240  							return true
   241  						default:
   242  							return false
   243  						}
   244  					}
   245  				})
   246  
   247  				It("returns a PluginCommandsConflictError containing all conflicting command names", func() {
   248  					Expect(validateErr).To(MatchError(actionerror.PluginCommandsConflictError{
   249  						PluginName:     "some-plugin",
   250  						PluginVersion:  "1.1.1",
   251  						CommandNames:   []string{"version"},
   252  						CommandAliases: []string{},
   253  					}))
   254  				})
   255  			})
   256  
   257  			When("the plugin has command names that conflict with native command aliases", func() {
   258  				BeforeEach(func() {
   259  					fakeCommandList.HasAliasStub = func(commandAlias string) bool {
   260  						switch commandAlias {
   261  						case "p":
   262  							return true
   263  						default:
   264  							return false
   265  						}
   266  					}
   267  				})
   268  
   269  				It("returns a PluginCommandsConflictError containing all conflicting command names", func() {
   270  					Expect(validateErr).To(MatchError(actionerror.PluginCommandsConflictError{
   271  						PluginName:     "some-plugin",
   272  						PluginVersion:  "1.1.1",
   273  						CommandNames:   []string{"p"},
   274  						CommandAliases: []string{},
   275  					}))
   276  				})
   277  			})
   278  
   279  			When("the plugin has command aliases that conflict with native command names", func() {
   280  				BeforeEach(func() {
   281  					fakeCommandList.HasCommandStub = func(commandName string) bool {
   282  						switch commandName {
   283  						case "push":
   284  							return true
   285  						default:
   286  							return false
   287  						}
   288  					}
   289  				})
   290  
   291  				It("returns a PluginCommandsConflictError containing all conflicting command aliases", func() {
   292  					Expect(validateErr).To(MatchError(actionerror.PluginCommandsConflictError{
   293  						PluginName:     "some-plugin",
   294  						PluginVersion:  "1.1.1",
   295  						CommandAliases: []string{"push"},
   296  						CommandNames:   []string{},
   297  					}))
   298  				})
   299  			})
   300  
   301  			When("the plugin has command aliases that conflict with native command aliases", func() {
   302  				BeforeEach(func() {
   303  					fakeCommandList.HasAliasStub = func(commandAlias string) bool {
   304  						switch commandAlias {
   305  						case "v":
   306  							return true
   307  						default:
   308  							return false
   309  						}
   310  					}
   311  				})
   312  
   313  				It("returns a PluginCommandsConflictError containing all conflicting command aliases", func() {
   314  					Expect(validateErr).To(MatchError(actionerror.PluginCommandsConflictError{
   315  						PluginName:     "some-plugin",
   316  						PluginVersion:  "1.1.1",
   317  						CommandAliases: []string{"v"},
   318  						CommandNames:   []string{},
   319  					}))
   320  				})
   321  			})
   322  
   323  			When("the plugin has command names that conflict with existing plugin command names", func() {
   324  				BeforeEach(func() {
   325  					fakeConfig.PluginsReturns([]configv3.Plugin{{
   326  						Name:     "installed-plugin-2",
   327  						Commands: []configv3.PluginCommand{{Name: "some-command"}},
   328  					}})
   329  				})
   330  
   331  				It("returns a PluginCommandsConflictError containing all conflicting command names", func() {
   332  					Expect(validateErr).To(MatchError(actionerror.PluginCommandsConflictError{
   333  						PluginName:     "some-plugin",
   334  						PluginVersion:  "1.1.1",
   335  						CommandNames:   []string{"some-command"},
   336  						CommandAliases: []string{},
   337  					}))
   338  				})
   339  			})
   340  
   341  			When("the plugin has command names that conflict with existing plugin command aliases", func() {
   342  				BeforeEach(func() {
   343  					fakeConfig.PluginsReturns([]configv3.Plugin{{
   344  						Name:     "installed-plugin-2",
   345  						Commands: []configv3.PluginCommand{{Alias: "some-command"}}},
   346  					})
   347  				})
   348  
   349  				It("returns a PluginCommandsConflictError containing all conflicting command names", func() {
   350  					Expect(validateErr).To(MatchError(actionerror.PluginCommandsConflictError{
   351  						PluginName:     "some-plugin",
   352  						PluginVersion:  "1.1.1",
   353  						CommandNames:   []string{"some-command"},
   354  						CommandAliases: []string{},
   355  					}))
   356  				})
   357  			})
   358  
   359  			When("the plugin has command aliases that conflict with existing plugin command names", func() {
   360  				BeforeEach(func() {
   361  					fakeConfig.PluginsReturns([]configv3.Plugin{{
   362  						Name:     "installed-plugin-2",
   363  						Commands: []configv3.PluginCommand{{Name: "sc"}}},
   364  					})
   365  				})
   366  
   367  				It("returns a PluginCommandsConflictError containing all conflicting command aliases", func() {
   368  					Expect(validateErr).To(MatchError(actionerror.PluginCommandsConflictError{
   369  						PluginName:     "some-plugin",
   370  						PluginVersion:  "1.1.1",
   371  						CommandNames:   []string{},
   372  						CommandAliases: []string{"sc"},
   373  					}))
   374  				})
   375  			})
   376  
   377  			When("the plugin has command aliases that conflict with existing plugin command aliases", func() {
   378  				BeforeEach(func() {
   379  					fakeConfig.PluginsReturns([]configv3.Plugin{{
   380  						Name:     "installed-plugin-2",
   381  						Commands: []configv3.PluginCommand{{Alias: "sc"}}},
   382  					})
   383  				})
   384  
   385  				It("returns a PluginCommandsConflictError containing all conflicting command aliases", func() {
   386  					Expect(validateErr).To(MatchError(actionerror.PluginCommandsConflictError{
   387  						PluginName:     "some-plugin",
   388  						PluginVersion:  "1.1.1",
   389  						CommandAliases: []string{"sc"},
   390  						CommandNames:   []string{},
   391  					}))
   392  				})
   393  			})
   394  
   395  			When("the plugin has command names and aliases that conflict with existing native and plugin command names and aliases", func() {
   396  				BeforeEach(func() {
   397  					fakeConfig.PluginsReturns([]configv3.Plugin{
   398  						{
   399  							Name: "installed-plugin-1",
   400  							Commands: []configv3.PluginCommand{
   401  								{Name: "some-command"},
   402  								{Alias: "some-other-command"},
   403  							},
   404  						},
   405  						{
   406  							Name: "installed-plugin-2",
   407  							Commands: []configv3.PluginCommand{
   408  								{Name: "sc"},
   409  								{Alias: "soc"},
   410  							},
   411  						},
   412  					})
   413  
   414  					fakeCommandList.HasCommandStub = func(commandName string) bool {
   415  						switch commandName {
   416  						case "version", "p":
   417  							return true
   418  						default:
   419  							return false
   420  						}
   421  					}
   422  
   423  					fakeCommandList.HasAliasStub = func(commandAlias string) bool {
   424  						switch commandAlias {
   425  						case "v", "push":
   426  							return true
   427  						default:
   428  							return false
   429  						}
   430  					}
   431  				})
   432  
   433  				It("returns a PluginCommandsConflictError with all conflicting command names and aliases", func() {
   434  					Expect(validateErr).To(MatchError(actionerror.PluginCommandsConflictError{
   435  						PluginName:     "some-plugin",
   436  						PluginVersion:  "1.1.1",
   437  						CommandNames:   []string{"p", "some-command", "some-other-command", "version"},
   438  						CommandAliases: []string{"push", "sc", "soc", "v"},
   439  					}))
   440  				})
   441  			})
   442  
   443  			When("the plugin is already installed", func() {
   444  				BeforeEach(func() {
   445  					fakeConfig.PluginsReturns([]configv3.Plugin{{
   446  						Name: "some-plugin",
   447  						Commands: []configv3.PluginCommand{
   448  							{Name: "some-command", Alias: "sc"},
   449  							{Name: "some-other-command", Alias: "soc"},
   450  						},
   451  					}})
   452  				})
   453  
   454  				It("does not return any errors due to command name or alias conflict", func() {
   455  					Expect(validateErr).ToNot(HaveOccurred())
   456  				})
   457  			})
   458  		})
   459  
   460  		Describe("Version checking", func() {
   461  			var pluginToBeInstalled configv3.Plugin
   462  			BeforeEach(func() {
   463  				pluginToBeInstalled = configv3.Plugin{
   464  					Name: "some-plugin",
   465  					Version: configv3.PluginVersion{
   466  						Major: 1,
   467  						Minor: 1,
   468  						Build: 1,
   469  					},
   470  					Commands: []configv3.PluginCommand{
   471  						{
   472  							Name:  "some-command",
   473  							Alias: "sc",
   474  						},
   475  						{
   476  							Name:  "some-other-command",
   477  							Alias: "soc",
   478  						},
   479  					},
   480  				}
   481  				fakeConfig.PluginsReturns([]configv3.Plugin{
   482  					{
   483  						Name: "installed-plugin-1",
   484  						Commands: []configv3.PluginCommand{
   485  							{
   486  								Name:  "unique-command-1",
   487  								Alias: "uc1",
   488  							},
   489  						},
   490  					},
   491  					{
   492  						Name: "installed-plugin-2",
   493  						Commands: []configv3.PluginCommand{
   494  							{
   495  								Name:  "unique-command-2",
   496  								Alias: "uc2",
   497  							},
   498  							{
   499  								Name:  "unique-command-3",
   500  								Alias: "uc3",
   501  							},
   502  						},
   503  					},
   504  				})
   505  			})
   506  
   507  			Describe("CLI v 6", func() {
   508  				BeforeEach(func() {
   509  					fakeConfig.BinaryVersionReturns("6.0.0")
   510  				})
   511  
   512  				When("the plugin is valid", func() {
   513  					BeforeEach(func() {
   514  						fakePluginMetadata.GetMetadataReturns(pluginToBeInstalled, nil)
   515  					})
   516  
   517  					It("returns the plugin and no errors", func() {
   518  						Expect(validateErr).ToNot(HaveOccurred())
   519  						Expect(plugin).To(Equal(pluginToBeInstalled))
   520  
   521  						Expect(fakePluginMetadata.GetMetadataCallCount()).To(Equal(1))
   522  						Expect(fakePluginMetadata.GetMetadataArgsForCall(0)).To(Equal("some-plugin-path"))
   523  
   524  						Expect(fakeCommandList.HasCommandCallCount()).To(Equal(4))
   525  						Expect(fakeCommandList.HasCommandArgsForCall(0)).To(Equal("some-command"))
   526  						Expect(fakeCommandList.HasCommandArgsForCall(1)).To(Equal("sc"))
   527  						Expect(fakeCommandList.HasCommandArgsForCall(2)).To(Equal("some-other-command"))
   528  						Expect(fakeCommandList.HasCommandArgsForCall(3)).To(Equal("soc"))
   529  
   530  						Expect(fakeCommandList.HasAliasCallCount()).To(Equal(4))
   531  						Expect(fakeCommandList.HasAliasArgsForCall(0)).To(Equal("some-command"))
   532  						Expect(fakeCommandList.HasAliasArgsForCall(1)).To(Equal("sc"))
   533  						Expect(fakeCommandList.HasAliasArgsForCall(2)).To(Equal("some-other-command"))
   534  						Expect(fakeCommandList.HasAliasArgsForCall(3)).To(Equal("soc"))
   535  
   536  						Expect(fakeConfig.PluginsCallCount()).To(Equal(1))
   537  					})
   538  				})
   539  
   540  				When("the plugin is for a later version", func() {
   541  					BeforeEach(func() {
   542  						pluginToBeInstalled.LibraryVersion = configv3.PluginVersion{
   543  							Major: 2,
   544  							Minor: 0,
   545  							Build: 0,
   546  						}
   547  						fakePluginMetadata.GetMetadataReturns(pluginToBeInstalled, nil)
   548  					})
   549  					It("reports the plugin is invalid", func() {
   550  						Expect(validateErr).To(MatchError(actionerror.PluginInvalidLibraryVersionError{}))
   551  					})
   552  				})
   553  			})
   554  
   555  			Describe("CLI v 7", func() {
   556  				BeforeEach(func() {
   557  					fakeConfig.BinaryVersionReturns("7.0.0")
   558  				})
   559  
   560  				When("the plugin is valid", func() {
   561  					BeforeEach(func() {
   562  						fakePluginMetadata.GetMetadataReturns(pluginToBeInstalled, nil)
   563  					})
   564  
   565  					It("returns the plugin and no errors", func() {
   566  						Expect(validateErr).ToNot(HaveOccurred())
   567  						Expect(plugin).To(Equal(pluginToBeInstalled))
   568  
   569  						Expect(fakePluginMetadata.GetMetadataCallCount()).To(Equal(1))
   570  						Expect(fakePluginMetadata.GetMetadataArgsForCall(0)).To(Equal("some-plugin-path"))
   571  
   572  						Expect(fakeCommandList.HasCommandCallCount()).To(Equal(4))
   573  						Expect(fakeCommandList.HasCommandArgsForCall(0)).To(Equal("some-command"))
   574  						Expect(fakeCommandList.HasCommandArgsForCall(1)).To(Equal("sc"))
   575  						Expect(fakeCommandList.HasCommandArgsForCall(2)).To(Equal("some-other-command"))
   576  						Expect(fakeCommandList.HasCommandArgsForCall(3)).To(Equal("soc"))
   577  
   578  						Expect(fakeCommandList.HasAliasCallCount()).To(Equal(4))
   579  						Expect(fakeCommandList.HasAliasArgsForCall(0)).To(Equal("some-command"))
   580  						Expect(fakeCommandList.HasAliasArgsForCall(1)).To(Equal("sc"))
   581  						Expect(fakeCommandList.HasAliasArgsForCall(2)).To(Equal("some-other-command"))
   582  						Expect(fakeCommandList.HasAliasArgsForCall(3)).To(Equal("soc"))
   583  
   584  						Expect(fakeConfig.PluginsCallCount()).To(Equal(1))
   585  					})
   586  				})
   587  
   588  				When("the plugin is for a newer CLI", func() {
   589  					BeforeEach(func() {
   590  						pluginToBeInstalled.LibraryVersion = configv3.PluginVersion{
   591  							Major: 2,
   592  							Minor: 0,
   593  							Build: 0,
   594  						}
   595  						fakePluginMetadata.GetMetadataReturns(pluginToBeInstalled, nil)
   596  					})
   597  					It("reports the plugin is wrong", func() {
   598  						Expect(validateErr).To(MatchError(actionerror.PluginInvalidLibraryVersionError{}))
   599  					})
   600  				})
   601  			})
   602  
   603  			Describe("CLI v 8", func() {
   604  				BeforeEach(func() {
   605  					fakeConfig.BinaryVersionReturns("8.0.0")
   606  					pluginToBeInstalled.LibraryVersion = configv3.PluginVersion{
   607  						Major: 1,
   608  						Minor: 0,
   609  						Build: 0,
   610  					}
   611  					fakePluginMetadata.GetMetadataReturns(pluginToBeInstalled, nil)
   612  				})
   613  
   614  				When("the plugin is valid", func() {
   615  					BeforeEach(func() {
   616  						fakePluginMetadata.GetMetadataReturns(pluginToBeInstalled, nil)
   617  					})
   618  
   619  					It("returns the plugin and no errors", func() {
   620  						Expect(validateErr).ToNot(HaveOccurred())
   621  						Expect(plugin).To(Equal(pluginToBeInstalled))
   622  
   623  						Expect(fakePluginMetadata.GetMetadataCallCount()).To(Equal(1))
   624  						Expect(fakePluginMetadata.GetMetadataArgsForCall(0)).To(Equal("some-plugin-path"))
   625  
   626  						Expect(fakeCommandList.HasCommandCallCount()).To(Equal(4))
   627  						Expect(fakeCommandList.HasCommandArgsForCall(0)).To(Equal("some-command"))
   628  						Expect(fakeCommandList.HasCommandArgsForCall(1)).To(Equal("sc"))
   629  						Expect(fakeCommandList.HasCommandArgsForCall(2)).To(Equal("some-other-command"))
   630  						Expect(fakeCommandList.HasCommandArgsForCall(3)).To(Equal("soc"))
   631  
   632  						Expect(fakeCommandList.HasAliasCallCount()).To(Equal(4))
   633  						Expect(fakeCommandList.HasAliasArgsForCall(0)).To(Equal("some-command"))
   634  						Expect(fakeCommandList.HasAliasArgsForCall(1)).To(Equal("sc"))
   635  						Expect(fakeCommandList.HasAliasArgsForCall(2)).To(Equal("some-other-command"))
   636  						Expect(fakeCommandList.HasAliasArgsForCall(3)).To(Equal("soc"))
   637  
   638  						Expect(fakeConfig.PluginsCallCount()).To(Equal(1))
   639  					})
   640  				})
   641  
   642  			})
   643  		})
   644  	})
   645  
   646  	Describe("InstallPluginFromLocalPath", func() {
   647  		var (
   648  			plugin     configv3.Plugin
   649  			installErr error
   650  
   651  			pluginHomeDir string
   652  			pluginPath    string
   653  			tempDir       string
   654  		)
   655  
   656  		BeforeEach(func() {
   657  			plugin = configv3.Plugin{
   658  				Name: "some-plugin",
   659  				Commands: []configv3.PluginCommand{
   660  					{Name: "some-command"},
   661  				},
   662  			}
   663  
   664  			pluginFile, err := ioutil.TempFile("", "")
   665  			Expect(err).NotTo(HaveOccurred())
   666  			err = pluginFile.Close()
   667  			Expect(err).NotTo(HaveOccurred())
   668  
   669  			pluginPath = pluginFile.Name()
   670  
   671  			tempDir, err = ioutil.TempDir("", "")
   672  			Expect(err).ToNot(HaveOccurred())
   673  
   674  			pluginHomeDir = filepath.Join(tempDir, ".cf", "plugin")
   675  		})
   676  
   677  		AfterEach(func() {
   678  			err := os.Remove(pluginPath)
   679  			Expect(err).NotTo(HaveOccurred())
   680  
   681  			err = os.RemoveAll(tempDir)
   682  			Expect(err).NotTo(HaveOccurred())
   683  		})
   684  
   685  		JustBeforeEach(func() {
   686  			installErr = actor.InstallPluginFromPath(pluginPath, plugin)
   687  		})
   688  
   689  		When("an error is encountered copying the plugin to the plugin directory", func() {
   690  			BeforeEach(func() {
   691  				fakeConfig.PluginHomeReturns(pluginPath)
   692  			})
   693  
   694  			It("returns the error", func() {
   695  				_, isPathError := installErr.(*os.PathError)
   696  				Expect(isPathError).To(BeTrue())
   697  			})
   698  		})
   699  
   700  		When("an error is encountered writing the plugin config to disk", func() {
   701  			var (
   702  				expectedErr error
   703  			)
   704  
   705  			BeforeEach(func() {
   706  				fakeConfig.PluginHomeReturns(pluginHomeDir)
   707  
   708  				expectedErr = errors.New("write config error")
   709  				fakeConfig.WritePluginConfigReturns(expectedErr)
   710  			})
   711  
   712  			It("returns the error", func() {
   713  				Expect(installErr).To(MatchError(expectedErr))
   714  			})
   715  		})
   716  
   717  		When("no errors are encountered", func() {
   718  			BeforeEach(func() {
   719  				fakeConfig.PluginHomeReturns(pluginHomeDir)
   720  			})
   721  
   722  			It("makes an executable copy of the plugin file in the plugin directory, updates the plugin config, and writes the config to disk", func() {
   723  				Expect(installErr).ToNot(HaveOccurred())
   724  
   725  				installedPluginPath := generic.ExecutableFilename(filepath.Join(pluginHomeDir, "some-plugin"))
   726  
   727  				Expect(fakeConfig.PluginHomeCallCount()).To(Equal(1))
   728  
   729  				Expect(fakeConfig.AddPluginCallCount()).To(Equal(1))
   730  				Expect(fakeConfig.AddPluginArgsForCall(0)).To(Equal(configv3.Plugin{
   731  					Name: "some-plugin",
   732  					Commands: []configv3.PluginCommand{
   733  						{Name: "some-command"},
   734  					},
   735  					Location: installedPluginPath,
   736  				}))
   737  
   738  				Expect(fakeConfig.WritePluginConfigCallCount()).To(Equal(1))
   739  			})
   740  		})
   741  	})
   742  })