github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+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/util/testhelpers/configuration" 20 testterm "code.cloudfoundry.org/cli/util/testhelpers/terminal" 21 22 . "code.cloudfoundry.org/cli/util/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 var filename string 426 BeforeEach(func() { 427 flagContext = flags.NewFlagContext(cmd.MetaData().Flags) 428 tempfile, err := ioutil.TempFile("", "get-data-test") 429 Expect(err).NotTo(HaveOccurred()) 430 Expect(tempfile.Close()).NotTo(HaveOccurred()) 431 filename = tempfile.Name() 432 433 jsonData := `{"some":"json"}` 434 ioutil.WriteFile(filename, []byte(jsonData), os.ModePerm) 435 err = flagContext.Parse("domain-name", "service-instance", "-c", filename) 436 Expect(err).NotTo(HaveOccurred()) 437 }) 438 439 AfterEach(func() { 440 os.RemoveAll(filename) 441 }) 442 443 It("tries to find the route with the given parameters", func() { 444 Expect(runCLIErr).NotTo(HaveOccurred()) 445 Expect(routeRepo.FindCallCount()).To(Equal(1)) 446 _, _, _, parameters := routeServiceBindingRepo.BindArgsForCall(0) 447 Expect(parameters).To(Equal(`{"some":"json"}`)) 448 }) 449 }) 450 451 Context("when binding the route service succeeds", func() { 452 BeforeEach(func() { 453 routeServiceBindingRepo.BindReturns(nil) 454 }) 455 456 It("says OK", func() { 457 Expect(runCLIErr).NotTo(HaveOccurred()) 458 Expect(ui.Outputs()).To(ContainSubstrings( 459 []string{"OK"}, 460 )) 461 }) 462 }) 463 464 Context("when binding the route service fails because it is already bound", func() { 465 BeforeEach(func() { 466 routeServiceBindingRepo.BindReturns(errors.NewHTTPError(http.StatusOK, errors.ServiceInstanceAlreadyBoundToSameRoute, "http-err")) 467 }) 468 469 It("says OK", func() { 470 Expect(runCLIErr).NotTo(HaveOccurred()) 471 Expect(ui.Outputs()).To(ContainSubstrings( 472 []string{"OK"}, 473 )) 474 }) 475 }) 476 477 Context("when binding the route service fails for any other reason", func() { 478 BeforeEach(func() { 479 routeServiceBindingRepo.BindReturns(errors.New("bind-err")) 480 }) 481 482 It("fails with the error", func() { 483 Expect(runCLIErr).To(HaveOccurred()) 484 Expect(runCLIErr.Error()).To(Equal("bind-err")) 485 }) 486 }) 487 }) 488 }) 489 490 Context("when finding the route results in an error", func() { 491 BeforeEach(func() { 492 routeRepo.FindReturns(models.Route{GUID: "route-guid"}, errors.New("find-err")) 493 }) 494 495 It("fails with error", func() { 496 Expect(runCLIErr).To(HaveOccurred()) 497 Expect(runCLIErr.Error()).To(Equal("find-err")) 498 }) 499 }) 500 }) 501 })