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