github.com/DaAlbrecht/cf-cli@v0.0.0-20231128151943-1fe19bb400b9/actor/v7action/service_instance_test.go (about)

     1  package v7action_test
     2  
     3  import (
     4  	"errors"
     5  
     6  	"code.cloudfoundry.org/cli/actor/actionerror"
     7  	. "code.cloudfoundry.org/cli/actor/v7action"
     8  	"code.cloudfoundry.org/cli/actor/v7action/v7actionfakes"
     9  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
    10  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
    11  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
    12  	"code.cloudfoundry.org/cli/resources"
    13  	"code.cloudfoundry.org/cli/types"
    14  	. "github.com/onsi/ginkgo"
    15  	. "github.com/onsi/gomega"
    16  )
    17  
    18  var _ = Describe("Service Instance Actions", func() {
    19  	var (
    20  		actor                     *Actor
    21  		fakeCloudControllerClient *v7actionfakes.FakeCloudControllerClient
    22  	)
    23  
    24  	BeforeEach(func() {
    25  		fakeCloudControllerClient = new(v7actionfakes.FakeCloudControllerClient)
    26  		actor = NewActor(fakeCloudControllerClient, nil, nil, nil, nil, nil)
    27  	})
    28  
    29  	Describe("GetServiceInstanceByNameAndSpace", func() {
    30  		const (
    31  			serviceInstanceName = "some-service-instance"
    32  			spaceGUID           = "some-source-space-guid"
    33  		)
    34  
    35  		var (
    36  			serviceInstance resources.ServiceInstance
    37  			warnings        Warnings
    38  			executionError  error
    39  		)
    40  
    41  		JustBeforeEach(func() {
    42  			serviceInstance, warnings, executionError = actor.GetServiceInstanceByNameAndSpace(serviceInstanceName, spaceGUID)
    43  		})
    44  
    45  		When("the cloud controller request is successful", func() {
    46  			BeforeEach(func() {
    47  				fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(resources.ServiceInstance{
    48  					Name: "some-service-instance",
    49  					GUID: "some-service-instance-guid",
    50  				}, ccv3.IncludedResources{}, ccv3.Warnings{"some-service-instance-warning"}, nil)
    51  			})
    52  
    53  			It("returns a service instance and warnings", func() {
    54  				Expect(executionError).NotTo(HaveOccurred())
    55  
    56  				Expect(serviceInstance).To(Equal(resources.ServiceInstance{Name: "some-service-instance", GUID: "some-service-instance-guid"}))
    57  				Expect(warnings).To(ConsistOf("some-service-instance-warning"))
    58  				Expect(fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceCallCount()).To(Equal(1))
    59  				actualName, actualSpaceGUID, actualQuery := fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceArgsForCall(0)
    60  				Expect(actualName).To(Equal(serviceInstanceName))
    61  				Expect(actualSpaceGUID).To(Equal(spaceGUID))
    62  				Expect(actualQuery).To(BeEmpty())
    63  			})
    64  		})
    65  
    66  		When("the service instance cannot be found", func() {
    67  			BeforeEach(func() {
    68  				fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
    69  					resources.ServiceInstance{},
    70  					ccv3.IncludedResources{},
    71  					ccv3.Warnings{"some-service-instance-warning"},
    72  					ccerror.ServiceInstanceNotFoundError{Name: serviceInstanceName, SpaceGUID: spaceGUID},
    73  				)
    74  			})
    75  
    76  			It("returns an actor error and warnings", func() {
    77  				Expect(executionError).To(MatchError(actionerror.ServiceInstanceNotFoundError{Name: serviceInstanceName}))
    78  				Expect(warnings).To(ConsistOf("some-service-instance-warning"))
    79  			})
    80  		})
    81  
    82  		When("the cloud controller returns an error", func() {
    83  			BeforeEach(func() {
    84  				fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
    85  					resources.ServiceInstance{},
    86  					ccv3.IncludedResources{},
    87  					ccv3.Warnings{"some-service-instance-warning"},
    88  					errors.New("no service instance"),
    89  				)
    90  			})
    91  
    92  			It("returns an error and warnings", func() {
    93  				Expect(executionError).To(MatchError("no service instance"))
    94  				Expect(warnings).To(ConsistOf("some-service-instance-warning"))
    95  			})
    96  		})
    97  	})
    98  
    99  	Describe("CreateUserProvidedServiceInstance", func() {
   100  		When("the service instance is created successfully", func() {
   101  			It("returns warnings", func() {
   102  				fakeCloudControllerClient.CreateServiceInstanceReturns("", ccv3.Warnings{"fake-warning"}, nil)
   103  
   104  				warnings, err := actor.CreateUserProvidedServiceInstance(resources.ServiceInstance{
   105  					Name:            "fake-upsi-name",
   106  					SpaceGUID:       "fake-space-guid",
   107  					Tags:            types.NewOptionalStringSlice("foo", "bar"),
   108  					RouteServiceURL: types.NewOptionalString("https://fake-route.com"),
   109  					SyslogDrainURL:  types.NewOptionalString("https://fake-sylogg.com"),
   110  					Credentials: types.NewOptionalObject(map[string]interface{}{
   111  						"foo": "bar",
   112  						"baz": 42,
   113  					}),
   114  				})
   115  				Expect(warnings).To(ConsistOf("fake-warning"))
   116  				Expect(err).NotTo(HaveOccurred())
   117  
   118  				Expect(fakeCloudControllerClient.CreateServiceInstanceCallCount()).To(Equal(1))
   119  				Expect(fakeCloudControllerClient.CreateServiceInstanceArgsForCall(0)).To(Equal(resources.ServiceInstance{
   120  					Type:            "user-provided",
   121  					Name:            "fake-upsi-name",
   122  					SpaceGUID:       "fake-space-guid",
   123  					Tags:            types.NewOptionalStringSlice("foo", "bar"),
   124  					RouteServiceURL: types.NewOptionalString("https://fake-route.com"),
   125  					SyslogDrainURL:  types.NewOptionalString("https://fake-sylogg.com"),
   126  					Credentials: types.NewOptionalObject(map[string]interface{}{
   127  						"foo": "bar",
   128  						"baz": 42,
   129  					}),
   130  				}))
   131  			})
   132  		})
   133  
   134  		When("there is an error creating the service instance", func() {
   135  			It("returns warnings and an error", func() {
   136  				fakeCloudControllerClient.CreateServiceInstanceReturns("", ccv3.Warnings{"fake-warning"}, errors.New("bang"))
   137  
   138  				warnings, err := actor.CreateUserProvidedServiceInstance(resources.ServiceInstance{
   139  					Name:      "fake-upsi-name",
   140  					SpaceGUID: "fake-space-guid",
   141  				})
   142  				Expect(warnings).To(ConsistOf("fake-warning"))
   143  				Expect(err).To(MatchError("bang"))
   144  			})
   145  		})
   146  	})
   147  
   148  	Describe("UpdateUserProvidedServiceInstance", func() {
   149  		const (
   150  			serviceInstanceName = "fake-service-instance-name"
   151  			guid                = "fake-service-instance-guid"
   152  			spaceGUID           = "fake-space-guid"
   153  		)
   154  
   155  		When("the service instance is updated successfully", func() {
   156  			BeforeEach(func() {
   157  				fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
   158  					resources.ServiceInstance{
   159  						Type: resources.UserProvidedServiceInstance,
   160  						GUID: guid,
   161  					},
   162  					ccv3.IncludedResources{},
   163  					ccv3.Warnings{"warning from get"},
   164  					nil,
   165  				)
   166  				fakeCloudControllerClient.UpdateServiceInstanceReturns(
   167  					"",
   168  					ccv3.Warnings{"warning from update"},
   169  					nil,
   170  				)
   171  			})
   172  
   173  			It("makes the right calls and returns all warnings", func() {
   174  				warnings, err := actor.UpdateUserProvidedServiceInstance(
   175  					serviceInstanceName,
   176  					spaceGUID,
   177  					resources.ServiceInstance{
   178  						Tags:            types.NewOptionalStringSlice("foo", "bar"),
   179  						RouteServiceURL: types.NewOptionalString("https://fake-route.com"),
   180  						SyslogDrainURL:  types.NewOptionalString("https://fake-sylogg.com"),
   181  						Credentials: types.NewOptionalObject(map[string]interface{}{
   182  							"foo": "bar",
   183  							"baz": 42,
   184  						}),
   185  					},
   186  				)
   187  				Expect(warnings).To(ConsistOf("warning from get", "warning from update"))
   188  				Expect(err).NotTo(HaveOccurred())
   189  
   190  				Expect(fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceCallCount()).To(Equal(1))
   191  				actualName, actualSpaceGUID, actualQuery := fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceArgsForCall(0)
   192  				Expect(actualName).To(Equal(serviceInstanceName))
   193  				Expect(actualSpaceGUID).To(Equal(spaceGUID))
   194  				Expect(actualQuery).To(BeEmpty())
   195  
   196  				Expect(fakeCloudControllerClient.UpdateServiceInstanceCallCount()).To(Equal(1))
   197  				actualGUID, actualServiceInstance := fakeCloudControllerClient.UpdateServiceInstanceArgsForCall(0)
   198  				Expect(actualGUID).To(Equal(guid))
   199  				Expect(actualServiceInstance).To(Equal(resources.ServiceInstance{
   200  					Tags:            types.NewOptionalStringSlice("foo", "bar"),
   201  					RouteServiceURL: types.NewOptionalString("https://fake-route.com"),
   202  					SyslogDrainURL:  types.NewOptionalString("https://fake-sylogg.com"),
   203  					Credentials: types.NewOptionalObject(map[string]interface{}{
   204  						"foo": "bar",
   205  						"baz": 42,
   206  					}),
   207  				}))
   208  			})
   209  		})
   210  
   211  		When("the service instance is not user-provided", func() {
   212  			BeforeEach(func() {
   213  				fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
   214  					resources.ServiceInstance{
   215  						Type: resources.ManagedServiceInstance,
   216  						Name: serviceInstanceName,
   217  						GUID: guid,
   218  					},
   219  					ccv3.IncludedResources{},
   220  					ccv3.Warnings{"warning from get"},
   221  					nil,
   222  				)
   223  			})
   224  
   225  			It("fails with warnings", func() {
   226  				warnings, err := actor.UpdateUserProvidedServiceInstance(
   227  					serviceInstanceName,
   228  					spaceGUID,
   229  					resources.ServiceInstance{
   230  						Tags:            types.NewOptionalStringSlice("foo", "bar"),
   231  						RouteServiceURL: types.NewOptionalString("https://fake-route.com"),
   232  						SyslogDrainURL:  types.NewOptionalString("https://fake-sylogg.com"),
   233  						Credentials: types.NewOptionalObject(map[string]interface{}{
   234  							"foo": "bar",
   235  							"baz": 42,
   236  						}),
   237  					},
   238  				)
   239  				Expect(warnings).To(ConsistOf("warning from get"))
   240  
   241  				Expect(err).To(MatchError(actionerror.ServiceInstanceTypeError{
   242  					Name:         serviceInstanceName,
   243  					RequiredType: resources.UserProvidedServiceInstance,
   244  				}))
   245  			})
   246  		})
   247  
   248  		When("there is an error getting the service instance", func() {
   249  			BeforeEach(func() {
   250  				fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
   251  					resources.ServiceInstance{},
   252  					ccv3.IncludedResources{},
   253  					ccv3.Warnings{"warning from get"},
   254  					errors.New("bang"),
   255  				)
   256  			})
   257  
   258  			It("returns warnings and an error", func() {
   259  				warnings, err := actor.UpdateUserProvidedServiceInstance(
   260  					serviceInstanceName,
   261  					spaceGUID,
   262  					resources.ServiceInstance{
   263  						Tags:            types.NewOptionalStringSlice("foo", "bar"),
   264  						RouteServiceURL: types.NewOptionalString("https://fake-route.com"),
   265  						SyslogDrainURL:  types.NewOptionalString("https://fake-sylogg.com"),
   266  						Credentials: types.NewOptionalObject(map[string]interface{}{
   267  							"foo": "bar",
   268  							"baz": 42,
   269  						}),
   270  					},
   271  				)
   272  				Expect(warnings).To(ConsistOf("warning from get"))
   273  				Expect(err).To(MatchError("bang"))
   274  			})
   275  		})
   276  
   277  		When("there is an error updating the service instance", func() {
   278  			BeforeEach(func() {
   279  				fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
   280  					resources.ServiceInstance{
   281  						Type: resources.UserProvidedServiceInstance,
   282  						GUID: guid,
   283  					},
   284  					ccv3.IncludedResources{},
   285  					ccv3.Warnings{"warning from get"},
   286  					nil,
   287  				)
   288  				fakeCloudControllerClient.UpdateServiceInstanceReturns(
   289  					"",
   290  					ccv3.Warnings{"warning from update"},
   291  					errors.New("boom"),
   292  				)
   293  			})
   294  
   295  			It("returns warnings and an error", func() {
   296  				warnings, err := actor.UpdateUserProvidedServiceInstance(
   297  					serviceInstanceName,
   298  					spaceGUID,
   299  					resources.ServiceInstance{
   300  						Tags:            types.NewOptionalStringSlice("foo", "bar"),
   301  						RouteServiceURL: types.NewOptionalString("https://fake-route.com"),
   302  						SyslogDrainURL:  types.NewOptionalString("https://fake-sylogg.com"),
   303  						Credentials: types.NewOptionalObject(map[string]interface{}{
   304  							"foo": "bar",
   305  							"baz": 42,
   306  						}),
   307  					},
   308  				)
   309  				Expect(warnings).To(ConsistOf("warning from get", "warning from update"))
   310  				Expect(err).To(MatchError("boom"))
   311  			})
   312  		})
   313  	})
   314  
   315  	Describe("UpdateManagedServiceInstance", func() {
   316  		const (
   317  			serviceInstanceName = "fake-service-instance-name"
   318  			serviceInstanceGUID = "fake-service-instance-guid"
   319  			servicePlanGUID     = "fake-service-plan-guid"
   320  			serviceOfferingName = "fake-service-offering-name"
   321  			serviceOfferingGUID = "fake-service-offering-guid"
   322  			serviceBrokerName   = "fake-service-broker-name"
   323  			spaceGUID           = "fake-space-guid"
   324  			newServicePlanGUID  = "fake-new-service-plan-guid"
   325  			newServicePlanName  = "fake-new-service-plan-name"
   326  			fakeJobURL          = ccv3.JobURL("fake-job-url")
   327  		)
   328  
   329  		var (
   330  			warnings      Warnings
   331  			executeErr    error
   332  			stream        chan PollJobEvent
   333  			params        UpdateManagedServiceInstanceParams
   334  			newTags       types.OptionalStringSlice
   335  			newParameters types.OptionalObject
   336  		)
   337  
   338  		BeforeEach(func() {
   339  			newTags = types.NewOptionalStringSlice("foo")
   340  			newParameters = types.NewOptionalObject(map[string]interface{}{"foo": "bar"})
   341  			params = UpdateManagedServiceInstanceParams{
   342  				ServiceInstanceName: serviceInstanceName,
   343  				SpaceGUID:           spaceGUID,
   344  				ServicePlanName:     newServicePlanName,
   345  				Tags:                newTags,
   346  				Parameters:          newParameters,
   347  			}
   348  
   349  			fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
   350  				resources.ServiceInstance{
   351  					Type:            resources.ManagedServiceInstance,
   352  					GUID:            serviceInstanceGUID,
   353  					Name:            serviceInstanceName,
   354  					ServicePlanGUID: servicePlanGUID,
   355  				},
   356  				ccv3.IncludedResources{
   357  					ServiceBrokers: []resources.ServiceBroker{{
   358  						Name: serviceBrokerName,
   359  					}},
   360  					ServiceOfferings: []resources.ServiceOffering{{
   361  						Name: serviceOfferingName,
   362  						GUID: serviceOfferingGUID,
   363  					}},
   364  				},
   365  				ccv3.Warnings{"fake get service instance warning"},
   366  				nil,
   367  			)
   368  
   369  			fakeCloudControllerClient.GetServicePlansReturns(
   370  				[]resources.ServicePlan{{
   371  					GUID: newServicePlanGUID,
   372  					Name: newServicePlanName,
   373  				}},
   374  				ccv3.Warnings{"fake get service plan warning"},
   375  				nil,
   376  			)
   377  
   378  			fakeCloudControllerClient.UpdateServiceInstanceReturns(
   379  				fakeJobURL,
   380  				ccv3.Warnings{"fake update service instance warning"},
   381  				nil,
   382  			)
   383  
   384  			fakeStream := make(chan ccv3.PollJobEvent)
   385  			go func() {
   386  				fakeStream <- ccv3.PollJobEvent{
   387  					State:    constant.JobProcessing,
   388  					Warnings: ccv3.Warnings{"stream warning"},
   389  				}
   390  			}()
   391  			fakeCloudControllerClient.PollJobToEventStreamReturns(fakeStream)
   392  		})
   393  
   394  		JustBeforeEach(func() {
   395  			stream, warnings, executeErr = actor.UpdateManagedServiceInstance(params)
   396  		})
   397  
   398  		It("returns a stream, warnings, and no error", func() {
   399  			Expect(executeErr).NotTo(HaveOccurred())
   400  			Expect(warnings).To(ConsistOf(
   401  				"fake get service instance warning",
   402  				"fake get service plan warning",
   403  				"fake update service instance warning",
   404  			))
   405  			Eventually(stream).Should(Receive(Equal(PollJobEvent{
   406  				State:    JobProcessing,
   407  				Warnings: Warnings{"stream warning"},
   408  			})))
   409  		})
   410  
   411  		Describe("getting the service instance", func() {
   412  			It("makes the right call", func() {
   413  				Expect(fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceCallCount()).To(Equal(1))
   414  				actualName, actualSpaceGUID, actualQuery := fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceArgsForCall(0)
   415  				Expect(actualName).To(Equal(serviceInstanceName))
   416  				Expect(actualSpaceGUID).To(Equal(spaceGUID))
   417  				Expect(actualQuery).To(ConsistOf(
   418  					[]ccv3.Query{
   419  						{
   420  							Key:    ccv3.FieldsServicePlanServiceOffering,
   421  							Values: []string{"name", "guid"},
   422  						},
   423  						{
   424  							Key:    ccv3.FieldsServicePlanServiceOfferingServiceBroker,
   425  							Values: []string{"name"},
   426  						},
   427  					},
   428  				))
   429  			})
   430  
   431  			When("not updating the plan", func() {
   432  				BeforeEach(func() {
   433  					params.ServicePlanName = ""
   434  				})
   435  
   436  				It("does not include the offering and broker", func() {
   437  					Expect(fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceCallCount()).To(Equal(1))
   438  					_, _, actualQuery := fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceArgsForCall(0)
   439  
   440  					Expect(actualQuery).To(BeEmpty())
   441  				})
   442  			})
   443  
   444  			When("not a managed service instance", func() {
   445  				BeforeEach(func() {
   446  					fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
   447  						resources.ServiceInstance{
   448  							Type: resources.UserProvidedServiceInstance,
   449  							Name: serviceInstanceName,
   450  							GUID: serviceInstanceGUID,
   451  						},
   452  						ccv3.IncludedResources{},
   453  						ccv3.Warnings{"warning from get"},
   454  						nil,
   455  					)
   456  				})
   457  
   458  				It("returns an error and warnings", func() {
   459  					Expect(warnings).To(ConsistOf("warning from get"))
   460  					Expect(executeErr).To(MatchError(actionerror.ServiceInstanceTypeError{
   461  						Name:         serviceInstanceName,
   462  						RequiredType: resources.ManagedServiceInstance,
   463  					}))
   464  				})
   465  			})
   466  
   467  			When("not found", func() {
   468  				BeforeEach(func() {
   469  					fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
   470  						resources.ServiceInstance{},
   471  						ccv3.IncludedResources{},
   472  						ccv3.Warnings{"warning from get"},
   473  						ccerror.ServiceInstanceNotFoundError{Name: serviceInstanceName},
   474  					)
   475  				})
   476  
   477  				It("returns warnings and an actionerror", func() {
   478  					Expect(warnings).To(ConsistOf("warning from get"))
   479  					Expect(executeErr).To(MatchError(actionerror.ServiceInstanceNotFoundError{
   480  						Name: serviceInstanceName,
   481  					}))
   482  				})
   483  			})
   484  
   485  			When("fails", func() {
   486  				BeforeEach(func() {
   487  					fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
   488  						resources.ServiceInstance{},
   489  						ccv3.IncludedResources{},
   490  						ccv3.Warnings{"warning from get"},
   491  						errors.New("bang"),
   492  					)
   493  				})
   494  
   495  				It("returns warnings and an error", func() {
   496  					Expect(warnings).To(ContainElement("warning from get"))
   497  					Expect(executeErr).To(MatchError("bang"))
   498  				})
   499  			})
   500  		})
   501  
   502  		Describe("checking the new plan", func() {
   503  			It("gets the plan", func() {
   504  				Expect(fakeCloudControllerClient.GetServicePlansCallCount()).To(Equal(1))
   505  				Expect(fakeCloudControllerClient.GetServicePlansArgsForCall(0)).To(ConsistOf(
   506  					ccv3.Query{Key: ccv3.ServiceOfferingGUIDsFilter, Values: []string{serviceOfferingGUID}},
   507  					ccv3.Query{Key: ccv3.NameFilter, Values: []string{newServicePlanName}},
   508  				))
   509  			})
   510  
   511  			When("no plan change requested", func() {
   512  				BeforeEach(func() {
   513  					params.ServicePlanName = ""
   514  				})
   515  
   516  				It("does not get the plan", func() {
   517  					Expect(fakeCloudControllerClient.GetServicePlansCallCount()).To(BeZero())
   518  				})
   519  			})
   520  
   521  			When("no plan found", func() {
   522  				BeforeEach(func() {
   523  					fakeCloudControllerClient.GetServicePlansReturns(
   524  						[]resources.ServicePlan{},
   525  						ccv3.Warnings{"fake get service plan warning"},
   526  						nil,
   527  					)
   528  				})
   529  
   530  				It("returns an error and warnings", func() {
   531  					Expect(executeErr).To(MatchError(actionerror.ServicePlanNotFoundError{
   532  						PlanName:          newServicePlanName,
   533  						OfferingName:      serviceOfferingName,
   534  						ServiceBrokerName: serviceBrokerName,
   535  					}))
   536  
   537  					Expect(warnings).To(ConsistOf(
   538  						"fake get service instance warning",
   539  						"fake get service plan warning",
   540  					))
   541  				})
   542  			})
   543  
   544  			When("fails", func() {
   545  				BeforeEach(func() {
   546  					fakeCloudControllerClient.GetServicePlansReturns(
   547  						[]resources.ServicePlan{},
   548  						ccv3.Warnings{"fake get service plan warning"},
   549  						errors.New("bang"),
   550  					)
   551  				})
   552  
   553  				It("returns the error and warnings", func() {
   554  					Expect(executeErr).To(MatchError("bang"))
   555  
   556  					Expect(warnings).To(ConsistOf(
   557  						"fake get service instance warning",
   558  						"fake get service plan warning",
   559  					))
   560  				})
   561  			})
   562  
   563  		})
   564  
   565  		Describe("detecting no-op updates", func() {
   566  			When("no updates are requested", func() {
   567  				BeforeEach(func() {
   568  					params.ServicePlanName = ""
   569  					params.Tags = types.OptionalStringSlice{}
   570  					params.Parameters = types.OptionalObject{}
   571  				})
   572  
   573  				It("returns a no-op error", func() {
   574  					Expect(executeErr).To(MatchError(actionerror.ServiceInstanceUpdateIsNoop{}))
   575  				})
   576  			})
   577  
   578  			When("the new plan is the same as the old plan", func() {
   579  				BeforeEach(func() {
   580  					fakeCloudControllerClient.GetServicePlansReturns(
   581  						[]resources.ServicePlan{{
   582  							GUID: servicePlanGUID,
   583  						}},
   584  						ccv3.Warnings{"fake get service plan warning"},
   585  						nil,
   586  					)
   587  				})
   588  
   589  				Context("and no other updates are requested", func() {
   590  					BeforeEach(func() {
   591  						params.Tags = types.OptionalStringSlice{}
   592  						params.Parameters = types.OptionalObject{}
   593  					})
   594  
   595  					It("returns a no-op error", func() {
   596  						Expect(executeErr).To(MatchError(actionerror.ServiceInstanceUpdateIsNoop{}))
   597  					})
   598  				})
   599  
   600  				Context("and a tag change is requested", func() {
   601  					BeforeEach(func() {
   602  						params.Parameters = types.OptionalObject{}
   603  					})
   604  
   605  					It("does not return a no-op error", func() {
   606  						Expect(executeErr).NotTo(HaveOccurred())
   607  					})
   608  				})
   609  
   610  				Context("and a parameter change is requested", func() {
   611  					BeforeEach(func() {
   612  						params.Tags = types.OptionalStringSlice{}
   613  					})
   614  
   615  					It("does not return a no-op error", func() {
   616  						Expect(executeErr).NotTo(HaveOccurred())
   617  					})
   618  				})
   619  			})
   620  		})
   621  
   622  		Describe("initiating the update", func() {
   623  			It("makes the right call", func() {
   624  				Expect(fakeCloudControllerClient.UpdateServiceInstanceCallCount()).To(Equal(1))
   625  				actualServiceInstanceGUID, actualUpdateResource := fakeCloudControllerClient.UpdateServiceInstanceArgsForCall(0)
   626  				Expect(actualServiceInstanceGUID).To(Equal(serviceInstanceGUID))
   627  				Expect(actualUpdateResource).To(Equal(resources.ServiceInstance{
   628  					ServicePlanGUID: newServicePlanGUID,
   629  					Tags:            newTags,
   630  					Parameters:      newParameters,
   631  				}))
   632  			})
   633  
   634  			When("just changing tags", func() {
   635  				BeforeEach(func() {
   636  					params.ServicePlanName = ""
   637  					params.Parameters = types.OptionalObject{}
   638  				})
   639  
   640  				It("makes the right call", func() {
   641  					Expect(fakeCloudControllerClient.UpdateServiceInstanceCallCount()).To(Equal(1))
   642  					actualServiceInstanceGUID, actualUpdateResource := fakeCloudControllerClient.UpdateServiceInstanceArgsForCall(0)
   643  					Expect(actualServiceInstanceGUID).To(Equal(serviceInstanceGUID))
   644  					Expect(actualUpdateResource).To(Equal(resources.ServiceInstance{
   645  						Tags: newTags,
   646  					}))
   647  				})
   648  			})
   649  
   650  			When("just changing parameters", func() {
   651  				BeforeEach(func() {
   652  					params.ServicePlanName = ""
   653  					params.Tags = types.OptionalStringSlice{}
   654  				})
   655  
   656  				It("makes the right call", func() {
   657  					Expect(fakeCloudControllerClient.UpdateServiceInstanceCallCount()).To(Equal(1))
   658  					actualServiceInstanceGUID, actualUpdateResource := fakeCloudControllerClient.UpdateServiceInstanceArgsForCall(0)
   659  					Expect(actualServiceInstanceGUID).To(Equal(serviceInstanceGUID))
   660  					Expect(actualUpdateResource).To(Equal(resources.ServiceInstance{
   661  						Parameters: newParameters,
   662  					}))
   663  				})
   664  
   665  				When("just changing plan", func() {
   666  
   667  				})
   668  			})
   669  
   670  			When("fails", func() {
   671  				BeforeEach(func() {
   672  					fakeCloudControllerClient.UpdateServiceInstanceReturns(
   673  						"",
   674  						ccv3.Warnings{"fake update service instance warning"},
   675  						errors.New("boom"),
   676  					)
   677  				})
   678  
   679  				It("returns the error and warnings", func() {
   680  					Expect(stream).To(BeNil())
   681  					Expect(warnings).To(ConsistOf(
   682  						"fake get service instance warning",
   683  						"fake get service plan warning",
   684  						"fake update service instance warning",
   685  					))
   686  					Expect(executeErr).To(MatchError("boom"))
   687  				})
   688  			})
   689  		})
   690  
   691  		Describe("polling the job", func() {
   692  			It("polls the job", func() {
   693  				Expect(fakeCloudControllerClient.PollJobToEventStreamCallCount()).To(Equal(1))
   694  				Expect(fakeCloudControllerClient.PollJobToEventStreamArgsForCall(0)).To(Equal(fakeJobURL))
   695  			})
   696  		})
   697  	})
   698  
   699  	Describe("RenameServiceInstance", func() {
   700  		const (
   701  			currentServiceInstanceName = "current-service-instance-name"
   702  			currentServiceInstanceGUID = "current-service-instance-guid"
   703  			newServiceInstanceName     = "new-service-instance-name"
   704  			spaceGUID                  = "some-source-space-guid"
   705  		)
   706  
   707  		var (
   708  			warnings       Warnings
   709  			executionError error
   710  		)
   711  
   712  		BeforeEach(func() {
   713  			fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
   714  				resources.ServiceInstance{
   715  					Name: currentServiceInstanceName,
   716  					GUID: currentServiceInstanceGUID,
   717  				},
   718  				ccv3.IncludedResources{},
   719  				ccv3.Warnings{"some-get-service-instance-warning"},
   720  				nil,
   721  			)
   722  
   723  			fakeCloudControllerClient.UpdateServiceInstanceReturns(
   724  				"",
   725  				ccv3.Warnings{"some-update-service-instance-warning"},
   726  				nil,
   727  			)
   728  		})
   729  
   730  		JustBeforeEach(func() {
   731  			warnings, executionError = actor.RenameServiceInstance(currentServiceInstanceName, spaceGUID, newServiceInstanceName)
   732  		})
   733  
   734  		It("gets the service instance", func() {
   735  			Expect(fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceCallCount()).To(Equal(1))
   736  			actualName, actualSpaceGUID, actualQuery := fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceArgsForCall(0)
   737  			Expect(actualName).To(Equal(currentServiceInstanceName))
   738  			Expect(actualSpaceGUID).To(Equal(spaceGUID))
   739  			Expect(actualQuery).To(BeEmpty())
   740  		})
   741  
   742  		It("updates the service instance", func() {
   743  			Expect(fakeCloudControllerClient.UpdateServiceInstanceCallCount()).To(Equal(1))
   744  			actualGUID, actualUpdates := fakeCloudControllerClient.UpdateServiceInstanceArgsForCall(0)
   745  			Expect(actualGUID).To(Equal(currentServiceInstanceGUID))
   746  			Expect(actualUpdates).To(Equal(resources.ServiceInstance{Name: newServiceInstanceName}))
   747  		})
   748  
   749  		It("returns warnings and no errors", func() {
   750  			Expect(executionError).NotTo(HaveOccurred())
   751  			Expect(warnings).To(ConsistOf("some-get-service-instance-warning", "some-update-service-instance-warning"))
   752  		})
   753  
   754  		When("the update is synchronous", func() {
   755  			It("does not wait on the job", func() {
   756  				Expect(fakeCloudControllerClient.PollJobCallCount()).To(Equal(0))
   757  			})
   758  		})
   759  
   760  		When("the update is asynchronous", func() {
   761  			const job = "fake-job-url"
   762  
   763  			BeforeEach(func() {
   764  				fakeCloudControllerClient.UpdateServiceInstanceReturns(
   765  					job,
   766  					ccv3.Warnings{"some-update-service-instance-warning"},
   767  					nil,
   768  				)
   769  
   770  				fakeCloudControllerClient.PollJobForStateReturns(
   771  					ccv3.Warnings{"some-poll-job-warning"},
   772  					nil,
   773  				)
   774  			})
   775  
   776  			It("waits on the job", func() {
   777  				Expect(fakeCloudControllerClient.PollJobForStateCallCount()).To(Equal(1))
   778  				actualURL, actualState := fakeCloudControllerClient.PollJobForStateArgsForCall(0)
   779  				Expect(actualURL).To(Equal(actualURL))
   780  				Expect(actualState).To(Equal(constant.JobPolling))
   781  				Expect(warnings).To(ContainElement("some-poll-job-warning"))
   782  			})
   783  
   784  			When("polling the job returns an error", func() {
   785  				BeforeEach(func() {
   786  					fakeCloudControllerClient.PollJobForStateReturns(
   787  						ccv3.Warnings{"some-poll-job-warning"},
   788  						errors.New("bad polling issue"),
   789  					)
   790  				})
   791  
   792  				It("returns an error and warnings", func() {
   793  					Expect(executionError).To(MatchError("bad polling issue"))
   794  					Expect(warnings).To(ContainElement("some-poll-job-warning"))
   795  				})
   796  			})
   797  		})
   798  
   799  		When("the service instance cannot be found", func() {
   800  			BeforeEach(func() {
   801  				fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
   802  					resources.ServiceInstance{},
   803  					ccv3.IncludedResources{},
   804  					ccv3.Warnings{"some-service-instance-warning"},
   805  					ccerror.ServiceInstanceNotFoundError{Name: currentServiceInstanceName, SpaceGUID: spaceGUID},
   806  				)
   807  			})
   808  
   809  			It("returns an actor error and warnings", func() {
   810  				Expect(executionError).To(MatchError(actionerror.ServiceInstanceNotFoundError{Name: currentServiceInstanceName}))
   811  				Expect(warnings).To(ConsistOf("some-service-instance-warning"))
   812  			})
   813  		})
   814  
   815  		When("getting the service instance returns an error", func() {
   816  			BeforeEach(func() {
   817  				fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
   818  					resources.ServiceInstance{},
   819  					ccv3.IncludedResources{},
   820  					ccv3.Warnings{"some-service-instance-warning"},
   821  					errors.New("no service instance"),
   822  				)
   823  			})
   824  
   825  			It("returns an error and warnings", func() {
   826  				Expect(executionError).To(MatchError("no service instance"))
   827  				Expect(warnings).To(ConsistOf("some-service-instance-warning"))
   828  			})
   829  
   830  			It("does not attempt to update the service instance", func() {
   831  				Expect(fakeCloudControllerClient.UpdateServiceInstanceCallCount()).To(Equal(0))
   832  			})
   833  		})
   834  
   835  		When("updating the service instance returns an error", func() {
   836  			BeforeEach(func() {
   837  				fakeCloudControllerClient.UpdateServiceInstanceReturns(
   838  					"",
   839  					ccv3.Warnings{"some-update-service-instance-warning"},
   840  					errors.New("something awful"),
   841  				)
   842  			})
   843  
   844  			It("returns an error and warnings", func() {
   845  				Expect(executionError).To(MatchError("something awful"))
   846  				Expect(warnings).To(ContainElement("some-update-service-instance-warning"))
   847  			})
   848  		})
   849  	})
   850  
   851  	Describe("CreateManagedServiceInstance", func() {
   852  		const (
   853  			fakeServiceOfferingName = "fake-offering-name"
   854  			fakeServicePlanName     = "fake-plan-name"
   855  			fakeServiceInstanceName = "fake-service-instance-name"
   856  			fakeSpaceGUID           = "fake-space-GUID"
   857  		)
   858  
   859  		var (
   860  			fakeServiceBrokerName string
   861  			fakeTags              types.OptionalStringSlice
   862  			warnings              Warnings
   863  			err                   error
   864  			stream                chan PollJobEvent
   865  			fakeJobURL            ccv3.JobURL
   866  			fakeParams            types.OptionalObject
   867  		)
   868  
   869  		BeforeEach(func() {
   870  			fakeServiceBrokerName = "fake-broker-name"
   871  			fakeTags = types.NewOptionalStringSlice("tag1", "tag2")
   872  			fakeJobURL = "http://some-cc-api/v3/jobs/job-guid"
   873  			fakeParams = types.NewOptionalObject(map[string]interface{}{"param1": "some-value", "param-2": "cool service"})
   874  
   875  			fakeCloudControllerClient.GetServicePlansReturns(
   876  				[]resources.ServicePlan{{GUID: "fake-plan-guid"}},
   877  				ccv3.Warnings{"plan-warning"},
   878  				nil,
   879  			)
   880  			fakeCloudControllerClient.CreateServiceInstanceReturns(fakeJobURL, ccv3.Warnings{"fake-warning"}, nil)
   881  
   882  			fakeStream := make(chan ccv3.PollJobEvent)
   883  			go func() {
   884  				fakeStream <- ccv3.PollJobEvent{
   885  					State:    constant.JobPolling,
   886  					Err:      errors.New("fake error"),
   887  					Warnings: ccv3.Warnings{"fake warning"},
   888  				}
   889  			}()
   890  			fakeCloudControllerClient.PollJobToEventStreamReturns(fakeStream)
   891  		})
   892  
   893  		JustBeforeEach(func() {
   894  			params := CreateManagedServiceInstanceParams{
   895  				ServiceOfferingName: fakeServiceOfferingName,
   896  				ServicePlanName:     fakeServicePlanName,
   897  				ServiceInstanceName: fakeServiceInstanceName,
   898  				ServiceBrokerName:   fakeServiceBrokerName,
   899  				SpaceGUID:           fakeSpaceGUID,
   900  				Tags:                fakeTags,
   901  				Parameters:          fakeParams,
   902  			}
   903  			stream, warnings, err = actor.CreateManagedServiceInstance(params)
   904  		})
   905  
   906  		It("gets the service plan", func() {
   907  			Expect(fakeCloudControllerClient.GetServicePlansCallCount()).To(Equal(1))
   908  			query := fakeCloudControllerClient.GetServicePlansArgsForCall(0)
   909  			Expect(query[0].Values).To(ConsistOf(fakeServicePlanName))
   910  			Expect(query[0].Key).To(Equal(ccv3.NameFilter))
   911  			Expect(query[1].Values).To(ConsistOf(fakeServiceBrokerName))
   912  			Expect(query[1].Key).To(Equal(ccv3.ServiceBrokerNamesFilter))
   913  			Expect(query[2].Values).To(ConsistOf(fakeServiceOfferingName))
   914  			Expect(query[2].Key).To(Equal(ccv3.ServiceOfferingNamesFilter))
   915  		})
   916  
   917  		It("calls the client to create the instance", func() {
   918  			Expect(fakeCloudControllerClient.CreateServiceInstanceCallCount()).To(Equal(1))
   919  			Expect(fakeCloudControllerClient.CreateServiceInstanceArgsForCall(0)).To(Equal(resources.ServiceInstance{
   920  				Type:            "managed",
   921  				Name:            fakeServiceInstanceName,
   922  				ServicePlanGUID: "fake-plan-guid",
   923  				SpaceGUID:       fakeSpaceGUID,
   924  				Tags:            fakeTags,
   925  				Parameters:      fakeParams,
   926  			}))
   927  		})
   928  
   929  		It("polls the job", func() {
   930  			Expect(fakeCloudControllerClient.PollJobToEventStreamCallCount()).To(Equal(1))
   931  			jobUrl := fakeCloudControllerClient.PollJobToEventStreamArgsForCall(0)
   932  			Expect(jobUrl).To(Equal(fakeJobURL))
   933  		})
   934  
   935  		It("returns an event stream and warnings", func() {
   936  			Expect(err).NotTo(HaveOccurred())
   937  			Expect(warnings).To(ConsistOf("plan-warning", "fake-warning"))
   938  			Eventually(stream).Should(Receive(Equal(PollJobEvent{
   939  				State:    JobPolling,
   940  				Err:      errors.New("fake error"),
   941  				Warnings: Warnings{"fake warning"},
   942  			})))
   943  		})
   944  
   945  		Context("error scenarios", func() {
   946  			When("no plan found", func() {
   947  				BeforeEach(func() {
   948  					fakeCloudControllerClient.GetServicePlansReturns([]resources.ServicePlan{}, ccv3.Warnings{"be warned"}, nil)
   949  				})
   950  
   951  				It("returns with warnings and an error", func() {
   952  					Expect(fakeCloudControllerClient.CreateServiceInstanceCallCount()).To(Equal(0))
   953  					Expect(fakeCloudControllerClient.PollJobForStateCallCount()).To(Equal(0))
   954  
   955  					Expect(warnings).To(ConsistOf("be warned"))
   956  					Expect(err).To(MatchError(actionerror.ServicePlanNotFoundError{PlanName: fakeServicePlanName, OfferingName: fakeServiceOfferingName, ServiceBrokerName: fakeServiceBrokerName}))
   957  					Expect(stream).To(BeNil())
   958  				})
   959  			})
   960  
   961  			When("more than one plan found", func() {
   962  				BeforeEach(func() {
   963  					fakeCloudControllerClient.GetServicePlansReturns(
   964  						[]resources.ServicePlan{{GUID: "a-guid"}, {GUID: "another-guid"}},
   965  						ccv3.Warnings{"be warned"},
   966  						nil,
   967  					)
   968  					fakeServiceBrokerName = ""
   969  				})
   970  
   971  				It("returns warnings and an error", func() {
   972  					Expect(fakeCloudControllerClient.CreateServiceInstanceCallCount()).To(Equal(0))
   973  					Expect(fakeCloudControllerClient.PollJobForStateCallCount()).To(Equal(0))
   974  
   975  					Expect(warnings).To(ConsistOf("be warned"))
   976  					Expect(err).To(MatchError(actionerror.ServiceBrokerNameRequiredError{
   977  						ServiceOfferingName: fakeServiceOfferingName,
   978  					}))
   979  					Expect(stream).To(BeNil())
   980  				})
   981  			})
   982  
   983  			When("client error when getting the plan", func() {
   984  				BeforeEach(func() {
   985  					fakeCloudControllerClient.GetServicePlansReturns([]resources.ServicePlan{}, ccv3.Warnings{"be warned"}, errors.New("boom"))
   986  					fakeServiceBrokerName = ""
   987  				})
   988  
   989  				It("returns warnings and an error", func() {
   990  					Expect(fakeCloudControllerClient.CreateServiceInstanceCallCount()).To(Equal(0))
   991  					Expect(fakeCloudControllerClient.PollJobForStateCallCount()).To(Equal(0))
   992  
   993  					Expect(warnings).To(ConsistOf("be warned"))
   994  					Expect(err).To(MatchError("boom"))
   995  					Expect(stream).To(BeNil())
   996  				})
   997  			})
   998  
   999  			When("there is an error creating the service instance", func() {
  1000  				BeforeEach(func() {
  1001  					fakeCloudControllerClient.CreateServiceInstanceReturns("", ccv3.Warnings{"fake-warning"}, errors.New("bang"))
  1002  				})
  1003  
  1004  				It("returns warnings and an error", func() {
  1005  					Expect(fakeCloudControllerClient.PollJobForStateCallCount()).To(Equal(0))
  1006  
  1007  					Expect(warnings).To(ConsistOf("plan-warning", "fake-warning"))
  1008  					Expect(err).To(MatchError("bang"))
  1009  					Expect(stream).To(BeNil())
  1010  				})
  1011  			})
  1012  		})
  1013  	})
  1014  
  1015  	Describe("DeleteServiceInstance", func() {
  1016  		const (
  1017  			fakeServiceInstanceName = "fake-service-instance-name"
  1018  			fakeSpaceGUID           = "fake-space-GUID"
  1019  		)
  1020  
  1021  		var (
  1022  			warnings Warnings
  1023  			err      error
  1024  			stream   chan PollJobEvent
  1025  		)
  1026  
  1027  		JustBeforeEach(func() {
  1028  			stream, warnings, err = actor.DeleteServiceInstance(fakeServiceInstanceName, fakeSpaceGUID)
  1029  		})
  1030  
  1031  		It("makes a request to get the service instance", func() {
  1032  			Expect(fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceCallCount()).To(Equal(1))
  1033  			actualName, actualSpace, actualQuery := fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceArgsForCall(0)
  1034  			Expect(actualName).To(Equal(fakeServiceInstanceName))
  1035  			Expect(actualSpace).To(Equal(fakeSpaceGUID))
  1036  			Expect(actualQuery).To(BeEmpty())
  1037  		})
  1038  
  1039  		When("the service instance exists", func() {
  1040  			const fakeServiceInstanceGUID = "fake-si-guid"
  1041  
  1042  			BeforeEach(func() {
  1043  				fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
  1044  					resources.ServiceInstance{GUID: fakeServiceInstanceGUID},
  1045  					ccv3.IncludedResources{},
  1046  					ccv3.Warnings{"get warning"},
  1047  					nil,
  1048  				)
  1049  			})
  1050  
  1051  			It("makes the right call to delete the service instance", func() {
  1052  				Expect(fakeCloudControllerClient.DeleteServiceInstanceCallCount()).To(Equal(1))
  1053  				Expect(fakeCloudControllerClient.DeleteServiceInstanceArgsForCall(0)).To(Equal(fakeServiceInstanceGUID))
  1054  			})
  1055  
  1056  			When("the delete response is synchronous", func() {
  1057  				BeforeEach(func() {
  1058  					fakeCloudControllerClient.DeleteServiceInstanceReturns(
  1059  						"",
  1060  						ccv3.Warnings{"delete warning"},
  1061  						nil,
  1062  					)
  1063  				})
  1064  
  1065  				It("returns a nil channel", func() {
  1066  					Expect(err).NotTo(HaveOccurred())
  1067  					Expect(warnings).To(ConsistOf("get warning", "delete warning"))
  1068  					Expect(stream).To(BeNil())
  1069  				})
  1070  
  1071  				It("does not try to poll a job", func() {
  1072  					Expect(fakeCloudControllerClient.PollJobToEventStreamCallCount()).To(BeZero())
  1073  				})
  1074  			})
  1075  
  1076  			When("the delete response is asynchronous", func() {
  1077  				BeforeEach(func() {
  1078  					fakeCloudControllerClient.DeleteServiceInstanceReturns(
  1079  						"a fake job url",
  1080  						ccv3.Warnings{"delete warning"},
  1081  						nil,
  1082  					)
  1083  
  1084  					fakeStream := make(chan ccv3.PollJobEvent)
  1085  					go func() {
  1086  						fakeStream <- ccv3.PollJobEvent{
  1087  							State:    constant.JobPolling,
  1088  							Err:      errors.New("fake error"),
  1089  							Warnings: ccv3.Warnings{"fake warning"},
  1090  						}
  1091  					}()
  1092  					fakeCloudControllerClient.PollJobToEventStreamReturns(fakeStream)
  1093  				})
  1094  
  1095  				It("polls the job", func() {
  1096  					Expect(fakeCloudControllerClient.PollJobToEventStreamCallCount()).To(Equal(1))
  1097  					actualJobURL := fakeCloudControllerClient.PollJobToEventStreamArgsForCall(0)
  1098  					Expect(actualJobURL).To(BeEquivalentTo("a fake job url"))
  1099  				})
  1100  
  1101  				It("returns an event stream and warnings", func() {
  1102  					Expect(err).NotTo(HaveOccurred())
  1103  					Expect(warnings).To(ConsistOf("get warning", "delete warning"))
  1104  					Eventually(stream).Should(Receive(Equal(PollJobEvent{
  1105  						State:    JobPolling,
  1106  						Err:      errors.New("fake error"),
  1107  						Warnings: Warnings{"fake warning"},
  1108  					})))
  1109  				})
  1110  			})
  1111  
  1112  			When("the delete responds with failure", func() {
  1113  				BeforeEach(func() {
  1114  					fakeCloudControllerClient.DeleteServiceInstanceReturns(
  1115  						"a fake job url",
  1116  						ccv3.Warnings{"delete warning"},
  1117  						errors.New("bong"),
  1118  					)
  1119  				})
  1120  
  1121  				It("return the error and warnings", func() {
  1122  					Expect(err).To(MatchError("bong"))
  1123  					Expect(warnings).To(ConsistOf("get warning", "delete warning"))
  1124  					Expect(stream).To(BeNil())
  1125  				})
  1126  			})
  1127  		})
  1128  
  1129  		When("the service instance does not exist", func() {
  1130  			BeforeEach(func() {
  1131  				fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
  1132  					resources.ServiceInstance{},
  1133  					ccv3.IncludedResources{},
  1134  					ccv3.Warnings{"get warning"},
  1135  					ccerror.ServiceInstanceNotFoundError{Name: fakeServiceInstanceName},
  1136  				)
  1137  			})
  1138  
  1139  			It("returns an error", func() {
  1140  				Expect(err).To(MatchError(actionerror.ServiceInstanceNotFoundError{Name: fakeServiceInstanceName}))
  1141  				Expect(warnings).To(ConsistOf("get warning"))
  1142  				Expect(stream).To(BeNil())
  1143  			})
  1144  
  1145  			It("does not try to delete", func() {
  1146  				Expect(fakeCloudControllerClient.DeleteServiceInstanceCallCount()).To(BeZero())
  1147  			})
  1148  		})
  1149  
  1150  		When("getting the service instance fails", func() {
  1151  			BeforeEach(func() {
  1152  				fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
  1153  					resources.ServiceInstance{},
  1154  					ccv3.IncludedResources{},
  1155  					ccv3.Warnings{"get warning"},
  1156  					errors.New("boom"),
  1157  				)
  1158  			})
  1159  
  1160  			It("returns the error", func() {
  1161  				Expect(err).To(MatchError("boom"))
  1162  				Expect(warnings).To(ConsistOf("get warning"))
  1163  				Expect(stream).To(BeNil())
  1164  			})
  1165  
  1166  			It("does not try to delete", func() {
  1167  				Expect(fakeCloudControllerClient.DeleteServiceInstanceCallCount()).To(BeZero())
  1168  			})
  1169  		})
  1170  	})
  1171  
  1172  	Describe("PurgeServiceInstance", func() {
  1173  		const (
  1174  			fakeServiceInstanceName = "fake-service-instance-name"
  1175  			fakeSpaceGUID           = "fake-space-GUID"
  1176  		)
  1177  
  1178  		var (
  1179  			warnings Warnings
  1180  			err      error
  1181  		)
  1182  
  1183  		JustBeforeEach(func() {
  1184  			warnings, err = actor.PurgeServiceInstance(fakeServiceInstanceName, fakeSpaceGUID)
  1185  		})
  1186  
  1187  		It("makes a request to get the service instance", func() {
  1188  			Expect(fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceCallCount()).To(Equal(1))
  1189  			actualName, actualSpace, actualQuery := fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceArgsForCall(0)
  1190  			Expect(actualName).To(Equal(fakeServiceInstanceName))
  1191  			Expect(actualSpace).To(Equal(fakeSpaceGUID))
  1192  			Expect(actualQuery).To(BeEmpty())
  1193  		})
  1194  
  1195  		When("the service instance does not exist", func() {
  1196  			BeforeEach(func() {
  1197  				fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
  1198  					resources.ServiceInstance{},
  1199  					ccv3.IncludedResources{},
  1200  					ccv3.Warnings{"get warning"},
  1201  					ccerror.ServiceInstanceNotFoundError{Name: fakeServiceInstanceName},
  1202  				)
  1203  			})
  1204  
  1205  			It("returns the appropriate error", func() {
  1206  				Expect(err).To(MatchError(actionerror.ServiceInstanceNotFoundError{Name: fakeServiceInstanceName}))
  1207  				Expect(warnings).To(ConsistOf("get warning"))
  1208  			})
  1209  
  1210  			It("does not try to purge", func() {
  1211  				Expect(fakeCloudControllerClient.DeleteServiceInstanceCallCount()).To(BeZero())
  1212  			})
  1213  		})
  1214  
  1215  		When("the service instance exists", func() {
  1216  			const fakeServiceInstanceGUID = "fake-si-guid"
  1217  
  1218  			BeforeEach(func() {
  1219  				fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
  1220  					resources.ServiceInstance{GUID: fakeServiceInstanceGUID},
  1221  					ccv3.IncludedResources{},
  1222  					ccv3.Warnings{"get warning"},
  1223  					nil,
  1224  				)
  1225  
  1226  				fakeCloudControllerClient.DeleteServiceInstanceReturns(
  1227  					"",
  1228  					ccv3.Warnings{"purge warning"},
  1229  					nil,
  1230  				)
  1231  			})
  1232  
  1233  			It("makes the right call to purge the service instance", func() {
  1234  				Expect(fakeCloudControllerClient.DeleteServiceInstanceCallCount()).To(Equal(1))
  1235  				actualGUID, actualQuery := fakeCloudControllerClient.DeleteServiceInstanceArgsForCall(0)
  1236  				Expect(actualGUID).To(Equal(fakeServiceInstanceGUID))
  1237  				Expect(actualQuery).To(ConsistOf(ccv3.Query{Key: ccv3.Purge, Values: []string{"true"}}))
  1238  			})
  1239  
  1240  			It("returns warnings", func() {
  1241  				Expect(err).NotTo(HaveOccurred())
  1242  				Expect(warnings).To(ConsistOf("get warning", "purge warning"))
  1243  			})
  1244  
  1245  			When("the purge responds with failure", func() {
  1246  				BeforeEach(func() {
  1247  					fakeCloudControllerClient.DeleteServiceInstanceReturns(
  1248  						"a fake job url",
  1249  						ccv3.Warnings{"delete warning"},
  1250  						errors.New("bong"),
  1251  					)
  1252  				})
  1253  
  1254  				It("return the error and warnings", func() {
  1255  					Expect(err).To(MatchError("bong"))
  1256  					Expect(warnings).To(ConsistOf("get warning", "delete warning"))
  1257  				})
  1258  			})
  1259  		})
  1260  
  1261  		When("getting the service instance fails", func() {
  1262  			BeforeEach(func() {
  1263  				fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
  1264  					resources.ServiceInstance{},
  1265  					ccv3.IncludedResources{},
  1266  					ccv3.Warnings{"get warning"},
  1267  					errors.New("boom"),
  1268  				)
  1269  			})
  1270  
  1271  			It("returns the error", func() {
  1272  				Expect(err).To(MatchError("boom"))
  1273  				Expect(warnings).To(ConsistOf("get warning"))
  1274  			})
  1275  		})
  1276  	})
  1277  
  1278  	Describe("UpgradeManagedServiceInstance", func() {
  1279  		const (
  1280  			fakeServiceInstanceName = "fake-service-instance-name"
  1281  			fakeSpaceGUID           = "fake-space-GUID"
  1282  		)
  1283  
  1284  		var (
  1285  			executeErr error
  1286  			warnings   Warnings
  1287  			stream     chan PollJobEvent
  1288  		)
  1289  
  1290  		JustBeforeEach(func() {
  1291  			stream, warnings, executeErr = actor.UpgradeManagedServiceInstance(fakeServiceInstanceName, fakeSpaceGUID)
  1292  		})
  1293  
  1294  		It("makes a request to get the service instance", func() {
  1295  			Expect(fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceCallCount()).To(Equal(1))
  1296  			actualName, actualSpace, actualQuery := fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceArgsForCall(0)
  1297  			Expect(actualName).To(Equal(fakeServiceInstanceName))
  1298  			Expect(actualSpace).To(Equal(fakeSpaceGUID))
  1299  			Expect(actualQuery).To(BeEmpty())
  1300  		})
  1301  
  1302  		When("the service instance does not exist", func() {
  1303  			BeforeEach(func() {
  1304  				fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
  1305  					resources.ServiceInstance{},
  1306  					ccv3.IncludedResources{},
  1307  					ccv3.Warnings{"get SI warning"},
  1308  					ccerror.ServiceInstanceNotFoundError{Name: fakeServiceInstanceName},
  1309  				)
  1310  			})
  1311  
  1312  			It("does not try to upgrade", func() {
  1313  				Expect(fakeCloudControllerClient.UpdateServiceInstanceCallCount()).To(BeZero())
  1314  			})
  1315  
  1316  			It("returns the appropriate error", func() {
  1317  				Expect(stream).To(BeNil())
  1318  				Expect(warnings).To(ConsistOf("get SI warning"))
  1319  				Expect(executeErr).To(MatchError(actionerror.ServiceInstanceNotFoundError{
  1320  					Name: fakeServiceInstanceName,
  1321  				}))
  1322  			})
  1323  		})
  1324  
  1325  		When("there's no upgrade available", func() {
  1326  			BeforeEach(func() {
  1327  				fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
  1328  					resources.ServiceInstance{
  1329  						GUID: "some-guid",
  1330  						Name: "some-name",
  1331  						UpgradeAvailable: types.OptionalBoolean{
  1332  							IsSet: true,
  1333  							Value: false,
  1334  						},
  1335  					},
  1336  					ccv3.IncludedResources{},
  1337  					ccv3.Warnings{"get SI warning"},
  1338  					nil,
  1339  				)
  1340  			})
  1341  
  1342  			It("does not try to upgrade", func() {
  1343  				Expect(fakeCloudControllerClient.UpdateServiceInstanceCallCount()).To(BeZero())
  1344  			})
  1345  
  1346  			It("returns the appropriate error", func() {
  1347  				Expect(stream).To(BeNil())
  1348  				Expect(warnings).To(ConsistOf("get SI warning"))
  1349  				Expect(executeErr).To(MatchError(actionerror.ServiceInstanceUpgradeNotAvailableError{}))
  1350  			})
  1351  		})
  1352  
  1353  		When("there is an upgrade available", func() {
  1354  			const guid = "some-guid"
  1355  			const planGUID = "some-plan-guid"
  1356  			const jobURL = ccv3.JobURL("fake-job-url")
  1357  
  1358  			BeforeEach(func() {
  1359  				fakeCloudControllerClient.GetServiceInstanceByNameAndSpaceReturns(
  1360  					resources.ServiceInstance{
  1361  						Type: resources.ManagedServiceInstance,
  1362  						GUID: guid,
  1363  						Name: "some-name",
  1364  						UpgradeAvailable: types.OptionalBoolean{
  1365  							IsSet: true,
  1366  							Value: true,
  1367  						},
  1368  						ServicePlanGUID: planGUID,
  1369  					},
  1370  					ccv3.IncludedResources{},
  1371  					ccv3.Warnings{"warning from get"},
  1372  					nil,
  1373  				)
  1374  				fakeCloudControllerClient.GetServicePlanByGUIDReturns(
  1375  					resources.ServicePlan{
  1376  						MaintenanceInfoVersion: "9.1.2",
  1377  					},
  1378  					ccv3.Warnings{"warning from plan"},
  1379  					nil,
  1380  				)
  1381  				fakeCloudControllerClient.UpdateServiceInstanceReturns(
  1382  					jobURL,
  1383  					ccv3.Warnings{"warning from update"},
  1384  					nil,
  1385  				)
  1386  
  1387  				fakeStream := make(chan ccv3.PollJobEvent)
  1388  				go func() {
  1389  					fakeStream <- ccv3.PollJobEvent{
  1390  						State:    constant.JobProcessing,
  1391  						Warnings: ccv3.Warnings{"stream warning"},
  1392  					}
  1393  				}()
  1394  				fakeCloudControllerClient.PollJobToEventStreamReturns(fakeStream)
  1395  			})
  1396  
  1397  			It("makes the right calls and returns all warnings", func() {
  1398  				By("getting the service plan", func() {
  1399  					Expect(fakeCloudControllerClient.GetServicePlanByGUIDCallCount()).To(Equal(1))
  1400  					actualPlanGUID := fakeCloudControllerClient.GetServicePlanByGUIDArgsForCall(0)
  1401  					Expect(actualPlanGUID).To(Equal(planGUID))
  1402  				})
  1403  
  1404  				By("updating the service instance", func() {
  1405  					Expect(fakeCloudControllerClient.UpdateServiceInstanceCallCount()).To(Equal(1))
  1406  					actualGUID, actualServiceInstance := fakeCloudControllerClient.UpdateServiceInstanceArgsForCall(0)
  1407  					Expect(actualGUID).To(Equal(guid))
  1408  					Expect(actualServiceInstance).To(Equal(resources.ServiceInstance{
  1409  						MaintenanceInfoVersion: "9.1.2",
  1410  					}))
  1411  				})
  1412  
  1413  				By("polling the job", func() {
  1414  					Expect(fakeCloudControllerClient.PollJobToEventStreamCallCount()).To(Equal(1))
  1415  					Expect(fakeCloudControllerClient.PollJobToEventStreamArgsForCall(0)).To(Equal(jobURL))
  1416  				})
  1417  
  1418  				By("returning a stream, warnings and no error", func() {
  1419  					Eventually(stream).Should(Receive(Equal(PollJobEvent{
  1420  						State:    JobProcessing,
  1421  						Warnings: Warnings{"stream warning"},
  1422  					})))
  1423  
  1424  					Expect(executeErr).NotTo(HaveOccurred())
  1425  					Expect(warnings).To(ConsistOf("warning from get", "warning from plan", "warning from update"))
  1426  				})
  1427  			})
  1428  		})
  1429  	})
  1430  })