github.com/jasonkeene/cli@v6.14.1-0.20160816203908-ca5715166dfb+incompatible/cf/commands/plugin/install_plugin_test.go (about)

     1  package plugin_test
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"os"
     9  	"path/filepath"
    10  	"runtime"
    11  
    12  	"github.com/cloudfoundry/cli/cf/actors/pluginrepo/pluginrepofakes"
    13  	"github.com/cloudfoundry/cli/cf/commandregistry"
    14  	"github.com/cloudfoundry/cli/cf/commandregistry/commandregistryfakes"
    15  	"github.com/cloudfoundry/cli/cf/configuration/coreconfig"
    16  	"github.com/cloudfoundry/cli/cf/configuration/pluginconfig"
    17  	"github.com/cloudfoundry/cli/cf/configuration/pluginconfig/pluginconfigfakes"
    18  	"github.com/cloudfoundry/cli/cf/flags"
    19  	"github.com/cloudfoundry/cli/cf/models"
    20  	"github.com/cloudfoundry/cli/cf/requirements"
    21  	"github.com/cloudfoundry/cli/cf/requirements/requirementsfakes"
    22  	"github.com/cloudfoundry/cli/plugin"
    23  	testcmd "github.com/cloudfoundry/cli/testhelpers/commands"
    24  	testconfig "github.com/cloudfoundry/cli/testhelpers/configuration"
    25  	testterm "github.com/cloudfoundry/cli/testhelpers/terminal"
    26  	"github.com/cloudfoundry/cli/utils/utilsfakes"
    27  
    28  	clipr "github.com/cloudfoundry-incubator/cli-plugin-repo/web"
    29  
    30  	. "github.com/cloudfoundry/cli/testhelpers/matchers"
    31  	. "github.com/onsi/ginkgo"
    32  	. "github.com/onsi/gomega"
    33  )
    34  
    35  var _ = Describe("Install", func() {
    36  	var (
    37  		ui                  *testterm.FakeUI
    38  		requirementsFactory *requirementsfakes.FakeFactory
    39  		config              coreconfig.Repository
    40  		pluginConfig        *pluginconfigfakes.FakePluginConfiguration
    41  		fakePluginRepo      *pluginrepofakes.FakePluginRepo
    42  		fakeChecksum        *utilsfakes.FakeSha1Checksum
    43  
    44  		pluginFile *os.File
    45  		homeDir    string
    46  		pluginDir  string
    47  		curDir     string
    48  
    49  		test_1                    string
    50  		test_2                    string
    51  		test_curDir               string
    52  		test_with_help            string
    53  		test_with_orgs            string
    54  		test_with_orgs_short_name string
    55  		aliasConflicts            string
    56  		deps                      commandregistry.Dependency
    57  	)
    58  
    59  	updateCommandDependency := func(pluginCall bool) {
    60  		deps.UI = ui
    61  		deps.Config = config
    62  		deps.PluginConfig = pluginConfig
    63  		deps.PluginRepo = fakePluginRepo
    64  		deps.ChecksumUtil = fakeChecksum
    65  		commandregistry.Commands.SetCommand(commandregistry.Commands.FindCommand("install-plugin").SetDependency(deps, pluginCall))
    66  	}
    67  
    68  	BeforeEach(func() {
    69  		ui = &testterm.FakeUI{}
    70  		requirementsFactory = new(requirementsfakes.FakeFactory)
    71  		pluginConfig = new(pluginconfigfakes.FakePluginConfiguration)
    72  		config = testconfig.NewRepositoryWithDefaults()
    73  		fakePluginRepo = new(pluginrepofakes.FakePluginRepo)
    74  		fakeChecksum = new(utilsfakes.FakeSha1Checksum)
    75  
    76  		dir, err := os.Getwd()
    77  		if err != nil {
    78  			panic(err)
    79  		}
    80  		test_1 = filepath.Join(dir, "..", "..", "..", "fixtures", "plugins", "test_1.exe")
    81  		test_2 = filepath.Join(dir, "..", "..", "..", "fixtures", "plugins", "test_2.exe")
    82  		test_curDir = filepath.Join("test_1.exe")
    83  		test_with_help = filepath.Join(dir, "..", "..", "..", "fixtures", "plugins", "test_with_help.exe")
    84  		test_with_orgs = filepath.Join(dir, "..", "..", "..", "fixtures", "plugins", "test_with_orgs.exe")
    85  		test_with_orgs_short_name = filepath.Join(dir, "..", "..", "..", "fixtures", "plugins", "test_with_orgs_short_name.exe")
    86  		aliasConflicts = filepath.Join(dir, "..", "..", "..", "fixtures", "plugins", "alias_conflicts.exe")
    87  
    88  		homeDir, err = ioutil.TempDir(os.TempDir(), "plugins")
    89  		Expect(err).ToNot(HaveOccurred())
    90  
    91  		pluginDir = filepath.Join(homeDir, ".cf", "plugins")
    92  		pluginConfig.GetPluginPathReturns(pluginDir)
    93  
    94  		curDir, err = os.Getwd()
    95  		Expect(err).ToNot(HaveOccurred())
    96  		pluginFile, err = ioutil.TempFile("./", "test_plugin")
    97  		Expect(err).ToNot(HaveOccurred())
    98  
    99  		if runtime.GOOS != "windows" {
   100  			err = os.Chmod(test_1, 0700)
   101  			Expect(err).ToNot(HaveOccurred())
   102  		}
   103  	})
   104  
   105  	AfterEach(func() {
   106  		os.Remove(filepath.Join(curDir, pluginFile.Name()))
   107  		os.Remove(homeDir)
   108  	})
   109  
   110  	runCommand := func(args ...string) bool {
   111  		return testcmd.RunCLICommand("install-plugin", args, requirementsFactory, updateCommandDependency, false, ui)
   112  	}
   113  
   114  	Describe("requirements", func() {
   115  		It("fails with usage when not provided a path to the plugin executable", func() {
   116  			Expect(runCommand()).ToNot(HavePassedRequirements())
   117  		})
   118  	})
   119  
   120  	Context("when the -f flag is not provided", func() {
   121  		Context("and the user responds with 'y'", func() {
   122  			It("continues to install the plugin", func() {
   123  				ui.Inputs = []string{"y"}
   124  				runCommand("pluggy", "-r", "somerepo")
   125  				Expect(ui.Outputs()).To(ContainSubstrings([]string{"Looking up 'pluggy' from repository 'somerepo'"}))
   126  			})
   127  		})
   128  
   129  		Context("but the user responds with 'n'", func() {
   130  			It("quits with a message", func() {
   131  				ui.Inputs = []string{"n"}
   132  				runCommand("pluggy", "-r", "somerepo")
   133  				Expect(ui.Outputs()).To(ContainSubstrings([]string{"Plugin installation cancelled"}))
   134  			})
   135  		})
   136  	})
   137  
   138  	Describe("Locating binary file", func() {
   139  
   140  		Describe("install from plugin repository when '-r' provided", func() {
   141  			Context("gets metadata of the plugin from repo", func() {
   142  				Context("when repo is not found in config", func() {
   143  					It("informs user repo is not found", func() {
   144  						runCommand("plugin1", "-r", "repo1", "-f")
   145  						Expect(ui.Outputs()).To(ContainSubstrings([]string{"Looking up 'plugin1' from repository 'repo1'"}))
   146  						Expect(ui.Outputs()).To(ContainSubstrings([]string{"repo1 not found"}))
   147  					})
   148  				})
   149  
   150  				Context("when repo is found in config", func() {
   151  					Context("when repo endpoint returns an error", func() {
   152  						It("informs user about the error", func() {
   153  							config.SetPluginRepo(models.PluginRepo{Name: "repo1", URL: ""})
   154  							fakePluginRepo.GetPluginsReturns(nil, []string{"repo error1"})
   155  							runCommand("plugin1", "-r", "repo1", "-f")
   156  
   157  							Expect(ui.Outputs()).To(ContainSubstrings([]string{"Error getting plugin metadata from repo"}))
   158  							Expect(ui.Outputs()).To(ContainSubstrings([]string{"repo error1"}))
   159  						})
   160  					})
   161  
   162  					Context("when plugin metadata is available and desired plugin is not found", func() {
   163  						It("informs user about the error", func() {
   164  							config.SetPluginRepo(models.PluginRepo{Name: "repo1", URL: ""})
   165  							fakePluginRepo.GetPluginsReturns(nil, nil)
   166  							runCommand("plugin1", "-r", "repo1", "-f")
   167  
   168  							Expect(ui.Outputs()).To(ContainSubstrings([]string{"plugin1 is not available in repo 'repo1'"}))
   169  						})
   170  					})
   171  
   172  					It("ignore cases in repo name", func() {
   173  						config.SetPluginRepo(models.PluginRepo{Name: "repo1", URL: ""})
   174  						fakePluginRepo.GetPluginsReturns(nil, nil)
   175  						runCommand("plugin1", "-r", "REPO1", "-f")
   176  
   177  						Expect(ui.Outputs()).NotTo(ContainSubstrings([]string{"REPO1 not found"}))
   178  					})
   179  				})
   180  			})
   181  
   182  			Context("downloads the binary for the machine's OS", func() {
   183  				Context("when binary is not available", func() {
   184  					It("informs user when binary is not available for OS", func() {
   185  						p := clipr.Plugin{
   186  							Name: "plugin1",
   187  						}
   188  						result := make(map[string][]clipr.Plugin)
   189  						result["repo1"] = []clipr.Plugin{p}
   190  
   191  						config.SetPluginRepo(models.PluginRepo{Name: "repo1", URL: ""})
   192  						fakePluginRepo.GetPluginsReturns(result, nil)
   193  						runCommand("plugin1", "-r", "repo1", "-f")
   194  
   195  						Expect(ui.Outputs()).To(ContainSubstrings([]string{"Plugin requested has no binary available"}))
   196  					})
   197  				})
   198  
   199  				Context("when binary is available", func() {
   200  					var (
   201  						testServer *httptest.Server
   202  					)
   203  
   204  					BeforeEach(func() {
   205  						h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   206  							fmt.Fprintln(w, "abc")
   207  						})
   208  
   209  						testServer = httptest.NewServer(h)
   210  
   211  						fakeChecksum.CheckSha1Returns(true)
   212  
   213  						p := clipr.Plugin{
   214  							Name: "plugin1",
   215  							Binaries: []clipr.Binary{
   216  								{
   217  									Platform: "osx",
   218  									Url:      testServer.URL + "/test.exe",
   219  								},
   220  								{
   221  									Platform: "win64",
   222  									Url:      testServer.URL + "/test.exe",
   223  								},
   224  								{
   225  									Platform: "win32",
   226  									Url:      testServer.URL + "/test.exe",
   227  								},
   228  								{
   229  									Platform: "linux32",
   230  									Url:      testServer.URL + "/test.exe",
   231  								},
   232  								{
   233  									Platform: "linux64",
   234  									Url:      testServer.URL + "/test.exe",
   235  								},
   236  							},
   237  						}
   238  						result := make(map[string][]clipr.Plugin)
   239  						result["repo1"] = []clipr.Plugin{p}
   240  
   241  						config.SetPluginRepo(models.PluginRepo{Name: "repo1", URL: ""})
   242  						fakePluginRepo.GetPluginsReturns(result, nil)
   243  					})
   244  
   245  					AfterEach(func() {
   246  						testServer.Close()
   247  					})
   248  
   249  					It("performs sha1 checksum validation on the downloaded binary", func() {
   250  						runCommand("plugin1", "-r", "repo1", "-f")
   251  						Expect(fakeChecksum.CheckSha1CallCount()).To(Equal(1))
   252  					})
   253  
   254  					It("reports error downloaded file's sha1 does not match the sha1 in metadata", func() {
   255  						fakeChecksum.CheckSha1Returns(false)
   256  
   257  						runCommand("plugin1", "-r", "repo1", "-f")
   258  						Expect(ui.Outputs()).To(ContainSubstrings(
   259  							[]string{"FAILED"},
   260  							[]string{"checksum does not match"},
   261  						))
   262  
   263  					})
   264  
   265  					It("downloads and installs binary when it is available and checksum matches", func() {
   266  						runCommand("plugin1", "-r", "repo1", "-f")
   267  
   268  						Expect(ui.Outputs()).To(ContainSubstrings([]string{"4 bytes downloaded..."}))
   269  						Expect(ui.Outputs()).To(ContainSubstrings([]string{"FAILED"}))
   270  						Expect(ui.Outputs()).To(ContainSubstrings([]string{"Installing plugin"}))
   271  					})
   272  				})
   273  			})
   274  		})
   275  
   276  		Describe("install from plugin repository with no '-r' provided", func() {
   277  			Context("downloads file from internet if path prefix with 'http','ftp' etc...", func() {
   278  				It("will not try locate file locally", func() {
   279  					runCommand("http://127.0.0.1/plugin.exe", "-f")
   280  
   281  					Expect(ui.Outputs()).ToNot(ContainSubstrings(
   282  						[]string{"File not found locally"},
   283  					))
   284  					Expect(ui.Outputs()).To(ContainSubstrings(
   285  						[]string{"download binary file from internet address"},
   286  					))
   287  				})
   288  
   289  				It("informs users when binary is not downloadable from net", func() {
   290  					runCommand("http://path/to/not/a/thing.exe", "-f")
   291  
   292  					Expect(ui.Outputs()).To(ContainSubstrings(
   293  						[]string{"Download attempt failed"},
   294  						[]string{"Unable to install"},
   295  						[]string{"FAILED"},
   296  					))
   297  				})
   298  
   299  				It("downloads and installs binary when it is available", func() {
   300  					h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   301  						fmt.Fprintln(w, "hi")
   302  					})
   303  
   304  					testServer := httptest.NewServer(h)
   305  					defer testServer.Close()
   306  
   307  					runCommand(testServer.URL+"/testfile.exe", "-f")
   308  
   309  					Expect(ui.Outputs()).To(ContainSubstrings([]string{"3 bytes downloaded..."}))
   310  					Expect(ui.Outputs()).To(ContainSubstrings([]string{"FAILED"}))
   311  					Expect(ui.Outputs()).To(ContainSubstrings([]string{"Installing plugin"}))
   312  				})
   313  			})
   314  
   315  			Context("tries to locate binary file at local path if path has no internet prefix", func() {
   316  				It("installs the plugin from a local file if found", func() {
   317  					runCommand("./install_plugin.go", "-f")
   318  
   319  					Expect(ui.Outputs()).ToNot(ContainSubstrings(
   320  						[]string{"download binary file from internet"},
   321  					))
   322  					Expect(ui.Outputs()).To(ContainSubstrings(
   323  						[]string{"Installing plugin install_plugin.go"},
   324  					))
   325  				})
   326  
   327  				It("reports error if local file is not found at given path", func() {
   328  					runCommand("./no/file/is/here.exe", "-f")
   329  
   330  					Expect(ui.Outputs()).To(ContainSubstrings(
   331  						[]string{"File not found locally",
   332  							"./no/file/is/here.exe",
   333  						},
   334  					))
   335  				})
   336  			})
   337  		})
   338  
   339  	})
   340  
   341  	Describe("install failures", func() {
   342  		Context("when the plugin contains a 'help' command", func() {
   343  			It("fails", func() {
   344  				runCommand(test_with_help, "-f")
   345  
   346  				Expect(ui.Outputs()).To(ContainSubstrings(
   347  					[]string{"Command `help` in the plugin being installed is a native CF command/alias.  Rename the `help` command in the plugin being installed in order to enable its installation and use."},
   348  					[]string{"FAILED"},
   349  				))
   350  			})
   351  		})
   352  
   353  		Context("when the plugin's command conflicts with a core command/alias", func() {
   354  			var originalCommand commandregistry.Command
   355  
   356  			BeforeEach(func() {
   357  				originalCommand = commandregistry.Commands.FindCommand("org")
   358  
   359  				commandregistry.Register(testOrgsCmd{})
   360  			})
   361  
   362  			AfterEach(func() {
   363  				if originalCommand != nil {
   364  					commandregistry.Register(originalCommand)
   365  				}
   366  			})
   367  
   368  			It("fails if is shares a command name", func() {
   369  				runCommand(test_with_orgs, "-f")
   370  
   371  				Expect(ui.Outputs()).To(ContainSubstrings(
   372  					[]string{"Command `orgs` in the plugin being installed is a native CF command/alias.  Rename the `orgs` command in the plugin being installed in order to enable its installation and use."},
   373  					[]string{"FAILED"},
   374  				))
   375  			})
   376  
   377  			It("fails if it shares a command short name", func() {
   378  				runCommand(test_with_orgs_short_name, "-f")
   379  
   380  				Expect(ui.Outputs()).To(ContainSubstrings(
   381  					[]string{"Command `o` in the plugin being installed is a native CF command/alias.  Rename the `o` command in the plugin being installed in order to enable its installation and use."},
   382  					[]string{"FAILED"},
   383  				))
   384  			})
   385  		})
   386  
   387  		Context("when the plugin's alias conflicts with a core command/alias", func() {
   388  			var fakeCmd *commandregistryfakes.FakeCommand
   389  			BeforeEach(func() {
   390  				fakeCmd = new(commandregistryfakes.FakeCommand)
   391  			})
   392  
   393  			AfterEach(func() {
   394  				commandregistry.Commands.RemoveCommand("non-conflict-cmd")
   395  				commandregistry.Commands.RemoveCommand("conflict-alias")
   396  			})
   397  
   398  			It("fails if it shares a command name", func() {
   399  				fakeCmd.MetaDataReturns(commandregistry.CommandMetadata{Name: "conflict-alias"})
   400  				commandregistry.Register(fakeCmd)
   401  
   402  				runCommand(aliasConflicts, "-f")
   403  
   404  				Expect(ui.Outputs()).To(ContainSubstrings(
   405  					[]string{"Alias `conflict-alias` in the plugin being installed is a native CF command/alias.  Rename the `conflict-alias` command in the plugin being installed in order to enable its installation and use."},
   406  					[]string{"FAILED"},
   407  				))
   408  			})
   409  
   410  			It("fails if it shares a command short name", func() {
   411  				fakeCmd.MetaDataReturns(commandregistry.CommandMetadata{Name: "non-conflict-cmd", ShortName: "conflict-alias"})
   412  				commandregistry.Register(fakeCmd)
   413  
   414  				runCommand(aliasConflicts, "-f")
   415  
   416  				Expect(ui.Outputs()).To(ContainSubstrings(
   417  					[]string{"Alias `conflict-alias` in the plugin being installed is a native CF command/alias.  Rename the `conflict-alias` command in the plugin being installed in order to enable its installation and use."},
   418  					[]string{"FAILED"},
   419  				))
   420  			})
   421  		})
   422  
   423  		Context("when the plugin's alias conflicts with other installed plugin", func() {
   424  			It("fails if it shares a command name", func() {
   425  				pluginsMap := make(map[string]pluginconfig.PluginMetadata)
   426  				pluginsMap["AliasCollision"] = pluginconfig.PluginMetadata{
   427  					Location: "location/to/config.exe",
   428  					Commands: []plugin.Command{
   429  						{
   430  							Name:     "conflict-alias",
   431  							HelpText: "Hi!",
   432  						},
   433  					},
   434  				}
   435  				pluginConfig.PluginsReturns(pluginsMap)
   436  
   437  				runCommand(aliasConflicts, "-f")
   438  
   439  				Expect(ui.Outputs()).To(ContainSubstrings(
   440  					[]string{"Alias `conflict-alias` is a command/alias in plugin 'AliasCollision'.  You could try uninstalling plugin 'AliasCollision' and then install this plugin in order to invoke the `conflict-alias` command.  However, you should first fully understand the impact of uninstalling the existing 'AliasCollision' plugin."},
   441  					[]string{"FAILED"},
   442  				))
   443  			})
   444  
   445  			It("fails if it shares a command alias", func() {
   446  				pluginsMap := make(map[string]pluginconfig.PluginMetadata)
   447  				pluginsMap["AliasCollision"] = pluginconfig.PluginMetadata{
   448  					Location: "location/to/alias.exe",
   449  					Commands: []plugin.Command{
   450  						{
   451  							Name:     "non-conflict-cmd",
   452  							Alias:    "conflict-alias",
   453  							HelpText: "Hi!",
   454  						},
   455  					},
   456  				}
   457  				pluginConfig.PluginsReturns(pluginsMap)
   458  
   459  				runCommand(aliasConflicts, "-f")
   460  
   461  				Expect(ui.Outputs()).To(ContainSubstrings(
   462  					[]string{"Alias `conflict-alias` is a command/alias in plugin 'AliasCollision'.  You could try uninstalling plugin 'AliasCollision' and then install this plugin in order to invoke the `conflict-alias` command.  However, you should first fully understand the impact of uninstalling the existing 'AliasCollision' plugin."},
   463  					[]string{"FAILED"},
   464  				))
   465  			})
   466  		})
   467  
   468  		Context("when the plugin's command conflicts with other installed plugin", func() {
   469  			It("fails if it shares a command name", func() {
   470  				pluginsMap := make(map[string]pluginconfig.PluginMetadata)
   471  				pluginsMap["Test1Collision"] = pluginconfig.PluginMetadata{
   472  					Location: "location/to/config.exe",
   473  					Commands: []plugin.Command{
   474  						{
   475  							Name:     "test_1_cmd1",
   476  							HelpText: "Hi!",
   477  						},
   478  					},
   479  				}
   480  				pluginConfig.PluginsReturns(pluginsMap)
   481  
   482  				runCommand(test_1, "-f")
   483  
   484  				Expect(ui.Outputs()).To(ContainSubstrings(
   485  					[]string{"Command `test_1_cmd1` is a command/alias in plugin 'Test1Collision'.  You could try uninstalling plugin 'Test1Collision' and then install this plugin in order to invoke the `test_1_cmd1` command.  However, you should first fully understand the impact of uninstalling the existing 'Test1Collision' plugin."},
   486  					[]string{"FAILED"},
   487  				))
   488  			})
   489  
   490  			It("fails if it shares a command alias", func() {
   491  				pluginsMap := make(map[string]pluginconfig.PluginMetadata)
   492  				pluginsMap["AliasCollision"] = pluginconfig.PluginMetadata{
   493  					Location: "location/to/alias.exe",
   494  					Commands: []plugin.Command{
   495  						{
   496  							Name:     "non-conflict-cmd",
   497  							Alias:    "conflict-cmd",
   498  							HelpText: "Hi!",
   499  						},
   500  					},
   501  				}
   502  				pluginConfig.PluginsReturns(pluginsMap)
   503  
   504  				runCommand(aliasConflicts, "-f")
   505  
   506  				Expect(ui.Outputs()).To(ContainSubstrings(
   507  					[]string{"Command `conflict-cmd` is a command/alias in plugin 'AliasCollision'.  You could try uninstalling plugin 'AliasCollision' and then install this plugin in order to invoke the `conflict-cmd` command.  However, you should first fully understand the impact of uninstalling the existing 'AliasCollision' plugin."},
   508  					[]string{"FAILED"},
   509  				))
   510  			})
   511  		})
   512  
   513  		It("if plugin name is already taken", func() {
   514  			pluginConfig.PluginsReturns(map[string]pluginconfig.PluginMetadata{"Test1": {}})
   515  			runCommand(test_1, "-f")
   516  
   517  			Expect(ui.Outputs()).To(ContainSubstrings(
   518  				[]string{"Plugin name", "Test1", "is already taken"},
   519  				[]string{"FAILED"},
   520  			))
   521  		})
   522  
   523  		Context("io", func() {
   524  			BeforeEach(func() {
   525  				err := os.MkdirAll(pluginDir, 0700)
   526  				Expect(err).NotTo(HaveOccurred())
   527  			})
   528  
   529  			It("if a file with the plugin name already exists under ~/.cf/plugin/", func() {
   530  				pluginConfig.PluginsReturns(map[string]pluginconfig.PluginMetadata{"useless": {}})
   531  				pluginConfig.GetPluginPathReturns(curDir)
   532  
   533  				runCommand(filepath.Join(curDir, pluginFile.Name()), "-f")
   534  				Expect(ui.Outputs()).To(ContainSubstrings(
   535  					[]string{"Installing plugin"},
   536  					[]string{"The file", pluginFile.Name(), "already exists"},
   537  					[]string{"FAILED"},
   538  				))
   539  			})
   540  		})
   541  	})
   542  
   543  	Describe("install success", func() {
   544  		BeforeEach(func() {
   545  			err := os.MkdirAll(pluginDir, 0700)
   546  			Expect(err).ToNot(HaveOccurred())
   547  			pluginConfig.GetPluginPathReturns(pluginDir)
   548  		})
   549  
   550  		It("finds plugin in the current directory without having to specify `./`", func() {
   551  			curDir, err := os.Getwd()
   552  			Expect(err).ToNot(HaveOccurred())
   553  
   554  			err = os.Chdir("../../../fixtures/plugins")
   555  			Expect(err).ToNot(HaveOccurred())
   556  
   557  			runCommand(test_curDir, "-f")
   558  			_, err = os.Stat(filepath.Join(pluginDir, "test_1.exe"))
   559  			Expect(err).ToNot(HaveOccurred())
   560  
   561  			err = os.Chdir(curDir)
   562  			Expect(err).ToNot(HaveOccurred())
   563  		})
   564  
   565  		It("copies the plugin into directory <FAKE_HOME_DIR>/.cf/plugins/PLUGIN_FILE_NAME", func() {
   566  			runCommand(test_1, "-f")
   567  
   568  			_, err := os.Stat(test_1)
   569  			Expect(err).ToNot(HaveOccurred())
   570  			_, err = os.Stat(filepath.Join(pluginDir, "test_1.exe"))
   571  			Expect(err).ToNot(HaveOccurred())
   572  		})
   573  
   574  		if runtime.GOOS != "windows" {
   575  			It("Chmods the plugin so it is executable", func() {
   576  				runCommand(test_1, "-f")
   577  
   578  				fileInfo, err := os.Stat(filepath.Join(pluginDir, "test_1.exe"))
   579  				Expect(err).ToNot(HaveOccurred())
   580  				Expect(int(fileInfo.Mode())).To(Equal(0700))
   581  			})
   582  		}
   583  
   584  		It("populate the configuration with plugin metadata", func() {
   585  			runCommand(test_1, "-f")
   586  
   587  			pluginName, pluginMetadata := pluginConfig.SetPluginArgsForCall(0)
   588  
   589  			Expect(pluginName).To(Equal("Test1"))
   590  			Expect(pluginMetadata.Location).To(Equal(filepath.Join(pluginDir, "test_1.exe")))
   591  			Expect(pluginMetadata.Version.Major).To(Equal(1))
   592  			Expect(pluginMetadata.Version.Minor).To(Equal(2))
   593  			Expect(pluginMetadata.Version.Build).To(Equal(4))
   594  			Expect(pluginMetadata.Commands[0].Name).To(Equal("test_1_cmd1"))
   595  			Expect(pluginMetadata.Commands[0].HelpText).To(Equal("help text for test_1_cmd1"))
   596  			Expect(pluginMetadata.Commands[1].Name).To(Equal("test_1_cmd2"))
   597  			Expect(pluginMetadata.Commands[1].HelpText).To(Equal("help text for test_1_cmd2"))
   598  			Expect(ui.Outputs()).To(ContainSubstrings(
   599  				[]string{"Installing plugin test_1.exe"},
   600  				[]string{"OK"},
   601  				[]string{"Plugin", "Test1", "v1.2.4", "successfully installed"},
   602  			))
   603  		})
   604  
   605  		It("installs multiple plugins with no aliases", func() {
   606  			Expect(runCommand(test_1, "-f")).To(Equal(true))
   607  			Expect(runCommand(test_2, "-f")).To(Equal(true))
   608  		})
   609  	})
   610  })
   611  
   612  type testOrgsCmd struct{}
   613  
   614  func (t testOrgsCmd) MetaData() commandregistry.CommandMetadata {
   615  	return commandregistry.CommandMetadata{
   616  		Name:      "orgs",
   617  		ShortName: "o",
   618  	}
   619  }
   620  
   621  func (cmd testOrgsCmd) Requirements(requirementsFactory requirements.Factory, fc flags.FlagContext) ([]requirements.Requirement, error) {
   622  	return []requirements.Requirement{}, nil
   623  }
   624  
   625  func (cmd testOrgsCmd) SetDependency(deps commandregistry.Dependency, pluginCall bool) (c commandregistry.Command) {
   626  	return
   627  }
   628  
   629  func (cmd testOrgsCmd) Execute(c flags.FlagContext) error {
   630  	return nil
   631  }