github.com/willmadison/cli@v6.40.1-0.20181018160101-29d5937903ff+incompatible/cf/commands/service/bind_route_service_test.go (about)

     1  package service_test
     2  
     3  import (
     4  	"io/ioutil"
     5  	"net/http"
     6  	"os"
     7  
     8  	"code.cloudfoundry.org/cli/cf/commandregistry"
     9  	"code.cloudfoundry.org/cli/cf/commands/service"
    10  	"code.cloudfoundry.org/cli/cf/configuration/coreconfig"
    11  	"code.cloudfoundry.org/cli/cf/errors"
    12  	"code.cloudfoundry.org/cli/cf/flags"
    13  	"code.cloudfoundry.org/cli/cf/models"
    14  	"code.cloudfoundry.org/cli/cf/requirements"
    15  	"code.cloudfoundry.org/cli/cf/requirements/requirementsfakes"
    16  
    17  	"code.cloudfoundry.org/cli/cf/api/apifakes"
    18  	testconfig "code.cloudfoundry.org/cli/cf/util/testhelpers/configuration"
    19  	testterm "code.cloudfoundry.org/cli/cf/util/testhelpers/terminal"
    20  
    21  	. "code.cloudfoundry.org/cli/cf/util/testhelpers/matchers"
    22  	. "github.com/onsi/ginkgo"
    23  	. "github.com/onsi/gomega"
    24  )
    25  
    26  var _ = Describe("BindRouteService", func() {
    27  	var (
    28  		ui                      *testterm.FakeUI
    29  		configRepo              coreconfig.Repository
    30  		routeRepo               *apifakes.FakeRouteRepository
    31  		routeServiceBindingRepo *apifakes.FakeRouteServiceBindingRepository
    32  
    33  		cmd         commandregistry.Command
    34  		deps        commandregistry.Dependency
    35  		factory     *requirementsfakes.FakeFactory
    36  		flagContext flags.FlagContext
    37  
    38  		fakeDomain models.DomainFields
    39  
    40  		loginRequirement           requirements.Requirement
    41  		domainRequirement          *requirementsfakes.FakeDomainRequirement
    42  		serviceInstanceRequirement *requirementsfakes.FakeServiceInstanceRequirement
    43  		minAPIVersionRequirement   requirements.Requirement
    44  	)
    45  
    46  	BeforeEach(func() {
    47  		ui = new(testterm.FakeUI)
    48  
    49  		configRepo = testconfig.NewRepositoryWithDefaults()
    50  
    51  		routeRepo = new(apifakes.FakeRouteRepository)
    52  		repoLocator := deps.RepoLocator.SetRouteRepository(routeRepo)
    53  
    54  		routeServiceBindingRepo = new(apifakes.FakeRouteServiceBindingRepository)
    55  		repoLocator = repoLocator.SetRouteServiceBindingRepository(routeServiceBindingRepo)
    56  
    57  		deps = commandregistry.Dependency{
    58  			UI:          ui,
    59  			Config:      configRepo,
    60  			RepoLocator: repoLocator,
    61  		}
    62  
    63  		cmd = new(service.BindRouteService)
    64  		cmd.SetDependency(deps, false)
    65  
    66  		flagContext = flags.NewFlagContext(cmd.MetaData().Flags)
    67  
    68  		factory = new(requirementsfakes.FakeFactory)
    69  
    70  		loginRequirement = &passingRequirement{Name: "login-requirement"}
    71  		factory.NewLoginRequirementReturns(loginRequirement)
    72  
    73  		domainRequirement = new(requirementsfakes.FakeDomainRequirement)
    74  		factory.NewDomainRequirementReturns(domainRequirement)
    75  
    76  		fakeDomain = models.DomainFields{
    77  			GUID: "fake-domain-guid",
    78  			Name: "fake-domain-name",
    79  		}
    80  		domainRequirement.GetDomainReturns(fakeDomain)
    81  
    82  		serviceInstanceRequirement = new(requirementsfakes.FakeServiceInstanceRequirement)
    83  		factory.NewServiceInstanceRequirementReturns(serviceInstanceRequirement)
    84  
    85  		minAPIVersionRequirement = &passingRequirement{Name: "min-api-version-requirement"}
    86  		factory.NewMinAPIVersionRequirementReturns(minAPIVersionRequirement)
    87  	})
    88  
    89  	Describe("Requirements", func() {
    90  		Context("when not provided exactly two args", func() {
    91  			BeforeEach(func() {
    92  				flagContext.Parse("domain-name")
    93  			})
    94  
    95  			It("fails with usage", func() {
    96  				_, err := cmd.Requirements(factory, flagContext)
    97  				Expect(err).To(HaveOccurred())
    98  				Expect(ui.Outputs()).To(ContainSubstrings(
    99  					[]string{"FAILED"},
   100  					[]string{"Incorrect Usage. Requires DOMAIN and SERVICE_INSTANCE as arguments"},
   101  				))
   102  			})
   103  		})
   104  
   105  		Context("when provided exactly two args", func() {
   106  			BeforeEach(func() {
   107  				flagContext.Parse("domain-name", "service-instance")
   108  			})
   109  
   110  			It("returns a LoginRequirement", func() {
   111  				actualRequirements, err := cmd.Requirements(factory, flagContext)
   112  				Expect(err).NotTo(HaveOccurred())
   113  				Expect(factory.NewLoginRequirementCallCount()).To(Equal(1))
   114  				Expect(actualRequirements).To(ContainElement(loginRequirement))
   115  			})
   116  
   117  			It("returns a DomainRequirement", func() {
   118  				actualRequirements, err := cmd.Requirements(factory, flagContext)
   119  				Expect(err).NotTo(HaveOccurred())
   120  				Expect(factory.NewLoginRequirementCallCount()).To(Equal(1))
   121  				Expect(actualRequirements).To(ContainElement(loginRequirement))
   122  			})
   123  
   124  			It("returns a ServiceInstanceRequirement", func() {
   125  				actualRequirements, err := cmd.Requirements(factory, flagContext)
   126  				Expect(err).NotTo(HaveOccurred())
   127  				Expect(factory.NewServiceInstanceRequirementCallCount()).To(Equal(1))
   128  				Expect(actualRequirements).To(ContainElement(serviceInstanceRequirement))
   129  			})
   130  		})
   131  	})
   132  
   133  	Describe("Execute", func() {
   134  		var runCLIErr error
   135  
   136  		BeforeEach(func() {
   137  			err := flagContext.Parse("domain-name", "service-instance")
   138  			Expect(err).NotTo(HaveOccurred())
   139  			cmd.Requirements(factory, flagContext)
   140  		})
   141  
   142  		JustBeforeEach(func() {
   143  			runCLIErr = cmd.Execute(flagContext)
   144  		})
   145  
   146  		It("tries to find the route", func() {
   147  			Expect(runCLIErr).NotTo(HaveOccurred())
   148  			Expect(routeRepo.FindCallCount()).To(Equal(1))
   149  			host, domain, path, port := routeRepo.FindArgsForCall(0)
   150  			Expect(host).To(Equal(""))
   151  			Expect(domain).To(Equal(fakeDomain))
   152  			Expect(path).To(Equal(""))
   153  			Expect(port).To(Equal(0))
   154  		})
   155  
   156  		Context("when given a hostname", func() {
   157  			BeforeEach(func() {
   158  				flagContext = flags.NewFlagContext(cmd.MetaData().Flags)
   159  				err := flagContext.Parse("domain-name", "service-instance", "-n", "the-hostname")
   160  				Expect(err).NotTo(HaveOccurred())
   161  			})
   162  
   163  			It("tries to find the route with the given hostname", func() {
   164  				Expect(runCLIErr).NotTo(HaveOccurred())
   165  				Expect(routeRepo.FindCallCount()).To(Equal(1))
   166  				host, _, _, _ := routeRepo.FindArgsForCall(0)
   167  				Expect(host).To(Equal("the-hostname"))
   168  			})
   169  		})
   170  
   171  		Context("when given a path", func() {
   172  			BeforeEach(func() {
   173  				flagContext = flags.NewFlagContext(cmd.MetaData().Flags)
   174  				err := flagContext.Parse("domain-name", "service-instance", "--path", "/path")
   175  				Expect(err).NotTo(HaveOccurred())
   176  			})
   177  
   178  			It("tries to find the route with the given path", func() {
   179  				Expect(runCLIErr).NotTo(HaveOccurred())
   180  				Expect(routeRepo.FindCallCount()).To(Equal(1))
   181  				_, _, path, _ := routeRepo.FindArgsForCall(0)
   182  				Expect(path).To(Equal("/path"))
   183  			})
   184  
   185  			Context("when the path does not have a leading slash", func() {
   186  				BeforeEach(func() {
   187  					flagContext = flags.NewFlagContext(cmd.MetaData().Flags)
   188  					err := flagContext.Parse("domain-name", "service-instance", "--path", "path")
   189  					Expect(err).NotTo(HaveOccurred())
   190  				})
   191  
   192  				It("prepends a leading slash and tries to find the route with the given path", func() {
   193  					Expect(runCLIErr).NotTo(HaveOccurred())
   194  					Expect(routeRepo.FindCallCount()).To(Equal(1))
   195  					_, _, path, _ := routeRepo.FindArgsForCall(0)
   196  					Expect(path).To(Equal("/path"))
   197  				})
   198  			})
   199  		})
   200  
   201  		Context("when given a path and a hostname", func() {
   202  			BeforeEach(func() {
   203  				flagContext = flags.NewFlagContext(cmd.MetaData().Flags)
   204  				err := flagContext.Parse("domain-name", "service-instance", "-n", "the-hostname", "--path", "/path")
   205  				Expect(err).NotTo(HaveOccurred())
   206  			})
   207  
   208  			It("tries to find the route with both the given hostname and path", func() {
   209  				Expect(runCLIErr).NotTo(HaveOccurred())
   210  				Expect(routeRepo.FindCallCount()).To(Equal(1))
   211  				host, _, path, _ := routeRepo.FindArgsForCall(0)
   212  				Expect(host).To(Equal("the-hostname"))
   213  				Expect(path).To(Equal("/path"))
   214  			})
   215  		})
   216  
   217  		Context("when the route can be found", func() {
   218  			BeforeEach(func() {
   219  				routeRepo.FindReturns(models.Route{GUID: "route-guid"}, nil)
   220  			})
   221  
   222  			Context("when the service instance is not user-provided and requires route forwarding", func() {
   223  				BeforeEach(func() {
   224  					serviceInstance := models.ServiceInstance{
   225  						ServiceOffering: models.ServiceOfferingFields{
   226  							Requires: []string{"route_forwarding"},
   227  						},
   228  					}
   229  					serviceInstance.ServicePlan = models.ServicePlanFields{
   230  						GUID: "service-plan-guid",
   231  					}
   232  					serviceInstanceRequirement.GetServiceInstanceReturns(serviceInstance)
   233  				})
   234  
   235  				It("does not warn", func() {
   236  					Expect(runCLIErr).NotTo(HaveOccurred())
   237  					Expect(ui.Outputs()).NotTo(ContainSubstrings(
   238  						[]string{"Bind cancelled"},
   239  					))
   240  				})
   241  
   242  				It("tries to bind the route service", func() {
   243  					Expect(runCLIErr).NotTo(HaveOccurred())
   244  					Expect(routeServiceBindingRepo.BindCallCount()).To(Equal(1))
   245  				})
   246  
   247  				Context("when binding the route service succeeds", func() {
   248  					BeforeEach(func() {
   249  						routeServiceBindingRepo.BindReturns(nil)
   250  					})
   251  
   252  					It("says OK", func() {
   253  						Expect(runCLIErr).NotTo(HaveOccurred())
   254  						Expect(ui.Outputs()).To(ContainSubstrings(
   255  							[]string{"OK"},
   256  						))
   257  					})
   258  				})
   259  
   260  				Context("when binding the route service fails because it is already bound", func() {
   261  					BeforeEach(func() {
   262  						routeServiceBindingRepo.BindReturns(errors.NewHTTPError(http.StatusOK, errors.ServiceInstanceAlreadyBoundToSameRoute, "http-err"))
   263  					})
   264  
   265  					It("says OK", func() {
   266  						Expect(runCLIErr).NotTo(HaveOccurred())
   267  						Expect(ui.Outputs()).To(ContainSubstrings(
   268  							[]string{"OK"},
   269  						))
   270  					})
   271  				})
   272  
   273  				Context("when binding the route service fails for any other reason", func() {
   274  					BeforeEach(func() {
   275  						routeServiceBindingRepo.BindReturns(errors.New("bind-err"))
   276  					})
   277  
   278  					It("fails with the error", func() {
   279  						Expect(runCLIErr).To(HaveOccurred())
   280  						Expect(runCLIErr.Error()).To(Equal("bind-err"))
   281  					})
   282  				})
   283  
   284  				Context("when the -f flag has been passed", func() {
   285  					BeforeEach(func() {
   286  						flagContext = flags.NewFlagContext(cmd.MetaData().Flags)
   287  					})
   288  
   289  					It("does not alter the behavior", func() {
   290  						err := flagContext.Parse("domain-name", "-f")
   291  						Expect(err).NotTo(HaveOccurred())
   292  
   293  						Expect(runCLIErr).NotTo(HaveOccurred())
   294  						Expect(ui.Outputs()).To(ContainSubstrings(
   295  							[]string{"OK"},
   296  						))
   297  					})
   298  				})
   299  			})
   300  
   301  			Context("when the service instance does not require route forwarding", func() {
   302  				BeforeEach(func() {
   303  					serviceInstance := models.ServiceInstance{
   304  						ServiceOffering: models.ServiceOfferingFields{
   305  							Requires: []string{""},
   306  						},
   307  					}
   308  					serviceInstanceRequirement.GetServiceInstanceReturns(serviceInstance)
   309  				})
   310  
   311  				It("does not ask the user to confirm", func() {
   312  					Expect(runCLIErr).NotTo(HaveOccurred())
   313  					Expect(ui.Prompts).NotTo(ContainSubstrings(
   314  						[]string{"Binding may cause requests for route", "Do you want to proceed?"},
   315  					))
   316  				})
   317  
   318  				It("tells the user it is binding the route service", func() {
   319  					Expect(runCLIErr).NotTo(HaveOccurred())
   320  					Expect(ui.Outputs()).To(ContainSubstrings(
   321  						[]string{"Binding route", "to service instance"},
   322  					))
   323  				})
   324  
   325  				It("tries to bind the route service", func() {
   326  					Expect(runCLIErr).NotTo(HaveOccurred())
   327  					Expect(routeServiceBindingRepo.BindCallCount()).To(Equal(1))
   328  				})
   329  
   330  				Context("when binding the route service succeeds", func() {
   331  					BeforeEach(func() {
   332  						routeServiceBindingRepo.BindReturns(nil)
   333  					})
   334  
   335  					It("says OK", func() {
   336  						Expect(runCLIErr).NotTo(HaveOccurred())
   337  						Expect(ui.Outputs()).To(ContainSubstrings(
   338  							[]string{"OK"},
   339  						))
   340  					})
   341  				})
   342  
   343  				Context("when binding the route service fails because it is already bound", func() {
   344  					BeforeEach(func() {
   345  						routeServiceBindingRepo.BindReturns(errors.NewHTTPError(http.StatusOK, errors.ServiceInstanceAlreadyBoundToSameRoute, "http-err"))
   346  					})
   347  
   348  					It("says OK", func() {
   349  						Expect(runCLIErr).NotTo(HaveOccurred())
   350  						Expect(ui.Outputs()).To(ContainSubstrings(
   351  							[]string{"OK"},
   352  						))
   353  					})
   354  				})
   355  
   356  				Context("when binding the route service fails for any other reason", func() {
   357  					BeforeEach(func() {
   358  						routeServiceBindingRepo.BindReturns(errors.New("bind-err"))
   359  					})
   360  
   361  					It("fails with the error", func() {
   362  						Expect(runCLIErr).To(HaveOccurred())
   363  						Expect(runCLIErr.Error()).To(Equal("bind-err"))
   364  					})
   365  				})
   366  			})
   367  
   368  			Context("when the service instance is user-provided", func() {
   369  				BeforeEach(func() {
   370  					serviceInstance := models.ServiceInstance{}
   371  					serviceInstance.GUID = "service-instance-guid"
   372  					serviceInstance.ServicePlan = models.ServicePlanFields{
   373  						GUID: "",
   374  					}
   375  					serviceInstanceRequirement.GetServiceInstanceReturns(serviceInstance)
   376  				})
   377  
   378  				It("does not ask the user to confirm", func() {
   379  					Expect(runCLIErr).NotTo(HaveOccurred())
   380  					Expect(ui.Prompts).NotTo(ContainSubstrings(
   381  						[]string{"Binding may cause requests for route", "Do you want to proceed?"},
   382  					))
   383  				})
   384  
   385  				It("tries to bind the route service", func() {
   386  					Expect(runCLIErr).NotTo(HaveOccurred())
   387  					Expect(routeServiceBindingRepo.BindCallCount()).To(Equal(1))
   388  					serviceInstanceGUID, routeGUID, isUserProvided, parameters := routeServiceBindingRepo.BindArgsForCall(0)
   389  					Expect(serviceInstanceGUID).To(Equal("service-instance-guid"))
   390  					Expect(routeGUID).To(Equal("route-guid"))
   391  					Expect(isUserProvided).To(BeTrue())
   392  					Expect(parameters).To(Equal(""))
   393  				})
   394  
   395  				Context("when given parameters as JSON", func() {
   396  					BeforeEach(func() {
   397  						flagContext = flags.NewFlagContext(cmd.MetaData().Flags)
   398  						err := flagContext.Parse("domain-name", "service-instance", "-c", `"{"some":"json"}"`)
   399  						Expect(err).NotTo(HaveOccurred())
   400  					})
   401  
   402  					It("tries to find the route with the given parameters", func() {
   403  						Expect(runCLIErr).NotTo(HaveOccurred())
   404  						Expect(routeRepo.FindCallCount()).To(Equal(1))
   405  						_, _, _, parameters := routeServiceBindingRepo.BindArgsForCall(0)
   406  						Expect(parameters).To(Equal(`{"some":"json"}`))
   407  					})
   408  				})
   409  
   410  				Context("when given parameters as a file containing JSON", func() {
   411  					var filename string
   412  					BeforeEach(func() {
   413  						flagContext = flags.NewFlagContext(cmd.MetaData().Flags)
   414  						tempfile, err := ioutil.TempFile("", "get-data-test")
   415  						Expect(err).NotTo(HaveOccurred())
   416  						Expect(tempfile.Close()).NotTo(HaveOccurred())
   417  						filename = tempfile.Name()
   418  
   419  						jsonData := `{"some":"json"}`
   420  						ioutil.WriteFile(filename, []byte(jsonData), os.ModePerm)
   421  						err = flagContext.Parse("domain-name", "service-instance", "-c", filename)
   422  						Expect(err).NotTo(HaveOccurred())
   423  					})
   424  
   425  					AfterEach(func() {
   426  						os.RemoveAll(filename)
   427  					})
   428  
   429  					It("tries to find the route with the given parameters", func() {
   430  						Expect(runCLIErr).NotTo(HaveOccurred())
   431  						Expect(routeRepo.FindCallCount()).To(Equal(1))
   432  						_, _, _, parameters := routeServiceBindingRepo.BindArgsForCall(0)
   433  						Expect(parameters).To(Equal(`{"some":"json"}`))
   434  					})
   435  				})
   436  
   437  				Context("when binding the route service succeeds", func() {
   438  					BeforeEach(func() {
   439  						routeServiceBindingRepo.BindReturns(nil)
   440  					})
   441  
   442  					It("says OK", func() {
   443  						Expect(runCLIErr).NotTo(HaveOccurred())
   444  						Expect(ui.Outputs()).To(ContainSubstrings(
   445  							[]string{"OK"},
   446  						))
   447  					})
   448  				})
   449  
   450  				Context("when binding the route service fails because it is already bound", func() {
   451  					BeforeEach(func() {
   452  						routeServiceBindingRepo.BindReturns(errors.NewHTTPError(http.StatusOK, errors.ServiceInstanceAlreadyBoundToSameRoute, "http-err"))
   453  					})
   454  
   455  					It("says OK", func() {
   456  						Expect(runCLIErr).NotTo(HaveOccurred())
   457  						Expect(ui.Outputs()).To(ContainSubstrings(
   458  							[]string{"OK"},
   459  						))
   460  					})
   461  				})
   462  
   463  				Context("when binding the route service fails for any other reason", func() {
   464  					BeforeEach(func() {
   465  						routeServiceBindingRepo.BindReturns(errors.New("bind-err"))
   466  					})
   467  
   468  					It("fails with the error", func() {
   469  						Expect(runCLIErr).To(HaveOccurred())
   470  						Expect(runCLIErr.Error()).To(Equal("bind-err"))
   471  					})
   472  				})
   473  			})
   474  		})
   475  
   476  		Context("when finding the route results in an error", func() {
   477  			BeforeEach(func() {
   478  				routeRepo.FindReturns(models.Route{GUID: "route-guid"}, errors.New("find-err"))
   479  			})
   480  
   481  			It("fails with error", func() {
   482  				Expect(runCLIErr).To(HaveOccurred())
   483  				Expect(runCLIErr.Error()).To(Equal("find-err"))
   484  			})
   485  		})
   486  	})
   487  })