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