github.com/sleungcy-sap/cli@v7.1.0+incompatible/integration/v7/isolated/login_command_test.go (about)

     1  package isolated
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"net/url"
     9  	"path/filepath"
    10  	"regexp"
    11  
    12  	"code.cloudfoundry.org/cli/integration/helpers"
    13  	"code.cloudfoundry.org/cli/util/configv3"
    14  	. "github.com/onsi/ginkgo"
    15  	. "github.com/onsi/gomega"
    16  	. "github.com/onsi/gomega/gbytes"
    17  	. "github.com/onsi/gomega/gexec"
    18  	"github.com/onsi/gomega/ghttp"
    19  )
    20  
    21  var _ = Describe("login command", func() {
    22  	BeforeEach(func() {
    23  		helpers.SkipIfClientCredentialsTestMode()
    24  	})
    25  
    26  	Describe("Help Text", func() {
    27  		When("--help flag is set", func() {
    28  			It("displays the command usage", func() {
    29  				session := helpers.CF("login", "--help")
    30  				Eventually(session).Should(Exit(0))
    31  
    32  				Expect(session).Should(Say("NAME:\n"))
    33  				Expect(session).Should(Say("login - Log user in"))
    34  
    35  				Expect(session).Should(Say("USAGE:\n"))
    36  				Expect(session).Should(Say(`cf login \[-a API_URL\] \[-u USERNAME\] \[-p PASSWORD\] \[-o ORG\] \[-s SPACE\] \[--sso | --sso-passcode PASSCODE\] \[--origin ORIGIN\]`))
    37  
    38  				Expect(session).Should(Say("WARNING:\n"))
    39  				Expect(session).Should(Say("Providing your password as a command line option is highly discouraged\n"))
    40  				Expect(session).Should(Say("Your password may be visible to others and may be recorded in your shell history\n"))
    41  
    42  				Expect(session).Should(Say("EXAMPLES:\n"))
    43  				Expect(session).Should(Say(regexp.QuoteMeta("cf login (omit username and password to login interactively -- cf will prompt for both)")))
    44  				Expect(session).Should(Say(regexp.QuoteMeta("cf login -u name@example.com -p pa55woRD (specify username and password as arguments)")))
    45  				Expect(session).Should(Say(regexp.QuoteMeta("cf login -u name@example.com -p \"my password\" (use quotes for passwords with a space)")))
    46  				Expect(session).Should(Say(regexp.QuoteMeta("cf login -u name@example.com -p \"\\\"password\\\"\" (escape quotes if used in password)")))
    47  				Expect(session).Should(Say(regexp.QuoteMeta("cf login --sso (cf will provide a url to obtain a one-time passcode to login)")))
    48  				Expect(session).Should(Say(regexp.QuoteMeta("cf login --origin ldap")))
    49  
    50  				Expect(session).Should(Say("ALIAS:\n"))
    51  				Expect(session).Should(Say("l"))
    52  
    53  				Expect(session).Should(Say("OPTIONS:\n"))
    54  				Expect(session).Should(Say(`-a\s+API endpoint \(e.g. https://api\.example\.com\)`))
    55  				Expect(session).Should(Say(`-o\s+Org`))
    56  				Expect(session).Should(Say(`-p\s+Password`))
    57  				Expect(session).Should(Say(`-s\s+Space`))
    58  				Expect(session).Should(Say(`--skip-ssl-validation\s+Skip verification of the API endpoint\. Not recommended\!`))
    59  				Expect(session).Should(Say(`--sso\s+Prompt for a one-time passcode to login`))
    60  				Expect(session).Should(Say(`--sso-passcode\s+One-time passcode`))
    61  				Expect(session).Should(Say(`-u\s+Username`))
    62  
    63  				Expect(session).Should(Say("SEE ALSO:\n"))
    64  				Expect(session).Should(Say("api, auth, target"))
    65  			})
    66  		})
    67  	})
    68  
    69  	Describe("API Endpoint", func() {
    70  		var (
    71  			username string
    72  			password string
    73  		)
    74  
    75  		When("the API endpoint is not set", func() {
    76  			BeforeEach(func() {
    77  				helpers.UnsetAPI()
    78  				username, password = helpers.GetCredentials()
    79  			})
    80  
    81  			When("the user does not provide the -a flag", func() {
    82  				It("prompts the user for an endpoint", func() {
    83  					session := helpers.CF("login")
    84  					Eventually(session).Should(Say("API endpoint:"))
    85  					session.Interrupt()
    86  					Eventually(session).Should(Exit())
    87  				})
    88  
    89  				When("the API endpoint provided at the prompt is unreachable", func() {
    90  					It("returns an error", func() {
    91  						input := NewBuffer()
    92  						_, err := input.Write([]byte("does.not.exist\n"))
    93  						Expect(err).ToNot(HaveOccurred())
    94  
    95  						session := helpers.CFWithStdin(input, "login")
    96  						Eventually(session).Should(Say("API endpoint:"))
    97  						Eventually(session).Should(Say("FAILED"))
    98  						Eventually(session.Err).Should(Say("Request error: "))
    99  						Eventually(session.Err).Should(Say("TIP: If you are behind a firewall and require an HTTP proxy, verify the https_proxy environment variable is correctly set. Else, check your network connection."))
   100  						Eventually(session).Should(Exit(1))
   101  					})
   102  				})
   103  			})
   104  
   105  			When("the user provides the -a flag", func() {
   106  
   107  				When("the provided API endpoint is reachable", func() {
   108  
   109  					var session *Session
   110  
   111  					BeforeEach(func() {
   112  						session = helpers.CF("login", "-a", apiURL, "-u", username, "-p", password, "--skip-ssl-validation")
   113  						Eventually(session).Should(Say("API endpoint: %s", apiURL))
   114  						Eventually(session).Should(Exit(0))
   115  					})
   116  					It("writes fields to the config file when targeting an API", func() {
   117  						rawConfig, err := ioutil.ReadFile(filepath.Join(homeDir, ".cf", "config.json"))
   118  						Expect(err).NotTo(HaveOccurred())
   119  
   120  						var configFile configv3.JSONConfig
   121  						err = json.Unmarshal(rawConfig, &configFile)
   122  						Expect(err).NotTo(HaveOccurred())
   123  
   124  						Expect(configFile.ConfigVersion).To(Equal(configv3.CurrentConfigVersion))
   125  						Expect(configFile.Target).To(Equal(apiURL))
   126  						Expect(configFile.APIVersion).To(MatchRegexp(`\d+\.\d+\.\d+`))
   127  						Expect(configFile.AuthorizationEndpoint).ToNot(BeEmpty())
   128  						Expect(configFile.DopplerEndpoint).To(MatchRegexp("^wss://"))
   129  						Expect(configFile.LogCacheEndpoint).To(MatchRegexp(".*log-cache.*"))
   130  
   131  					})
   132  
   133  					It("sets the API endpoint to the provided value", func() {
   134  						session = helpers.CF("api")
   135  						Eventually(session).Should(Exit(0))
   136  						Expect(session).Should(Say("API endpoint:   %s", apiURL))
   137  					})
   138  				})
   139  
   140  				When("the provided API endpoint is unreachable", func() {
   141  					It("displays an error and fails", func() {
   142  						session := helpers.CF("login", "-a", "does.not.exist", "--skip-ssl-validation")
   143  
   144  						Eventually(session).Should(Say("API endpoint: does.not.exist"))
   145  						Eventually(session).Should(Say("FAILED"))
   146  						Eventually(session.Err).Should(Say("Request error: "))
   147  						Eventually(session.Err).Should(Say("TIP: If you are behind a firewall and require an HTTP proxy, verify the https_proxy environment variable is correctly set. Else, check your network connection."))
   148  						Eventually(session).Should(Exit(1))
   149  					})
   150  				})
   151  			})
   152  		})
   153  
   154  		When("the API endpoint is already set", func() {
   155  			It("does not prompt the user for API endpoint", func() {
   156  				session := helpers.CF("login")
   157  				Consistently(session).ShouldNot(Say("API endpoint>"))
   158  				session.Interrupt()
   159  				Eventually(session).Should(Exit())
   160  			})
   161  
   162  			When("the user provides a new API endpoint with the -a flag", func() {
   163  				It("attempts the new API endpoint, and does not update the API endpoint unless it succeeds", func() {
   164  					session := helpers.CF("login", "-a", "does.not.exist", "--skip-ssl-validation")
   165  					Eventually(session).Should(Say("API endpoint: does.not.exist"))
   166  					Eventually(session).Should(Say("FAILED"))
   167  					Eventually(session).Should(Exit(1))
   168  
   169  					apiSession := helpers.CF("api")
   170  					Eventually(apiSession).Should(Exit(0))
   171  					Eventually(apiSession).Should(Say("API endpoint:   %s", apiURL))
   172  				})
   173  			})
   174  		})
   175  	})
   176  
   177  	Describe("API URL scheme", func() {
   178  		When("no scheme is included in the API endpoint", func() {
   179  			var (
   180  				hostname  string
   181  				serverURL *url.URL
   182  				err       error
   183  				session   *Session
   184  			)
   185  
   186  			BeforeEach(func() {
   187  				serverURL, err = url.Parse(helpers.GetAPI())
   188  				Expect(err).NotTo(HaveOccurred())
   189  
   190  				hostname = serverURL.Hostname()
   191  				username, password := helpers.GetCredentials()
   192  
   193  				session = helpers.CF("login", "-u", username, "-p", password, "-a", hostname, "--skip-ssl-validation")
   194  			})
   195  
   196  			It("displays the API endpoint as https once targeted", func() {
   197  				Eventually(session).Should(Say("API endpoint: %s", hostname))
   198  				Eventually(session).Should(Say(`API version:\s+\d\.\d{1,3}\.\d{1,3}`))
   199  				Eventually(session).Should(Exit(0))
   200  			})
   201  
   202  		})
   203  
   204  		When("the API endpoint's scheme is http", func() {
   205  			var httpURL string
   206  
   207  			BeforeEach(func() {
   208  				apiURL, err := url.Parse(helpers.GetAPI())
   209  				Expect(err).NotTo(HaveOccurred())
   210  				apiURL.Scheme = "http"
   211  
   212  				httpURL = apiURL.String()
   213  			})
   214  
   215  			It("shows a warning to the user and logs in successfully", func() {
   216  				username, password := helpers.GetCredentials()
   217  				session := helpers.CF("login", "-u", username, "-p", password, "-a", httpURL, "--skip-ssl-validation")
   218  
   219  				Eventually(session).Should(Say("API endpoint: %s", httpURL))
   220  				Eventually(session.Err).Should(Say("Warning: Insecure http API endpoint detected: secure https API endpoints are recommended"))
   221  				Eventually(session).Should(Exit(0))
   222  			})
   223  		})
   224  
   225  		When("the API endpoint's scheme is https", func() {
   226  			// This test is somewhat redundant because the integration test setup will have alreayd logged in successfully with certificates at this point
   227  			// In the interest of test coverage however, we have decided to keep it in.
   228  			When("the OS provides a valid SSL Certificate (Unix: SSL_CERT_FILE or SSL_CERT_DIR Environment variables) (Windows: Import-Certificate call)", func() {
   229  				BeforeEach(func() {
   230  					if skipSSLValidation {
   231  						Skip("SKIP_SSL_VALIDATION is enabled")
   232  					}
   233  				})
   234  
   235  				It("trusts the cert and allows the users to log in", func() {
   236  					username, password := helpers.GetCredentials()
   237  					session := helpers.CF("login", "-u", username, "-p", password, "-a", helpers.GetAPI())
   238  					Eventually(session).Should(Say("API endpoint: %s", apiURL))
   239  					Eventually(session).Should(Exit())
   240  
   241  					session = helpers.CF("api")
   242  					Eventually(session).Should(Exit(0))
   243  					Expect(session).Should(Say("API endpoint:   %s", apiURL))
   244  				})
   245  			})
   246  
   247  			When("the SSL Certificate is invalid", func() {
   248  				var server *ghttp.Server
   249  
   250  				BeforeEach(func() {
   251  					cliVersion := "1.0.0"
   252  					server = helpers.StartMockServerWithMinimumCLIVersion(cliVersion)
   253  					fakeTokenResponse := map[string]string{
   254  						"access_token": "",
   255  						"token_type":   "bearer",
   256  					}
   257  					server.RouteToHandler(http.MethodPost, "/oauth/token",
   258  						ghttp.RespondWithJSONEncoded(http.StatusOK, fakeTokenResponse))
   259  					server.RouteToHandler(http.MethodGet, "/v3/organizations",
   260  						ghttp.RespondWith(http.StatusOK, `{
   261  							"total_results": 0,
   262  							"total_pages": 1,
   263  							"resources": []}`))
   264  				})
   265  
   266  				AfterEach(func() {
   267  					server.Close()
   268  				})
   269  
   270  				It("errors when --skip-ssl-validation is not provided", func() {
   271  					session := helpers.CF("login", "-a", server.URL())
   272  					Eventually(session).Should(Say("API endpoint: %s", server.URL()))
   273  					Eventually(session).Should(Say("FAILED"))
   274  					Eventually(session.Err).Should(Say("Invalid SSL Cert for %s", server.URL()))
   275  					Eventually(session.Err).Should(Say("TIP: Use 'cf login --skip-ssl-validation' to continue with an insecure API endpoint"))
   276  					Eventually(session).Should(Exit(1))
   277  				})
   278  
   279  				It("doesn't complain about an invalid cert when we specify --skip-ssl-validation", func() {
   280  					session := helpers.CF("login", "-a", server.URL(), "--skip-ssl-validation")
   281  					Eventually(session).Should(Exit(0))
   282  
   283  					Expect(string(session.Err.Contents())).Should(Not(ContainSubstring("Invalid SSL Cert for %s", server.URL())))
   284  				})
   285  
   286  				When("targeted with --skip-ssl-validation", func() {
   287  					BeforeEach(func() {
   288  						Eventually(helpers.CF("api", server.URL(), "--skip-ssl-validation")).Should(Exit(0))
   289  					})
   290  
   291  					When("logging in without --skip-ssl-validation", func() {
   292  						It("displays a helpful error message and exits 1", func() {
   293  							session := helpers.CF("login", "-a", server.URL())
   294  							Eventually(session).Should(Say("API endpoint: %s", server.URL()))
   295  							Eventually(session).Should(Say("FAILED"))
   296  							Eventually(session.Err).Should(Say("Invalid SSL Cert for %s", server.URL()))
   297  							Eventually(session.Err).Should(Say("TIP: Use 'cf login --skip-ssl-validation' to continue with an insecure API endpoint"))
   298  							Eventually(session).Should(Exit(1))
   299  						})
   300  					})
   301  				})
   302  			})
   303  		})
   304  	})
   305  
   306  	Describe("SSO", func() {
   307  		When("--sso-passcode is provided", func() {
   308  			Context("and --sso is also passed", func() {
   309  				It("fails with a useful error message", func() {
   310  					session := helpers.CF("login", "--sso-passcode", "some-passcode", "--sso")
   311  					Eventually(session.Err).Should(Say("Incorrect Usage: The following arguments cannot be used together: --sso-passcode, --sso"))
   312  					Eventually(session).Should(Exit(1))
   313  				})
   314  			})
   315  
   316  			Context("and the provided passcode is incorrect", func() {
   317  				It("prompts twice, displays an error and fails", func() {
   318  					input := NewBuffer()
   319  					_, err := input.Write([]byte("bad-passcode-again\nbad-passcode-strikes-back\n"))
   320  					Expect(err).ToNot(HaveOccurred())
   321  					session := helpers.CFWithStdin(input, "login", "--sso-passcode", "some-passcode")
   322  					Eventually(session).Should(Say("API endpoint:\\s+" + helpers.GetAPI()))
   323  					Eventually(session).Should(Say(`Authenticating\.\.\.`))
   324  					Eventually(session.Err).Should(Say(`Invalid passcode`))
   325  
   326  					// Leaving out expectation of prompt text, since it comes from UAA (and doesn't show up on Windows)
   327  					Eventually(session).Should(Say(`Authenticating\.\.\.`))
   328  					Eventually(session.Err).Should(Say(`Invalid passcode`))
   329  					Eventually(session).Should(Say(`Authenticating\.\.\.`))
   330  					Eventually(session.Err).Should(Say(`Invalid passcode`))
   331  					Eventually(session).Should(Say(`API endpoint:\s+` + helpers.GetAPI()))
   332  					Eventually(session).Should(Say(`API version:\s+\d\.\d{1,3}\.\d{1,3}`))
   333  					Eventually(session).Should(Say(`Not logged in. Use 'cf login' or 'cf login --sso' to log in.`))
   334  					Eventually(session.Err).Should(Say(`Unable to authenticate`))
   335  					Eventually(session).Should(Say(`FAILED`))
   336  
   337  					Eventually(session).Should(Exit(1))
   338  				})
   339  			})
   340  
   341  			When("a passcode isn't provided", func() {
   342  				It("prompts the user to try again", func() {
   343  					session := helpers.CF("login", "--sso-passcode")
   344  					Eventually(session.Err).Should(Say("Incorrect Usage: expected argument for flag `--sso-passcode'"))
   345  					Eventually(session).Should(Exit(1))
   346  				})
   347  			})
   348  		})
   349  	})
   350  
   351  	Describe("Choosing the identity provider", func() {
   352  		When("the user provides the --origin flag", func() {
   353  			It("logs in successfully", func() {
   354  				username, password := helpers.GetCredentials()
   355  				session := helpers.CF("login", "-u", username, "-p", password, "--origin", "uaa")
   356  				Eventually(session).Should(Say("API endpoint:\\s+" + helpers.GetAPI()))
   357  				Eventually(session).Should(Say(`Authenticating\.\.\.`))
   358  				Eventually(session).Should(Say(`OK`))
   359  				Eventually(session).Should(Say(`API endpoint:\s+` + helpers.GetAPI()))
   360  				Eventually(session).Should(Say(`API version:\s+\d\.\d{1,3}\.\d{1,3}`))
   361  				Eventually(session).Should(Say("user:\\s+" + username))
   362  				Eventually(session).Should(Exit(0))
   363  			})
   364  		})
   365  	})
   366  
   367  	Describe("User Credentials", func() {
   368  		var (
   369  			username string
   370  			password string
   371  		)
   372  
   373  		BeforeEach(func() {
   374  			username, password = helpers.GetCredentials()
   375  		})
   376  
   377  		It("prompts the user for email and password", func() {
   378  			buffer := NewBuffer()
   379  			_, err := buffer.Write([]byte(fmt.Sprintf("%s\n%s\n", username, password)))
   380  			Expect(err).ToNot(HaveOccurred())
   381  			session := helpers.CFWithStdin(buffer, "login")
   382  			Eventually(session).Should(Say("Email:"))
   383  			Eventually(session).Should(Say("\n"))
   384  			Eventually(session).Should(Say("Password:"))
   385  			Eventually(session).Should(Say("\n\n"))
   386  			Eventually(session).Should(Exit(0))
   387  		})
   388  
   389  		When("the user's account has been locked due to too many failed attempts", func() {
   390  			BeforeEach(func() {
   391  				helpers.LoginCF()
   392  				username, _ = helpers.CreateUser()
   393  				helpers.LogoutCF()
   394  			})
   395  
   396  			It("displays a helpful error and does not reprompt", func() {
   397  				input := NewBuffer()
   398  				_, err := input.Write([]byte("garbage\ngarbage\ngarbage\n"))
   399  				Expect(err).ToNot(HaveOccurred())
   400  				session := helpers.CFWithStdin(input, "login", "-u", username)
   401  				Eventually(session).Should(Exit(1))
   402  
   403  				input = NewBuffer()
   404  				_, err = input.Write([]byte("garbage\ngarbage\ngarbage\n"))
   405  				Expect(err).ToNot(HaveOccurred())
   406  				session = helpers.CFWithStdin(input, "login", "-u", username)
   407  				Eventually(session).Should(Exit(1))
   408  
   409  				input = NewBuffer()
   410  				_, err = input.Write([]byte("garbage\ngarbage\ngarbage\n"))
   411  				Expect(err).NotTo(HaveOccurred())
   412  				session = helpers.CFWithStdin(input, "login", "-u", username)
   413  				Eventually(session).Should(Say(`Password`))
   414  				Eventually(session.Err).Should(Say(`Your account has been locked because of too many failed attempts to login\.`))
   415  				Consistently(session).ShouldNot(Say(`Password`))
   416  				Eventually(session.Err).Should(Say(`Unable to authenticate.`))
   417  				Eventually(session).Should(Say("FAILED"))
   418  				Eventually(session).Should(Exit(1))
   419  			})
   420  		})
   421  
   422  		When("the -u flag is provided", func() {
   423  			It("prompts the user for their password", func() {
   424  				buffer := NewBuffer()
   425  				_, err := buffer.Write([]byte(fmt.Sprintf("%s\n", password)))
   426  				Expect(err).ToNot(HaveOccurred())
   427  				session := helpers.CFWithStdin(buffer, "login", "-u", username)
   428  				Eventually(session).Should(Say("Password: "))
   429  				Eventually(session).Should(Exit(0))
   430  			})
   431  		})
   432  
   433  		When("the user provides the -p flag", func() {
   434  			It("prompts the user for their email and logs in successfully", func() {
   435  				input := NewBuffer()
   436  				_, err := input.Write([]byte(username + "\n"))
   437  				Expect(err).ToNot(HaveOccurred())
   438  				session := helpers.CFWithStdin(input, "login", "-p", password)
   439  				Eventually(session).Should(Say("Email: "))
   440  				Eventually(session).Should(Exit(0))
   441  			})
   442  
   443  			When("the password flag is given incorrectly", func() {
   444  				It("Prompts the user two more times before exiting with an error", func() {
   445  					input := NewBuffer()
   446  					_, err := input.Write([]byte(username + "\n" + "bad-password\n" + "bad-password\n"))
   447  					Expect(err).ToNot(HaveOccurred())
   448  					session := helpers.CFWithStdin(input, "login", "-p", "bad-password")
   449  					Eventually(session).Should(Say("Email: "))
   450  					Eventually(session.Err).Should(Say("Credentials were rejected, please try again."))
   451  					Eventually(session).Should(Say("Password: "))
   452  					Eventually(session.Err).Should(Say("Credentials were rejected, please try again."))
   453  					Eventually(session).Should(Say("Not logged in. Use 'cf login' or 'cf login --sso' to log in."))
   454  					Eventually(session).Should(Say("FAILED"))
   455  					Eventually(session.Err).Should(Say("Unable to authenticate."))
   456  					Eventually(session).Should(Exit(1))
   457  				})
   458  			})
   459  		})
   460  
   461  		When("the user provides the -p and -u flags", func() {
   462  			Context("and the credentials are correct", func() {
   463  				It("logs in successfully", func() {
   464  					session := helpers.CF("login", "-p", password, "-u", username)
   465  					Eventually(session).Should(Say("API endpoint:\\s+" + helpers.GetAPI()))
   466  					Eventually(session).Should(Say(`Authenticating\.\.\.`))
   467  					Eventually(session).Should(Say(`OK`))
   468  					Eventually(session).Should(Say(`API endpoint:\s+` + helpers.GetAPI()))
   469  					Eventually(session).Should(Say(`API version:\s+\d\.\d{1,3}\.\d{1,3}`))
   470  					Eventually(session).Should(Say("user:\\s+" + username))
   471  					Eventually(session).Should(Exit(0))
   472  				})
   473  			})
   474  
   475  			Context("and the credentials are incorrect", func() {
   476  				It("prompts twice, displays an error and fails", func() {
   477  					input := NewBuffer()
   478  					_, err := input.Write([]byte("garbage\ngarbage\n"))
   479  					Expect(err).ToNot(HaveOccurred())
   480  					session := helpers.CFWithStdin(input, "login", "-p", "nope", "-u", "faker")
   481  					Eventually(session).Should(Say("API endpoint:\\s+" + helpers.GetAPI()))
   482  					Eventually(session).Should(Say(`Authenticating\.\.\.`))
   483  					Eventually(session.Err).Should(Say(`Credentials were rejected, please try again.`))
   484  					Eventually(session).Should(Say(`Password:`))
   485  					Eventually(session).Should(Say(`Authenticating\.\.\.`))
   486  					Eventually(session.Err).Should(Say(`Credentials were rejected, please try again.`))
   487  					Eventually(session).Should(Say(`Password:`))
   488  					Eventually(session).Should(Say(`Authenticating\.\.\.`))
   489  					Eventually(session.Err).Should(Say(`Credentials were rejected, please try again.`))
   490  					Eventually(session).Should(Say(`API endpoint:\s+` + helpers.GetAPI()))
   491  					Eventually(session).Should(Say(`API version:\s+\d\.\d{1,3}\.\d{1,3}`))
   492  					Eventually(session).Should(Say(`Not logged in. Use 'cf login' or 'cf login --sso' to log in.`))
   493  					Eventually(session.Err).Should(Say(`Unable to authenticate.`))
   494  					Eventually(session).Should(Say(`FAILED`))
   495  
   496  					Eventually(session).Should(Exit(1))
   497  				})
   498  
   499  				Context("and the user was previously logged in", func() {
   500  					BeforeEach(func() {
   501  						helpers.LoginCF()
   502  					})
   503  
   504  					It("logs them out", func() {
   505  						session := helpers.CF("login", "-p", "nope", "-u", "faker")
   506  						Eventually(session).Should(Say(`Not logged in. Use 'cf login' or 'cf login --sso' to log in.`))
   507  						Eventually(session).Should(Exit())
   508  
   509  						orgsSession := helpers.CF("orgs")
   510  						Eventually(orgsSession.Err).Should(Say(`Not logged in. Use 'cf login' or 'cf login --sso' to log in.`))
   511  						Eventually(orgsSession).Should(Exit())
   512  					})
   513  				})
   514  			})
   515  		})
   516  
   517  		When("MFA is enabled", func() {
   518  			var (
   519  				mfaCode string
   520  				server  *ghttp.Server
   521  			)
   522  
   523  			BeforeEach(func() {
   524  				password = "some-password"
   525  				mfaCode = "123456"
   526  				server = helpers.StartAndTargetMockServerWithAPIVersions(helpers.DefaultV2Version, helpers.DefaultV3Version)
   527  				helpers.AddMfa(server, password, mfaCode)
   528  			})
   529  
   530  			AfterEach(func() {
   531  				server.Close()
   532  			})
   533  
   534  			When("correct MFA code and credentials are provided", func() {
   535  				BeforeEach(func() {
   536  					fakeTokenResponse := map[string]string{
   537  						"access_token": "",
   538  						"token_type":   "bearer",
   539  					}
   540  					server.RouteToHandler(http.MethodPost, "/oauth/token",
   541  						ghttp.RespondWithJSONEncoded(http.StatusOK, fakeTokenResponse))
   542  					server.RouteToHandler(http.MethodGet, "/v3/organizations",
   543  						ghttp.RespondWith(http.StatusOK, `{
   544  						 "total_results": 0,
   545  						 "total_pages": 1,
   546  						 "resources": []}`))
   547  				})
   548  
   549  				It("logs in the user", func() {
   550  					input := NewBuffer()
   551  					_, err := input.Write([]byte(username + "\n" + password + "\n" + mfaCode + "\n"))
   552  					Expect(err).ToNot(HaveOccurred())
   553  					session := helpers.CFWithStdin(input, "login")
   554  					Eventually(session).Should(Say("Email: "))
   555  					Eventually(session).Should(Say("\n"))
   556  					Eventually(session).Should(Say("Password:"))
   557  					Eventually(session).Should(Say("\n"))
   558  					Eventually(session).Should(Say("MFA Code \\( Register at %[1]s \\)", server.URL()))
   559  					Eventually(session).Should(Exit(0))
   560  				})
   561  			})
   562  
   563  			When("incorrect MFA code and credentials are provided", func() {
   564  				It("fails", func() {
   565  					input := NewBuffer()
   566  					wrongMfaCode := mfaCode + "foo"
   567  					_, err := input.Write([]byte(username + "\n" + password + "\n" + wrongMfaCode + "\n" + password + "\n" + wrongMfaCode + "\n"))
   568  					Expect(err).ToNot(HaveOccurred())
   569  					session := helpers.CFWithStdin(input, "login")
   570  					Eventually(session).Should(Say("Password: "))
   571  					Eventually(session).Should(Say("MFA Code \\( Register at %[1]s \\)", server.URL()))
   572  					Eventually(session).Should(Say("Password: "))
   573  					Eventually(session).Should(Say("MFA Code \\( Register at %[1]s \\)", server.URL()))
   574  					Eventually(session).Should(Say("Not logged in. Use 'cf login' or 'cf login --sso' to log in."))
   575  					Eventually(session).Should(Say("FAILED"))
   576  					Eventually(session.Err).Should(Say("Unable to authenticate."))
   577  
   578  					Eventually(session).Should(Exit(1))
   579  				})
   580  			})
   581  		})
   582  	})
   583  
   584  	Describe("Client Credentials", func() {
   585  		var (
   586  			username     string
   587  			password     string
   588  			clientID     string
   589  			clientSecret string
   590  		)
   591  
   592  		When("the user has manually added client credentials to the config file", func() {
   593  			BeforeEach(func() {
   594  				clientID, clientSecret = helpers.SkipIfCustomClientCredentialsNotSet()
   595  				username, password = helpers.GetCredentials()
   596  				helpers.SetConfig(func(config *configv3.Config) {
   597  					config.ConfigFile.UAAOAuthClient = clientID
   598  					config.ConfigFile.UAAOAuthClientSecret = clientSecret
   599  					config.ConfigFile.UAAGrantType = ""
   600  				})
   601  			})
   602  
   603  			It("returns an unsupported error", func() {
   604  				errorMessage := "Error: Support for manually writing your client credentials to config.json has been removed. For similar functionality please use `cf auth --client-credentials`."
   605  
   606  				session := helpers.CF("login", "-u", username, "-p", password)
   607  				Eventually(session.Err).Should(Say(errorMessage))
   608  				Eventually(session).Should(Exit(1))
   609  			})
   610  		})
   611  
   612  		When("already logged in with client credentials", func() {
   613  			BeforeEach(func() {
   614  				clientID, clientSecret = helpers.SkipIfClientCredentialsNotSet()
   615  				username, password = helpers.GetCredentials()
   616  
   617  				session := helpers.CF("auth", clientID, clientSecret, "--client-credentials")
   618  				Eventually(session).Should(Exit(0))
   619  			})
   620  
   621  			It("should fail log in and display an error informing the user they need to log out", func() {
   622  				session := helpers.CF("login", "-p", password, "-u", username)
   623  				Eventually(session).Should(Say("FAILED"))
   624  				Eventually(session.Err).Should(Say("Service account currently logged in. Use 'cf logout' to log out service account and try again."))
   625  				Eventually(session).Should(Exit(1))
   626  
   627  				//And I am still logged in
   628  				targetSession := helpers.CF("target")
   629  				Eventually(targetSession).Should(Exit(0))
   630  			})
   631  		})
   632  	})
   633  
   634  	Describe("Target Organization", func() {
   635  		var (
   636  			orgName  string
   637  			username string
   638  			password string
   639  		)
   640  
   641  		BeforeEach(func() {
   642  			helpers.LoginCF()
   643  			orgName = helpers.NewOrgName()
   644  			session := helpers.CF("create-org", orgName)
   645  			Eventually(session).Should(Exit(0))
   646  			username, password = helpers.CreateUserInOrgRole(orgName, "OrgManager")
   647  		})
   648  
   649  		When("there is only one org available to the user", func() {
   650  			It("logs the user in and targets the organization automatically", func() {
   651  				session := helpers.CF("login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation")
   652  				Eventually(session).Should(Exit(0))
   653  
   654  				targetSession := helpers.CF("target")
   655  				Eventually(targetSession).Should(Exit(0))
   656  				Eventually(targetSession).Should(Say(`org:\s+%s`, orgName))
   657  			})
   658  		})
   659  
   660  		When("the -o flag is not passed", func() {
   661  			When("there are multiple orgs available to the user", func() {
   662  				BeforeEach(func() {
   663  					orgName = helpers.NewOrgName()
   664  					createOrgSession := helpers.CF("create-org", orgName)
   665  					Eventually(createOrgSession).Should(Exit(0))
   666  					setOrgRoleSession := helpers.CF("set-org-role", username, orgName, "OrgManager")
   667  					Eventually(setOrgRoleSession).Should(Exit(0))
   668  				})
   669  
   670  				When("there are more than 50 orgs", func() {
   671  					var server *ghttp.Server
   672  
   673  					BeforeEach(func() {
   674  						server = helpers.StartAndTargetMockServerWithAPIVersions(helpers.DefaultV2Version, helpers.DefaultV3Version)
   675  						helpers.AddLoginRoutes(server)
   676  						helpers.AddFiftyOneOrgs(server)
   677  						// handle request for spaces under "org20"
   678  						helpers.AddEmptyPaginatedResponse(server, "/v3/spaces?order_by=name&organization_guids=f6653aac-938e-4469-9a66-56a02796412b")
   679  					})
   680  
   681  					AfterEach(func() {
   682  						server.Close()
   683  					})
   684  
   685  					It("displays a message and prompts the user for the org name", func() {
   686  						input := NewBuffer()
   687  						_, wErr := input.Write([]byte(fmt.Sprintf("%s\n", "org20"))) // "org20" is one of the orgs in the test fixture
   688  						Expect(wErr).ToNot(HaveOccurred())
   689  
   690  						session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "--skip-ssl-validation")
   691  
   692  						Eventually(session).Should(Say("Select an org:"))
   693  						Eventually(session).Should(Say("There are too many options to display; please type in the name."))
   694  						Eventually(session).Should(Say("\n\n"))
   695  						Eventually(session).Should(Say(regexp.QuoteMeta(`Org (enter to skip):`)))
   696  						Eventually(session).Should(Say(`Targeted org org20\.`))
   697  
   698  						Eventually(session).Should(Exit(0))
   699  					})
   700  				})
   701  
   702  				When("user selects an organization by using numbered list", func() {
   703  					It("prompts the user for org and targets the selected org", func() {
   704  						input := NewBuffer()
   705  						_, err := input.Write([]byte("1\n"))
   706  						Expect(err).ToNot(HaveOccurred())
   707  
   708  						session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation")
   709  						Eventually(session).Should(Exit(0))
   710  
   711  						re := regexp.MustCompile("1\\. (?P<OrgName>.*)\n")
   712  						matches := re.FindStringSubmatch(string(session.Out.Contents()))
   713  						Expect(matches).To(HaveLen((2)))
   714  						expectedOrgName := matches[1]
   715  
   716  						targetSession := helpers.CF("target")
   717  						Eventually(targetSession).Should(Exit(0))
   718  						Eventually(targetSession).Should(Say(`org:\s+%s`, expectedOrgName))
   719  					})
   720  
   721  					When("the user selects a number greater than the number of orgs", func() {
   722  						It("prompts the user until a valid number is entered", func() {
   723  							input := NewBuffer()
   724  							_, err := input.Write([]byte("3\n"))
   725  							Expect(err).ToNot(HaveOccurred())
   726  
   727  							session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password)
   728  
   729  							Eventually(session).Should(Say(regexp.QuoteMeta("Select an org:")))
   730  							Eventually(session).Should(Say(regexp.QuoteMeta(`Org (enter to skip):`)))
   731  							Eventually(session).Should(Say(regexp.QuoteMeta(`Org (enter to skip):`)))
   732  
   733  							session.Interrupt()
   734  							Eventually(session).Should(Exit())
   735  						})
   736  					})
   737  				})
   738  
   739  				When("user selects an organization by org name", func() {
   740  					It("prompts the user for an org and then targets the selected org", func() {
   741  						input := NewBuffer()
   742  						_, err := input.Write([]byte(fmt.Sprintf("%s\n", orgName)))
   743  						Expect(err).ToNot(HaveOccurred())
   744  
   745  						session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation")
   746  						Eventually(session).Should(Say(`\d\. %s`, orgName))
   747  						Eventually(session).Should(Exit(0))
   748  
   749  						targetSession := helpers.CF("target")
   750  						Eventually(targetSession).Should(Exit(0))
   751  						Eventually(targetSession).Should(Say(`org:\s+%s`, orgName))
   752  					})
   753  				})
   754  
   755  				When("user does not select an organization", func() {
   756  					It("succesfully logs in but does not target any org", func() {
   757  						input := NewBuffer()
   758  						_, err := input.Write([]byte("\n"))
   759  						Expect(err).ToNot(HaveOccurred())
   760  
   761  						session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation")
   762  						Eventually(session).Should(Say(`Org \(enter to skip\):`))
   763  						Consistently(session).ShouldNot(Say(`Org \(enter to skip\):`))
   764  						Eventually(session).Should(Exit(0))
   765  
   766  						targetSession := helpers.CF("target")
   767  						Eventually(targetSession).Should(Exit(0))
   768  						Eventually(targetSession).Should(Say("No org or space targeted, use 'cf target -o ORG -s SPACE'"))
   769  					})
   770  				})
   771  
   772  				When("the user enters an invalid organization at the prompt", func() {
   773  					It("displays an error message and does not target the org", func() {
   774  						orgName = "invalid-org"
   775  						input := NewBuffer()
   776  						_, err := input.Write([]byte(fmt.Sprintf("%s\n", orgName)))
   777  						Expect(err).ToNot(HaveOccurred())
   778  
   779  						session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "--skip-ssl-validation")
   780  						Eventually(session).Should(Exit(1))
   781  						Eventually(session).Should(Say("FAILED"))
   782  						Eventually(session.Err).Should(Say("Organization '%s' not found", orgName))
   783  
   784  						targetSession := helpers.CF("target")
   785  						Eventually(targetSession).Should(Exit(0))
   786  						Eventually(targetSession).Should(Say(`user:\s+%s`, username))
   787  						Eventually(targetSession).ShouldNot(Say(`org:\s+%s`, orgName))
   788  						Eventually(targetSession).Should(Say("No org or space targeted, use 'cf target -o ORG -s SPACE'"))
   789  					})
   790  				})
   791  			})
   792  		})
   793  
   794  		When("the -o flag is not passed, and the -s flag is passed", func() {
   795  			var (
   796  				spaceName     string
   797  				secondOrgName string
   798  				thirdOrgName  string
   799  			)
   800  
   801  			BeforeEach(func() {
   802  				orgName = helpers.NewOrgName()
   803  				createOrgSession := helpers.CF("create-org", orgName)
   804  				Eventually(createOrgSession).Should(Exit(0))
   805  				setOrgRoleSession := helpers.CF("set-org-role", username, orgName, "OrgManager")
   806  				Eventually(setOrgRoleSession).Should(Exit(0))
   807  				spaceName = helpers.NewSpaceName()
   808  				createSpaceSession := helpers.CF("create-space", "-o", orgName, spaceName)
   809  				Eventually(createSpaceSession).Should(Exit(0))
   810  				roleSession := helpers.CF("set-space-role", username, orgName, spaceName, "SpaceManager")
   811  				Eventually(roleSession).Should(Exit(0))
   812  
   813  				secondOrgName = helpers.NewOrgName()
   814  				createOrgSession = helpers.CF("create-org", secondOrgName)
   815  				Eventually(createOrgSession).Should(Exit(0))
   816  				setOrgRoleSession = helpers.CF("set-org-role", username, secondOrgName, "OrgManager")
   817  				Eventually(setOrgRoleSession).Should(Exit(0))
   818  				secondSpaceName := helpers.NewSpaceName()
   819  				createSpaceSession = helpers.CF("create-space", "-o", secondOrgName, secondSpaceName)
   820  				Eventually(createSpaceSession).Should(Exit(0))
   821  				roleSession = helpers.CF("set-space-role", username, secondOrgName, secondSpaceName, "SpaceManager")
   822  				Eventually(roleSession).Should(Exit(0))
   823  			})
   824  
   825  			Context("only one org has a correctly named space", func() {
   826  				It("automatically targets the org associated with the space", func() {
   827  					session := helpers.CF("login", "-u", username, "-p", password, "-s", spaceName, "-a", apiURL, "--skip-ssl-validation")
   828  					Eventually(session).Should(Exit(0))
   829  
   830  					targetSession := helpers.CF("target")
   831  					Eventually(targetSession).Should(Exit(0))
   832  					Eventually(targetSession).Should(Say(`org:\s+%s`, orgName))
   833  					Eventually(targetSession).Should(Say(`space:\s+%s`, spaceName))
   834  				})
   835  			})
   836  
   837  			Context("multiple orgs have the correctly named space", func() {
   838  				BeforeEach(func() {
   839  					thirdOrgName = helpers.NewOrgName()
   840  					createOrgSession := helpers.CF("create-org", thirdOrgName)
   841  					Eventually(createOrgSession).Should(Exit(0))
   842  					setOrgRoleSession := helpers.CF("set-org-role", username, thirdOrgName, "OrgManager")
   843  					Eventually(setOrgRoleSession).Should(Exit(0))
   844  					createSpaceSession := helpers.CF("create-space", "-o", thirdOrgName, spaceName)
   845  					Eventually(createSpaceSession).Should(Exit(0))
   846  					roleSession := helpers.CF("set-space-role", username, thirdOrgName, spaceName, "SpaceManager")
   847  					Eventually(roleSession).Should(Exit(0))
   848  				})
   849  
   850  				It("prompts the user to select from the matching orgs", func() {
   851  					input := NewBuffer()
   852  					_, err := input.Write([]byte("1\n"))
   853  					Expect(err).ToNot(HaveOccurred())
   854  
   855  					session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-s", spaceName, "-a", apiURL, "--skip-ssl-validation")
   856  					Eventually(session).Should(Exit(0))
   857  
   858  					re := regexp.MustCompile("[0-9]+\\. (?P<OrgName>.*)")
   859  					matches := re.FindStringSubmatch(string(session.Out.Contents()))
   860  					Expect(matches).To(HaveLen((2)))
   861  					expectedOrgName := matches[1]
   862  
   863  					targetSession := helpers.CF("target")
   864  					Eventually(targetSession).Should(Exit(0))
   865  					Eventually(targetSession).Should(Say(`org:\s+%s`, expectedOrgName))
   866  					Eventually(targetSession).Should(Say(`space:\s+%s`, spaceName))
   867  				})
   868  
   869  				When("the user enters an organization that doesn't contain the space at the prompt", func() {
   870  					It("targets the org, then displays an error message and does not target the space", func() {
   871  						input := NewBuffer()
   872  						_, err := input.Write([]byte(fmt.Sprintf("%s\n", secondOrgName)))
   873  						Expect(err).ToNot(HaveOccurred())
   874  
   875  						session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-s", spaceName, "-a", apiURL, "--skip-ssl-validation")
   876  
   877  						Eventually(session).Should(Exit(1))
   878  						Eventually(session).Should(Say("FAILED"))
   879  						Eventually(session.Err).Should(Say("Organization '%s' containing space '%s' not found.", secondOrgName, spaceName))
   880  
   881  						targetSession := helpers.CF("target")
   882  						Eventually(targetSession).Should(Exit(0))
   883  						Eventually(targetSession).Should(Say(`user:\s+%s`, username))
   884  						Eventually(targetSession).ShouldNot(Say(`org:\s+%s`, orgName))
   885  						Eventually(targetSession).Should(Say("No org or space targeted, use 'cf target -o ORG -s SPACE'"))
   886  					})
   887  				})
   888  			})
   889  		})
   890  
   891  		When("the -o flag is passed", func() {
   892  			BeforeEach(func() {
   893  				helpers.LogoutCF()
   894  			})
   895  
   896  			When("the organization is valid", func() {
   897  				It("targets the organization that was passed as an argument", func() {
   898  					session := helpers.CF("login", "-u", username, "-p", password, "-o", orgName)
   899  
   900  					Eventually(session).Should(Exit(0))
   901  					Eventually(session).Should(Say(`org:\s+%s`, orgName))
   902  
   903  					targetSession := helpers.CF("target")
   904  					Eventually(targetSession).Should(Exit(0))
   905  					Eventually(targetSession).Should(Say(`org:\s+%s`, orgName))
   906  				})
   907  			})
   908  
   909  			When("the organization is invalid", func() {
   910  				It("logs in the user, displays an error message, and does not target any organization", func() {
   911  					orgName = "invalid-org"
   912  					session := helpers.CF("login", "-u", username, "-p", password, "-o", orgName)
   913  
   914  					Eventually(session).Should(Exit(1))
   915  					Eventually(session).Should(Say("FAILED"))
   916  					Eventually(session.Err).Should(Say("Organization '%s' not found", orgName))
   917  
   918  					targetSession := helpers.CF("target")
   919  					Eventually(targetSession).Should(Exit(0))
   920  					Eventually(targetSession).Should(Say(`user:\s+%s`, username))
   921  					Eventually(targetSession).ShouldNot(Say(`org:\s+%s`, orgName))
   922  					Eventually(targetSession).Should(Say("No org or space targeted, use 'cf target -o ORG -s SPACE'"))
   923  				})
   924  			})
   925  		})
   926  	})
   927  
   928  	Describe("Target Space", func() {
   929  		var (
   930  			orgName  string
   931  			username string
   932  			password string
   933  		)
   934  
   935  		BeforeEach(func() {
   936  			helpers.LoginCF()
   937  			orgName = helpers.NewOrgName()
   938  			session := helpers.CF("create-org", orgName)
   939  			Eventually(session).Should(Exit(0))
   940  			username, password = helpers.CreateUser()
   941  		})
   942  
   943  		When("only one space is available to the user", func() {
   944  			var spaceName string
   945  
   946  			BeforeEach(func() {
   947  				spaceName = helpers.NewSpaceName()
   948  				session := helpers.CF("create-space", "-o", orgName, spaceName)
   949  				Eventually(session).Should(Exit(0))
   950  				roleSession := helpers.CF("set-space-role", username, orgName, spaceName, "SpaceManager")
   951  				Eventually(roleSession).Should(Exit(0))
   952  			})
   953  
   954  			It("logs the user in and targets the org and the space", func() {
   955  				session := helpers.CF("login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation")
   956  				Eventually(session).Should(Exit(0))
   957  
   958  				targetSession := helpers.CF("target")
   959  				Eventually(targetSession).Should(Exit(0))
   960  				Eventually(targetSession).Should(Say(`org:\s+%s`, orgName))
   961  				Eventually(targetSession).Should(Say(`space:\s+%s`, spaceName))
   962  			})
   963  		})
   964  
   965  		When("multiple spaces are available to the user", func() {
   966  			var (
   967  				spaceName  string
   968  				spaceName2 string
   969  			)
   970  
   971  			BeforeEach(func() {
   972  				spaceName = helpers.NewSpaceName()
   973  				session := helpers.CF("create-space", "-o", orgName, spaceName)
   974  				Eventually(session).Should(Exit(0))
   975  				roleSession := helpers.CF("set-space-role", username, orgName, spaceName, "SpaceManager")
   976  				Eventually(roleSession).Should(Exit(0))
   977  
   978  				spaceName2 = helpers.NewSpaceName()
   979  				session2 := helpers.CF("create-space", "-o", orgName, spaceName2)
   980  				Eventually(session2).Should(Exit(0))
   981  				roleSession2 := helpers.CF("set-space-role", username, orgName, spaceName2, "SpaceManager")
   982  				Eventually(roleSession2).Should(Exit(0))
   983  			})
   984  
   985  			When("the -s flag is passed", func() {
   986  				BeforeEach(func() {
   987  					orgName2 := helpers.NewOrgName()
   988  					session := helpers.CF("create-org", orgName2)
   989  					Eventually(session).Should(Exit(0))
   990  					session = helpers.CF("set-org-role", username, orgName2, "OrgManager")
   991  					Eventually(session).Should(Exit(0))
   992  				})
   993  
   994  				It("targets the org and the space", func() {
   995  					stdin := NewBuffer()
   996  					_, writeErr := stdin.Write([]byte(orgName + "\n"))
   997  					Expect(writeErr).ToNot(HaveOccurred())
   998  					session := helpers.CFWithStdin(stdin, "login", "-u", username, "-p", password, "-a", apiURL, "-s", spaceName, "--skip-ssl-validation")
   999  
  1000  					Eventually(session).Should(Say(`Targeted org\s+%s\.`, orgName))
  1001  					Eventually(session).Should(Say(`\nTargeted space\s+%s\.`, spaceName))
  1002  
  1003  					Eventually(session).Should(Say(`org:\s+%s`, orgName))
  1004  					Eventually(session).Should(Say(`space:\s+%s`, spaceName))
  1005  					Eventually(session).Should(Exit(0))
  1006  
  1007  					sessionOutput := string(session.Out.Contents())
  1008  					Expect(sessionOutput).To(MatchRegexp(`\S\nAPI`))
  1009  
  1010  					targetSession := helpers.CF("target")
  1011  					Eventually(targetSession).Should(Exit(0))
  1012  					Eventually(targetSession).Should(Say(`org:\s+%s`, orgName))
  1013  					Eventually(targetSession).Should(Say(`space:\s+%s`, spaceName))
  1014  				})
  1015  
  1016  				When("the space name is invalid", func() {
  1017  					BeforeEach(func() {
  1018  						spaceName = "invalid-space-name"
  1019  					})
  1020  
  1021  					It("the command fails and displays an error message. It targets the org but not the space.", func() {
  1022  						stdin := NewBuffer()
  1023  						_, writeErr := stdin.Write([]byte(orgName + "\n"))
  1024  						Expect(writeErr).ToNot(HaveOccurred())
  1025  						session := helpers.CFWithStdin(stdin, "login", "-u", username, "-p", password, "-a", apiURL, "-o", orgName, "-s", spaceName, "--skip-ssl-validation")
  1026  						Eventually(session).Should(Exit(1))
  1027  						Eventually(session).Should(Say("FAILED"))
  1028  						Eventually(session.Err).Should(Say("Space '%s' not found", spaceName))
  1029  
  1030  						targetSession := helpers.CF("target")
  1031  						Eventually(targetSession).Should(Exit(0))
  1032  						Eventually(targetSession).Should(Say(`org:\s+%s`, orgName))
  1033  						Eventually(targetSession).Should(Say("No space targeted, use 'cf target -s SPACE'"))
  1034  					})
  1035  				})
  1036  			})
  1037  
  1038  			When("the -s flag is not passed", func() {
  1039  				It("prompts the user to pick their space by list position", func() {
  1040  					input := NewBuffer()
  1041  					_, err := input.Write([]byte("1\n"))
  1042  					Expect(err).ToNot(HaveOccurred())
  1043  
  1044  					session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation")
  1045  					Eventually(session).Should(Exit(0))
  1046  
  1047  					re := regexp.MustCompile("1\\. (?P<SpaceName>.*)\n")
  1048  					submatches := re.FindStringSubmatch(string(session.Out.Contents()))
  1049  					Expect(submatches).ToNot(BeEmpty(), "missing numbered space list")
  1050  					expectedSpaceName := submatches[1]
  1051  
  1052  					targetSession := helpers.CF("target")
  1053  					Eventually(targetSession).Should(Exit(0))
  1054  					Eventually(targetSession).Should(Say(`space:\s+%s`, expectedSpaceName))
  1055  				})
  1056  
  1057  				It("reprompts the user if an invalid number is entered", func() {
  1058  					input := NewBuffer()
  1059  					_, err := input.Write([]byte("4\n"))
  1060  					Expect(err).ToNot(HaveOccurred())
  1061  
  1062  					session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation")
  1063  					Eventually(session).Should(Say(regexp.QuoteMeta("Space (enter to skip):")))
  1064  					Eventually(session).Should(Say(regexp.QuoteMeta("Space (enter to skip):")))
  1065  					session.Interrupt()
  1066  					Eventually(session).Should(Exit())
  1067  				})
  1068  
  1069  				It("allows the user to pick their space by name", func() {
  1070  					input := NewBuffer()
  1071  					_, err := input.Write([]byte(spaceName + "\n"))
  1072  					Expect(err).ToNot(HaveOccurred())
  1073  
  1074  					session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation")
  1075  					Eventually(session).Should(Exit(0))
  1076  
  1077  					targetSession := helpers.CF("target")
  1078  					Eventually(targetSession).Should(Exit(0))
  1079  					Eventually(targetSession).Should(Say(`space:\s+%s`, spaceName))
  1080  				})
  1081  
  1082  				It("allows the user to skip picking a space", func() {
  1083  					input := NewBuffer()
  1084  					_, err := input.Write([]byte("\n"))
  1085  					Expect(err).ToNot(HaveOccurred())
  1086  
  1087  					session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation")
  1088  					Eventually(session).Should(Exit(0))
  1089  
  1090  					targetSession := helpers.CF("target")
  1091  					Eventually(targetSession).Should(Exit(0))
  1092  					Eventually(targetSession).Should(Say(`No space targeted, use 'cf target -s SPACE'`))
  1093  				})
  1094  
  1095  				When("the input space name is invalid", func() {
  1096  					BeforeEach(func() {
  1097  						spaceName = "invalid-space-name"
  1098  					})
  1099  
  1100  					It("the command fails and displays an error message. It does not target the space.", func() {
  1101  						input := NewBuffer()
  1102  						_, err := input.Write([]byte(spaceName + "\n"))
  1103  						Expect(err).ToNot(HaveOccurred())
  1104  
  1105  						session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation")
  1106  						Eventually(session).Should(Exit(1))
  1107  						Eventually(session).Should(Say("FAILED"))
  1108  						Eventually(session.Err).Should(Say("Space '%s' not found", spaceName))
  1109  
  1110  						targetSession := helpers.CF("target")
  1111  						Eventually(targetSession).Should(Exit(0))
  1112  						Eventually(targetSession).Should(Say(`org:\s+%s`, orgName))
  1113  						Eventually(targetSession).Should(Say("No space targeted, use 'cf target -s SPACE'"))
  1114  					})
  1115  				})
  1116  
  1117  				When("there are more than 50 spaces", func() {
  1118  					var server *ghttp.Server
  1119  					BeforeEach(func() {
  1120  						server = helpers.StartAndTargetMockServerWithAPIVersions(helpers.DefaultV2Version, helpers.DefaultV3Version)
  1121  						helpers.AddLoginRoutes(server)
  1122  						helpers.AddFiftyOneSpaces(server)
  1123  					})
  1124  
  1125  					AfterEach(func() {
  1126  						server.Close()
  1127  					})
  1128  
  1129  					It("displays a message and prompts the user for the space name", func() {
  1130  						input := NewBuffer()
  1131  						_, wErr := input.Write([]byte(fmt.Sprintf("%s\n", "test-space-1")))
  1132  						Expect(wErr).ToNot(HaveOccurred())
  1133  
  1134  						session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "--skip-ssl-validation")
  1135  
  1136  						Eventually(session).Should(Say("Select a space:"))
  1137  						Eventually(session).Should(Say("There are too many options to display; please type in the name."))
  1138  						Eventually(session).Should(Say("\n\n"))
  1139  						Eventually(session).Should(Say(regexp.QuoteMeta(`Space (enter to skip):`)))
  1140  						Eventually(session).Should(Say(`Targeted space test-space-1\.`))
  1141  
  1142  						Eventually(session).Should(Exit(0))
  1143  					})
  1144  				})
  1145  			})
  1146  		})
  1147  	})
  1148  
  1149  	Describe("Full interactive happy path", func() {
  1150  		var (
  1151  			orgName1   string
  1152  			orgName2   string
  1153  			spaceName1 string
  1154  			spaceName2 string
  1155  			username   string
  1156  			password   string
  1157  		)
  1158  
  1159  		BeforeEach(func() {
  1160  			helpers.LoginCF()
  1161  			orgName1 = helpers.NewOrgName()
  1162  			orgName2 = helpers.NewOrgName()
  1163  			spaceName1 = helpers.NewSpaceName()
  1164  			spaceName2 = helpers.NewSpaceName()
  1165  
  1166  			Eventually(helpers.CF("create-org", orgName1)).Should(Exit(0))
  1167  			Eventually(helpers.CF("create-org", orgName2)).Should(Exit(0))
  1168  			Eventually(helpers.CF("create-space", "-o", orgName1, spaceName1)).Should(Exit(0))
  1169  			Eventually(helpers.CF("create-space", "-o", orgName1, spaceName2)).Should(Exit(0))
  1170  
  1171  			username, password = helpers.CreateUser()
  1172  			Eventually(helpers.CF("set-org-role", username, orgName1, "OrgManager")).Should(Exit(0))
  1173  			Eventually(helpers.CF("set-org-role", username, orgName2, "OrgManager")).Should(Exit(0))
  1174  			Eventually(helpers.CF("set-space-role", username, orgName1, spaceName1, "SpaceManager")).Should(Exit(0))
  1175  			Eventually(helpers.CF("set-space-role", username, orgName1, spaceName2, "SpaceManager")).Should(Exit(0))
  1176  
  1177  			helpers.UnsetAPI()
  1178  		})
  1179  
  1180  		When("there are multiple orgs and spaces available to a user", func() {
  1181  			It("prompts for username, password, org, and space. Then logs in and targets correctly", func() {
  1182  				buffer := NewBuffer()
  1183  				_, err := buffer.Write([]byte(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n", apiURL, username, password, orgName1, spaceName2)))
  1184  				Expect(err).ToNot(HaveOccurred())
  1185  
  1186  				session := helpers.CFWithStdin(buffer, "login", "--skip-ssl-validation")
  1187  				Eventually(session).Should(Say("API endpoint:"))
  1188  				Eventually(session).Should(Say("\n"))
  1189  				Eventually(session).Should(Say("Email:"))
  1190  				Eventually(session).Should(Say("\n"))
  1191  				Eventually(session).Should(Say("Password:"))
  1192  				Eventually(session).Should(Say("OK"))
  1193  				Eventually(session).Should(Say("\n\n"))
  1194  				Eventually(session).Should(Say("Select an org:"))
  1195  				Eventually(session).Should(Say("\n\n"))
  1196  				Eventually(session).Should(Say(regexp.QuoteMeta(`Org (enter to skip):`)))
  1197  				Eventually(session).Should(Say(fmt.Sprintf("Targeted org %s", orgName1)))
  1198  				Eventually(session).Should(Say("\n\n"))
  1199  				Eventually(session).Should(Say("Select a space:"))
  1200  				Eventually(session).Should(Say("\n\n"))
  1201  				Eventually(session).Should(Say(regexp.QuoteMeta(`Space (enter to skip):`)))
  1202  				Eventually(session).Should(Say(fmt.Sprintf(`Targeted space %s\.`, spaceName2)))
  1203  				Eventually(session).Should(Say("\n\n"))
  1204  				Eventually(session).Should(Say(`org:\s+%s`, orgName1))
  1205  				Eventually(session).Should(Say(`space:\s+%s`, spaceName2))
  1206  				Eventually(session).Should(Exit(0))
  1207  
  1208  				targetSession := helpers.CF("target")
  1209  				Eventually(targetSession).Should(Exit(0))
  1210  				Eventually(targetSession).Should(Say(`org:\s+%s`, orgName1))
  1211  				Eventually(targetSession).Should(Say(`space:\s+%s`, spaceName2))
  1212  			})
  1213  		})
  1214  	})
  1215  })