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 })