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 })