github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+incompatible/command/v6/login_command_test.go (about)

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