github.com/wanddynosios/cli/v8@v8.7.9-0.20240221182337-1a92e3a7017f/command/v7/login_command_test.go (about) 1 package v7_test 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 8 "code.cloudfoundry.org/cli/actor/actionerror" 9 "code.cloudfoundry.org/cli/actor/v7action" 10 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 11 "code.cloudfoundry.org/cli/api/uaa" 12 "code.cloudfoundry.org/cli/api/uaa/constant" 13 "code.cloudfoundry.org/cli/cf/configuration/coreconfig" 14 "code.cloudfoundry.org/cli/command/commandfakes" 15 "code.cloudfoundry.org/cli/command/translatableerror" 16 . "code.cloudfoundry.org/cli/command/v7" 17 "code.cloudfoundry.org/cli/command/v7/v7fakes" 18 "code.cloudfoundry.org/cli/resources" 19 "code.cloudfoundry.org/cli/util/configv3" 20 "code.cloudfoundry.org/cli/util/ui" 21 . "github.com/onsi/ginkgo" 22 . "github.com/onsi/gomega" 23 . "github.com/onsi/gomega/gbytes" 24 ) 25 26 var _ = Describe("login Command", func() { 27 var ( 28 binaryName string 29 cmd LoginCommand 30 testUI *ui.UI 31 fakeActor *v7fakes.FakeActor 32 fakeConfig *commandfakes.FakeConfig 33 fakeActorReloader *v7fakes.FakeActorReloader 34 executeErr error 35 input *Buffer 36 ) 37 38 BeforeEach(func() { 39 input = NewBuffer() 40 testUI = ui.NewTestUI(input, NewBuffer(), NewBuffer()) 41 fakeConfig = new(commandfakes.FakeConfig) 42 fakeActor = new(v7fakes.FakeActor) 43 fakeActorReloader = new(v7fakes.FakeActorReloader) 44 45 binaryName = "some-executable" 46 fakeConfig.BinaryNameReturns(binaryName) 47 48 fakeConfig.UAAOAuthClientReturns("cf") 49 50 cmd = LoginCommand{ 51 UI: testUI, 52 Actor: fakeActor, 53 Config: fakeConfig, 54 ActorReloader: fakeActorReloader, 55 } 56 cmd.APIEndpoint = "" 57 58 fakeActorReloader.ReloadReturns(fakeActor, nil) 59 fakeConfig.APIVersionReturns("3.99.0") 60 }) 61 62 JustBeforeEach(func() { 63 executeErr = cmd.Execute(nil) 64 }) 65 66 Describe("Validations", func() { 67 When("the --sso and the --origin flag are used together", func() { 68 BeforeEach(func() { 69 cmd.SSO = true 70 cmd.Origin = "some-origin" 71 }) 72 73 It("returns an error", func() { 74 Expect(executeErr).To(MatchError(translatableerror.ArgumentCombinationError{ 75 Args: []string{"--sso", "--origin"}, 76 })) 77 }) 78 }) 79 80 When("the --sso-passcode and the --origin flag are used together", func() { 81 BeforeEach(func() { 82 cmd.SSOPasscode = "some-passcode" 83 cmd.Origin = "some-origin" 84 }) 85 86 It("returns an error", func() { 87 Expect(executeErr).To(MatchError(translatableerror.ArgumentCombinationError{ 88 Args: []string{"--sso-passcode", "--origin"}, 89 })) 90 }) 91 }) 92 93 When("the --sso and the --sso-passcode flag are used together", func() { 94 BeforeEach(func() { 95 cmd.SSO = true 96 cmd.SSOPasscode = "some-passcode" 97 }) 98 99 It("returns an error", func() { 100 Expect(executeErr).To(MatchError(translatableerror.ArgumentCombinationError{ 101 Args: []string{"--sso-passcode", "--sso"}, 102 })) 103 }) 104 }) 105 106 When("the user has manually added their client credentials", func() { 107 BeforeEach(func() { 108 fakeConfig.UAAOAuthClientReturns("some-other-client-id") 109 fakeConfig.UAAOAuthClientSecretReturns("some-secret") 110 }) 111 112 It("returns an unsupported error", func() { 113 Expect(executeErr).To(MatchError(translatableerror.ManualClientCredentialsError{})) 114 }) 115 }) 116 117 When("the current grant type is client credentials", func() { 118 BeforeEach(func() { 119 fakeConfig.UAAGrantTypeReturns(string(constant.GrantTypeClientCredentials)) 120 }) 121 122 It("returns an error", func() { 123 Expect(executeErr).To(MatchError(translatableerror.PasswordGrantTypeLogoutRequiredError{})) 124 }) 125 }) 126 127 When("running against cf-on-k8s API", func() { 128 BeforeEach(func() { 129 fakeConfig.IsCFOnK8sReturns(true) 130 fakeConfig.TargetReturns("https://foo.bar") 131 }) 132 133 When("sso flag is provided", func() { 134 BeforeEach(func() { 135 cmd.SSO = true 136 }) 137 138 It("returns unsupported flag error", func() { 139 Expect(executeErr).To(Equal(translatableerror.NotSupportedOnKubernetesArgumentError{Arg: "--sso"})) 140 }) 141 }) 142 143 When("sso passcode flag is provided", func() { 144 BeforeEach(func() { 145 cmd.SSOPasscode = "sso-pass" 146 }) 147 148 It("returns unsupported flag error", func() { 149 Expect(executeErr).To(Equal(translatableerror.NotSupportedOnKubernetesArgumentError{Arg: "--sso-passcode"})) 150 }) 151 }) 152 153 When("origin flag is provided", func() { 154 BeforeEach(func() { 155 cmd.Origin = "my-origin" 156 }) 157 158 It("returns unsupported flag error", func() { 159 Expect(executeErr).To(Equal(translatableerror.NotSupportedOnKubernetesArgumentError{Arg: "--origin"})) 160 }) 161 }) 162 }) 163 }) 164 165 Describe("API Endpoint", func() { 166 When("user provides the api endpoint using the -a flag", func() { 167 BeforeEach(func() { 168 fakeActor.SetTargetReturns(v7action.Warnings{"some-warning-1", "some-warning-2"}, nil) 169 cmd.APIEndpoint = "api.example.com" 170 }) 171 172 It("targets the provided api endpoint and prints all warnings", func() { 173 Expect(executeErr).ToNot(HaveOccurred()) 174 Expect(fakeActor.SetTargetCallCount()).To(Equal(1)) 175 176 actualSettings := fakeActor.SetTargetArgsForCall(0) 177 Expect(actualSettings.URL).To(Equal("https://api.example.com")) 178 Expect(actualSettings.SkipSSLValidation).To(Equal(false)) 179 180 Expect(testUI.Err).To(Say("some-warning-1")) 181 Expect(testUI.Err).To(Say("some-warning-2")) 182 }) 183 184 When("the user specifies --skip-ssl-validation", func() { 185 BeforeEach(func() { 186 cmd.SkipSSLValidation = true 187 }) 188 189 It("targets the provided api endpoint", func() { 190 Expect(executeErr).ToNot(HaveOccurred()) 191 192 actualSettings := fakeActor.SetTargetArgsForCall(0) 193 Expect(actualSettings.URL).To(Equal("https://api.example.com")) 194 Expect(actualSettings.SkipSSLValidation).To(Equal(true)) 195 }) 196 }) 197 198 When("targeting the API fails", func() { 199 BeforeEach(func() { 200 fakeActor.SetTargetReturns( 201 v7action.Warnings{"some-warning-1", "some-warning-2"}, 202 errors.New("some error")) 203 }) 204 205 It("errors and prints all warnings", func() { 206 Expect(executeErr).To(MatchError("some error")) 207 Expect(testUI.Err).To(Say("some-warning-1")) 208 Expect(testUI.Err).To(Say("some-warning-2")) 209 }) 210 }) 211 }) 212 213 When("user does not provide the api endpoint using the -a flag", func() { 214 When("config has API endpoint already set", func() { 215 BeforeEach(func() { 216 fakeConfig.TargetReturns("api.fake.com") 217 }) 218 219 It("uses the API endpoint from the config", func() { 220 Expect(executeErr).ToNot(HaveOccurred()) 221 Expect(fakeActor.SetTargetCallCount()).To(Equal(1)) 222 223 actualSettings := fakeActor.SetTargetArgsForCall(0) 224 Expect(actualSettings.URL).To(Equal("https://api.fake.com")) 225 }) 226 227 When("the API version is older than the minimum supported API version for the v7 CLI", func() { 228 BeforeEach(func() { 229 fakeConfig.APIVersionReturns("3.83.0") 230 }) 231 It("warns that the user is targeting an unsupported API version and that things may not work correctly", func() { 232 Expect(testUI.Err).To(Say("Warning: Your targeted API's version \\(3.83.0\\) is less than the minimum supported API version \\(3.99.0\\). Some commands may not function correctly.")) 233 }) 234 }) 235 236 When("the API version is empty", func() { 237 BeforeEach(func() { 238 fakeConfig.APIVersionReturns("") 239 }) 240 It("prints a warning message", func() { 241 Expect(executeErr).ToNot(HaveOccurred()) 242 Expect(testUI.Err).To(Say("Warning: unable to determine whether targeted API's version meets minimum supported.")) 243 }) 244 }) 245 246 It("should NOT warn that the user is targeting an unsupported API version", func() { 247 Expect(testUI.Err).ToNot(Say("is less than the minimum supported API version")) 248 }) 249 250 When("the config has SkipSSLValidation false and the --skip-ssl-validation flag is passed", func() { 251 BeforeEach(func() { 252 fakeConfig.SkipSSLValidationReturns(false) 253 cmd.SkipSSLValidation = true 254 }) 255 256 It("sets the target with SkipSSLValidation is true", func() { 257 Expect(executeErr).ToNot(HaveOccurred()) 258 Expect(fakeActor.SetTargetCallCount()).To(Equal(1)) 259 targetSettings := fakeActor.SetTargetArgsForCall(0) 260 Expect(targetSettings.SkipSSLValidation).To(BeTrue()) 261 }) 262 }) 263 }) 264 265 When("config does not have an API endpoint set and the user enters the endpoint at the prompt", func() { 266 BeforeEach(func() { 267 cmd.APIEndpoint = "" 268 _, err := input.Write([]byte("api.example.com\n")) 269 Expect(err).ToNot(HaveOccurred()) 270 fakeConfig.TargetReturnsOnCall(0, "") 271 fakeConfig.TargetReturnsOnCall(1, "https://api.example.com") 272 }) 273 274 It("targets the API that the user inputted", func() { 275 Expect(executeErr).ToNot(HaveOccurred()) 276 Expect(fakeActor.SetTargetCallCount()).To(Equal(1)) 277 actualSettings := fakeActor.SetTargetArgsForCall(0) 278 Expect(actualSettings.URL).To(Equal("https://api.example.com")) 279 Expect(actualSettings.SkipSSLValidation).To(Equal(false)) 280 Expect(fakeConfig.TargetCallCount()).To(Equal(2)) 281 }) 282 283 When("the user specifies --skip-ssl-validation", func() { 284 BeforeEach(func() { 285 cmd.SkipSSLValidation = true 286 }) 287 288 It("targets the API that the user inputted", func() { 289 Expect(executeErr).ToNot(HaveOccurred()) 290 291 actualSettings := fakeActor.SetTargetArgsForCall(0) 292 Expect(actualSettings.SkipSSLValidation).To(Equal(true)) 293 }) 294 }) 295 }) 296 }) 297 298 When("the endpoint has trailing slashes", func() { 299 BeforeEach(func() { 300 cmd.APIEndpoint = "api.example.com////" 301 fakeConfig.TargetReturns("https://api.example.com///") 302 }) 303 304 It("strips the backslashes before using the endpoint", func() { 305 Expect(executeErr).ToNot(HaveOccurred()) 306 Expect(fakeActor.SetTargetCallCount()).To(Equal(1)) 307 actualSettings := fakeActor.SetTargetArgsForCall(0) 308 Expect(actualSettings.URL).To(Equal("https://api.example.com")) 309 }) 310 }) 311 312 When("targeting the API fails due to an invalid certificate", func() { 313 BeforeEach(func() { 314 cmd.APIEndpoint = "api.example.com" 315 fakeActor.SetTargetReturns(nil, ccerror.UnverifiedServerError{URL: "https://api.example.com"}) 316 }) 317 318 It("returns an error mentioning the login command", func() { 319 Expect(executeErr).To(MatchError( 320 translatableerror.InvalidSSLCertError{URL: "https://api.example.com", SuggestedCommand: "login"})) 321 }) 322 }) 323 }) 324 325 Describe("username and password", func() { 326 BeforeEach(func() { 327 fakeConfig.TargetReturns("https://some.random.endpoint") 328 }) 329 330 When("the current grant type is password", func() { 331 BeforeEach(func() { 332 fakeConfig.UAAGrantTypeReturns(string(constant.GrantTypePassword)) 333 }) 334 335 It("fetches prompts from the UAA", func() { 336 Expect(executeErr).ToNot(HaveOccurred()) 337 Expect(fakeActor.GetLoginPromptsCallCount()).To(Equal(1)) 338 }) 339 340 When("one of the prompts has a username key and is text type", func() { 341 BeforeEach(func() { 342 fakeActor.GetLoginPromptsReturns(map[string]coreconfig.AuthPrompt{ 343 "username": { 344 DisplayName: "Username", 345 Type: coreconfig.AuthPromptTypeText, 346 }, 347 }, nil) 348 }) 349 350 When("the username flag is set", func() { 351 BeforeEach(func() { 352 cmd.Username = "potatoface" 353 }) 354 355 It("uses the provided value for the username", func() { 356 Expect(executeErr).ToNot(HaveOccurred()) 357 Expect(fakeActor.AuthenticateCallCount()).To(Equal(1)) 358 credentials, _, _ := fakeActor.AuthenticateArgsForCall(0) 359 Expect(credentials["username"]).To(Equal("potatoface")) 360 }) 361 362 When("the --origin flag is set", func() { 363 BeforeEach(func() { 364 cmd.Origin = "picklebike" 365 }) 366 367 It("authenticates with the specified origin", func() { 368 Expect(executeErr).NotTo(HaveOccurred()) 369 Expect(fakeActor.AuthenticateCallCount()).To(Equal(1)) 370 credentials, origin, _ := fakeActor.AuthenticateArgsForCall(0) 371 Expect(credentials["username"]).To(Equal("potatoface")) 372 Expect(origin).To(Equal("picklebike")) 373 }) 374 }) 375 }) 376 }) 377 378 When("one of the prompts has password key and is password type", func() { 379 BeforeEach(func() { 380 fakeActor.GetLoginPromptsReturns(map[string]coreconfig.AuthPrompt{ 381 "password": { 382 DisplayName: "Your Password", 383 Type: coreconfig.AuthPromptTypePassword, 384 }, 385 }, nil) 386 }) 387 388 When("the password flag is set", func() { 389 BeforeEach(func() { 390 cmd.Password = "noprompto" 391 }) 392 393 It("uses the provided value", func() { 394 Expect(executeErr).ToNot(HaveOccurred()) 395 Expect(fakeActor.AuthenticateCallCount()).To(Equal(1)) 396 credentials, _, _ := fakeActor.AuthenticateArgsForCall(0) 397 Expect(credentials["password"]).To(Equal("noprompto")) 398 }) 399 400 When("the password is incorrect", func() { 401 BeforeEach(func() { 402 _, err := input.Write([]byte("other-password\n")) 403 Expect(err).ToNot(HaveOccurred()) 404 fakeActor.AuthenticateReturnsOnCall(0, errors.New("bad creds")) 405 fakeActor.AuthenticateReturnsOnCall(1, nil) 406 }) 407 408 It("does not reuse the flag value for subsequent attempts", func() { 409 credentials, _, _ := fakeActor.AuthenticateArgsForCall(1) 410 Expect(credentials["password"]).To(Equal("other-password")) 411 }) 412 }) 413 414 When("there have been too many failed login attempts", func() { 415 BeforeEach(func() { 416 _, err := input.Write([]byte("other-password\n")) 417 Expect(err).ToNot(HaveOccurred()) 418 fakeActor.AuthenticateReturns( 419 uaa.AccountLockedError{ 420 Message: "Your account has been locked because of too many failed attempts to login.", 421 }, 422 ) 423 }) 424 425 It("does not reuse the flag value for subsequent attempts", func() { 426 Expect(fakeActor.AuthenticateCallCount()).To(Equal(1), "called Authenticate again after lockout") 427 Expect(testUI.Err).To(Say("Your account has been locked because of too many failed attempts to login.")) 428 }) 429 }) 430 }) 431 }) 432 433 When("UAA prompts for the SSO passcode during non-SSO flow", func() { 434 BeforeEach(func() { 435 cmd.SSO = false 436 cmd.Password = "some-password" 437 fakeActor.GetLoginPromptsReturns(map[string]coreconfig.AuthPrompt{ 438 "password": { 439 DisplayName: "Your Password", 440 Type: coreconfig.AuthPromptTypePassword, 441 }, 442 "passcode": { 443 DisplayName: "gimme your passcode", 444 Type: coreconfig.AuthPromptTypePassword, 445 }, 446 }, nil) 447 }) 448 449 It("does not prompt for the passcode, only the password", func() { 450 Expect(executeErr).ToNot(HaveOccurred()) 451 Expect(testUI.Out).NotTo(Say("gimme your passcode")) 452 credentials, _, _ := fakeActor.AuthenticateArgsForCall(0) 453 Expect(credentials).To(HaveKeyWithValue("password", "some-password")) 454 Expect(credentials).NotTo(HaveKey("passcode")) 455 }) 456 }) 457 458 When("multiple prompts of text and password type are returned", func() { 459 BeforeEach(func() { 460 fakeActor.GetLoginPromptsReturns(map[string]coreconfig.AuthPrompt{ 461 "account_number": { 462 DisplayName: "Account Number", 463 Type: coreconfig.AuthPromptTypeText, 464 }, 465 "username": { 466 DisplayName: "Username", 467 Type: coreconfig.AuthPromptTypeText, 468 }, 469 "passcode": { 470 DisplayName: "It's a passcode, what you want it to be???", 471 Type: coreconfig.AuthPromptTypePassword, 472 }, 473 "password": { 474 DisplayName: "Your Password", 475 Type: coreconfig.AuthPromptTypePassword, 476 }, 477 "supersecret": { 478 DisplayName: "MFA Code", 479 Type: coreconfig.AuthPromptTypePassword, 480 }, 481 }, nil) 482 }) 483 484 When("all authentication information is coming from prompts, not flags", func() { 485 BeforeEach(func() { 486 _, err := input.Write([]byte("faker\nsomeaccount\nsomepassword\ngarbage\n")) 487 Expect(err).ToNot(HaveOccurred()) 488 }) 489 490 It("displays text prompts, starting with username, then password prompts, starting with password", func() { 491 Expect(executeErr).ToNot(HaveOccurred()) 492 493 Expect(testUI.Out).To(Say("\n")) 494 Expect(testUI.Out).To(Say("Username:")) 495 Expect(testUI.Out).To(Say("faker")) 496 497 Expect(testUI.Out).To(Say("\n")) 498 Expect(testUI.Out).To(Say("Account Number:")) 499 Expect(testUI.Out).To(Say("someaccount")) 500 501 Expect(testUI.Out).To(Say("\n")) 502 Expect(testUI.Out).To(Say("Your Password:")) 503 Expect(testUI.Out).NotTo(Say("somepassword")) 504 505 Expect(testUI.Out).To(Say("\n")) 506 Expect(testUI.Out).To(Say("MFA Code:")) 507 Expect(testUI.Out).NotTo(Say("garbage")) 508 }) 509 510 It("authenticates with the responses", func() { 511 Expect(fakeActor.AuthenticateCallCount()).To(Equal(1)) 512 credentials, _, grantType := fakeActor.AuthenticateArgsForCall(0) 513 Expect(credentials["username"]).To(Equal("faker")) 514 Expect(credentials["password"]).To(Equal("somepassword")) 515 Expect(credentials["supersecret"]).To(Equal("garbage")) 516 Expect(grantType).To(Equal(constant.GrantTypePassword)) 517 }) 518 }) 519 520 When("an error occurs prompting for the username", func() { 521 var fakeUI *commandfakes.FakeUI 522 523 BeforeEach(func() { 524 fakeUI = new(commandfakes.FakeUI) 525 fakeUI.DisplayTextPromptReturns("", errors.New("some-error")) 526 cmd = LoginCommand{ 527 UI: fakeUI, 528 Actor: fakeActor, 529 Config: fakeConfig, 530 ActorReloader: fakeActorReloader, 531 } 532 }) 533 534 It("stops prompting after the first prompt and errors", func() { 535 Expect(fakeUI.DisplayTextPromptCallCount()).To(Equal(1)) 536 Expect(executeErr).To(MatchError("Unable to authenticate.")) 537 }) 538 }) 539 540 When("an error occurs in an additional text prompt after username", func() { 541 var fakeUI *commandfakes.FakeUI 542 543 BeforeEach(func() { 544 fakeUI = new(commandfakes.FakeUI) 545 fakeUI.DisplayTextPromptReturnsOnCall(0, "some-name", nil) 546 fakeUI.DisplayTextPromptReturnsOnCall(1, "", errors.New("some-error")) 547 cmd = LoginCommand{ 548 UI: fakeUI, 549 Actor: fakeActor, 550 Config: fakeConfig, 551 ActorReloader: fakeActorReloader, 552 } 553 }) 554 555 It("returns the error", func() { 556 Expect(executeErr).To(MatchError("Unable to authenticate.")) 557 }) 558 }) 559 560 When("an error occurs prompting for the password", func() { 561 var fakeUI *commandfakes.FakeUI 562 563 BeforeEach(func() { 564 fakeUI = new(commandfakes.FakeUI) 565 fakeUI.DisplayPasswordPromptReturns("", errors.New("some-error")) 566 cmd = LoginCommand{ 567 UI: fakeUI, 568 Actor: fakeActor, 569 Config: fakeConfig, 570 ActorReloader: fakeActorReloader, 571 } 572 }) 573 574 It("stops prompting after the first prompt and errors", func() { 575 Expect(fakeUI.DisplayPasswordPromptCallCount()).To(Equal(1)) 576 Expect(executeErr).To(MatchError("Unable to authenticate.")) 577 }) 578 }) 579 580 When("an error occurs prompting for prompts of type password that are not the 'password'", func() { 581 var fakeUI *commandfakes.FakeUI 582 583 BeforeEach(func() { 584 fakeUI = new(commandfakes.FakeUI) 585 fakeUI.DisplayPasswordPromptReturnsOnCall(0, "some-password", nil) 586 fakeUI.DisplayPasswordPromptReturnsOnCall(1, "", errors.New("some-error")) 587 588 cmd = LoginCommand{ 589 UI: fakeUI, 590 Actor: fakeActor, 591 Config: fakeConfig, 592 ActorReloader: fakeActorReloader, 593 } 594 }) 595 596 It("stops prompting after the second prompt and errors", func() { 597 Expect(executeErr).To(MatchError("Unable to authenticate.")) 598 }) 599 }) 600 601 When("authenticating succeeds", func() { 602 BeforeEach(func() { 603 fakeActor.GetCurrentUserReturns(configv3.User{Name: "potatoface"}, nil) 604 _, err := input.Write([]byte("faker\nsomeaccount\nsomepassword\ngarbage\n")) 605 Expect(err).ToNot(HaveOccurred()) 606 }) 607 608 It("displays OK and a status summary", func() { 609 Expect(executeErr).ToNot(HaveOccurred()) 610 Expect(testUI.Out).To(Say("OK")) 611 Expect(testUI.Out).To(Say(`API endpoint:\s+%s`, cmd.APIEndpoint)) 612 Expect(testUI.Out).To(Say(`user:\s+potatoface`)) 613 614 Expect(fakeActor.AuthenticateCallCount()).To(Equal(1)) 615 }) 616 }) 617 618 When("authenticating fails", func() { 619 BeforeEach(func() { 620 fakeActor.AuthenticateReturns(errors.New("something died")) 621 _, err := input.Write([]byte("faker\nsomeaccount\nsomepassword\ngarbage\nfaker\nsomeaccount\nsomepassword\ngarbage\nfaker\nsomeaccount\nsomepassword\ngarbage\n")) 622 Expect(err).ToNot(HaveOccurred()) 623 }) 624 625 It("prints the error message three times", func() { 626 Expect(testUI.Out).To(Say("Your Password:")) 627 Expect(testUI.Out).To(Say("MFA Code:")) 628 Expect(testUI.Err).To(Say("something died")) 629 Expect(testUI.Out).To(Say("Your Password:")) 630 Expect(testUI.Out).To(Say("MFA Code:")) 631 Expect(testUI.Err).To(Say("something died")) 632 Expect(testUI.Out).To(Say("Your Password:")) 633 Expect(testUI.Out).To(Say("MFA Code:")) 634 Expect(testUI.Err).To(Say("something died")) 635 636 Expect(executeErr).To(MatchError("Unable to authenticate.")) 637 Expect(fakeActor.AuthenticateCallCount()).To(Equal(3)) 638 }) 639 640 It("displays a status summary", func() { 641 Expect(testUI.Out).To(Say(`API endpoint:\s+%s`, cmd.APIEndpoint)) 642 Expect(testUI.Out).To(Say(`Not logged in. Use '%s login' or '%s login --sso' to log in.`, cmd.Config.BinaryName(), cmd.Config.BinaryName())) 643 }) 644 }) 645 646 When("authenticating fails with a bad credentials error", func() { 647 BeforeEach(func() { 648 fakeActor.AuthenticateReturns(uaa.UnauthorizedError{Message: "Bad credentials"}) 649 _, err := input.Write([]byte("faker\nsomeaccount\nsomepassword\ngarbage\nfaker\nsomeaccount\nsomepassword\ngarbage\nfaker\nsomeaccount\nsomepassword\ngarbage\n")) 650 Expect(err).ToNot(HaveOccurred()) 651 }) 652 653 It("converts the error before printing it", func() { 654 Expect(testUI.Out).To(Say("Your Password:")) 655 Expect(testUI.Out).To(Say("MFA Code:")) 656 Expect(testUI.Err).To(Say("Credentials were rejected, please try again.")) 657 Expect(testUI.Out).To(Say("Your Password:")) 658 Expect(testUI.Out).To(Say("MFA Code:")) 659 Expect(testUI.Err).To(Say("Credentials were rejected, please try again.")) 660 Expect(testUI.Out).To(Say("Your Password:")) 661 Expect(testUI.Out).To(Say("MFA Code:")) 662 Expect(testUI.Err).To(Say("Credentials were rejected, please try again.")) 663 }) 664 }) 665 }) 666 }) 667 668 When("authenticating against Korifi", func() { 669 BeforeEach(func() { 670 fakeConfig.IsCFOnK8sReturns(true) 671 k8sLoginPrompts := map[string]coreconfig.AuthPrompt{ 672 "username": { 673 Type: coreconfig.AuthPromptTypeMenu, 674 Entries: []string{"myuser", "overly-powerful-admin"}, 675 DisplayName: "Choose your Kubernetes authentication info", 676 }, 677 } 678 fakeActor.GetLoginPromptsReturns(k8sLoginPrompts, nil) 679 }) 680 681 When("the user selects a valid username", func() { 682 BeforeEach(func() { 683 _, err := input.Write([]byte("1\n")) 684 Expect(err).ToNot(HaveOccurred()) 685 }) 686 687 It("prompts the user with available k8s usernames", func() { 688 Expect(executeErr).NotTo(HaveOccurred()) 689 690 Expect(testUI.Out).To(Say("myuser")) 691 Expect(testUI.Out).To(Say("overly-powerful-admin")) 692 Expect(testUI.Out).To(Say("Choose your Kubernetes authentication info")) 693 }) 694 695 It("authenticates with the chosen user", func() { 696 Expect(executeErr).NotTo(HaveOccurred()) 697 698 Expect(fakeActor.AuthenticateCallCount()).To(Equal(1)) 699 credentials, _, _ := fakeActor.AuthenticateArgsForCall(0) 700 Expect(credentials).To(Equal(map[string]string{ 701 "username": "myuser", 702 })) 703 }) 704 }) 705 706 When("the user selects an invalid username", func() { 707 BeforeEach(func() { 708 _, err := input.Write([]byte("3\n")) 709 Expect(err).ToNot(HaveOccurred()) 710 }) 711 712 It("errors", func() { 713 Expect(executeErr).To(MatchError("Unable to authenticate.")) 714 Expect(fakeActor.AuthenticateCallCount()).To(Equal(0)) 715 }) 716 }) 717 718 When("the username flag is set", func() { 719 BeforeEach(func() { 720 cmd.Username = "myuser" 721 }) 722 723 It("sets that username in the credentials", func() { 724 Expect(executeErr).ToNot(HaveOccurred()) 725 Expect(fakeActor.AuthenticateCallCount()).To(Equal(1)) 726 credentials, _, _ := fakeActor.AuthenticateArgsForCall(0) 727 Expect(credentials).To(Equal(map[string]string{ 728 "username": "myuser", 729 })) 730 731 Expect(testUI.Out).NotTo(Say("Choose your Kubernetes authentication info")) 732 }) 733 734 When("the password flag is also set", func() { 735 BeforeEach(func() { 736 cmd.Password = "should-be-ignored" 737 }) 738 739 It("succeeds and sets only the username in the credentials", func() { 740 Expect(executeErr).ToNot(HaveOccurred()) 741 Expect(fakeActor.AuthenticateCallCount()).To(Equal(1)) 742 credentials, _, _ := fakeActor.AuthenticateArgsForCall(0) 743 Expect(credentials).To(Equal(map[string]string{ 744 "username": "myuser", 745 })) 746 }) 747 748 It("displays a warning that password will be ignored", func() { 749 Expect(executeErr).ToNot(HaveOccurred()) 750 Expect(testUI.Err).To(Say("Warning: password is ignored when authenticating against Kubernetes.")) 751 }) 752 }) 753 754 When("authentication fails", func() { 755 BeforeEach(func() { 756 fakeActor.AuthenticateReturns(errors.New("boom")) 757 }) 758 759 It("errors", func() { 760 Expect(executeErr).To(MatchError("Unable to authenticate.")) 761 }) 762 }) 763 }) 764 }) 765 }) 766 767 Describe("SSO Passcode", func() { 768 fakeAPI := "whatever.com" 769 BeforeEach(func() { 770 fakeConfig.TargetReturns(fakeAPI) 771 772 _, err := input.Write([]byte("some-passcode\n")) 773 Expect(err).ToNot(HaveOccurred()) 774 fakeActor.GetLoginPromptsReturns(map[string]coreconfig.AuthPrompt{ 775 "passcode": { 776 DisplayName: "some-sso-prompt", 777 Type: coreconfig.AuthPromptTypePassword, 778 }, 779 }, nil) 780 781 fakeActor.GetCurrentUserReturns(configv3.User{Name: "potatoface"}, nil) 782 }) 783 784 When("--sso flag is set", func() { 785 BeforeEach(func() { 786 cmd.SSO = true 787 }) 788 789 It("prompts the user for SSO passcode", func() { 790 Expect(executeErr).NotTo(HaveOccurred()) 791 Expect(fakeActor.GetLoginPromptsCallCount()).To(Equal(1)) 792 Expect(testUI.Out).To(Say("some-sso-prompt:")) 793 }) 794 795 It("authenticates with the inputted code", func() { 796 Expect(testUI.Out).To(Say("OK")) 797 Expect(testUI.Out).To(Say(`API endpoint:\s+%s`, fakeAPI)) 798 Expect(testUI.Out).To(Say(`user:\s+potatoface`)) 799 800 Expect(fakeActor.AuthenticateCallCount()).To(Equal(1)) 801 credentials, origin, grantType := fakeActor.AuthenticateArgsForCall(0) 802 Expect(credentials["passcode"]).To(Equal("some-passcode")) 803 Expect(origin).To(BeEmpty()) 804 Expect(grantType).To(Equal(constant.GrantTypePassword)) 805 }) 806 807 When("an error occurs prompting for the code", func() { 808 var fakeUI *commandfakes.FakeUI 809 810 BeforeEach(func() { 811 fakeUI = new(commandfakes.FakeUI) 812 fakeUI.DisplayPasswordPromptReturns("", errors.New("some-error")) 813 cmd = LoginCommand{ 814 UI: fakeUI, 815 Actor: fakeActor, 816 Config: fakeConfig, 817 ActorReloader: fakeActorReloader, 818 SSO: true, 819 } 820 }) 821 822 It("errors", func() { 823 Expect(fakeUI.DisplayPasswordPromptCallCount()).To(Equal(1)) 824 Expect(executeErr).To(MatchError("Unable to authenticate.")) 825 }) 826 }) 827 }) 828 829 When("the --sso-passcode flag is set", func() { 830 BeforeEach(func() { 831 cmd.SSOPasscode = "a-passcode" 832 }) 833 834 It("does not prompt the user for SSO passcode", func() { 835 Expect(executeErr).NotTo(HaveOccurred()) 836 Expect(testUI.Out).ToNot(Say("some-sso-prompt:")) 837 }) 838 839 It("uses the flag value to authenticate", func() { 840 Expect(executeErr).NotTo(HaveOccurred()) 841 Expect(fakeActor.AuthenticateCallCount()).To(Equal(1)) 842 credentials, origin, grantType := fakeActor.AuthenticateArgsForCall(0) 843 Expect(credentials["passcode"]).To(Equal("a-passcode")) 844 Expect(origin).To(BeEmpty()) 845 Expect(grantType).To(Equal(constant.GrantTypePassword)) 846 }) 847 848 It("displays a summary with user information", func() { 849 Expect(executeErr).NotTo(HaveOccurred()) 850 Expect(testUI.Out).To(Say("OK")) 851 Expect(testUI.Out).To(Say(`API endpoint:\s+%s`, fakeAPI)) 852 Expect(testUI.Out).To(Say(`user:\s+potatoface`)) 853 }) 854 855 When("an incorrect passcode is inputted", func() { 856 BeforeEach(func() { 857 cmd.SSOPasscode = "some-garbage" 858 fakeActor.AuthenticateReturns(uaa.UnauthorizedError{ 859 Message: "Bad credentials", 860 }) 861 fakeActor.GetCurrentUserReturns(configv3.User{}, nil) 862 _, err := input.Write([]byte("some-passcode\n")) 863 Expect(err).ToNot(HaveOccurred()) 864 }) 865 866 It("re-prompts two more times", func() { 867 Expect(testUI.Out).To(Say("some-sso-prompt:")) 868 Expect(testUI.Out).To(Say(`Authenticating\.\.\.`)) 869 Expect(testUI.Err).To(Say("Credentials were rejected, please try again.")) 870 Expect(testUI.Out).To(Say("some-sso-prompt:")) 871 Expect(testUI.Out).To(Say(`Authenticating\.\.\.`)) 872 Expect(testUI.Err).To(Say("Credentials were rejected, please try again.")) 873 }) 874 875 It("returns an error message", func() { 876 Expect(executeErr).To(MatchError("Unable to authenticate.")) 877 }) 878 879 It("does not include user information in the summary", func() { 880 Expect(testUI.Out).To(Say(`API endpoint:\s+%s`, fakeAPI)) 881 Expect(testUI.Out).To(Say(`Not logged in. Use '%s login' or '%s login --sso' to log in.`, cmd.Config.BinaryName(), cmd.Config.BinaryName())) 882 }) 883 }) 884 }) 885 886 When("both --sso and --sso-passcode flags are set", func() { 887 BeforeEach(func() { 888 cmd.SSO = true 889 cmd.SSOPasscode = "a-passcode" 890 }) 891 892 It("returns an error message", func() { 893 Expect(fakeActor.AuthenticateCallCount()).To(Equal(0)) 894 Expect(executeErr).To(MatchError(translatableerror.ArgumentCombinationError{Args: []string{"--sso-passcode", "--sso"}})) 895 }) 896 }) 897 }) 898 899 Describe("Config", func() { 900 When("a user has successfully authenticated", func() { 901 BeforeEach(func() { 902 cmd.APIEndpoint = "example.com" 903 cmd.Username = "some-user" 904 cmd.Password = "some-password" 905 fakeConfig.APIVersionReturns("3.4.5") 906 fakeActor.GetCurrentUserReturns(configv3.User{Name: "some-user"}, nil) 907 }) 908 909 It("writes to the config", func() { 910 Expect(executeErr).ToNot(HaveOccurred()) 911 Expect(fakeConfig.WriteConfigCallCount()).To(Equal(1)) 912 }) 913 914 When("GetOrganizations fails", func() { 915 BeforeEach(func() { 916 fakeActor.GetOrganizationsReturns(nil, nil, errors.New("Org Failure")) 917 }) 918 It("writes to the config", func() { 919 Expect(executeErr).To(HaveOccurred()) 920 Expect(fakeConfig.WriteConfigCallCount()).To(Equal(1)) 921 }) 922 }) 923 924 When("WriteConfig returns an error", func() { 925 BeforeEach(func() { 926 fakeConfig.WriteConfigReturns(errors.New("Config Failure")) 927 }) 928 It("throws that error", func() { 929 Expect(executeErr).To(MatchError("Error writing config: Config Failure")) 930 }) 931 }) 932 }) 933 }) 934 935 Describe("Targeting Org", func() { 936 BeforeEach(func() { 937 cmd.APIEndpoint = "example.com" 938 cmd.Username = "some-user" 939 cmd.Password = "some-password" 940 fakeConfig.APIVersionReturns("3.4.5") 941 fakeActor.GetCurrentUserReturns(configv3.User{Name: "some-user"}, nil) 942 }) 943 944 When("-o was passed", func() { 945 BeforeEach(func() { 946 cmd.Organization = "some-org" 947 }) 948 949 It("fetches the specified organization", func() { 950 Expect(fakeActor.GetOrganizationByNameCallCount()).To(Equal(1)) 951 Expect(fakeActor.GetOrganizationsCallCount()).To(Equal(0)) 952 Expect(fakeActor.GetOrganizationByNameArgsForCall(0)).To(Equal("some-org")) 953 }) 954 955 When("fetching the organization succeeds", func() { 956 BeforeEach(func() { 957 fakeActor.GetOrganizationByNameReturns( 958 resources.Organization{Name: "some-org", GUID: "some-guid"}, 959 v7action.Warnings{"some-warning-1", "some-warning-2"}, 960 nil) 961 fakeConfig.TargetedOrganizationNameReturns("some-org") 962 fakeConfig.TargetReturns("https://example.com") 963 }) 964 965 It("prints all warnings", func() { 966 Expect(testUI.Err).To(Say("some-warning-1")) 967 Expect(testUI.Err).To(Say("some-warning-2")) 968 }) 969 970 It("sets the targeted organization in the config", func() { 971 Expect(fakeConfig.SetOrganizationInformationCallCount()).To(Equal(1)) 972 orgGUID, orgName := fakeConfig.SetOrganizationInformationArgsForCall(0) 973 Expect(orgGUID).To(Equal("some-guid")) 974 Expect(orgName).To(Equal("some-org")) 975 }) 976 977 It("reports to the user that the org is targeted", func() { 978 Expect(testUI.Out).To(Say(`API endpoint:\s+https://example.com`)) 979 Expect(testUI.Out).To(Say(`API version:\s+3.4.5`)) 980 Expect(testUI.Out).To(Say("user: some-user")) 981 Expect(testUI.Out).To(Say("org: some-org")) 982 }) 983 }) 984 985 When("fetching the organization fails", func() { 986 BeforeEach(func() { 987 fakeActor.GetOrganizationByNameReturns( 988 resources.Organization{}, 989 v7action.Warnings{"some-warning-1", "some-warning-2"}, 990 errors.New("org-not-found"), 991 ) 992 }) 993 994 It("prints all warnings", func() { 995 Expect(testUI.Err).To(Say("some-warning-1")) 996 Expect(testUI.Err).To(Say("some-warning-2")) 997 }) 998 999 It("does not set the targeted org", func() { 1000 Expect(fakeConfig.SetOrganizationInformationCallCount()).To(Equal(0)) 1001 }) 1002 }) 1003 }) 1004 1005 When("-o was not passed, -s was passed", func() { 1006 BeforeEach(func() { 1007 cmd.APIEndpoint = "example.com" 1008 cmd.Username = "some-user" 1009 cmd.Password = "some-password" 1010 cmd.Space = "some-space" 1011 fakeActor.GetCurrentUserReturns(configv3.User{Name: "some-user"}, nil) 1012 fakeConfig.TargetReturns("https://example.com") 1013 fakeActor.GetOrganizationsReturns( 1014 []resources.Organization{}, 1015 v7action.Warnings{"some-org-warning-1", "some-org-warning-2"}, 1016 nil, 1017 ) 1018 fakeActor.GetSpaceByNameAndOrganizationCalls(func(spaceName string, orgGUID string) (resources.Space, v7action.Warnings, error) { 1019 if orgGUID != "some-org-guid1" { 1020 return resources.Space{Name: spaceName}, v7action.Warnings{}, nil 1021 } 1022 return resources.Space{}, v7action.Warnings{}, actionerror.SpaceNotFoundError{} 1023 }) 1024 }) 1025 1026 When("no org valid org exists", func() { 1027 BeforeEach(func() { 1028 fakeActor.GetOrganizationsReturns( 1029 []resources.Organization{{ 1030 GUID: "some-org-guid", 1031 Name: "some-org-name", 1032 }}, 1033 v7action.Warnings{"some-org-warning-1", "some-org-warning-2"}, 1034 nil, 1035 ) 1036 }) 1037 1038 It("does not prompt the user to select an org", func() { 1039 Expect(executeErr).ToNot(HaveOccurred()) 1040 Expect(testUI.Out).ToNot(Say("Select an org:")) 1041 Expect(testUI.Out).ToNot(Say(`Org \(enter to skip\):`)) 1042 }) 1043 1044 It("displays how to target an org and space", func() { 1045 Expect(executeErr).ToNot(HaveOccurred()) 1046 1047 Expect(testUI.Out).To(Say(`API endpoint:\s+https://example.com`)) 1048 Expect(testUI.Out).To(Say(`API version:\s+3.4.5`)) 1049 Expect(testUI.Out).To(Say(`user:\s+some-user`)) 1050 Expect(testUI.Out).To(Say("No org or space targeted, use '%s target -o ORG -s SPACE'", binaryName)) 1051 }) 1052 }) 1053 1054 When("only one valid org exists", func() { 1055 BeforeEach(func() { 1056 fakeActor.GetOrganizationsReturns( 1057 []resources.Organization{{ 1058 GUID: "some-org-guid1", 1059 Name: "some-org-name1", 1060 }, { 1061 GUID: "some-org-guid2", 1062 Name: "some-org-name2", 1063 }}, 1064 v7action.Warnings{"some-org-warning-1", "some-org-warning-2"}, 1065 nil, 1066 ) 1067 }) 1068 1069 It("targets that org", func() { 1070 Expect(executeErr).ToNot(HaveOccurred()) 1071 Expect(fakeConfig.SetOrganizationInformationCallCount()).To(Equal(1)) 1072 orgGUID, orgName := fakeConfig.SetOrganizationInformationArgsForCall(0) 1073 Expect(orgGUID).To(Equal("some-org-guid2")) 1074 Expect(orgName).To(Equal("some-org-name2")) 1075 }) 1076 }) 1077 1078 When("more than one valid org exists", func() { 1079 BeforeEach(func() { 1080 fakeActor.GetOrganizationsReturns( 1081 []resources.Organization{ 1082 { 1083 GUID: "some-org-guid3", 1084 Name: "1234", 1085 }, 1086 { 1087 GUID: "some-org-guid1", 1088 Name: "some-org-name1", 1089 }, 1090 { 1091 GUID: "some-org-guid2", 1092 Name: "some-org-name2", 1093 }, 1094 }, 1095 v7action.Warnings{"some-org-warning-1", "some-org-warning-2"}, 1096 nil, 1097 ) 1098 }) 1099 1100 It("prompts the user to select an org from the filtered selection", func() { 1101 Expect(testUI.Out).To(Say("Select an org:")) 1102 Expect(testUI.Out).To(Say("1. 1234")) 1103 Expect(testUI.Out).To(Say("2. some-org-name2")) 1104 Expect(testUI.Out).To(Say("\n\n")) 1105 Expect(testUI.Out).To(Say(`Org \(enter to skip\):`)) 1106 Expect(executeErr).ToNot(HaveOccurred()) 1107 }) 1108 }) 1109 1110 When("filtering the orgs errors", func() { 1111 BeforeEach(func() { 1112 fakeActor.GetOrganizationsReturns( 1113 []resources.Organization{{ 1114 GUID: "some-org-guid", 1115 Name: "some-org-name", 1116 }}, 1117 v7action.Warnings{"some-org-warning-1", "some-org-warning-2"}, 1118 nil, 1119 ) 1120 fakeActor.GetSpaceByNameAndOrganizationReturns(resources.Space{}, v7action.Warnings{}, errors.New("oh noooooooo")) 1121 }) 1122 1123 It("returns the error", func() { 1124 Expect(executeErr).To(MatchError(errors.New("oh noooooooo"))) 1125 }) 1126 }) 1127 }) 1128 1129 When("-o and -s were both not passed", func() { 1130 BeforeEach(func() { 1131 cmd.APIEndpoint = "example.com" 1132 cmd.Username = "some-user" 1133 cmd.Password = "some-password" 1134 fakeActor.GetOrganizationsReturns( 1135 []resources.Organization{}, 1136 v7action.Warnings{"some-org-warning-1", "some-org-warning-2"}, 1137 nil, 1138 ) 1139 }) 1140 1141 It("fetches the available organizations", func() { 1142 Expect(executeErr).ToNot(HaveOccurred()) 1143 Expect(fakeActor.GetOrganizationsCallCount()).To(Equal(1)) 1144 }) 1145 1146 It("prints all warnings", func() { 1147 Expect(testUI.Err).To(Say("some-org-warning-1")) 1148 Expect(testUI.Err).To(Say("some-org-warning-2")) 1149 }) 1150 1151 When("fetching the organizations succeeds", func() { 1152 BeforeEach(func() { 1153 fakeActor.GetCurrentUserReturns(configv3.User{Name: "some-user"}, nil) 1154 fakeConfig.TargetReturns("https://example.com") 1155 }) 1156 1157 When("no org exists", func() { 1158 It("does not prompt the user to select an org", func() { 1159 Expect(executeErr).ToNot(HaveOccurred()) 1160 Expect(testUI.Out).ToNot(Say("Select an org:")) 1161 Expect(testUI.Out).ToNot(Say(`Org \(enter to skip\):`)) 1162 }) 1163 1164 It("displays how to target an org and space", func() { 1165 Expect(executeErr).ToNot(HaveOccurred()) 1166 1167 Expect(testUI.Out).To(Say(`API endpoint:\s+https://example.com`)) 1168 Expect(testUI.Out).To(Say(`API version:\s+3.4.5`)) 1169 Expect(testUI.Out).To(Say(`user:\s+some-user`)) 1170 Expect(testUI.Out).To(Say("No org or space targeted, use '%s target -o ORG -s SPACE'", binaryName)) 1171 }) 1172 }) 1173 1174 When("only one org exists", func() { 1175 BeforeEach(func() { 1176 fakeActor.GetOrganizationsReturns( 1177 []resources.Organization{{ 1178 GUID: "some-org-guid", 1179 Name: "some-org-name", 1180 }}, 1181 v7action.Warnings{"some-org-warning-1", "some-org-warning-2"}, 1182 nil, 1183 ) 1184 }) 1185 1186 It("targets that org", func() { 1187 Expect(executeErr).ToNot(HaveOccurred()) 1188 Expect(fakeConfig.SetOrganizationInformationCallCount()).To(Equal(1)) 1189 orgGUID, orgName := fakeConfig.SetOrganizationInformationArgsForCall(0) 1190 Expect(orgGUID).To(Equal("some-org-guid")) 1191 Expect(orgName).To(Equal("some-org-name")) 1192 }) 1193 }) 1194 1195 When("more than one but fewer than 50 orgs exists", func() { 1196 BeforeEach(func() { 1197 fakeActor.GetOrganizationsReturns( 1198 []resources.Organization{ 1199 { 1200 GUID: "some-org-guid3", 1201 Name: "1234", 1202 }, 1203 { 1204 GUID: "some-org-guid1", 1205 Name: "some-org-name1", 1206 }, 1207 { 1208 GUID: "some-org-guid2", 1209 Name: "some-org-name2", 1210 }, 1211 }, 1212 v7action.Warnings{"some-org-warning-1", "some-org-warning-2"}, 1213 nil, 1214 ) 1215 }) 1216 1217 When("the user selects an org by list position", func() { 1218 When("the position is valid", func() { 1219 BeforeEach(func() { 1220 fakeConfig.TargetedOrganizationReturns(configv3.Organization{ 1221 GUID: "targeted-org-guid1", 1222 }) 1223 fakeConfig.TargetedOrganizationNameReturns("targeted-org-name") 1224 _, err := input.Write([]byte("2\n")) 1225 Expect(err).ToNot(HaveOccurred()) 1226 }) 1227 1228 It("prompts the user to select an org", func() { 1229 Expect(testUI.Out).To(Say("Select an org:")) 1230 Expect(testUI.Out).To(Say("1. 1234")) 1231 Expect(testUI.Out).To(Say("2. some-org-name1")) 1232 Expect(testUI.Out).To(Say("3. some-org-name2")) 1233 Expect(testUI.Out).To(Say("\n\n")) 1234 Expect(testUI.Out).To(Say(`Org \(enter to skip\):`)) 1235 Expect(executeErr).ToNot(HaveOccurred()) 1236 }) 1237 1238 It("targets that org", func() { 1239 Expect(fakeConfig.SetOrganizationInformationCallCount()).To(Equal(1)) 1240 orgGUID, orgName := fakeConfig.SetOrganizationInformationArgsForCall(0) 1241 Expect(orgGUID).To(Equal("some-org-guid1")) 1242 Expect(orgName).To(Equal("some-org-name1")) 1243 }) 1244 1245 It("outputs targeted org", func() { 1246 Expect(testUI.Out).To(Say("Targeted org targeted-org-name")) 1247 }) 1248 }) 1249 1250 When("the position is invalid", func() { 1251 BeforeEach(func() { 1252 _, err := input.Write([]byte("4\n")) 1253 Expect(err).ToNot(HaveOccurred()) 1254 }) 1255 1256 It("reprompts the user", func() { 1257 Expect(testUI.Out).To(Say("Select an org:")) 1258 Expect(testUI.Out).To(Say("1. 1234")) 1259 Expect(testUI.Out).To(Say("2. some-org-name1")) 1260 Expect(testUI.Out).To(Say("3. some-org-name2")) 1261 Expect(testUI.Out).To(Say(`Org \(enter to skip\):`)) 1262 Expect(testUI.Out).To(Say("Select an org:")) 1263 Expect(testUI.Out).To(Say("1. 1234")) 1264 Expect(testUI.Out).To(Say("2. some-org-name1")) 1265 Expect(testUI.Out).To(Say("3. some-org-name2")) 1266 Expect(testUI.Out).To(Say(`Org \(enter to skip\):`)) 1267 }) 1268 }) 1269 }) 1270 1271 When("the user selects an org by name", func() { 1272 When("the list contains that org", func() { 1273 BeforeEach(func() { 1274 _, err := input.Write([]byte("some-org-name2\n")) 1275 Expect(err).NotTo(HaveOccurred()) 1276 }) 1277 1278 It("prompts the user to select an org", func() { 1279 Expect(testUI.Out).To(Say("Select an org:")) 1280 Expect(testUI.Out).To(Say("1. 1234")) 1281 Expect(testUI.Out).To(Say("2. some-org-name1")) 1282 Expect(testUI.Out).To(Say("3. some-org-name2")) 1283 Expect(testUI.Out).To(Say(`Org \(enter to skip\):`)) 1284 Expect(executeErr).ToNot(HaveOccurred()) 1285 }) 1286 1287 It("targets that org", func() { 1288 Expect(fakeConfig.SetOrganizationInformationCallCount()).To(Equal(1)) 1289 orgGUID, orgName := fakeConfig.SetOrganizationInformationArgsForCall(0) 1290 Expect(orgGUID).To(Equal("some-org-guid2")) 1291 Expect(orgName).To(Equal("some-org-name2")) 1292 }) 1293 }) 1294 1295 When("the org is not in the list", func() { 1296 BeforeEach(func() { 1297 _, err := input.Write([]byte("invalid-org\n")) 1298 Expect(err).NotTo(HaveOccurred()) 1299 }) 1300 1301 It("returns an error", func() { 1302 Expect(executeErr).To(MatchError(translatableerror.OrganizationNotFoundError{Name: "invalid-org"})) 1303 }) 1304 1305 It("does not target the org", func() { 1306 Expect(fakeConfig.SetOrganizationInformationCallCount()).To(Equal(0)) 1307 }) 1308 }) 1309 }) 1310 1311 When("the user exits the prompt early", func() { 1312 var fakeUI *commandfakes.FakeUI 1313 1314 BeforeEach(func() { 1315 fakeUI = new(commandfakes.FakeUI) 1316 cmd.UI = fakeUI 1317 }) 1318 1319 When("the prompt returns with an EOF", func() { 1320 BeforeEach(func() { 1321 fakeUI.DisplayTextMenuReturns("", io.EOF) 1322 }) 1323 1324 It("selects no org and returns no error", func() { 1325 Expect(executeErr).ToNot(HaveOccurred()) 1326 Expect(fakeConfig.SetOrganizationInformationCallCount()).To(Equal(0)) 1327 }) 1328 }) 1329 }) 1330 }) 1331 1332 When("more than 50 orgs exist", func() { 1333 BeforeEach(func() { 1334 orgs := make([]resources.Organization, 51) 1335 for i := range orgs { 1336 orgs[i].Name = fmt.Sprintf("org%d", i+1) 1337 orgs[i].GUID = fmt.Sprintf("org-guid%d", i+1) 1338 } 1339 1340 fakeActor.GetOrganizationsReturns( 1341 orgs, 1342 v7action.Warnings{"some-org-warning-1", "some-org-warning-2"}, 1343 nil, 1344 ) 1345 }) 1346 1347 When("the user selects an org by name", func() { 1348 When("the list contains that org", func() { 1349 BeforeEach(func() { 1350 _, err := input.Write([]byte("org37\n")) 1351 Expect(err).NotTo(HaveOccurred()) 1352 }) 1353 1354 It("prompts the user to select an org", func() { 1355 Expect(testUI.Out).To(Say("There are too many options to display; please type in the name.")) 1356 Expect(testUI.Out).To(Say(`Org \(enter to skip\):`)) 1357 Expect(executeErr).ToNot(HaveOccurred()) 1358 }) 1359 1360 It("targets that org", func() { 1361 Expect(fakeConfig.SetOrganizationInformationCallCount()).To(Equal(1)) 1362 orgGUID, orgName := fakeConfig.SetOrganizationInformationArgsForCall(0) 1363 Expect(orgGUID).To(Equal("org-guid37")) 1364 Expect(orgName).To(Equal("org37")) 1365 }) 1366 }) 1367 1368 When("the org is not in the list", func() { 1369 BeforeEach(func() { 1370 _, err := input.Write([]byte("invalid-org\n")) 1371 Expect(err).NotTo(HaveOccurred()) 1372 }) 1373 1374 It("returns an error", func() { 1375 Expect(executeErr).To(MatchError(translatableerror.OrganizationNotFoundError{Name: "invalid-org"})) 1376 }) 1377 1378 It("does not target the org", func() { 1379 Expect(fakeConfig.SetOrganizationInformationCallCount()).To(Equal(0)) 1380 }) 1381 }) 1382 }) 1383 }) 1384 }) 1385 1386 When("fetching the organizations fails", func() { 1387 BeforeEach(func() { 1388 fakeActor.GetOrganizationsReturns( 1389 []resources.Organization{}, 1390 v7action.Warnings{"some-warning-1", "some-warning-2"}, 1391 errors.New("api call failed"), 1392 ) 1393 }) 1394 1395 It("returns the error", func() { 1396 Expect(executeErr).To(MatchError("api call failed")) 1397 }) 1398 1399 It("prints all warnings", func() { 1400 Expect(testUI.Err).To(Say("some-warning-1")) 1401 Expect(testUI.Err).To(Say("some-warning-2")) 1402 }) 1403 }) 1404 }) 1405 }) 1406 1407 Describe("Targeting Space", func() { 1408 BeforeEach(func() { 1409 cmd.APIEndpoint = "example.com" 1410 cmd.Username = "some-user" 1411 cmd.Password = "some-password" 1412 fakeConfig.APIVersionReturns("3.4.5") 1413 fakeActor.GetCurrentUserReturns(configv3.User{Name: "some-user"}, nil) 1414 }) 1415 1416 When("an org has been successfully targeted", func() { 1417 BeforeEach(func() { 1418 fakeConfig.TargetedOrganizationReturns(configv3.Organization{ 1419 GUID: "targeted-org-guid", 1420 Name: "targeted-org-name", 1421 }, 1422 ) 1423 fakeConfig.TargetedOrganizationNameReturns("targeted-org-name") 1424 }) 1425 1426 When("-s was passed", func() { 1427 BeforeEach(func() { 1428 cmd.Space = "some-space" 1429 }) 1430 1431 When("the specified space exists", func() { 1432 BeforeEach(func() { 1433 fakeActor.GetSpaceByNameAndOrganizationReturns( 1434 resources.Space{ 1435 Name: "some-space", 1436 GUID: "some-space-guid", 1437 }, 1438 v7action.Warnings{"some-warning-1", "some-warning-2"}, 1439 nil, 1440 ) 1441 }) 1442 1443 It("targets that space", func() { 1444 Expect(fakeConfig.SetSpaceInformationCallCount()).To(Equal(1)) 1445 spaceGUID, spaceName, allowSSH := fakeConfig.SetSpaceInformationArgsForCall(0) 1446 Expect(spaceGUID).To(Equal("some-space-guid")) 1447 Expect(spaceName).To(Equal("some-space")) 1448 Expect(allowSSH).To(BeTrue()) 1449 }) 1450 1451 It("prints all warnings", func() { 1452 Expect(testUI.Err).To(Say("some-warning-1")) 1453 Expect(testUI.Err).To(Say("some-warning-2")) 1454 }) 1455 1456 When("the space has been successfully targeted", func() { 1457 BeforeEach(func() { 1458 fakeConfig.TargetedSpaceReturns(configv3.Space{Name: "some-space"}) 1459 }) 1460 1461 It("displays that the space has been targeted", func() { 1462 Expect(testUI.Out).To(Say(`space:\s+some-space`)) 1463 }) 1464 }) 1465 }) 1466 1467 When("the specified space does not exist or does not belong to the targeted org", func() { 1468 BeforeEach(func() { 1469 fakeActor.GetSpaceByNameAndOrganizationReturns( 1470 resources.Space{}, 1471 v7action.Warnings{"some-warning-1", "some-warning-2"}, 1472 actionerror.SpaceNotFoundError{Name: "some-space"}, 1473 ) 1474 }) 1475 1476 It("returns an error", func() { 1477 Expect(executeErr).To(MatchError(actionerror.SpaceNotFoundError{Name: "some-space"})) 1478 }) 1479 1480 It("prints all warnings", func() { 1481 Expect(testUI.Err).To(Say("some-warning-1")) 1482 Expect(testUI.Err).To(Say("some-warning-2")) 1483 }) 1484 1485 It("reports that no space is targeted", func() { 1486 Expect(testUI.Out).To(Say(`space:\s+No space targeted, use 'some-executable target -s SPACE'`)) 1487 }) 1488 }) 1489 }) 1490 1491 When("-s was not passed", func() { 1492 When("fetching the spaces for an organization succeeds", func() { 1493 When("no space exists", func() { 1494 BeforeEach(func() { 1495 fakeActor.GetOrganizationSpacesReturns( 1496 []resources.Space{}, 1497 v7action.Warnings{}, 1498 nil, 1499 ) 1500 fakeConfig.TargetReturns("https://example.com") 1501 }) 1502 It("does not prompt the user to select a space", func() { 1503 Expect(executeErr).ToNot(HaveOccurred()) 1504 Expect(testUI.Out).ToNot(Say("Select a space:")) 1505 Expect(testUI.Out).ToNot(Say(`Space \(enter to skip\):`)) 1506 }) 1507 1508 It("displays how to target a space", func() { 1509 Expect(executeErr).ToNot(HaveOccurred()) 1510 Expect(testUI.Out).To(Say(`API endpoint:\s+https://example.com`)) 1511 Expect(testUI.Out).To(Say(`API version:\s+3.4.5`)) 1512 Expect(testUI.Out).To(Say(`user:\s+some-user`)) 1513 Expect(testUI.Out).To(Say("No space targeted, use '%s target -s SPACE'", binaryName)) 1514 }) 1515 }) 1516 1517 When("only one space is available", func() { 1518 BeforeEach(func() { 1519 spaces := []resources.Space{ 1520 { 1521 GUID: "some-space-guid", 1522 Name: "some-space-name", 1523 }, 1524 } 1525 1526 fakeActor.GetOrganizationSpacesReturns( 1527 spaces, 1528 v7action.Warnings{}, 1529 nil, 1530 ) 1531 1532 fakeConfig.TargetedSpaceReturns(configv3.Space{ 1533 GUID: "some-space-guid", 1534 Name: "some-space-name", 1535 }) 1536 }) 1537 1538 It("targets this space", func() { 1539 Expect(executeErr).NotTo(HaveOccurred()) 1540 1541 Expect(fakeActor.GetOrganizationSpacesCallCount()).To(Equal(1)) 1542 Expect(fakeActor.GetOrganizationSpacesArgsForCall(0)).To(Equal("targeted-org-guid")) 1543 1544 Expect(fakeConfig.SetSpaceInformationCallCount()).To(Equal(1)) 1545 1546 firstArg, secondArg, _ := fakeConfig.SetSpaceInformationArgsForCall(0) 1547 Expect(firstArg).To(Equal("some-space-guid")) 1548 Expect(secondArg).To(Equal("some-space-name")) 1549 1550 Expect(testUI.Out).To(Say(`Targeted space some-space-name\.`)) 1551 Expect(testUI.Out).To(Say(`space:\s+some-space-name`)) 1552 Expect(testUI.Out).NotTo(Say(`space:\s+No space targeted, use 'some-executable target -s SPACE`)) 1553 }) 1554 }) 1555 1556 When("more than one space is available", func() { 1557 BeforeEach(func() { 1558 spaces := []resources.Space{ 1559 { 1560 GUID: "some-space-guid", 1561 Name: "some-space-name", 1562 }, 1563 { 1564 GUID: "some-space-guid1", 1565 Name: "some-space-name1", 1566 }, 1567 { 1568 GUID: "some-space-guid2", 1569 Name: "some-space-name2", 1570 }, 1571 { 1572 GUID: "some-space-guid3", 1573 Name: "3", 1574 }, 1575 { 1576 GUID: "some-space-guid3", 1577 Name: "100", 1578 }, 1579 } 1580 1581 fakeActor.GetOrganizationSpacesReturns( 1582 spaces, 1583 v7action.Warnings{}, 1584 nil, 1585 ) 1586 }) 1587 1588 It("displays a numbered list of spaces", func() { 1589 Expect(testUI.Out).To(Say("Select a space:")) 1590 Expect(testUI.Out).To(Say("1. some-space-name")) 1591 Expect(testUI.Out).To(Say("2. some-space-name1")) 1592 Expect(testUI.Out).To(Say("3. some-space-name2")) 1593 Expect(testUI.Out).To(Say("4. 3")) 1594 Expect(testUI.Out).To(Say("5. 100")) 1595 Expect(testUI.Out).To(Say("\n\n")) 1596 Expect(testUI.Out).To(Say(`Space \(enter to skip\):`)) 1597 }) 1598 1599 When("the user selects a space by list position", func() { 1600 When("the position is valid", func() { 1601 BeforeEach(func() { 1602 _, err := input.Write([]byte("2\n")) 1603 Expect(err).NotTo(HaveOccurred()) 1604 }) 1605 1606 It("targets that space", func() { 1607 Expect(fakeConfig.SetSpaceInformationCallCount()).To(Equal(1)) 1608 guid, name, allowSSH := fakeConfig.SetSpaceInformationArgsForCall(0) 1609 Expect(guid).To(Equal("some-space-guid1")) 1610 Expect(name).To(Equal("some-space-name1")) 1611 Expect(allowSSH).To(BeTrue()) 1612 Expect(executeErr).NotTo(HaveOccurred()) 1613 }) 1614 }) 1615 1616 When("the position is invalid", func() { 1617 BeforeEach(func() { 1618 _, err := input.Write([]byte("-1\n")) 1619 Expect(err).NotTo(HaveOccurred()) 1620 }) 1621 1622 It("reprompts the user", func() { 1623 Expect(testUI.Out).To(Say("Select a space:")) 1624 Expect(testUI.Out).To(Say("1. some-space-name")) 1625 Expect(testUI.Out).To(Say("2. some-space-name1")) 1626 Expect(testUI.Out).To(Say("3. some-space-name2")) 1627 Expect(testUI.Out).To(Say("4. 3")) 1628 Expect(testUI.Out).To(Say("5. 100")) 1629 Expect(testUI.Out).To(Say("\n\n")) 1630 Expect(testUI.Out).To(Say(`Space \(enter to skip\):`)) 1631 Expect(testUI.Out).To(Say("Select a space:")) 1632 Expect(testUI.Out).To(Say("1. some-space-name")) 1633 Expect(testUI.Out).To(Say("2. some-space-name1")) 1634 Expect(testUI.Out).To(Say("3. some-space-name2")) 1635 Expect(testUI.Out).To(Say("4. 3")) 1636 Expect(testUI.Out).To(Say("5. 100")) 1637 Expect(testUI.Out).To(Say("\n\n")) 1638 Expect(testUI.Out).To(Say(`Space \(enter to skip\):`)) 1639 }) 1640 }) 1641 }) 1642 1643 When("the user selects a space by name", func() { 1644 When("the list contains that space", func() { 1645 BeforeEach(func() { 1646 _, err := input.Write([]byte("some-space-name2\n")) 1647 Expect(err).NotTo(HaveOccurred()) 1648 }) 1649 1650 It("targets that space", func() { 1651 Expect(fakeConfig.SetSpaceInformationCallCount()).To(Equal(1)) 1652 guid, name, allowSSH := fakeConfig.SetSpaceInformationArgsForCall(0) 1653 Expect(guid).To(Equal("some-space-guid2")) 1654 Expect(name).To(Equal("some-space-name2")) 1655 Expect(allowSSH).To(BeTrue()) 1656 Expect(executeErr).NotTo(HaveOccurred()) 1657 }) 1658 }) 1659 1660 When("the space is not in the list", func() { 1661 BeforeEach(func() { 1662 _, err := input.Write([]byte("invalid-space\n")) 1663 Expect(err).NotTo(HaveOccurred()) 1664 }) 1665 1666 It("returns an error", func() { 1667 Expect(executeErr).To(MatchError(translatableerror.SpaceNotFoundError{Name: "invalid-space"})) 1668 }) 1669 1670 It("does not target the space", func() { 1671 Expect(fakeConfig.SetSpaceInformationCallCount()).To(Equal(0)) 1672 }) 1673 }) 1674 1675 When("the user exits the prompt early", func() { 1676 var fakeUI *commandfakes.FakeUI 1677 1678 BeforeEach(func() { 1679 fakeUI = new(commandfakes.FakeUI) 1680 cmd.UI = fakeUI 1681 }) 1682 1683 When("the prompt returns with an EOF", func() { 1684 BeforeEach(func() { 1685 fakeUI.DisplayTextMenuReturns("", io.EOF) 1686 }) 1687 It("selects no space and returns no error", func() { 1688 Expect(executeErr).ToNot(HaveOccurred()) 1689 Expect(fakeConfig.SetSpaceInformationCallCount()).To(Equal(0)) 1690 }) 1691 }) 1692 }) 1693 }) 1694 1695 When("the user enters text which is both a space name and a digit", func() { 1696 When("the entry is a valid position", func() { 1697 BeforeEach(func() { 1698 _, err := input.Write([]byte("3\n")) 1699 Expect(err).NotTo(HaveOccurred()) 1700 }) 1701 1702 It("targets the space at the index specified", func() { 1703 Expect(fakeConfig.SetSpaceInformationCallCount()).To(Equal(1)) 1704 guid, name, allowSSH := fakeConfig.SetSpaceInformationArgsForCall(0) 1705 Expect(guid).To(Equal("some-space-guid2")) 1706 Expect(name).To(Equal("some-space-name2")) 1707 Expect(allowSSH).To(BeTrue()) 1708 Expect(executeErr).NotTo(HaveOccurred()) 1709 }) 1710 }) 1711 1712 When("the entry is an invalid position", func() { 1713 BeforeEach(func() { 1714 _, err := input.Write([]byte("100\n")) 1715 Expect(err).NotTo(HaveOccurred()) 1716 }) 1717 1718 It("reprompts the user", func() { 1719 Expect(testUI.Out).To(Say("Select a space:")) 1720 Expect(testUI.Out).To(Say("1. some-space-name")) 1721 Expect(testUI.Out).To(Say("2. some-space-name1")) 1722 Expect(testUI.Out).To(Say("3. some-space-name2")) 1723 Expect(testUI.Out).To(Say("4. 3")) 1724 Expect(testUI.Out).To(Say("5. 100")) 1725 Expect(testUI.Out).To(Say("\n\n")) 1726 Expect(testUI.Out).To(Say(`Space \(enter to skip\):`)) 1727 Expect(testUI.Out).To(Say("1. some-space-name")) 1728 Expect(testUI.Out).To(Say("2. some-space-name1")) 1729 Expect(testUI.Out).To(Say("3. some-space-name2")) 1730 Expect(testUI.Out).To(Say("4. 3")) 1731 Expect(testUI.Out).To(Say("5. 100")) 1732 Expect(testUI.Out).To(Say("\n\n")) 1733 Expect(testUI.Out).To(Say(`Space \(enter to skip\):`)) 1734 }) 1735 }) 1736 }) 1737 }) 1738 1739 When("more than 50 spaces exist", func() { 1740 BeforeEach(func() { 1741 spaces := make([]resources.Space, 51) 1742 for i := range spaces { 1743 spaces[i].Name = fmt.Sprintf("space-%d", i+1) 1744 spaces[i].GUID = fmt.Sprintf("space-guid-%d", i+1) 1745 } 1746 1747 fakeActor.GetOrganizationSpacesReturns( 1748 spaces, 1749 v7action.Warnings{}, 1750 nil, 1751 ) 1752 }) 1753 1754 It("prompts the user to select an space", func() { 1755 Expect(testUI.Out).To(Say("There are too many options to display; please type in the name.")) 1756 Expect(testUI.Out).To(Say("\n\n")) 1757 Expect(testUI.Out).To(Say(`Space \(enter to skip\):`)) 1758 }) 1759 1760 When("the user selects an space by name", func() { 1761 When("the list contains that space", func() { 1762 BeforeEach(func() { 1763 _, err := input.Write([]byte("space-37\n")) 1764 Expect(err).NotTo(HaveOccurred()) 1765 }) 1766 1767 It("targets that space", func() { 1768 Expect(fakeConfig.SetSpaceInformationCallCount()).To(Equal(1)) 1769 spaceGUID, spaceName, allowSSH := fakeConfig.SetSpaceInformationArgsForCall(0) 1770 Expect(spaceGUID).To(Equal("space-guid-37")) 1771 Expect(spaceName).To(Equal("space-37")) 1772 Expect(allowSSH).To(BeTrue()) 1773 }) 1774 }) 1775 1776 When("the name is a valid list position, but it does not match a space name", func() { 1777 BeforeEach(func() { 1778 _, err := input.Write([]byte("31\n")) 1779 Expect(err).NotTo(HaveOccurred()) 1780 }) 1781 1782 It("returns an error", func() { 1783 Expect(executeErr).To(MatchError(translatableerror.SpaceNotFoundError{Name: "31"})) 1784 }) 1785 1786 It("does not target the space", func() { 1787 Expect(fakeConfig.SetSpaceInformationCallCount()).To(Equal(0)) 1788 }) 1789 }) 1790 1791 When("the space is not in the list", func() { 1792 BeforeEach(func() { 1793 _, err := input.Write([]byte("invalid-space\n")) 1794 Expect(err).NotTo(HaveOccurred()) 1795 }) 1796 1797 It("returns an error", func() { 1798 Expect(executeErr).To(MatchError(translatableerror.SpaceNotFoundError{Name: "invalid-space"})) 1799 }) 1800 1801 It("does not target the space", func() { 1802 Expect(fakeConfig.SetSpaceInformationCallCount()).To(Equal(0)) 1803 }) 1804 }) 1805 }) 1806 }) 1807 }) 1808 1809 When("fetching the spaces for an organization fails", func() { 1810 BeforeEach(func() { 1811 fakeActor.GetOrganizationSpacesReturns( 1812 []resources.Space{}, 1813 v7action.Warnings{"some-warning-1", "some-warning-2"}, 1814 errors.New("fetching spaces failed"), 1815 ) 1816 }) 1817 1818 It("returns an error", func() { 1819 Expect(executeErr).To(MatchError("fetching spaces failed")) 1820 }) 1821 1822 It("returns all warnings", func() { 1823 Expect(testUI.Err).To(Say("some-warning-1")) 1824 Expect(testUI.Err).To(Say("some-warning-2")) 1825 }) 1826 }) 1827 }) 1828 }) 1829 }) 1830 })