github.com/asifdxtreme/cli@v6.1.3-0.20150123051144-9ead8700b4ae+incompatible/cf/commands/plugin/install_plugin_test.go (about)

     1  package plugin_test
     2  
     3  import (
     4  	"io/ioutil"
     5  	"net/rpc"
     6  	"os"
     7  	"path/filepath"
     8  	"runtime"
     9  
    10  	"github.com/cloudfoundry/cli/cf/command"
    11  	testCommand "github.com/cloudfoundry/cli/cf/command/fakes"
    12  	"github.com/cloudfoundry/cli/cf/command_metadata"
    13  	"github.com/cloudfoundry/cli/cf/configuration/plugin_config"
    14  	testconfig "github.com/cloudfoundry/cli/cf/configuration/plugin_config/fakes"
    15  	"github.com/cloudfoundry/cli/plugin"
    16  	testcmd "github.com/cloudfoundry/cli/testhelpers/commands"
    17  	testreq "github.com/cloudfoundry/cli/testhelpers/requirements"
    18  	testterm "github.com/cloudfoundry/cli/testhelpers/terminal"
    19  
    20  	. "github.com/cloudfoundry/cli/cf/commands/plugin"
    21  	. "github.com/cloudfoundry/cli/testhelpers/matchers"
    22  	. "github.com/onsi/ginkgo"
    23  	. "github.com/onsi/gomega"
    24  )
    25  
    26  var _ = Describe("Install", func() {
    27  	var (
    28  		ui                  *testterm.FakeUI
    29  		requirementsFactory *testreq.FakeReqFactory
    30  		config              *testconfig.FakePluginConfiguration
    31  
    32  		coreCmds   map[string]command.Command
    33  		pluginFile *os.File
    34  		homeDir    string
    35  		pluginDir  string
    36  		curDir     string
    37  
    38  		test_1                    string
    39  		test_2                    string
    40  		test_curDir               string
    41  		test_with_help            string
    42  		test_with_push            string
    43  		test_with_push_short_name string
    44  		aliasConflicts            string
    45  	)
    46  
    47  	BeforeEach(func() {
    48  		ui = &testterm.FakeUI{}
    49  		requirementsFactory = &testreq.FakeReqFactory{}
    50  		config = &testconfig.FakePluginConfiguration{}
    51  		coreCmds = make(map[string]command.Command)
    52  
    53  		dir, err := os.Getwd()
    54  		if err != nil {
    55  			panic(err)
    56  		}
    57  		test_1 = filepath.Join(dir, "..", "..", "..", "fixtures", "plugins", "test_1.exe")
    58  		test_2 = filepath.Join(dir, "..", "..", "..", "fixtures", "plugins", "test_2.exe")
    59  		test_curDir = filepath.Join("test_1.exe")
    60  		test_with_help = filepath.Join(dir, "..", "..", "..", "fixtures", "plugins", "test_with_help.exe")
    61  		test_with_push = filepath.Join(dir, "..", "..", "..", "fixtures", "plugins", "test_with_push.exe")
    62  		test_with_push_short_name = filepath.Join(dir, "..", "..", "..", "fixtures", "plugins", "test_with_push_short_name.exe")
    63  		aliasConflicts = filepath.Join(dir, "..", "..", "..", "fixtures", "plugins", "alias_conflicts.exe")
    64  
    65  		rpc.DefaultServer = rpc.NewServer()
    66  
    67  		homeDir, err = ioutil.TempDir(os.TempDir(), "plugins")
    68  		Expect(err).ToNot(HaveOccurred())
    69  
    70  		pluginDir = filepath.Join(homeDir, ".cf", "plugins")
    71  		config.GetPluginPathReturns(pluginDir)
    72  
    73  		curDir, err = os.Getwd()
    74  		Expect(err).ToNot(HaveOccurred())
    75  		pluginFile, err = ioutil.TempFile("./", "test_plugin")
    76  		Expect(err).ToNot(HaveOccurred())
    77  
    78  		if runtime.GOOS != "windows" {
    79  			err = os.Chmod(test_1, 0700)
    80  			Expect(err).ToNot(HaveOccurred())
    81  		}
    82  	})
    83  
    84  	AfterEach(func() {
    85  		os.Remove(filepath.Join(curDir, pluginFile.Name()))
    86  		os.Remove(homeDir)
    87  	})
    88  
    89  	runCommand := func(args ...string) bool {
    90  		cmd := NewPluginInstall(ui, config, coreCmds)
    91  		return testcmd.RunCommand(cmd, args, requirementsFactory)
    92  	}
    93  
    94  	Describe("requirements", func() {
    95  		It("fails with usage when not provided a path to the plugin executable", func() {
    96  			Expect(runCommand()).ToNot(HavePassedRequirements())
    97  		})
    98  	})
    99  
   100  	Describe("failures", func() {
   101  		Context("when the plugin contains a 'help' command", func() {
   102  			It("fails", func() {
   103  				runCommand(test_with_help)
   104  
   105  				Expect(ui.Outputs).To(ContainSubstrings(
   106  					[]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."},
   107  					[]string{"FAILED"},
   108  				))
   109  			})
   110  		})
   111  
   112  		Context("when the plugin's command conflicts with a core command", func() {
   113  			It("fails if is shares a command name", func() {
   114  				coreCmds["push"] = &testCommand.FakeCommand{}
   115  				runCommand(test_with_push)
   116  
   117  				Expect(ui.Outputs).To(ContainSubstrings(
   118  					[]string{"Command `push` in the plugin being installed is a native CF command/alias.  Rename the `push` command in the plugin being installed in order to enable its installation and use."},
   119  					[]string{"FAILED"},
   120  				))
   121  			})
   122  
   123  			It("fails if it shares a command short name", func() {
   124  				push := &testCommand.FakeCommand{}
   125  				push.MetadataReturns(command_metadata.CommandMetadata{
   126  					ShortName: "p",
   127  				})
   128  
   129  				coreCmds["push"] = push
   130  				runCommand(test_with_push_short_name)
   131  
   132  				Expect(ui.Outputs).To(ContainSubstrings(
   133  					[]string{"Command `p` in the plugin being installed is a native CF command/alias.  Rename the `p` command in the plugin being installed in order to enable its installation and use."},
   134  					[]string{"FAILED"},
   135  				))
   136  			})
   137  		})
   138  
   139  		Context("when the plugin's alias conflicts with a core command/alias", func() {
   140  			It("fails if is shares a command name", func() {
   141  				coreCmds["conflict-alias"] = &testCommand.FakeCommand{}
   142  				runCommand(aliasConflicts)
   143  
   144  				Expect(ui.Outputs).To(ContainSubstrings(
   145  					[]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."},
   146  					[]string{"FAILED"},
   147  				))
   148  			})
   149  
   150  			It("fails if it shares a command short name", func() {
   151  				push := &testCommand.FakeCommand{}
   152  				push.MetadataReturns(command_metadata.CommandMetadata{
   153  					ShortName: "conflict-alias",
   154  				})
   155  
   156  				coreCmds["push"] = push
   157  				runCommand(aliasConflicts)
   158  
   159  				Expect(ui.Outputs).To(ContainSubstrings(
   160  					[]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."},
   161  					[]string{"FAILED"},
   162  				))
   163  			})
   164  		})
   165  
   166  		Context("when the plugin's alias conflicts with other installed plugin", func() {
   167  			It("fails if it shares a command name", func() {
   168  				pluginsMap := make(map[string]plugin_config.PluginMetadata)
   169  				pluginsMap["AliasCollision"] = plugin_config.PluginMetadata{
   170  					Location: "location/to/config.exe",
   171  					Commands: []plugin.Command{
   172  						{
   173  							Name:     "conflict-alias",
   174  							HelpText: "Hi!",
   175  						},
   176  					},
   177  				}
   178  				config.PluginsReturns(pluginsMap)
   179  
   180  				runCommand(aliasConflicts)
   181  
   182  				Expect(ui.Outputs).To(ContainSubstrings(
   183  					[]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."},
   184  					[]string{"FAILED"},
   185  				))
   186  			})
   187  
   188  			It("fails if it shares a command alias", func() {
   189  				pluginsMap := make(map[string]plugin_config.PluginMetadata)
   190  				pluginsMap["AliasCollision"] = plugin_config.PluginMetadata{
   191  					Location: "location/to/alias.exe",
   192  					Commands: []plugin.Command{
   193  						{
   194  							Name:     "non-conflict-cmd",
   195  							Alias:    "conflict-alias",
   196  							HelpText: "Hi!",
   197  						},
   198  					},
   199  				}
   200  				config.PluginsReturns(pluginsMap)
   201  
   202  				runCommand(aliasConflicts)
   203  
   204  				Expect(ui.Outputs).To(ContainSubstrings(
   205  					[]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."},
   206  					[]string{"FAILED"},
   207  				))
   208  			})
   209  		})
   210  
   211  		Context("when the plugin's command conflicts with other installed plugin", func() {
   212  			It("fails if it shares a command name", func() {
   213  				pluginsMap := make(map[string]plugin_config.PluginMetadata)
   214  				pluginsMap["Test1Collision"] = plugin_config.PluginMetadata{
   215  					Location: "location/to/config.exe",
   216  					Commands: []plugin.Command{
   217  						{
   218  							Name:     "test_1_cmd1",
   219  							HelpText: "Hi!",
   220  						},
   221  					},
   222  				}
   223  				config.PluginsReturns(pluginsMap)
   224  
   225  				runCommand(test_1)
   226  
   227  				Expect(ui.Outputs).To(ContainSubstrings(
   228  					[]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."},
   229  					[]string{"FAILED"},
   230  				))
   231  			})
   232  
   233  			It("fails if it shares a command alias", func() {
   234  				pluginsMap := make(map[string]plugin_config.PluginMetadata)
   235  				pluginsMap["AliasCollision"] = plugin_config.PluginMetadata{
   236  					Location: "location/to/alias.exe",
   237  					Commands: []plugin.Command{
   238  						{
   239  							Name:     "non-conflict-cmd",
   240  							Alias:    "conflict-cmd",
   241  							HelpText: "Hi!",
   242  						},
   243  					},
   244  				}
   245  				config.PluginsReturns(pluginsMap)
   246  
   247  				runCommand(aliasConflicts)
   248  
   249  				Expect(ui.Outputs).To(ContainSubstrings(
   250  					[]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."},
   251  					[]string{"FAILED"},
   252  				))
   253  			})
   254  		})
   255  
   256  		Context("Locating binary file", func() {
   257  
   258  			Context("first tries to locate binary file at local path", func() {
   259  				It("will not try downloading from internet if file is found locally", func() {
   260  					runCommand("./install_plugin.go")
   261  
   262  					Expect(ui.Outputs).ToNot(ContainSubstrings(
   263  						[]string{"Attempting to download binary file from internet"},
   264  					))
   265  				})
   266  			})
   267  
   268  			Context("tries to download binary from net if file is not found locally", func() {
   269  				It("informs users when binary is not downloadable from net", func() {
   270  					runCommand("path/to/not/a/thing.exe")
   271  
   272  					Expect(ui.Outputs).To(ContainSubstrings(
   273  						[]string{"Download attempt failed"},
   274  						[]string{"Unable to install"},
   275  						[]string{"FAILED"},
   276  					))
   277  				})
   278  			})
   279  
   280  		})
   281  
   282  		It("if plugin name is already taken", func() {
   283  			config.PluginsReturns(map[string]plugin_config.PluginMetadata{"Test1": plugin_config.PluginMetadata{}})
   284  			runCommand(test_1)
   285  
   286  			Expect(ui.Outputs).To(ContainSubstrings(
   287  				[]string{"Plugin name", "Test1", "is already taken"},
   288  				[]string{"FAILED"},
   289  			))
   290  		})
   291  
   292  		Context("io", func() {
   293  			BeforeEach(func() {
   294  				err := os.MkdirAll(pluginDir, 0700)
   295  				Expect(err).NotTo(HaveOccurred())
   296  			})
   297  
   298  			It("if a file with the plugin name already exists under ~/.cf/plugin/", func() {
   299  				config.PluginsReturns(map[string]plugin_config.PluginMetadata{"useless": plugin_config.PluginMetadata{}})
   300  				config.GetPluginPathReturns(curDir)
   301  
   302  				runCommand(filepath.Join(curDir, pluginFile.Name()))
   303  				Expect(ui.Outputs).To(ContainSubstrings(
   304  					[]string{"Installing plugin"},
   305  					[]string{"The file", pluginFile.Name(), "already exists"},
   306  					[]string{"FAILED"},
   307  				))
   308  			})
   309  		})
   310  	})
   311  
   312  	Describe("success", func() {
   313  		BeforeEach(func() {
   314  			err := os.MkdirAll(pluginDir, 0700)
   315  			Expect(err).ToNot(HaveOccurred())
   316  			config.GetPluginPathReturns(pluginDir)
   317  		})
   318  
   319  		It("finds plugin in the current directory without having to specify `./`", func() {
   320  			curDir, err := os.Getwd()
   321  			Expect(err).ToNot(HaveOccurred())
   322  
   323  			err = os.Chdir("../../../fixtures/plugins")
   324  			Expect(err).ToNot(HaveOccurred())
   325  
   326  			runCommand(test_curDir)
   327  			_, err = os.Stat(filepath.Join(pluginDir, "test_1.exe"))
   328  			Expect(err).ToNot(HaveOccurred())
   329  
   330  			err = os.Chdir(curDir)
   331  			Expect(err).ToNot(HaveOccurred())
   332  		})
   333  
   334  		It("copies the plugin into directory <FAKE_HOME_DIR>/.cf/plugins/PLUGIN_FILE_NAME", func() {
   335  			runCommand(test_1)
   336  
   337  			_, err := os.Stat(test_1)
   338  			Expect(err).ToNot(HaveOccurred())
   339  			_, err = os.Stat(filepath.Join(pluginDir, "test_1.exe"))
   340  			Expect(err).ToNot(HaveOccurred())
   341  		})
   342  
   343  		if runtime.GOOS != "windows" {
   344  			It("Chmods the plugin so it is executable", func() {
   345  				runCommand(test_1)
   346  
   347  				fileInfo, err := os.Stat(filepath.Join(pluginDir, "test_1.exe"))
   348  				Expect(err).ToNot(HaveOccurred())
   349  				Expect(int(fileInfo.Mode())).To(Equal(0700))
   350  			})
   351  		}
   352  
   353  		It("populate the configuration with plugin metadata", func() {
   354  			runCommand(test_1)
   355  
   356  			pluginName, pluginMetadata := config.SetPluginArgsForCall(0)
   357  
   358  			Expect(pluginName).To(Equal("Test1"))
   359  			Expect(pluginMetadata.Location).To(Equal(filepath.Join(pluginDir, "test_1.exe")))
   360  			Expect(pluginMetadata.Version.Major).To(Equal(1))
   361  			Expect(pluginMetadata.Version.Minor).To(Equal(2))
   362  			Expect(pluginMetadata.Version.Build).To(Equal(3))
   363  			Expect(pluginMetadata.Commands[0].Name).To(Equal("test_1_cmd1"))
   364  			Expect(pluginMetadata.Commands[0].HelpText).To(Equal("help text for test_1_cmd1"))
   365  			Expect(pluginMetadata.Commands[1].Name).To(Equal("test_1_cmd2"))
   366  			Expect(pluginMetadata.Commands[1].HelpText).To(Equal("help text for test_1_cmd2"))
   367  			Expect(ui.Outputs).To(ContainSubstrings(
   368  				[]string{"Installing plugin", test_1},
   369  				[]string{"OK"},
   370  				[]string{"Plugin", "Test1", "v1.2.3", "successfully installed"},
   371  			))
   372  		})
   373  
   374  		It("installs multiple plugins with no aliases", func() {
   375  			Expect(runCommand(test_1)).To(Equal(true))
   376  			Expect(runCommand(test_2)).To(Equal(true))
   377  		})
   378  	})
   379  })