sigs.k8s.io/kubebuilder/v3@v3.14.0/pkg/cli/options_test.go (about) 1 /* 2 Copyright 2020 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cli 18 19 import ( 20 "errors" 21 "fmt" 22 "os" 23 "path/filepath" 24 "runtime" 25 26 . "github.com/onsi/ginkgo/v2" 27 . "github.com/onsi/gomega" 28 "github.com/spf13/afero" 29 "github.com/spf13/cobra" 30 31 "sigs.k8s.io/kubebuilder/v3/pkg/config" 32 "sigs.k8s.io/kubebuilder/v3/pkg/machinery" 33 "sigs.k8s.io/kubebuilder/v3/pkg/model/stage" 34 "sigs.k8s.io/kubebuilder/v3/pkg/plugin" 35 ) 36 37 var _ = Describe("Discover external plugins", func() { 38 Context("with valid plugins root path", func() { 39 var ( 40 homePath string = os.Getenv("HOME") 41 customPath string = "/tmp/myplugins" 42 // store user's original EXTERNAL_PLUGINS_PATH 43 originalPluginPath string 44 xdghome string 45 // store user's original XDG_CONFIG_HOME 46 originalXdghome string 47 ) 48 49 When("XDG_CONFIG_HOME is not set and using the $HOME environment variable", func() { 50 // store and unset the XDG_CONFIG_HOME 51 BeforeEach(func() { 52 originalXdghome = os.Getenv("XDG_CONFIG_HOME") 53 err := os.Unsetenv("XDG_CONFIG_HOME") 54 Expect(err).To(BeNil()) 55 }) 56 57 AfterEach(func() { 58 if originalXdghome != "" { 59 // restore the original value 60 err := os.Setenv("XDG_CONFIG_HOME", originalXdghome) 61 Expect(err).To(BeNil()) 62 } 63 }) 64 65 It("should return the correct path for the darwin OS", func() { 66 plgPath, err := getPluginsRoot("darwin") 67 Expect(err).To(BeNil()) 68 Expect(plgPath).To(Equal(fmt.Sprintf("%s/Library/Application Support/kubebuilder/plugins", homePath))) 69 }) 70 71 It("should return the correct path for the linux OS", func() { 72 plgPath, err := getPluginsRoot("linux") 73 Expect(err).To(BeNil()) 74 Expect(plgPath).To(Equal(fmt.Sprintf("%s/.config/kubebuilder/plugins", homePath))) 75 }) 76 77 It("should return error when the host is not darwin / linux", func() { 78 plgPath, err := getPluginsRoot("random") 79 Expect(plgPath).To(Equal("")) 80 Expect(err).ToNot(BeNil()) 81 Expect(err.Error()).To(ContainSubstring("host not supported")) 82 }) 83 }) 84 85 When("XDG_CONFIG_HOME is set", func() { 86 BeforeEach(func() { 87 // store and set the XDG_CONFIG_HOME 88 originalXdghome = os.Getenv("XDG_CONFIG_HOME") 89 err := os.Setenv("XDG_CONFIG_HOME", fmt.Sprintf("%s/.config", homePath)) 90 Expect(err).To(BeNil()) 91 92 xdghome = os.Getenv("XDG_CONFIG_HOME") 93 }) 94 95 AfterEach(func() { 96 if originalXdghome != "" { 97 // restore the original value 98 err := os.Setenv("XDG_CONFIG_HOME", originalXdghome) 99 Expect(err).To(BeNil()) 100 } else { 101 // unset if it was originally unset 102 err := os.Unsetenv("XDG_CONFIG_HOME") 103 Expect(err).To(BeNil()) 104 } 105 }) 106 107 It("should return the correct path for the darwin OS", func() { 108 plgPath, err := getPluginsRoot("darwin") 109 Expect(err).To(BeNil()) 110 Expect(plgPath).To(Equal(fmt.Sprintf("%s/kubebuilder/plugins", xdghome))) 111 }) 112 113 It("should return the correct path for the linux OS", func() { 114 plgPath, err := getPluginsRoot("linux") 115 Expect(err).To(BeNil()) 116 Expect(plgPath).To(Equal(fmt.Sprintf("%s/kubebuilder/plugins", xdghome))) 117 }) 118 119 It("should return error when the host is not darwin / linux", func() { 120 plgPath, err := getPluginsRoot("random") 121 Expect(plgPath).To(Equal("")) 122 Expect(err).ToNot(BeNil()) 123 Expect(err.Error()).To(ContainSubstring("host not supported")) 124 }) 125 }) 126 127 When("using the custom path", func() { 128 BeforeEach(func() { 129 err := os.MkdirAll(customPath, 0750) 130 Expect(err).To(BeNil()) 131 132 // store and set the EXTERNAL_PLUGINS_PATH 133 originalPluginPath = os.Getenv("EXTERNAL_PLUGINS_PATH") 134 err = os.Setenv("EXTERNAL_PLUGINS_PATH", customPath) 135 Expect(err).To(BeNil()) 136 }) 137 138 AfterEach(func() { 139 if originalPluginPath != "" { 140 // restore the original value 141 err := os.Setenv("EXTERNAL_PLUGINS_PATH", originalPluginPath) 142 Expect(err).To(BeNil()) 143 } else { 144 // unset if it was originally unset 145 err := os.Unsetenv("EXTERNAL_PLUGINS_PATH") 146 Expect(err).To(BeNil()) 147 } 148 }) 149 150 It("should return the user given path for darwin OS", func() { 151 plgPath, err := getPluginsRoot("darwin") 152 Expect(plgPath).To(Equal(customPath)) 153 Expect(err).To(BeNil()) 154 }) 155 156 It("should return the user given path for linux OS", func() { 157 plgPath, err := getPluginsRoot("linux") 158 Expect(plgPath).To(Equal(customPath)) 159 Expect(err).To(BeNil()) 160 }) 161 162 It("should report error when the host is not darwin / linux", func() { 163 plgPath, err := getPluginsRoot("random") 164 Expect(plgPath).To(Equal("")) 165 Expect(err).ToNot(BeNil()) 166 Expect(err.Error()).To(ContainSubstring("host not supported")) 167 }) 168 }) 169 }) 170 171 Context("with invalid plugins root path", func() { 172 var originalPluginPath string 173 174 BeforeEach(func() { 175 originalPluginPath = os.Getenv("EXTERNAL_PLUGINS_PATH") 176 err := os.Setenv("EXTERNAL_PLUGINS_PATH", "/non/existent/path") 177 Expect(err).To(BeNil()) 178 }) 179 180 AfterEach(func() { 181 if originalPluginPath != "" { 182 // restore the original value 183 err := os.Setenv("EXTERNAL_PLUGINS_PATH", originalPluginPath) 184 Expect(err).To(BeNil()) 185 } else { 186 // unset if it was originally unset 187 err := os.Unsetenv("EXTERNAL_PLUGINS_PATH") 188 Expect(err).To(BeNil()) 189 } 190 }) 191 192 It("should return an error for the darwin OS", func() { 193 plgPath, err := getPluginsRoot("darwin") 194 Expect(err).ToNot(BeNil()) 195 Expect(plgPath).To(Equal("")) 196 }) 197 198 It("should return an error for the linux OS", func() { 199 plgPath, err := getPluginsRoot("linux") 200 Expect(err).ToNot(BeNil()) 201 Expect(plgPath).To(Equal("")) 202 }) 203 204 It("should return an error when the host is not darwin / linux", func() { 205 plgPath, err := getPluginsRoot("random") 206 Expect(err).ToNot(BeNil()) 207 Expect(plgPath).To(Equal("")) 208 }) 209 }) 210 211 Context("when plugin executables exist in the expected plugin directories", func() { 212 const ( 213 filePermissions os.FileMode = 755 214 testPluginScript = `#!/bin/bash 215 echo "This is an external plugin" 216 ` 217 ) 218 219 var ( 220 pluginFilePath string 221 pluginFileName string 222 pluginPath string 223 f afero.File 224 fs machinery.Filesystem 225 err error 226 ) 227 228 BeforeEach(func() { 229 fs = machinery.Filesystem{ 230 FS: afero.NewMemMapFs(), 231 } 232 233 pluginPath, err = getPluginsRoot(runtime.GOOS) 234 Expect(err).To(BeNil()) 235 236 pluginFileName = "externalPlugin.sh" 237 pluginFilePath = filepath.Join(pluginPath, "externalPlugin", "v1", pluginFileName) 238 239 err = fs.FS.MkdirAll(filepath.Dir(pluginFilePath), 0o700) 240 Expect(err).To(BeNil()) 241 242 f, err = fs.FS.Create(pluginFilePath) 243 Expect(err).To(BeNil()) 244 Expect(f).ToNot(BeNil()) 245 246 _, err = fs.FS.Stat(pluginFilePath) 247 Expect(err).To(BeNil()) 248 }) 249 250 It("should discover the external plugin executable without any errors", func() { 251 // test that DiscoverExternalPlugins works if the plugin file is an executable and 252 // is found in the expected path 253 _, err = f.WriteString(testPluginScript) 254 Expect(err).To(Not(HaveOccurred())) 255 256 err = fs.FS.Chmod(pluginFilePath, filePermissions) 257 Expect(err).To(Not(HaveOccurred())) 258 259 _, err = fs.FS.Stat(pluginFilePath) 260 Expect(err).To(BeNil()) 261 262 ps, err := DiscoverExternalPlugins(fs.FS) 263 Expect(err).To(BeNil()) 264 Expect(ps).NotTo(BeNil()) 265 Expect(len(ps)).To(Equal(1)) 266 Expect(ps[0].Name()).To(Equal("externalPlugin")) 267 Expect(ps[0].Version().Number).To(Equal(1)) 268 }) 269 270 It("should discover multiple external plugins and return the plugins without any errors", func() { 271 // set the execute permissions on the first plugin executable 272 err = fs.FS.Chmod(pluginFilePath, filePermissions) 273 274 pluginFileName = "myotherexternalPlugin.sh" 275 pluginFilePath = filepath.Join(pluginPath, "myotherexternalPlugin", "v1", pluginFileName) 276 277 f, err = fs.FS.Create(pluginFilePath) 278 Expect(err).To(BeNil()) 279 Expect(f).ToNot(BeNil()) 280 281 _, err = fs.FS.Stat(pluginFilePath) 282 Expect(err).To(BeNil()) 283 284 _, err = f.WriteString(testPluginScript) 285 Expect(err).To(Not(HaveOccurred())) 286 287 // set the execute permissions on the second plugin executable 288 err = fs.FS.Chmod(pluginFilePath, filePermissions) 289 Expect(err).To(Not(HaveOccurred())) 290 291 _, err = fs.FS.Stat(pluginFilePath) 292 Expect(err).To(BeNil()) 293 294 ps, err := DiscoverExternalPlugins(fs.FS) 295 Expect(err).To(BeNil()) 296 Expect(ps).NotTo(BeNil()) 297 Expect(len(ps)).To(Equal(2)) 298 299 Expect(ps[0].Name()).To(Equal("externalPlugin")) 300 Expect(ps[1].Name()).To(Equal("myotherexternalPlugin")) 301 }) 302 303 Context("that are invalid", func() { 304 BeforeEach(func() { 305 fs = machinery.Filesystem{ 306 FS: afero.NewMemMapFs(), 307 } 308 309 pluginPath, err = getPluginsRoot(runtime.GOOS) 310 Expect(err).To(BeNil()) 311 }) 312 313 It("should error if the plugin found is not an executable", func() { 314 pluginFileName = "externalPlugin.sh" 315 pluginFilePath = filepath.Join(pluginPath, "externalPlugin", "v1", pluginFileName) 316 317 err = fs.FS.MkdirAll(filepath.Dir(pluginFilePath), 0o700) 318 Expect(err).To(BeNil()) 319 320 f, err := fs.FS.Create(pluginFilePath) 321 Expect(err).To(BeNil()) 322 Expect(f).ToNot(BeNil()) 323 324 _, err = fs.FS.Stat(pluginFilePath) 325 Expect(err).To(BeNil()) 326 327 // set the plugin file permissions to read-only 328 err = fs.FS.Chmod(pluginFilePath, 0o444) 329 Expect(err).To(Not(HaveOccurred())) 330 331 ps, err := DiscoverExternalPlugins(fs.FS) 332 Expect(err).NotTo(BeNil()) 333 Expect(err.Error()).To(ContainSubstring("not an executable")) 334 Expect(len(ps)).To(Equal(0)) 335 }) 336 337 It("should error if the plugin found has an invalid plugin name", func() { 338 pluginFileName = ".sh" 339 pluginFilePath = filepath.Join(pluginPath, "externalPlugin", "v1", pluginFileName) 340 341 err = fs.FS.MkdirAll(filepath.Dir(pluginFilePath), 0o700) 342 Expect(err).To(BeNil()) 343 344 f, err = fs.FS.Create(pluginFilePath) 345 Expect(err).To(BeNil()) 346 Expect(f).ToNot(BeNil()) 347 348 ps, err := DiscoverExternalPlugins(fs.FS) 349 Expect(err).NotTo(BeNil()) 350 Expect(err.Error()).To(ContainSubstring("Invalid plugin name found")) 351 Expect(len(ps)).To(Equal(0)) 352 }) 353 }) 354 355 Context("that does not match the plugin root directory name", func() { 356 BeforeEach(func() { 357 fs = machinery.Filesystem{ 358 FS: afero.NewMemMapFs(), 359 } 360 361 pluginPath, err = getPluginsRoot(runtime.GOOS) 362 Expect(err).To(BeNil()) 363 }) 364 365 It("should skip adding the external plugin and not return any errors", func() { 366 pluginFileName = "random.sh" 367 pluginFilePath = filepath.Join(pluginPath, "externalPlugin", "v1", pluginFileName) 368 369 err = fs.FS.MkdirAll(filepath.Dir(pluginFilePath), 0o700) 370 Expect(err).To(BeNil()) 371 372 f, err = fs.FS.Create(pluginFilePath) 373 Expect(err).To(BeNil()) 374 Expect(f).ToNot(BeNil()) 375 376 err = fs.FS.Chmod(pluginFilePath, filePermissions) 377 Expect(err).To(BeNil()) 378 379 ps, err := DiscoverExternalPlugins(fs.FS) 380 Expect(err).To(BeNil()) 381 Expect(len(ps)).To(Equal(0)) 382 }) 383 384 It("should fail if pluginsroot is empty", func() { 385 errPluginsRoot := errors.New("could not retrieve plugins root") 386 retrievePluginsRoot = func(host string) (string, error) { 387 return "", errPluginsRoot 388 } 389 390 _, err := DiscoverExternalPlugins(fs.FS) 391 Expect(err).NotTo(BeNil()) 392 393 Expect(err).To(Equal(errPluginsRoot)) 394 }) 395 396 It("should skip parsing of directories if plugins root is not a directory", func() { 397 retrievePluginsRoot = func(host string) (string, error) { 398 return "externalplugin.sh", nil 399 } 400 401 _, err := DiscoverExternalPlugins(fs.FS) 402 Expect(err).To(BeNil()) 403 }) 404 405 It("should return full path to the external plugins without XDG_CONFIG_HOME", func() { 406 if _, ok := os.LookupEnv("XDG_CONFIG_HOME"); ok { 407 err = os.Setenv("XDG_CONFIG_HOME", "") 408 Expect(err).To(BeNil()) 409 } 410 411 home := os.Getenv("HOME") 412 413 pluginsRoot, err := getPluginsRoot("darwin") 414 Expect(err).To(BeNil()) 415 expected := filepath.Join(home, "Library", "Application Support", "kubebuilder", "plugins") 416 Expect(pluginsRoot).To(Equal(expected)) 417 418 pluginsRoot, err = getPluginsRoot("linux") 419 Expect(err).To(BeNil()) 420 expected = filepath.Join(home, ".config", "kubebuilder", "plugins") 421 Expect(pluginsRoot).To(Equal(expected)) 422 }) 423 424 It("should return full path to the external plugins with XDG_CONFIG_HOME", func() { 425 err = os.Setenv("XDG_CONFIG_HOME", "/some/random/path") 426 Expect(err).To(BeNil()) 427 428 pluginsRoot, err := getPluginsRoot(runtime.GOOS) 429 Expect(err).To(BeNil()) 430 Expect(pluginsRoot).To(Equal("/some/random/path/kubebuilder/plugins")) 431 }) 432 433 It("should return error when home directory is set to empty", func() { 434 _, ok := os.LookupEnv("XDG_CONFIG_HOME") 435 if !ok { 436 } else { 437 err = os.Setenv("XDG_CONFIG_HOME", "") 438 Expect(err).To(BeNil()) 439 } 440 441 _, ok = os.LookupEnv("HOME") 442 if !ok { 443 } else { 444 err = os.Setenv("HOME", "") 445 Expect(err).To(BeNil()) 446 } 447 448 pluginsroot, err := getPluginsRoot(runtime.GOOS) 449 Expect(err).NotTo(BeNil()) 450 Expect(pluginsroot).To(Equal("")) 451 Expect(err.Error()).To(ContainSubstring("error retrieving home dir")) 452 }) 453 }) 454 }) 455 456 Context("parsing flags for external plugins", func() { 457 It("should only parse flags excluding the `--plugins` flag", func() { 458 // change the os.Args for this test and set them back after 459 oldArgs := os.Args 460 defer func() { os.Args = oldArgs }() 461 os.Args = []string{ 462 "kubebuilder", 463 "init", 464 "--plugins", 465 "myexternalplugin/v1", 466 "--domain", 467 "example.com", 468 "--binary-flag", 469 "--license", 470 "apache2", 471 "--another-binary", 472 } 473 474 args := parseExternalPluginArgs() 475 Expect(args).Should(ContainElements( 476 "--domain", 477 "example.com", 478 "--binary-flag", 479 "--license", 480 "apache2", 481 "--another-binary", 482 )) 483 484 Expect(args).ShouldNot(ContainElements( 485 "kubebuilder", 486 "init", 487 "--plugins", 488 "myexternalplugin/v1", 489 )) 490 }) 491 }) 492 }) 493 494 var _ = Describe("CLI options", func() { 495 const ( 496 pluginName = "plugin" 497 pluginVersion = "v1" 498 ) 499 500 var ( 501 c *CLI 502 err error 503 504 projectVersion = config.Version{Number: 1} 505 506 p = newMockPlugin(pluginName, pluginVersion, projectVersion) 507 np1 = newMockPlugin("Plugin", pluginVersion, projectVersion) 508 np2 = mockPlugin{pluginName, plugin.Version{Number: -1}, []config.Version{projectVersion}} 509 np3 = newMockPlugin(pluginName, pluginVersion) 510 np4 = newMockPlugin(pluginName, pluginVersion, config.Version{}) 511 ) 512 513 Context("WithCommandName", func() { 514 It("should use provided command name", func() { 515 commandName := "other-command" 516 c, err = newCLI(WithCommandName(commandName)) 517 Expect(err).NotTo(HaveOccurred()) 518 Expect(c).NotTo(BeNil()) 519 Expect(c.commandName).To(Equal(commandName)) 520 }) 521 }) 522 523 Context("WithVersion", func() { 524 It("should use the provided version string", func() { 525 version := "Version: 0.0.0" 526 c, err = newCLI(WithVersion(version)) 527 Expect(err).NotTo(HaveOccurred()) 528 Expect(c).NotTo(BeNil()) 529 Expect(c.version).To(Equal(version)) 530 }) 531 }) 532 533 Context("WithDescription", func() { 534 It("should use the provided description string", func() { 535 description := "alternative description" 536 c, err = newCLI(WithDescription(description)) 537 Expect(err).NotTo(HaveOccurred()) 538 Expect(c).NotTo(BeNil()) 539 Expect(c.description).To(Equal(description)) 540 }) 541 }) 542 543 Context("WithPlugins", func() { 544 It("should return a valid CLI", func() { 545 c, err = newCLI(WithPlugins(p)) 546 Expect(err).NotTo(HaveOccurred()) 547 Expect(c).NotTo(BeNil()) 548 Expect(c.plugins).To(Equal(map[string]plugin.Plugin{plugin.KeyFor(p): p})) 549 }) 550 551 When("providing plugins with same keys", func() { 552 It("should return an error", func() { 553 _, err = newCLI(WithPlugins(p, p)) 554 Expect(err).To(HaveOccurred()) 555 }) 556 }) 557 558 When("providing plugins with same keys in two steps", func() { 559 It("should return an error", func() { 560 _, err = newCLI(WithPlugins(p), WithPlugins(p)) 561 Expect(err).To(HaveOccurred()) 562 }) 563 }) 564 565 When("providing a plugin with an invalid name", func() { 566 It("should return an error", func() { 567 _, err = newCLI(WithPlugins(np1)) 568 Expect(err).To(HaveOccurred()) 569 }) 570 }) 571 572 When("providing a plugin with an invalid version", func() { 573 It("should return an error", func() { 574 _, err = newCLI(WithPlugins(np2)) 575 Expect(err).To(HaveOccurred()) 576 }) 577 }) 578 579 When("providing a plugin with an empty list of supported versions", func() { 580 It("should return an error", func() { 581 _, err = newCLI(WithPlugins(np3)) 582 Expect(err).To(HaveOccurred()) 583 }) 584 }) 585 586 When("providing a plugin with an invalid list of supported versions", func() { 587 It("should return an error", func() { 588 _, err = newCLI(WithPlugins(np4)) 589 Expect(err).To(HaveOccurred()) 590 }) 591 }) 592 }) 593 594 Context("WithDefaultPlugins", func() { 595 It("should return a valid CLI", func() { 596 c, err = newCLI(WithDefaultPlugins(projectVersion, p)) 597 Expect(err).NotTo(HaveOccurred()) 598 Expect(c).NotTo(BeNil()) 599 Expect(c.defaultPlugins).To(Equal(map[config.Version][]string{projectVersion: {plugin.KeyFor(p)}})) 600 }) 601 602 When("providing an invalid project version", func() { 603 It("should return an error", func() { 604 _, err = newCLI(WithDefaultPlugins(config.Version{}, p)) 605 Expect(err).To(HaveOccurred()) 606 }) 607 }) 608 609 When("providing an empty set of plugins", func() { 610 It("should return an error", func() { 611 _, err = newCLI(WithDefaultPlugins(projectVersion)) 612 Expect(err).To(HaveOccurred()) 613 }) 614 }) 615 616 When("providing a plugin with an invalid name", func() { 617 It("should return an error", func() { 618 _, err = newCLI(WithDefaultPlugins(projectVersion, np1)) 619 Expect(err).To(HaveOccurred()) 620 }) 621 }) 622 623 When("providing a plugin with an invalid version", func() { 624 It("should return an error", func() { 625 _, err = newCLI(WithDefaultPlugins(projectVersion, np2)) 626 Expect(err).To(HaveOccurred()) 627 }) 628 }) 629 630 When("providing a plugin with an empty list of supported versions", func() { 631 It("should return an error", func() { 632 _, err = newCLI(WithDefaultPlugins(projectVersion, np3)) 633 Expect(err).To(HaveOccurred()) 634 }) 635 }) 636 637 When("providing a plugin with an invalid list of supported versions", func() { 638 It("should return an error", func() { 639 _, err = newCLI(WithDefaultPlugins(projectVersion, np4)) 640 Expect(err).To(HaveOccurred()) 641 }) 642 }) 643 644 When("providing a default plugin for an unsupported project version", func() { 645 It("should return an error", func() { 646 _, err = newCLI(WithDefaultPlugins(config.Version{Number: 2}, p)) 647 Expect(err).To(HaveOccurred()) 648 }) 649 }) 650 }) 651 652 Context("WithDefaultProjectVersion", func() { 653 DescribeTable("should return a valid CLI", 654 func(projectVersion config.Version) { 655 c, err = newCLI(WithDefaultProjectVersion(projectVersion)) 656 Expect(err).NotTo(HaveOccurred()) 657 Expect(c).NotTo(BeNil()) 658 Expect(c.defaultProjectVersion).To(Equal(projectVersion)) 659 }, 660 Entry("for version `2`", config.Version{Number: 2}), 661 Entry("for version `3-alpha`", config.Version{Number: 3, Stage: stage.Alpha}), 662 Entry("for version `3`", config.Version{Number: 3}), 663 ) 664 665 DescribeTable("should fail", 666 func(projectVersion config.Version) { 667 _, err = newCLI(WithDefaultProjectVersion(projectVersion)) 668 Expect(err).To(HaveOccurred()) 669 }, 670 Entry("for empty version", config.Version{}), 671 Entry("for invalid stage", config.Version{Number: 1, Stage: stage.Stage(27)}), 672 ) 673 }) 674 675 Context("WithExtraCommands", func() { 676 It("should return a valid CLI with extra commands", func() { 677 commandTest := &cobra.Command{ 678 Use: "example", 679 } 680 c, err = newCLI(WithExtraCommands(commandTest)) 681 Expect(err).NotTo(HaveOccurred()) 682 Expect(c).NotTo(BeNil()) 683 Expect(c.extraCommands).NotTo(BeNil()) 684 Expect(len(c.extraCommands)).To(Equal(1)) 685 Expect(c.extraCommands[0]).NotTo(BeNil()) 686 Expect(c.extraCommands[0].Use).To(Equal(commandTest.Use)) 687 }) 688 }) 689 690 Context("WithExtraAlphaCommands", func() { 691 It("should return a valid CLI with extra alpha commands", func() { 692 commandTest := &cobra.Command{ 693 Use: "example", 694 } 695 c, err = newCLI(WithExtraAlphaCommands(commandTest)) 696 Expect(err).NotTo(HaveOccurred()) 697 Expect(c).NotTo(BeNil()) 698 Expect(c.extraAlphaCommands).NotTo(BeNil()) 699 Expect(len(c.extraAlphaCommands)).To(Equal(1)) 700 Expect(c.extraAlphaCommands[0]).NotTo(BeNil()) 701 Expect(c.extraAlphaCommands[0].Use).To(Equal(commandTest.Use)) 702 }) 703 }) 704 705 Context("WithCompletion", func() { 706 It("should not add the completion command by default", func() { 707 c, err = newCLI() 708 Expect(err).NotTo(HaveOccurred()) 709 Expect(c).NotTo(BeNil()) 710 Expect(c.completionCommand).To(BeFalse()) 711 }) 712 713 It("should add the completion command if requested", func() { 714 c, err = newCLI(WithCompletion()) 715 Expect(err).NotTo(HaveOccurred()) 716 Expect(c).NotTo(BeNil()) 717 Expect(c.completionCommand).To(BeTrue()) 718 }) 719 }) 720 })