github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+incompatible/util/configv3/plugins_config_test.go (about) 1 package configv3_test 2 3 import ( 4 "io/ioutil" 5 "os" 6 "path/filepath" 7 "syscall" 8 9 . "code.cloudfoundry.org/cli/util/configv3" 10 . "github.com/onsi/ginkgo" 11 . "github.com/onsi/ginkgo/extensions/table" 12 . "github.com/onsi/gomega" 13 ) 14 15 var _ = Describe("PluginsConfig", func() { 16 var homeDir string 17 18 BeforeEach(func() { 19 homeDir = setup() 20 }) 21 22 AfterEach(func() { 23 teardown(homeDir) 24 }) 25 26 DescribeTable("when the plugin config exists", 27 func(setup func() (string, string)) { 28 location, CFPluginHome := setup() 29 if CFPluginHome != "" { 30 os.Setenv("CF_PLUGIN_HOME", CFPluginHome) 31 defer os.Unsetenv("CF_PLUGIN_HOME") 32 } 33 34 rawConfig := ` 35 { 36 "Plugins": { 37 "Diego-Enabler": { 38 "Location": "~/.cf/plugins/diego-enabler_darwin_amd64", 39 "Version": { 40 "Major": 1, 41 "Minor": 0, 42 "Build": 1 43 }, 44 "Commands": [ 45 { 46 "Name": "enable-diego", 47 "Alias": "", 48 "HelpText": "enable Diego support for an app", 49 "UsageDetails": { 50 "Usage": "cf enable-diego APP_NAME", 51 "Options": null 52 } 53 }, 54 { 55 "Name": "disable-diego", 56 "Alias": "", 57 "HelpText": "disable Diego support for an app", 58 "UsageDetails": { 59 "Usage": "cf disable-diego APP_NAME", 60 "Options": null 61 } 62 } 63 ] 64 } 65 } 66 }` 67 setPluginConfig(location, rawConfig) 68 config, err := LoadConfig() 69 Expect(err).ToNot(HaveOccurred()) 70 Expect(config).ToNot(BeNil()) 71 72 plugins := config.Plugins() 73 Expect(plugins).ToNot(BeEmpty()) 74 75 plugin := plugins[0] 76 Expect(plugin.Name).To(Equal("Diego-Enabler")) 77 Expect(plugin.Location).To(Equal("~/.cf/plugins/diego-enabler_darwin_amd64")) 78 Expect(plugin.Version.Major).To(Equal(1)) 79 Expect(plugin.Commands).To(HaveLen(2)) 80 Expect(plugin.Commands).To(ContainElement( 81 PluginCommand{ 82 Name: "enable-diego", 83 Alias: "", 84 HelpText: "enable Diego support for an app", 85 UsageDetails: PluginUsageDetails{ 86 Usage: "cf enable-diego APP_NAME", 87 }, 88 }, 89 )) 90 }, 91 92 Entry("standard location", func() (string, string) { 93 return filepath.Join(homeDir, ".cf", "plugins"), "" 94 }), 95 96 Entry("non-standard location", func() (string, string) { 97 return filepath.Join(homeDir, "foo", ".cf", "plugins"), filepath.Join(homeDir, "foo") 98 }), 99 ) 100 101 Describe("Config", func() { 102 Describe("RemovePlugin", func() { 103 var ( 104 config *Config 105 err error 106 ) 107 108 BeforeEach(func() { 109 rawConfig := ` 110 { 111 "Plugins": { 112 "Diego-Enabler": { 113 "Location": "~/.cf/plugins/diego-enabler_darwin_amd64", 114 "Version": { 115 "Major": 1, 116 "Minor": 0, 117 "Build": 1 118 }, 119 "Commands": [ 120 { 121 "Name": "enable-diego", 122 "Alias": "", 123 "HelpText": "enable Diego support for an app", 124 "UsageDetails": { 125 "Usage": "cf enable-diego APP_NAME", 126 "Options": null 127 } 128 } 129 ] 130 }, 131 "Dora-Non-Enabler": { 132 "Location": "~/.cf/plugins/diego-enabler_darwin_amd64", 133 "Version": { 134 "Major": 1, 135 "Minor": 0, 136 "Build": 1 137 }, 138 "Commands": [ 139 { 140 "Name": "disable-diego", 141 "Alias": "", 142 "HelpText": "disable Diego support for an app", 143 "UsageDetails": { 144 "Usage": "cf disable-diego APP_NAME", 145 "Options": null 146 } 147 } 148 ] 149 } 150 } 151 }` 152 setPluginConfig(filepath.Join(homeDir, ".cf", "plugins"), rawConfig) 153 154 config, err = LoadConfig() 155 Expect(err).ToNot(HaveOccurred()) 156 }) 157 158 When("the plugin exists", func() { 159 It("removes the plugin from the config", func() { 160 plugins := config.Plugins() 161 162 Expect(plugins).To(HaveLen(2)) 163 Expect(plugins[0].Name).To(Equal("Diego-Enabler")) 164 165 config.RemovePlugin("Diego-Enabler") 166 167 Expect(config.Plugins()).To(HaveLen(1)) 168 }) 169 }) 170 171 When("the plugin does not exist", func() { 172 It("doesn't blow up", func() { 173 config.RemovePlugin("does-not-exist") 174 }) 175 }) 176 }) 177 178 Describe("AddPlugin", func() { 179 var ( 180 config *Config 181 err error 182 ) 183 184 BeforeEach(func() { 185 rawConfig := ` 186 { 187 "Plugins": { 188 "plugin-1": {} 189 } 190 }` 191 192 pluginsPath := filepath.Join(homeDir, ".cf", "plugins") 193 setPluginConfig(pluginsPath, rawConfig) 194 config, err = LoadConfig() 195 Expect(err).ToNot(HaveOccurred()) 196 }) 197 198 It("adds the plugin to the config", func() { 199 config.AddPlugin(Plugin{ 200 Name: "plugin-2", 201 Location: "/over-there", 202 }) 203 204 plugin, exist := config.GetPlugin("plugin-2") 205 Expect(exist).To(BeTrue()) 206 Expect(plugin).To(Equal(Plugin{Name: "plugin-2", Location: "/over-there"})) 207 }) 208 }) 209 210 Describe("GetPlugin", func() { 211 var ( 212 config *Config 213 err error 214 ) 215 216 BeforeEach(func() { 217 rawConfig := ` 218 { 219 "Plugins": { 220 "plugin-1": {}, 221 "plugin-2": {} 222 } 223 }` 224 225 pluginsPath := filepath.Join(homeDir, ".cf", "plugins") 226 setPluginConfig(pluginsPath, rawConfig) 227 config, err = LoadConfig() 228 Expect(err).ToNot(HaveOccurred()) 229 }) 230 231 It("returns the plugin and true if it exists", func() { 232 plugin, exist := config.GetPlugin("plugin-1") 233 Expect(exist).To(BeTrue()) 234 Expect(plugin).To(Equal(Plugin{Name: "plugin-1"})) 235 plugin, exist = config.GetPlugin("plugin-2") 236 Expect(exist).To(BeTrue()) 237 Expect(plugin).To(Equal(Plugin{Name: "plugin-2"})) 238 }) 239 240 It("returns an empty plugin and false if it doesn't exist", func() { 241 plugin, exist := config.GetPlugin("does-not-exist") 242 Expect(exist).To(BeFalse()) 243 Expect(plugin).To(Equal(Plugin{})) 244 }) 245 }) 246 247 Describe("GetPluginCaseInsensitive", func() { 248 var ( 249 config *Config 250 err error 251 ) 252 253 BeforeEach(func() { 254 rawConfig := ` 255 { 256 "Plugins": { 257 "plugin-1": {}, 258 "plugin-2": {}, 259 "PLUGIN-2": {} 260 } 261 }` 262 263 pluginsPath := filepath.Join(homeDir, ".cf", "plugins") 264 setPluginConfig(pluginsPath, rawConfig) 265 config, err = LoadConfig() 266 Expect(err).ToNot(HaveOccurred()) 267 }) 268 269 When("there is a matching plugin", func() { 270 It("returns the plugin and true", func() { 271 plugin, exist := config.GetPluginCaseInsensitive("PlUgIn-1") 272 Expect(plugin).To(Equal(Plugin{Name: "plugin-1"})) 273 Expect(exist).To(BeTrue()) 274 }) 275 }) 276 277 When("there is no matching plugin", func() { 278 It("returns an empty plugin and false", func() { 279 plugin, exist := config.GetPluginCaseInsensitive("plugin-3") 280 Expect(plugin).To(Equal(Plugin{})) 281 Expect(exist).To(BeFalse()) 282 }) 283 }) 284 285 When("there are multiple matching plugins", func() { 286 // this should never happen 287 It("returns one of them", func() { 288 _, exist := config.GetPluginCaseInsensitive("pLuGiN-2") 289 // do not test the plugin because the plugins are in a map and the 290 // order is undefined 291 Expect(exist).To(BeTrue()) 292 }) 293 }) 294 }) 295 296 Describe("Plugins", func() { 297 BeforeEach(func() { 298 rawConfig := ` 299 { 300 "Plugins": { 301 "Q-plugin": {}, 302 "plugin-2": {}, 303 "plugin-1": {} 304 } 305 }` 306 307 pluginsPath := filepath.Join(homeDir, ".cf", "plugins") 308 setPluginConfig(pluginsPath, rawConfig) 309 }) 310 311 It("returns the pluging sorted by name", func() { 312 config, err := LoadConfig() 313 Expect(err).ToNot(HaveOccurred()) 314 Expect(config.Plugins()).To(Equal([]Plugin{ 315 {Name: "plugin-1"}, 316 {Name: "plugin-2"}, 317 {Name: "Q-plugin"}, 318 })) 319 }) 320 }) 321 322 Describe("WritePluginConfig", func() { 323 var config *Config 324 325 BeforeEach(func() { 326 rawConfig := ` 327 { 328 "Plugins": { 329 "Diego-Enabler": { 330 "Location": "~/.cf/plugins/diego-enabler_darwin_amd64", 331 "Version": { 332 "Major": 1, 333 "Minor": 0, 334 "Build": 1 335 }, 336 "Commands": [ 337 { 338 "Name": "enable-diego", 339 "Alias": "", 340 "HelpText": "enable Diego support for an app", 341 "UsageDetails": { 342 "Usage": "cf enable-diego APP_NAME", 343 "Options": null 344 } 345 } 346 ] 347 } 348 } 349 }` 350 setPluginConfig(filepath.Join(homeDir, ".cf", "plugins"), rawConfig) 351 352 var err error 353 config, err = LoadConfig() 354 Expect(err).ToNot(HaveOccurred()) 355 }) 356 357 When("no errors are encountered", func() { 358 It("writes the plugin config to pluginHome/.cf/plugin/config.json", func() { 359 config.RemovePlugin("Diego-Enabler") 360 361 err := config.WritePluginConfig() 362 Expect(err).ToNot(HaveOccurred()) 363 364 newConfig, err := LoadConfig() 365 Expect(err).ToNot(HaveOccurred()) 366 Expect(newConfig.Plugins()).To(HaveLen(0)) 367 }) 368 }) 369 370 When("an error is encountered", func() { 371 BeforeEach(func() { 372 pluginConfigPath := filepath.Join(homeDir, ".cf", "plugins", "config.json") 373 err := os.Remove(pluginConfigPath) 374 Expect(err).ToNot(HaveOccurred()) 375 err = os.Mkdir(pluginConfigPath, 0700) 376 Expect(err).ToNot(HaveOccurred()) 377 }) 378 379 It("returns the error", func() { 380 err := config.WritePluginConfig() 381 _, ok := err.(*os.PathError) 382 Expect(ok).To(BeTrue()) 383 }) 384 }) 385 }) 386 387 Describe("CreatePluginHome", func() { 388 var ( 389 config *Config 390 391 pluginHome string 392 393 executeErr error 394 ) 395 396 BeforeEach(func() { 397 config = new(Config) 398 399 var err error 400 pluginHome, err = ioutil.TempDir("", "my-plugin-home") 401 Expect(err).ToNot(HaveOccurred()) 402 Expect(os.RemoveAll(pluginHome)).ToNot(HaveOccurred()) 403 }) 404 405 AfterEach(func() { 406 Expect(os.RemoveAll(pluginHome)).ToNot(HaveOccurred()) 407 }) 408 409 JustBeforeEach(func() { 410 executeErr = config.CreatePluginHome() 411 }) 412 413 When("it correctly writes a directory", func() { 414 BeforeEach(func() { 415 config.ENV.CFPluginHome = pluginHome 416 }) 417 418 It("returns no error", func() { 419 Expect(executeErr).ToNot(HaveOccurred()) 420 421 _, statErr := os.Stat(pluginHome) 422 Expect(os.IsNotExist(statErr)).To(BeFalse()) 423 }) 424 }) 425 426 When("it fails to write a directory", func() { 427 var tempFile string 428 429 BeforeEach(func() { 430 f, err := ioutil.TempFile("", "fail-plugin-home") 431 Expect(err).ToNot(HaveOccurred()) 432 Expect(f.Close()).ToNot(HaveOccurred()) 433 tempFile = f.Name() 434 435 config.ENV.CFPluginHome = tempFile + "/some-path" 436 }) 437 438 AfterEach(func() { 439 Expect(os.RemoveAll(tempFile)).ToNot(HaveOccurred()) 440 }) 441 442 It("returns an error", func() { 443 Expect(executeErr).To(MatchError(&os.PathError{Op: "mkdir", Path: tempFile, Err: syscall.ENOTDIR})) 444 }) 445 }) 446 }) 447 }) 448 449 Describe("Plugin", func() { 450 Describe("CalculateSHA1", func() { 451 var plugin Plugin 452 453 When("no errors are encountered calculating the sha1 value", func() { 454 var file *os.File 455 456 BeforeEach(func() { 457 var err error 458 file, err = ioutil.TempFile("", "") 459 Expect(err).NotTo(HaveOccurred()) 460 defer file.Close() 461 462 err = ioutil.WriteFile(file.Name(), []byte("foo"), 0600) 463 Expect(err).NotTo(HaveOccurred()) 464 465 plugin.Location = file.Name() 466 }) 467 468 AfterEach(func() { 469 err := os.Remove(file.Name()) 470 Expect(err).NotTo(HaveOccurred()) 471 }) 472 473 It("returns the sha1 value", func() { 474 Expect(plugin.CalculateSHA1()).To(Equal("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33")) 475 }) 476 }) 477 478 When("an error is encountered calculating the sha1 value", func() { 479 var dirPath string 480 481 BeforeEach(func() { 482 var err error 483 dirPath, err = ioutil.TempDir("", "") 484 Expect(err).NotTo(HaveOccurred()) 485 486 plugin.Location = dirPath 487 }) 488 489 AfterEach(func() { 490 err := os.RemoveAll(dirPath) 491 Expect(err).NotTo(HaveOccurred()) 492 }) 493 494 It("returns 'N/A'", func() { 495 Expect(plugin.CalculateSHA1()).To(Equal("N/A")) 496 }) 497 }) 498 }) 499 500 Describe("PluginCommands", func() { 501 It("returns the plugin's commands sorted by command name", func() { 502 plugin := Plugin{ 503 Commands: []PluginCommand{ 504 {Name: "T-sort"}, 505 {Name: "sort-2"}, 506 {Name: "sort-1"}, 507 }, 508 } 509 510 Expect(plugin.PluginCommands()).To(Equal([]PluginCommand{ 511 PluginCommand{Name: "sort-1"}, 512 PluginCommand{Name: "sort-2"}, 513 PluginCommand{Name: "T-sort"}, 514 })) 515 }) 516 }) 517 }) 518 519 Describe("PluginVersion", func() { 520 var version PluginVersion 521 522 Describe("String", func() { 523 It("returns the version in the format x.y.z", func() { 524 version = PluginVersion{ 525 Major: 1, 526 Minor: 2, 527 Build: 3, 528 } 529 Expect(version.String()).To(Equal("1.2.3")) 530 }) 531 532 When("the major, minor, and build are all 0", func() { 533 BeforeEach(func() { 534 version = PluginVersion{ 535 Major: 0, 536 Minor: 0, 537 Build: 0, 538 } 539 }) 540 541 It("returns 'N/A'", func() { 542 Expect(version.String()).To(Equal("N/A")) 543 }) 544 }) 545 }) 546 }) 547 548 Describe("PluginCommand", func() { 549 var cmd PluginCommand 550 551 Describe("CommandName", func() { 552 It("returns the name of the command", func() { 553 cmd = PluginCommand{Name: "some-command"} 554 Expect(cmd.CommandName()).To(Equal("some-command")) 555 }) 556 557 When("the command name and command alias are not empty", func() { 558 BeforeEach(func() { 559 cmd = PluginCommand{Name: "some-command", Alias: "sp"} 560 }) 561 562 It("returns the command name concatenated with the command alias", func() { 563 Expect(cmd.CommandName()).To(Equal("some-command, sp")) 564 }) 565 }) 566 }) 567 }) 568 })