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