github.com/jenspinney/cli@v6.42.1-0.20190207184520-7450c600020e+incompatible/cf/commands/service/update_service_test.go (about) 1 package service_test 2 3 import ( 4 "errors" 5 "io/ioutil" 6 "os" 7 8 planbuilderfakes "code.cloudfoundry.org/cli/cf/actors/planbuilder/planbuilderfakes" 9 "code.cloudfoundry.org/cli/cf/api/apifakes" 10 "code.cloudfoundry.org/cli/cf/commandregistry" 11 "code.cloudfoundry.org/cli/cf/configuration/coreconfig" 12 "code.cloudfoundry.org/cli/cf/models" 13 "code.cloudfoundry.org/cli/cf/requirements" 14 "code.cloudfoundry.org/cli/cf/requirements/requirementsfakes" 15 testcmd "code.cloudfoundry.org/cli/cf/util/testhelpers/commands" 16 testconfig "code.cloudfoundry.org/cli/cf/util/testhelpers/configuration" 17 testterm "code.cloudfoundry.org/cli/cf/util/testhelpers/terminal" 18 . "github.com/onsi/ginkgo" 19 . "github.com/onsi/gomega" 20 21 . "code.cloudfoundry.org/cli/cf/util/testhelpers/matchers" 22 ) 23 24 var _ = Describe("update-service command", func() { 25 var ( 26 ui *testterm.FakeUI 27 config coreconfig.Repository 28 requirementsFactory *requirementsfakes.FakeFactory 29 serviceRepo *apifakes.FakeServiceRepository 30 planBuilder *planbuilderfakes.FakePlanBuilder 31 offering1 models.ServiceOffering 32 deps commandregistry.Dependency 33 ) 34 35 updateCommandDependency := func(pluginCall bool) { 36 deps.UI = ui 37 deps.RepoLocator = deps.RepoLocator.SetServiceRepository(serviceRepo) 38 deps.Config = config 39 deps.PlanBuilder = planBuilder 40 commandregistry.Commands.SetCommand(commandregistry.Commands.FindCommand("update-service").SetDependency(deps, pluginCall)) 41 } 42 43 BeforeEach(func() { 44 ui = &testterm.FakeUI{} 45 46 config = testconfig.NewRepositoryWithDefaults() 47 48 requirementsFactory = new(requirementsfakes.FakeFactory) 49 requirementsFactory.NewLoginRequirementReturns(requirements.Passing{}) 50 requirementsFactory.NewTargetedSpaceRequirementReturns(requirements.Passing{}) 51 requirementsFactory.NewMinAPIVersionRequirementReturns(requirements.Passing{Type: "minAPIVersionReq"}) 52 53 serviceRepo = new(apifakes.FakeServiceRepository) 54 planBuilder = new(planbuilderfakes.FakePlanBuilder) 55 56 offering1 = models.ServiceOffering{} 57 offering1.Label = "cleardb" 58 offering1.Plans = []models.ServicePlanFields{{ 59 Name: "spark", 60 GUID: "cleardb-spark-guid", 61 }, { 62 Name: "flare", 63 GUID: "cleardb-flare-guid", 64 }, 65 } 66 67 }) 68 69 var callUpdateService = func(args []string) bool { 70 return testcmd.RunCLICommand("update-service", args, requirementsFactory, updateCommandDependency, false, ui) 71 } 72 73 Describe("requirements", func() { 74 It("passes when logged in and a space is targeted", func() { 75 Expect(callUpdateService([]string{"cleardb"})).To(BeTrue()) 76 }) 77 78 It("fails with usage when not provided exactly one arg", func() { 79 Expect(callUpdateService([]string{})).To(BeFalse()) 80 }) 81 82 It("fails when not logged in", func() { 83 requirementsFactory.NewLoginRequirementReturns(requirements.Failing{Message: "not logged in"}) 84 Expect(callUpdateService([]string{"cleardb", "spark", "my-cleardb-service"})).To(BeFalse()) 85 }) 86 87 It("fails when a space is not targeted", func() { 88 requirementsFactory.NewTargetedSpaceRequirementReturns(requirements.Failing{Message: "not targeting space"}) 89 Expect(callUpdateService([]string{"cleardb", "spark", "my-cleardb-service"})).To(BeFalse()) 90 }) 91 }) 92 93 Context("when no flags are passed", func() { 94 95 Context("when the instance exists", func() { 96 It("prints a user indicating it is a no-op", func() { 97 callUpdateService([]string{"my-service"}) 98 99 Expect(ui.Outputs()).To(ContainSubstrings( 100 []string{"OK"}, 101 []string{"No changes were made"}, 102 )) 103 }) 104 }) 105 }) 106 107 Context("when passing arbitrary params", func() { 108 BeforeEach(func() { 109 serviceInstance := models.ServiceInstance{ 110 ServiceInstanceFields: models.ServiceInstanceFields{ 111 Name: "my-service-instance", 112 GUID: "my-service-instance-guid", 113 LastOperation: models.LastOperationFields{ 114 Type: "update", 115 State: "in progress", 116 Description: "fake service instance description", 117 }, 118 }, 119 ServiceOffering: models.ServiceOfferingFields{ 120 Label: "murkydb", 121 GUID: "murkydb-guid", 122 }, 123 } 124 125 servicePlans := []models.ServicePlanFields{{ 126 Name: "spark", 127 GUID: "murkydb-spark-guid", 128 }, { 129 Name: "flare", 130 GUID: "murkydb-flare-guid", 131 }, 132 } 133 serviceRepo.FindInstanceByNameReturns(serviceInstance, nil) 134 planBuilder.GetPlansForServiceForOrgReturns(servicePlans, nil) 135 }) 136 137 Context("as a json string", func() { 138 It("successfully updates a service", func() { 139 callUpdateService([]string{"-p", "flare", "-c", `{"foo": "bar"}`, "my-service-instance"}) 140 141 Expect(ui.Outputs()).To(ContainSubstrings( 142 []string{"Updating service", "my-service", "as", "my-user", "..."}, 143 []string{"OK"}, 144 []string{"Update in progress. Use 'cf services' or 'cf service my-service-instance' to check operation status."}, 145 )) 146 Expect(serviceRepo.FindInstanceByNameArgsForCall(0)).To(Equal("my-service-instance")) 147 148 instanceGUID, planGUID, params, _ := serviceRepo.UpdateServiceInstanceArgsForCall(0) 149 Expect(instanceGUID).To(Equal("my-service-instance-guid")) 150 Expect(planGUID).To(Equal("murkydb-flare-guid")) 151 Expect(params).To(Equal(map[string]interface{}{"foo": "bar"})) 152 }) 153 154 Context("that are not valid json", func() { 155 It("returns an error to the UI", func() { 156 callUpdateService([]string{"-p", "flare", "-c", `bad-json`, "my-service-instance"}) 157 158 Expect(ui.Outputs()).To(ContainSubstrings( 159 []string{"FAILED"}, 160 []string{"Invalid configuration provided for -c flag. Please provide a valid JSON object or path to a file containing a valid JSON object."}, 161 )) 162 }) 163 }) 164 }) 165 166 Context("as a file that contains json", func() { 167 var jsonFile *os.File 168 var params string 169 170 BeforeEach(func() { 171 params = "{\"foo\": \"bar\"}" 172 }) 173 174 AfterEach(func() { 175 if jsonFile != nil { 176 jsonFile.Close() 177 os.Remove(jsonFile.Name()) 178 } 179 }) 180 181 JustBeforeEach(func() { 182 var err error 183 jsonFile, err = ioutil.TempFile("", "") 184 Expect(err).ToNot(HaveOccurred()) 185 186 err = ioutil.WriteFile(jsonFile.Name(), []byte(params), os.ModePerm) 187 Expect(err).NotTo(HaveOccurred()) 188 }) 189 190 It("successfully updates a service and passes the params as a json", func() { 191 callUpdateService([]string{"-p", "flare", "-c", jsonFile.Name(), "my-service-instance"}) 192 193 Expect(ui.Outputs()).To(ContainSubstrings( 194 []string{"Updating service", "my-service", "as", "my-user", "..."}, 195 []string{"OK"}, 196 []string{"Update in progress. Use 'cf services' or 'cf service my-service-instance' to check operation status."}, 197 )) 198 199 Expect(serviceRepo.FindInstanceByNameArgsForCall(0)).To(Equal("my-service-instance")) 200 201 instanceGUID, planGUID, params, _ := serviceRepo.UpdateServiceInstanceArgsForCall(0) 202 Expect(instanceGUID).To(Equal("my-service-instance-guid")) 203 Expect(planGUID).To(Equal("murkydb-flare-guid")) 204 Expect(params).To(Equal(map[string]interface{}{"foo": "bar"})) 205 }) 206 207 Context("that are not valid json", func() { 208 BeforeEach(func() { 209 params = "bad-json" 210 }) 211 212 It("returns an error to the UI", func() { 213 callUpdateService([]string{"-p", "flare", "-c", jsonFile.Name(), "my-service-instance"}) 214 215 Expect(ui.Outputs()).To(ContainSubstrings( 216 []string{"FAILED"}, 217 []string{"Invalid configuration provided for -c flag. Please provide a valid JSON object or path to a file containing a valid JSON object."}, 218 )) 219 }) 220 }) 221 }) 222 }) 223 224 Context("when passing in tags", func() { 225 It("successfully updates a service and passes the tags as json", func() { 226 callUpdateService([]string{"-t", "tag1, tag2,tag3, tag4", "my-service-instance"}) 227 228 Expect(ui.Outputs()).To(ContainSubstrings( 229 []string{"Updating service instance", "my-service-instance"}, 230 []string{"OK"}, 231 )) 232 _, _, _, tags := serviceRepo.UpdateServiceInstanceArgsForCall(0) 233 Expect(tags).To(ConsistOf("tag1", "tag2", "tag3", "tag4")) 234 }) 235 236 It("successfully updates a service and passes the tags as json", func() { 237 callUpdateService([]string{"-t", "tag1", "my-service-instance"}) 238 239 Expect(ui.Outputs()).To(ContainSubstrings( 240 []string{"Updating service instance", "my-service-instance"}, 241 []string{"OK"}, 242 )) 243 _, _, _, tags := serviceRepo.UpdateServiceInstanceArgsForCall(0) 244 Expect(tags).To(ConsistOf("tag1")) 245 }) 246 247 Context("and the tags string is passed with an empty string", func() { 248 It("successfully updates the service", func() { 249 callUpdateService([]string{"-t", "", "my-service-instance"}) 250 251 Expect(ui.Outputs()).To(ContainSubstrings( 252 []string{"Updating service instance", "my-service-instance"}, 253 []string{"OK"}, 254 )) 255 _, _, _, tags := serviceRepo.UpdateServiceInstanceArgsForCall(0) 256 Expect(tags).To(Equal([]string{})) 257 }) 258 }) 259 }) 260 261 Context("when service update is asynchronous", func() { 262 Context("when the plan flag is passed", func() { 263 BeforeEach(func() { 264 serviceInstance := models.ServiceInstance{ 265 ServiceInstanceFields: models.ServiceInstanceFields{ 266 Name: "my-service-instance", 267 GUID: "my-service-instance-guid", 268 LastOperation: models.LastOperationFields{ 269 Type: "update", 270 State: "in progress", 271 Description: "fake service instance description", 272 }, 273 }, 274 ServiceOffering: models.ServiceOfferingFields{ 275 Label: "murkydb", 276 GUID: "murkydb-guid", 277 }, 278 } 279 280 servicePlans := []models.ServicePlanFields{{ 281 Name: "spark", 282 GUID: "murkydb-spark-guid", 283 }, { 284 Name: "flare", 285 GUID: "murkydb-flare-guid", 286 }, 287 } 288 serviceRepo.FindInstanceByNameReturns(serviceInstance, nil) 289 planBuilder.GetPlansForServiceForOrgReturns(servicePlans, nil) 290 }) 291 292 It("successfully updates a service", func() { 293 callUpdateService([]string{"-p", "flare", "my-service-instance"}) 294 295 Expect(ui.Outputs()).To(ContainSubstrings( 296 []string{"Updating service", "my-service", "as", "my-user", "..."}, 297 []string{"OK"}, 298 []string{"Update in progress. Use 'cf services' or 'cf service my-service-instance' to check operation status."}, 299 )) 300 301 Expect(serviceRepo.FindInstanceByNameArgsForCall(0)).To(Equal("my-service-instance")) 302 303 instanceGUID, planGUID, _, _ := serviceRepo.UpdateServiceInstanceArgsForCall(0) 304 Expect(instanceGUID).To(Equal("my-service-instance-guid")) 305 Expect(planGUID).To(Equal("murkydb-flare-guid")) 306 }) 307 308 It("successfully updates a service", func() { 309 callUpdateService([]string{"-p", "flare", "my-service-instance"}) 310 311 Expect(ui.Outputs()).To(ContainSubstrings( 312 []string{"Updating service", "my-service", "as", "my-user", "..."}, 313 []string{"OK"}, 314 []string{"Update in progress. Use 'cf services' or 'cf service my-service-instance' to check operation status."}, 315 )) 316 317 Expect(serviceRepo.FindInstanceByNameArgsForCall(0)).To(Equal("my-service-instance")) 318 319 instanceGUID, planGUID, _, _ := serviceRepo.UpdateServiceInstanceArgsForCall(0) 320 Expect(instanceGUID).To(Equal("my-service-instance-guid")) 321 Expect(planGUID).To(Equal("murkydb-flare-guid")) 322 }) 323 324 Context("when there is an err finding the instance", func() { 325 It("returns an error", func() { 326 serviceRepo.FindInstanceByNameReturns(models.ServiceInstance{}, errors.New("Error finding instance")) 327 328 callUpdateService([]string{"-p", "flare", "some-stupid-not-real-instance"}) 329 330 Expect(ui.Outputs()).To(ContainSubstrings( 331 []string{"Error finding instance"}, 332 []string{"FAILED"}, 333 )) 334 }) 335 }) 336 Context("when there is an err finding service plans", func() { 337 It("returns an error", func() { 338 planBuilder.GetPlansForServiceForOrgReturns(nil, errors.New("Error fetching plans")) 339 340 callUpdateService([]string{"-p", "flare", "some-stupid-not-real-instance"}) 341 342 Expect(ui.Outputs()).To(ContainSubstrings( 343 []string{"Error fetching plans"}, 344 []string{"FAILED"}, 345 )) 346 }) 347 }) 348 Context("when the plan specified does not exist in the service offering", func() { 349 It("returns an error", func() { 350 callUpdateService([]string{"-p", "not-a-real-plan", "instance-without-service-offering"}) 351 352 Expect(ui.Outputs()).To(ContainSubstrings( 353 []string{"Plan does not exist for the murkydb service"}, 354 []string{"FAILED"}, 355 )) 356 }) 357 }) 358 Context("when there is an error updating the service instance", func() { 359 It("returns an error", func() { 360 serviceRepo.UpdateServiceInstanceReturns(errors.New("Error updating service instance")) 361 callUpdateService([]string{"-p", "flare", "my-service-instance"}) 362 363 Expect(ui.Outputs()).To(ContainSubstrings( 364 []string{"Error updating service instance"}, 365 []string{"FAILED"}, 366 )) 367 }) 368 }) 369 }) 370 }) 371 372 Context("when service update is synchronous", func() { 373 Context("when the plan flag is passed", func() { 374 BeforeEach(func() { 375 serviceInstance := models.ServiceInstance{ 376 ServiceInstanceFields: models.ServiceInstanceFields{ 377 Name: "my-service-instance", 378 GUID: "my-service-instance-guid", 379 }, 380 ServiceOffering: models.ServiceOfferingFields{ 381 Label: "murkydb", 382 GUID: "murkydb-guid", 383 }, 384 } 385 386 servicePlans := []models.ServicePlanFields{{ 387 Name: "spark", 388 GUID: "murkydb-spark-guid", 389 }, { 390 Name: "flare", 391 GUID: "murkydb-flare-guid", 392 }, 393 } 394 serviceRepo.FindInstanceByNameReturns(serviceInstance, nil) 395 planBuilder.GetPlansForServiceForOrgReturns(servicePlans, nil) 396 397 }) 398 It("successfully updates a service", func() { 399 callUpdateService([]string{"-p", "flare", "my-service-instance"}) 400 401 Expect(ui.Outputs()).To(ContainSubstrings( 402 []string{"Updating service", "my-service", "as", "my-user", "..."}, 403 []string{"OK"}, 404 )) 405 Expect(serviceRepo.FindInstanceByNameArgsForCall(0)).To(Equal("my-service-instance")) 406 serviceGUID, orgName := planBuilder.GetPlansForServiceForOrgArgsForCall(0) 407 Expect(serviceGUID).To(Equal("murkydb-guid")) 408 Expect(orgName).To(Equal("my-org")) 409 410 instanceGUID, planGUID, _, _ := serviceRepo.UpdateServiceInstanceArgsForCall(0) 411 Expect(instanceGUID).To(Equal("my-service-instance-guid")) 412 Expect(planGUID).To(Equal("murkydb-flare-guid")) 413 }) 414 415 Context("when there is an err finding the instance", func() { 416 It("returns an error", func() { 417 serviceRepo.FindInstanceByNameReturns(models.ServiceInstance{}, errors.New("Error finding instance")) 418 419 callUpdateService([]string{"-p", "flare", "some-stupid-not-real-instance"}) 420 421 Expect(ui.Outputs()).To(ContainSubstrings( 422 []string{"Error finding instance"}, 423 []string{"FAILED"}, 424 )) 425 }) 426 }) 427 Context("when there is an err finding service plans", func() { 428 It("returns an error", func() { 429 planBuilder.GetPlansForServiceForOrgReturns(nil, errors.New("Error fetching plans")) 430 431 callUpdateService([]string{"-p", "flare", "some-stupid-not-real-instance"}) 432 433 Expect(ui.Outputs()).To(ContainSubstrings( 434 []string{"Error fetching plans"}, 435 []string{"FAILED"}, 436 )) 437 }) 438 }) 439 Context("when the plan specified does not exist in the service offering", func() { 440 It("returns an error", func() { 441 callUpdateService([]string{"-p", "not-a-real-plan", "instance-without-service-offering"}) 442 443 Expect(ui.Outputs()).To(ContainSubstrings( 444 []string{"Plan does not exist for the murkydb service"}, 445 []string{"FAILED"}, 446 )) 447 }) 448 }) 449 Context("when there is an error updating the service instance", func() { 450 It("returns an error", func() { 451 serviceRepo.UpdateServiceInstanceReturns(errors.New("Error updating service instance")) 452 callUpdateService([]string{"-p", "flare", "my-service-instance"}) 453 454 Expect(ui.Outputs()).To(ContainSubstrings( 455 []string{"Error updating service instance"}, 456 []string{"FAILED"}, 457 )) 458 }) 459 }) 460 }) 461 462 }) 463 })