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