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