github.com/DaAlbrecht/cf-cli@v0.0.0-20231128151943-1fe19bb400b9/actor/v7action/service_access_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/resources"
    12  	. "github.com/onsi/ginkgo"
    13  	. "github.com/onsi/gomega"
    14  )
    15  
    16  var _ = Describe("service access actions", func() {
    17  	var (
    18  		actor                     *Actor
    19  		fakeCloudControllerClient *v7actionfakes.FakeCloudControllerClient
    20  	)
    21  
    22  	BeforeEach(func() {
    23  		actor, fakeCloudControllerClient, _, _, _, _, _ = NewTestActor()
    24  	})
    25  
    26  	Describe("GetServiceAccess", func() {
    27  		BeforeEach(func() {
    28  			fakeCloudControllerClient.GetServicePlansWithSpaceAndOrganizationReturns(fakeServicePlans(), ccv3.Warnings{"plans warning"}, nil)
    29  			fakeCloudControllerClient.GetServiceOfferingsReturns(fakeServiceOfferings(), ccv3.Warnings{"offerings warning"}, nil)
    30  
    31  			visibility1 := resources.ServicePlanVisibility{
    32  				Organizations: []resources.ServicePlanVisibilityDetail{{Name: "org-3"}},
    33  			}
    34  			visibility2 := resources.ServicePlanVisibility{
    35  				Organizations: []resources.ServicePlanVisibilityDetail{{Name: "org-1"}, {Name: "org-2"}},
    36  			}
    37  			fakeCloudControllerClient.GetServicePlanVisibilityReturnsOnCall(0, visibility1, ccv3.Warnings{"visibility1 1 warning"}, nil)
    38  			fakeCloudControllerClient.GetServicePlanVisibilityReturnsOnCall(1, visibility2, ccv3.Warnings{"visibility1 2 warning"}, nil)
    39  		})
    40  
    41  		It("produces a slice of ServicePlanAccess objects", func() {
    42  			access, warnings, err := actor.GetServiceAccess("", "", "")
    43  			Expect(err).NotTo(HaveOccurred())
    44  			Expect(warnings).To(ConsistOf("plans warning", "offerings warning", "visibility1 1 warning", "visibility1 2 warning"))
    45  			Expect(access).To(ConsistOf(
    46  				ServicePlanAccess{
    47  					BrokerName:          "land-broker",
    48  					ServiceOfferingName: "yellow",
    49  					ServicePlanName:     "orange",
    50  					VisibilityType:      "organization",
    51  					VisibilityDetails:   []string{"org-1", "org-2"},
    52  				},
    53  				ServicePlanAccess{
    54  					BrokerName:          "land-broker",
    55  					ServiceOfferingName: "yellow",
    56  					ServicePlanName:     "yellow",
    57  					VisibilityType:      "organization",
    58  					VisibilityDetails:   []string{"org-3"},
    59  				},
    60  				ServicePlanAccess{
    61  					BrokerName:          "sea-broker",
    62  					ServiceOfferingName: "magenta",
    63  					ServicePlanName:     "red",
    64  					VisibilityType:      "public",
    65  					VisibilityDetails:   nil,
    66  				},
    67  				ServicePlanAccess{
    68  					BrokerName:          "sea-broker",
    69  					ServiceOfferingName: "magenta",
    70  					ServicePlanName:     "violet",
    71  					VisibilityType:      "public",
    72  					VisibilityDetails:   nil,
    73  				},
    74  				ServicePlanAccess{
    75  					BrokerName:          "sky-broker",
    76  					ServiceOfferingName: "cyan",
    77  					ServicePlanName:     "blue",
    78  					VisibilityType:      "space",
    79  					VisibilityDetails:   []string{"some-space (org: some-org)"},
    80  				},
    81  				ServicePlanAccess{
    82  					BrokerName:          "sky-broker",
    83  					ServiceOfferingName: "cyan",
    84  					ServicePlanName:     "green",
    85  					VisibilityType:      "space",
    86  					VisibilityDetails:   []string{"some-space (org: some-org)"},
    87  				},
    88  				ServicePlanAccess{
    89  					BrokerName:          "sky-broker",
    90  					ServiceOfferingName: "key",
    91  					ServicePlanName:     "indigo",
    92  					VisibilityType:      "space",
    93  					VisibilityDetails:   []string{"some-space (org: some-org)"},
    94  				},
    95  			))
    96  		})
    97  
    98  		When("there are no service offerings", func() {
    99  			BeforeEach(func() {
   100  				fakeCloudControllerClient.GetServiceOfferingsReturns([]resources.ServiceOffering{}, ccv3.Warnings{"offerings warning"}, nil)
   101  			})
   102  
   103  			It("returns an empty slice", func() {
   104  				result, warnings, err := actor.GetServiceAccess("", "", "")
   105  				Expect(err).NotTo(HaveOccurred())
   106  				Expect(warnings).To(ContainElement("offerings warning"))
   107  				Expect(result).To(BeEmpty())
   108  			})
   109  		})
   110  
   111  		When("filtering on organization", func() {
   112  			const (
   113  				guid    = "fake-org-guid"
   114  				name    = "fake-org-name"
   115  				warning = "fake get org warning"
   116  			)
   117  
   118  			BeforeEach(func() {
   119  				fakeCloudControllerClient.GetOrganizationsReturns([]resources.Organization{{GUID: guid}}, ccv3.Warnings{warning}, nil)
   120  			})
   121  
   122  			It("passes the organization in the plan filter", func() {
   123  				_, warnings, err := actor.GetServiceAccess("", "", name)
   124  				Expect(err).NotTo(HaveOccurred())
   125  				Expect(warnings).To(ContainElement(warning))
   126  
   127  				Expect(fakeCloudControllerClient.GetOrganizationsCallCount()).To(Equal(1))
   128  				Expect(fakeCloudControllerClient.GetOrganizationsArgsForCall(0)).To(ContainElement(ccv3.Query{
   129  					Key:    ccv3.NameFilter,
   130  					Values: []string{name},
   131  				}))
   132  
   133  				Expect(fakeCloudControllerClient.GetServicePlansWithSpaceAndOrganizationCallCount()).To(Equal(1))
   134  				Expect(fakeCloudControllerClient.GetServicePlansWithSpaceAndOrganizationArgsForCall(0)).To(ContainElement(ccv3.Query{
   135  					Key:    ccv3.OrganizationGUIDFilter,
   136  					Values: []string{guid},
   137  				}))
   138  			})
   139  
   140  			When("the organization is not found", func() {
   141  				BeforeEach(func() {
   142  					fakeCloudControllerClient.GetOrganizationsReturns(
   143  						[]resources.Organization{},
   144  						ccv3.Warnings{"org warning"},
   145  						nil,
   146  					)
   147  				})
   148  
   149  				It("returns an error and warnings", func() {
   150  					_, warnings, err := actor.GetServiceAccess("", "", "fake-org")
   151  					Expect(warnings).To(ContainElement("org warning"))
   152  					Expect(err).To(MatchError(actionerror.OrganizationNotFoundError{Name: "fake-org"}))
   153  				})
   154  			})
   155  		})
   156  
   157  		When("filtering on service offering", func() {
   158  			const (
   159  				guid    = "fake-service-offering-guid"
   160  				name    = "fake-service-offering-name"
   161  				warning = "fake get service offering warning"
   162  			)
   163  
   164  			BeforeEach(func() {
   165  				fakeCloudControllerClient.GetServiceOfferingsReturns([]resources.ServiceOffering{{GUID: guid}}, ccv3.Warnings{warning}, nil)
   166  			})
   167  
   168  			It("passes the service offering in the filters", func() {
   169  				_, warnings, err := actor.GetServiceAccess(name, "", "")
   170  				Expect(err).NotTo(HaveOccurred())
   171  				Expect(warnings).To(ContainElement(warning))
   172  
   173  				Expect(fakeCloudControllerClient.GetServiceOfferingsCallCount()).To(Equal(1))
   174  				Expect(fakeCloudControllerClient.GetServiceOfferingsArgsForCall(0)).To(ContainElement(ccv3.Query{
   175  					Key:    ccv3.NameFilter,
   176  					Values: []string{name},
   177  				}))
   178  
   179  				Expect(fakeCloudControllerClient.GetServicePlansWithSpaceAndOrganizationCallCount()).To(Equal(1))
   180  				Expect(fakeCloudControllerClient.GetServicePlansWithSpaceAndOrganizationArgsForCall(0)).To(ContainElement(ccv3.Query{
   181  					Key:    ccv3.ServiceOfferingNamesFilter,
   182  					Values: []string{name},
   183  				}))
   184  			})
   185  
   186  			When("the service offering is not found", func() {
   187  				BeforeEach(func() {
   188  					fakeCloudControllerClient.GetServiceOfferingsReturns([]resources.ServiceOffering{}, ccv3.Warnings{warning}, nil)
   189  				})
   190  
   191  				It("returns an error and warnings", func() {
   192  					_, warnings, err := actor.GetServiceAccess(name, "", "")
   193  					Expect(err).To(MatchError(actionerror.ServiceNotFoundError{Name: name}))
   194  					Expect(warnings).To(ContainElement(warning))
   195  				})
   196  			})
   197  		})
   198  
   199  		When("filtering on service broker", func() {
   200  			const name = "fake-service-broker-name"
   201  
   202  			It("passes the service broker in the filters", func() {
   203  				_, _, err := actor.GetServiceAccess("", name, "")
   204  				Expect(err).NotTo(HaveOccurred())
   205  
   206  				Expect(fakeCloudControllerClient.GetServiceOfferingsCallCount()).To(Equal(1))
   207  				Expect(fakeCloudControllerClient.GetServiceOfferingsArgsForCall(0)).To(ContainElement(ccv3.Query{
   208  					Key:    ccv3.ServiceBrokerNamesFilter,
   209  					Values: []string{name},
   210  				}))
   211  
   212  				Expect(fakeCloudControllerClient.GetServicePlansWithSpaceAndOrganizationCallCount()).To(Equal(1))
   213  				Expect(fakeCloudControllerClient.GetServicePlansWithSpaceAndOrganizationArgsForCall(0)).To(ContainElement(ccv3.Query{
   214  					Key:    ccv3.ServiceBrokerNamesFilter,
   215  					Values: []string{name},
   216  				}))
   217  			})
   218  
   219  			When("the service broker filter returns no service offerings", func() {
   220  				BeforeEach(func() {
   221  					fakeCloudControllerClient.GetServiceOfferingsReturns([]resources.ServiceOffering{}, ccv3.Warnings{"warning"}, nil)
   222  				})
   223  
   224  				It("returns an error and warnings", func() {
   225  					_, warnings, err := actor.GetServiceAccess("", name, "")
   226  					Expect(err).To(MatchError(actionerror.ServiceNotFoundError{Broker: name}))
   227  					Expect(warnings).To(ContainElement("warning"))
   228  				})
   229  			})
   230  		})
   231  
   232  		When("combining filters", func() {
   233  			const (
   234  				orgGUID         = "fake-org-guid"
   235  				orgName         = "fake-org-name"
   236  				orgWarning      = "fake get org warning"
   237  				offeringGUID    = "fake-service-offering-guid"
   238  				offeringName    = "fake-service-offering-name"
   239  				offeringWarning = "fake get service offering warning"
   240  				brokerName      = "fake-service-broker-name"
   241  			)
   242  
   243  			BeforeEach(func() {
   244  				fakeCloudControllerClient.GetOrganizationsReturns([]resources.Organization{{GUID: orgGUID}}, ccv3.Warnings{orgWarning}, nil)
   245  				fakeCloudControllerClient.GetServiceOfferingsReturns([]resources.ServiceOffering{{GUID: offeringGUID}}, ccv3.Warnings{offeringWarning}, nil)
   246  			})
   247  
   248  			It("passes all the filters", func() {
   249  				_, warnings, err := actor.GetServiceAccess(offeringName, brokerName, orgName)
   250  				Expect(err).NotTo(HaveOccurred())
   251  				Expect(warnings).To(ContainElements(orgWarning, offeringWarning))
   252  
   253  				Expect(fakeCloudControllerClient.GetOrganizationsCallCount()).To(Equal(1))
   254  				Expect(fakeCloudControllerClient.GetOrganizationsArgsForCall(0)).To(ContainElement(ccv3.Query{
   255  					Key:    ccv3.NameFilter,
   256  					Values: []string{orgName},
   257  				}))
   258  
   259  				Expect(fakeCloudControllerClient.GetServiceOfferingsCallCount()).To(Equal(1))
   260  				Expect(fakeCloudControllerClient.GetServiceOfferingsArgsForCall(0)).To(ContainElements(
   261  					ccv3.Query{
   262  						Key:    ccv3.NameFilter,
   263  						Values: []string{offeringName},
   264  					},
   265  					ccv3.Query{
   266  						Key:    ccv3.ServiceBrokerNamesFilter,
   267  						Values: []string{brokerName},
   268  					},
   269  				))
   270  
   271  				Expect(fakeCloudControllerClient.GetServicePlansWithSpaceAndOrganizationCallCount()).To(Equal(1))
   272  				Expect(fakeCloudControllerClient.GetServicePlansWithSpaceAndOrganizationArgsForCall(0)).To(ContainElements(
   273  					ccv3.Query{
   274  						Key:    ccv3.OrganizationGUIDFilter,
   275  						Values: []string{orgGUID},
   276  					},
   277  					ccv3.Query{
   278  						Key:    ccv3.ServiceOfferingNamesFilter,
   279  						Values: []string{offeringName},
   280  					},
   281  					ccv3.Query{
   282  						Key:    ccv3.ServiceBrokerNamesFilter,
   283  						Values: []string{brokerName},
   284  					},
   285  				))
   286  			})
   287  		})
   288  
   289  		When("the client fails to get resources", func() {
   290  			Context("service plans", func() {
   291  				BeforeEach(func() {
   292  					fakeCloudControllerClient.GetServicePlansWithSpaceAndOrganizationReturns(
   293  						nil,
   294  						ccv3.Warnings{"plans warning"},
   295  						errors.New("fake plans error"),
   296  					)
   297  				})
   298  
   299  				It("returns the error and warnings", func() {
   300  					_, warnings, err := actor.GetServiceAccess("", "", "")
   301  					Expect(warnings).To(ContainElement("plans warning"))
   302  					Expect(err).To(MatchError("fake plans error"))
   303  				})
   304  			})
   305  
   306  			Context("service offerings", func() {
   307  				BeforeEach(func() {
   308  					fakeCloudControllerClient.GetServiceOfferingsReturns(
   309  						nil,
   310  						ccv3.Warnings{"offerings warning"},
   311  						errors.New("fake offerings error"),
   312  					)
   313  				})
   314  
   315  				It("returns the error and warnings", func() {
   316  					_, warnings, err := actor.GetServiceAccess("", "", "")
   317  					Expect(warnings).To(ContainElement("offerings warning"))
   318  					Expect(err).To(MatchError("fake offerings error"))
   319  				})
   320  			})
   321  
   322  			Context("service plan visibility", func() {
   323  				BeforeEach(func() {
   324  					fakeCloudControllerClient.GetServicePlanVisibilityReturnsOnCall(
   325  						0,
   326  						resources.ServicePlanVisibility{},
   327  						ccv3.Warnings{"visibility warning"},
   328  						errors.New("fake visibility error"),
   329  					)
   330  				})
   331  
   332  				It("returns the error and warnings", func() {
   333  					_, warnings, err := actor.GetServiceAccess("", "", "")
   334  					Expect(warnings).To(ContainElement("visibility warning"))
   335  					Expect(err).To(MatchError("fake visibility error"))
   336  				})
   337  			})
   338  		})
   339  	})
   340  
   341  	Describe("EnableServiceAccess", func() {
   342  		BeforeEach(func() {
   343  			fakeCloudControllerClient.GetOrganizationsReturns(
   344  				[]resources.Organization{{GUID: "org-guid"}},
   345  				ccv3.Warnings{"org warning"},
   346  				nil,
   347  			)
   348  
   349  			fakeCloudControllerClient.GetServiceOfferingByNameAndBrokerReturns(
   350  				resources.ServiceOffering{GUID: "fake-offering-guid"},
   351  				ccv3.Warnings{"some warning"},
   352  				nil,
   353  			)
   354  
   355  			fakeCloudControllerClient.GetServicePlansReturns(
   356  				[]resources.ServicePlan{
   357  					{GUID: "fake-plan-guid-1"},
   358  					{GUID: "fake-plan-guid-2"},
   359  				},
   360  				ccv3.Warnings{"other warning"},
   361  				nil,
   362  			)
   363  
   364  			fakeCloudControllerClient.UpdateServicePlanVisibilityReturns(
   365  				resources.ServicePlanVisibility{},
   366  				ccv3.Warnings{"post warning"},
   367  				nil,
   368  			)
   369  		})
   370  
   371  		It("sets visibility to public", func() {
   372  			skipped, warnings, err := actor.EnableServiceAccess("fake-offering", "", "", "")
   373  			Expect(err).NotTo(HaveOccurred())
   374  			Expect(warnings).To(ConsistOf("some warning", "other warning", "post warning", "post warning"))
   375  			Expect(skipped).To(BeEmpty())
   376  
   377  			Expect(fakeCloudControllerClient.UpdateServicePlanVisibilityCallCount()).To(Equal(2))
   378  
   379  			planGUID, actualVisibility := fakeCloudControllerClient.UpdateServicePlanVisibilityArgsForCall(0)
   380  			Expect(planGUID).To(Equal("fake-plan-guid-1"))
   381  			Expect(actualVisibility).To(Equal(resources.ServicePlanVisibility{Type: "public"}))
   382  
   383  			planGUID, actualVisibility = fakeCloudControllerClient.UpdateServicePlanVisibilityArgsForCall(1)
   384  			Expect(planGUID).To(Equal("fake-plan-guid-2"))
   385  			Expect(actualVisibility).To(Equal(resources.ServicePlanVisibility{Type: "public"}))
   386  		})
   387  
   388  		Describe("fetching service offering", func() {
   389  			It("filters by service offering and broker name", func() {
   390  				_, _, err := actor.EnableServiceAccess("fake-offering", "fake-broker-name", "", "")
   391  				Expect(err).NotTo(HaveOccurred())
   392  				Expect(fakeCloudControllerClient.GetServiceOfferingByNameAndBrokerCallCount()).To(Equal(1))
   393  				requestedServiceName, requestedBrokerName := fakeCloudControllerClient.GetServiceOfferingByNameAndBrokerArgsForCall(0)
   394  				Expect(requestedServiceName).To(Equal("fake-offering"))
   395  				Expect(requestedBrokerName).To(Equal("fake-broker-name"))
   396  			})
   397  
   398  			When("the service offering does not exist", func() {
   399  				BeforeEach(func() {
   400  					fakeCloudControllerClient.GetServiceOfferingByNameAndBrokerReturns(
   401  						resources.ServiceOffering{},
   402  						ccv3.Warnings{"a warning"},
   403  						ccerror.ServiceOfferingNotFoundError{ServiceOfferingName: "no-such-offering"},
   404  					)
   405  				})
   406  
   407  				It("returns an error", func() {
   408  					_, warnings, err := actor.EnableServiceAccess("no-such-offering", "", "", "")
   409  					Expect(warnings).To(ContainElement("a warning"))
   410  					Expect(err).To(MatchError("Service offering 'no-such-offering' not found."))
   411  				})
   412  			})
   413  
   414  			When("the service offering name is ambiguous", func() {
   415  				BeforeEach(func() {
   416  					fakeCloudControllerClient.GetServiceOfferingByNameAndBrokerReturns(
   417  						resources.ServiceOffering{},
   418  						ccv3.Warnings{"another warning"},
   419  						ccerror.ServiceOfferingNameAmbiguityError{
   420  							ServiceOfferingName: "duplicate-offering",
   421  							ServiceBrokerNames:  []string{"a-broker", "another-broker"},
   422  						})
   423  				})
   424  
   425  				It("returns an error", func() {
   426  					_, warnings, err := actor.EnableServiceAccess("duplicate-offering", "", "", "")
   427  					Expect(warnings).To(ContainElement("another warning"))
   428  					Expect(err).To(MatchError("Service 'duplicate-offering' is provided by multiple service brokers: a-broker, another-broker\nSpecify a broker by using the '-b' flag."))
   429  				})
   430  			})
   431  		})
   432  
   433  		Describe("fetching service plans", func() {
   434  			It("gets all plans for the service offering", func() {
   435  				_, warnings, err := actor.EnableServiceAccess("fake-offering", "fake-broker-name", "", "")
   436  				Expect(err).NotTo(HaveOccurred())
   437  				Expect(warnings).To(ContainElements("some warning", "other warning"))
   438  
   439  				Expect(fakeCloudControllerClient.GetServicePlansCallCount()).To(Equal(1))
   440  				Expect(fakeCloudControllerClient.GetServicePlansArgsForCall(0)).To(ConsistOf(ccv3.Query{
   441  					Key:    ccv3.ServiceOfferingGUIDsFilter,
   442  					Values: []string{"fake-offering-guid"},
   443  				}))
   444  			})
   445  
   446  			When("a plan name is specified", func() {
   447  				It("filters by plan name and service offering GUID", func() {
   448  					_, warnings, err := actor.EnableServiceAccess("fake-offering", "fake-broker-name", "", "fake-plan-name")
   449  					Expect(err).NotTo(HaveOccurred())
   450  					Expect(warnings).To(ContainElements("some warning", "other warning"))
   451  
   452  					Expect(fakeCloudControllerClient.GetServicePlansCallCount()).To(Equal(1))
   453  					Expect(fakeCloudControllerClient.GetServicePlansArgsForCall(0)).To(ConsistOf(
   454  						ccv3.Query{
   455  							Key:    ccv3.ServiceOfferingGUIDsFilter,
   456  							Values: []string{"fake-offering-guid"},
   457  						},
   458  						ccv3.Query{
   459  							Key:    ccv3.NameFilter,
   460  							Values: []string{"fake-plan-name"},
   461  						},
   462  					))
   463  				})
   464  			})
   465  
   466  			When("no plans were found", func() {
   467  				BeforeEach(func() {
   468  					fakeCloudControllerClient.GetServicePlansReturns([]resources.ServicePlan{}, ccv3.Warnings{"other warning"}, nil)
   469  				})
   470  
   471  				It("fails", func() {
   472  					_, warnings, err := actor.EnableServiceAccess("fake-offering", "fake-broker-name", "", "fake-plan-name")
   473  					Expect(err).To(MatchError(actionerror.ServicePlanNotFoundError{
   474  						OfferingName: "fake-offering",
   475  						PlanName:     "fake-plan-name",
   476  					}))
   477  					Expect(warnings).To(ContainElements("some warning", "other warning"))
   478  				})
   479  			})
   480  
   481  			When("fetching the plans fail", func() {
   482  				BeforeEach(func() {
   483  					fakeCloudControllerClient.GetServicePlansReturns([]resources.ServicePlan{}, ccv3.Warnings{"other warning"}, errors.New("fetch plans error"))
   484  				})
   485  
   486  				It("fails", func() {
   487  					_, warnings, err := actor.EnableServiceAccess("", "", "", "")
   488  					Expect(err).To(MatchError("fetch plans error"))
   489  					Expect(warnings).To(ContainElements("some warning", "other warning"))
   490  				})
   491  			})
   492  		})
   493  
   494  		Context("with org", func() {
   495  			It("sets visibility to orgs", func() {
   496  				_, warnings, err := actor.EnableServiceAccess("fake-offering", "", "fake-org-name", "")
   497  
   498  				Expect(err).NotTo(HaveOccurred())
   499  				Expect(warnings).To(ConsistOf("some warning", "other warning", "post warning", "post warning", "org warning"))
   500  
   501  				Expect(fakeCloudControllerClient.UpdateServicePlanVisibilityCallCount()).To(Equal(2))
   502  
   503  				planGUID, actualVisibility := fakeCloudControllerClient.UpdateServicePlanVisibilityArgsForCall(0)
   504  				Expect(planGUID).To(Equal("fake-plan-guid-1"))
   505  				Expect(actualVisibility).To(Equal(resources.ServicePlanVisibility{
   506  					Type:          "organization",
   507  					Organizations: []resources.ServicePlanVisibilityDetail{{GUID: "org-guid"}}}))
   508  
   509  				planGUID, actualVisibility = fakeCloudControllerClient.UpdateServicePlanVisibilityArgsForCall(1)
   510  				Expect(planGUID).To(Equal("fake-plan-guid-2"))
   511  				Expect(actualVisibility).To(Equal(resources.ServicePlanVisibility{
   512  					Type:          "organization",
   513  					Organizations: []resources.ServicePlanVisibilityDetail{{GUID: "org-guid"}}}))
   514  			})
   515  
   516  			When("the plan is public", func() {
   517  				It("skips the plan", func() {
   518  					fakeCloudControllerClient.GetServicePlansReturns(
   519  						[]resources.ServicePlan{
   520  							{Name: "fake-plan-1", GUID: "fake-plan-guid-1", VisibilityType: "public"},
   521  							{Name: "fake-plan-2", GUID: "fake-plan-guid-2", VisibilityType: "public"},
   522  							{Name: "fake-plan-3", GUID: "fake-plan-guid-3", VisibilityType: "organization"},
   523  							{Name: "fake-plan-4", GUID: "fake-plan-guid-4", VisibilityType: "admin"},
   524  						},
   525  						ccv3.Warnings{"other warning"},
   526  						nil,
   527  					)
   528  
   529  					skippedPlans, _, err := actor.EnableServiceAccess("fake-offering", "", "fake-org-name", "")
   530  
   531  					Expect(err).NotTo(HaveOccurred())
   532  
   533  					Expect(fakeCloudControllerClient.UpdateServicePlanVisibilityCallCount()).To(Equal(2))
   534  					Expect(skippedPlans).To(ConsistOf("fake-plan-1", "fake-plan-2"))
   535  				})
   536  			})
   537  
   538  			When("the org does not exist", func() {
   539  				It("returns an error", func() {
   540  					fakeCloudControllerClient.GetOrganizationsReturns([]resources.Organization{}, ccv3.Warnings{"org warning"}, nil)
   541  
   542  					_, warnings, err := actor.EnableServiceAccess("fake-offering", "", "fake-org-name", "")
   543  					Expect(err).To(MatchError(actionerror.OrganizationNotFoundError{
   544  						Name: "fake-org-name",
   545  					}))
   546  					Expect(warnings).To(ConsistOf("some warning", "other warning", "org warning"))
   547  				})
   548  			})
   549  		})
   550  
   551  		When("setting visibility fails", func() {
   552  			BeforeEach(func() {
   553  				fakeCloudControllerClient.UpdateServicePlanVisibilityReturns(
   554  					resources.ServicePlanVisibility{},
   555  					ccv3.Warnings{"post warning"},
   556  					errors.New("post error"),
   557  				)
   558  			})
   559  
   560  			It("returns error and stops setting visibility for the remaining plans", func() {
   561  				_, warnings, err := actor.EnableServiceAccess("fake-offering", "", "", "")
   562  
   563  				Expect(err).To(HaveOccurred())
   564  				Expect(err).To(MatchError("post error"))
   565  
   566  				Expect(fakeCloudControllerClient.UpdateServicePlanVisibilityCallCount()).To(Equal(1))
   567  				Expect(warnings).To(ConsistOf("some warning", "other warning", "post warning"))
   568  			})
   569  		})
   570  
   571  		When("the plan has visibility type 'space'", func() {
   572  			It("returns the appropriate error", func() {
   573  				fakeCloudControllerClient.GetServicePlansReturns(
   574  					[]resources.ServicePlan{
   575  						{GUID: "fake-plan-guid-1", VisibilityType: "space"},
   576  					},
   577  					ccv3.Warnings{"other warning"},
   578  					nil,
   579  				)
   580  				_, warnings, err := actor.EnableServiceAccess("fake-offering", "", "", "")
   581  				Expect(err).To(MatchError(actionerror.ServicePlanVisibilityTypeError{}))
   582  				Expect(warnings).To(ContainElement("other warning"))
   583  			})
   584  		})
   585  	})
   586  
   587  	Describe("DisableServiceAccess", func() {
   588  		BeforeEach(func() {
   589  			fakeCloudControllerClient.GetOrganizationsReturns(
   590  				[]resources.Organization{{GUID: "org-guid"}},
   591  				ccv3.Warnings{"org warning"},
   592  				nil,
   593  			)
   594  
   595  			fakeCloudControllerClient.GetServiceOfferingByNameAndBrokerReturns(
   596  				resources.ServiceOffering{GUID: "fake-offering-guid"},
   597  				ccv3.Warnings{"some warning"},
   598  				nil,
   599  			)
   600  
   601  			fakeCloudControllerClient.GetServicePlansReturns(
   602  				[]resources.ServicePlan{
   603  					{GUID: "fake-plan-guid-1"},
   604  					{GUID: "fake-plan-guid-2"},
   605  				},
   606  				ccv3.Warnings{"other warning"},
   607  				nil,
   608  			)
   609  
   610  			fakeCloudControllerClient.UpdateServicePlanVisibilityReturns(
   611  				resources.ServicePlanVisibility{},
   612  				ccv3.Warnings{"post warning"},
   613  				nil,
   614  			)
   615  		})
   616  
   617  		It("sets visibility to `admin`", func() {
   618  			skipped, warnings, err := actor.DisableServiceAccess("fake-offering", "", "", "")
   619  			Expect(err).NotTo(HaveOccurred())
   620  			Expect(warnings).To(ConsistOf("some warning", "other warning", "post warning", "post warning"))
   621  			Expect(skipped).To(BeEmpty())
   622  
   623  			Expect(fakeCloudControllerClient.UpdateServicePlanVisibilityCallCount()).To(Equal(2))
   624  			Expect(fakeCloudControllerClient.DeleteServicePlanVisibilityCallCount()).To(Equal(0))
   625  
   626  			planGUID, actualVisibility := fakeCloudControllerClient.UpdateServicePlanVisibilityArgsForCall(0)
   627  			Expect(planGUID).To(Equal("fake-plan-guid-1"))
   628  			Expect(actualVisibility).To(Equal(resources.ServicePlanVisibility{Type: "admin"}))
   629  
   630  			planGUID, actualVisibility = fakeCloudControllerClient.UpdateServicePlanVisibilityArgsForCall(1)
   631  			Expect(planGUID).To(Equal("fake-plan-guid-2"))
   632  			Expect(actualVisibility).To(Equal(resources.ServicePlanVisibility{Type: "admin"}))
   633  		})
   634  
   635  		Describe("fetching service offering", func() {
   636  			It("filters by service offering name and broker", func() {
   637  				_, _, err := actor.DisableServiceAccess("fake-offering", "fake-broker-name", "", "")
   638  				Expect(err).NotTo(HaveOccurred())
   639  				Expect(fakeCloudControllerClient.GetServiceOfferingByNameAndBrokerCallCount()).To(Equal(1))
   640  				requestedServiceName, requestedBrokerName := fakeCloudControllerClient.GetServiceOfferingByNameAndBrokerArgsForCall(0)
   641  				Expect(requestedServiceName).To(Equal("fake-offering"))
   642  				Expect(requestedBrokerName).To(Equal("fake-broker-name"))
   643  			})
   644  
   645  			When("the service offering does not exist", func() {
   646  				BeforeEach(func() {
   647  					fakeCloudControllerClient.GetServiceOfferingByNameAndBrokerReturns(
   648  						resources.ServiceOffering{},
   649  						ccv3.Warnings{"a warning"},
   650  						ccerror.ServiceOfferingNotFoundError{ServiceOfferingName: "no-such-offering"},
   651  					)
   652  				})
   653  
   654  				It("returns an error", func() {
   655  					_, warnings, err := actor.DisableServiceAccess("no-such-offering", "", "", "")
   656  					Expect(warnings).To(ContainElement("a warning"))
   657  					Expect(err).To(MatchError("Service offering 'no-such-offering' not found."))
   658  				})
   659  			})
   660  
   661  			When("the service offering name is ambiguous", func() {
   662  				BeforeEach(func() {
   663  					fakeCloudControllerClient.GetServiceOfferingByNameAndBrokerReturns(
   664  						resources.ServiceOffering{},
   665  						ccv3.Warnings{"another warning"},
   666  						ccerror.ServiceOfferingNameAmbiguityError{
   667  							ServiceOfferingName: "duplicate-offering",
   668  							ServiceBrokerNames:  []string{"a-broker", "another-broker"},
   669  						})
   670  				})
   671  
   672  				It("returns an error", func() {
   673  					_, warnings, err := actor.DisableServiceAccess("duplicate-offering", "", "", "")
   674  					Expect(warnings).To(ContainElement("another warning"))
   675  					Expect(err).To(MatchError("Service 'duplicate-offering' is provided by multiple service brokers: a-broker, another-broker\nSpecify a broker by using the '-b' flag."))
   676  				})
   677  			})
   678  		})
   679  
   680  		Describe("fetching service plans", func() {
   681  			It("gets all plans for the service offering", func() {
   682  				_, warnings, err := actor.DisableServiceAccess("fake-offering", "fake-broker-name", "", "")
   683  				Expect(err).NotTo(HaveOccurred())
   684  				Expect(warnings).To(ContainElements("some warning", "other warning"))
   685  
   686  				Expect(fakeCloudControllerClient.GetServicePlansCallCount()).To(Equal(1))
   687  				Expect(fakeCloudControllerClient.GetServicePlansArgsForCall(0)).To(ConsistOf(ccv3.Query{
   688  					Key:    ccv3.ServiceOfferingGUIDsFilter,
   689  					Values: []string{"fake-offering-guid"},
   690  				}))
   691  			})
   692  
   693  			When("a plan name is specified", func() {
   694  				It("filters by plan name and service offering GUID", func() {
   695  					_, warnings, err := actor.DisableServiceAccess("fake-offering", "fake-broker-name", "", "fake-plan-name")
   696  					Expect(err).NotTo(HaveOccurred())
   697  					Expect(warnings).To(ContainElements("some warning", "other warning"))
   698  
   699  					Expect(fakeCloudControllerClient.GetServicePlansCallCount()).To(Equal(1))
   700  					Expect(fakeCloudControllerClient.GetServicePlansArgsForCall(0)).To(ConsistOf(
   701  						ccv3.Query{
   702  							Key:    ccv3.ServiceOfferingGUIDsFilter,
   703  							Values: []string{"fake-offering-guid"},
   704  						},
   705  						ccv3.Query{
   706  							Key:    ccv3.NameFilter,
   707  							Values: []string{"fake-plan-name"},
   708  						},
   709  					))
   710  				})
   711  			})
   712  
   713  			When("no plans were found", func() {
   714  				BeforeEach(func() {
   715  					fakeCloudControllerClient.GetServicePlansReturns([]resources.ServicePlan{}, ccv3.Warnings{"other warning"}, nil)
   716  				})
   717  
   718  				It("fails", func() {
   719  					_, warnings, err := actor.DisableServiceAccess("fake-offering", "fake-broker-name", "", "fake-plan-name")
   720  					Expect(err).To(MatchError(actionerror.ServicePlanNotFoundError{
   721  						OfferingName: "fake-offering",
   722  						PlanName:     "fake-plan-name",
   723  					}))
   724  					Expect(warnings).To(ContainElements("some warning", "other warning"))
   725  				})
   726  			})
   727  
   728  			When("fetching the plans fail", func() {
   729  				BeforeEach(func() {
   730  					fakeCloudControllerClient.GetServicePlansReturns([]resources.ServicePlan{}, ccv3.Warnings{"other warning"}, errors.New("fetch plans error"))
   731  				})
   732  
   733  				It("fails", func() {
   734  					_, warnings, err := actor.DisableServiceAccess("", "", "", "")
   735  					Expect(err).To(MatchError("fetch plans error"))
   736  					Expect(warnings).To(ContainElements("some warning", "other warning"))
   737  				})
   738  			})
   739  		})
   740  
   741  		Context("with org", func() {
   742  			BeforeEach(func() {
   743  				fakeCloudControllerClient.DeleteServicePlanVisibilityReturns(
   744  					ccv3.Warnings{"delete warning"},
   745  					nil,
   746  				)
   747  			})
   748  
   749  			It("disables visibility to orgs", func() {
   750  				_, warnings, err := actor.DisableServiceAccess("fake-offering", "", "fake-org-name", "")
   751  
   752  				Expect(err).NotTo(HaveOccurred())
   753  				Expect(warnings).To(ConsistOf("some warning", "other warning", "delete warning", "delete warning", "org warning"))
   754  
   755  				Expect(fakeCloudControllerClient.UpdateServicePlanVisibilityCallCount()).To(Equal(0))
   756  				Expect(fakeCloudControllerClient.DeleteServicePlanVisibilityCallCount()).To(Equal(2))
   757  
   758  				planGUID, orgGUID := fakeCloudControllerClient.DeleteServicePlanVisibilityArgsForCall(0)
   759  				Expect(planGUID).To(Equal("fake-plan-guid-1"))
   760  				Expect(orgGUID).To(Equal("org-guid"))
   761  
   762  				planGUID, orgGUID = fakeCloudControllerClient.DeleteServicePlanVisibilityArgsForCall(1)
   763  				Expect(planGUID).To(Equal("fake-plan-guid-2"))
   764  				Expect(orgGUID).To(Equal("org-guid"))
   765  			})
   766  
   767  			When("one of the plans is public", func() {
   768  				It("fails and does not update other plans", func() {
   769  					fakeCloudControllerClient.GetServicePlansReturns(
   770  						[]resources.ServicePlan{
   771  							{Name: "fake-plan-1", GUID: "fake-plan-guid-1", VisibilityType: "organization"},
   772  							{Name: "fake-plan-2", GUID: "fake-plan-guid-2", VisibilityType: "organization"},
   773  							{Name: "fake-plan-3", GUID: "fake-plan-guid-3", VisibilityType: "public"},
   774  							{Name: "fake-plan-4", GUID: "fake-plan-guid-4", VisibilityType: "organization"},
   775  						},
   776  						ccv3.Warnings{"other warning"},
   777  						nil,
   778  					)
   779  
   780  					_, warnings, err := actor.DisableServiceAccess("fake-offering", "", "fake-org-name", "")
   781  					Expect(err).To(MatchError("Cannot remove organization level access for public plans."))
   782  					Expect(fakeCloudControllerClient.DeleteServicePlanVisibilityCallCount()).To(Equal(0))
   783  					Expect(warnings).To(ConsistOf("some warning", "other warning", "org warning"))
   784  				})
   785  			})
   786  
   787  			When("there are admin access plans", func() {
   788  				It("reports that they were skipped", func() {
   789  					fakeCloudControllerClient.GetServicePlansReturns(
   790  						[]resources.ServicePlan{
   791  							{Name: "fake-plan-1", GUID: "fake-plan-guid-1", VisibilityType: "admin"},
   792  							{Name: "fake-plan-2", GUID: "fake-plan-guid-2", VisibilityType: "organization"},
   793  							{Name: "fake-plan-3", GUID: "fake-plan-guid-3", VisibilityType: "organization"},
   794  							{Name: "fake-plan-4", GUID: "fake-plan-guid-4", VisibilityType: "admin"},
   795  						},
   796  						ccv3.Warnings{"other warning"},
   797  						nil,
   798  					)
   799  
   800  					skipped, warnings, err := actor.DisableServiceAccess("fake-offering", "", "fake-org-name", "")
   801  					Expect(err).NotTo(HaveOccurred())
   802  					Expect(warnings).To(ConsistOf("some warning", "other warning", "org warning", "delete warning", "delete warning"))
   803  
   804  					Expect(skipped).To(ConsistOf("fake-plan-1", "fake-plan-4"))
   805  
   806  					Expect(fakeCloudControllerClient.DeleteServicePlanVisibilityCallCount()).To(Equal(2))
   807  				})
   808  			})
   809  
   810  			When("the org does not exist", func() {
   811  				It("returns an error", func() {
   812  					fakeCloudControllerClient.GetOrganizationsReturns([]resources.Organization{}, ccv3.Warnings{"org warning"}, nil)
   813  
   814  					_, warnings, err := actor.DisableServiceAccess("fake-offering", "", "fake-org-name", "")
   815  					Expect(err).To(MatchError(actionerror.OrganizationNotFoundError{
   816  						Name: "fake-org-name",
   817  					}))
   818  					Expect(warnings).To(ConsistOf("some warning", "other warning", "org warning"))
   819  				})
   820  			})
   821  
   822  			When("deleting access fails", func() {
   823  				It("fails", func() {
   824  					fakeCloudControllerClient.DeleteServicePlanVisibilityReturns(
   825  						ccv3.Warnings{"delete warning"},
   826  						errors.New("delete failed"),
   827  					)
   828  
   829  					_, warnings, err := actor.DisableServiceAccess("fake-offering", "", "fake-org-name", "")
   830  					Expect(err).To(MatchError("delete failed"))
   831  					Expect(warnings).To(ConsistOf("some warning", "other warning", "org warning", "delete warning"))
   832  
   833  				})
   834  			})
   835  		})
   836  
   837  		When("there are admin access plans", func() {
   838  			It("reports that they were skipped", func() {
   839  				fakeCloudControllerClient.GetServicePlansReturns(
   840  					[]resources.ServicePlan{
   841  						{Name: "fake-plan-1", GUID: "fake-plan-guid-1", VisibilityType: "admin"},
   842  						{Name: "fake-plan-2", GUID: "fake-plan-guid-2", VisibilityType: "organization"},
   843  						{Name: "fake-plan-3", GUID: "fake-plan-guid-3", VisibilityType: "organization"},
   844  						{Name: "fake-plan-4", GUID: "fake-plan-guid-4", VisibilityType: "admin"},
   845  					},
   846  					ccv3.Warnings{"other warning"},
   847  					nil,
   848  				)
   849  
   850  				skipped, warnings, err := actor.DisableServiceAccess("fake-offering", "", "", "")
   851  				Expect(err).NotTo(HaveOccurred())
   852  				Expect(warnings).To(ConsistOf("some warning", "other warning", "post warning", "post warning"))
   853  
   854  				Expect(skipped).To(ConsistOf("fake-plan-1", "fake-plan-4"))
   855  
   856  				Expect(fakeCloudControllerClient.UpdateServicePlanVisibilityCallCount()).To(Equal(2))
   857  			})
   858  		})
   859  
   860  		When("setting visibility fails", func() {
   861  			BeforeEach(func() {
   862  				fakeCloudControllerClient.UpdateServicePlanVisibilityReturns(
   863  					resources.ServicePlanVisibility{},
   864  					ccv3.Warnings{"post warning"},
   865  					errors.New("post error"),
   866  				)
   867  			})
   868  
   869  			It("returns error and stops setting visibility for the remaining plans", func() {
   870  				_, warnings, err := actor.DisableServiceAccess("fake-offering", "", "", "")
   871  
   872  				Expect(err).To(HaveOccurred())
   873  				Expect(err).To(MatchError("post error"))
   874  
   875  				Expect(fakeCloudControllerClient.UpdateServicePlanVisibilityCallCount()).To(Equal(1))
   876  				Expect(warnings).To(ConsistOf("some warning", "other warning", "post warning"))
   877  			})
   878  		})
   879  
   880  		When("the plan has visibility type 'space'", func() {
   881  			It("returns the appropriate error", func() {
   882  				fakeCloudControllerClient.GetServicePlansReturns(
   883  					[]resources.ServicePlan{
   884  						{GUID: "fake-plan-guid-1", VisibilityType: "space"},
   885  					},
   886  					ccv3.Warnings{"other warning"},
   887  					nil,
   888  				)
   889  				_, warnings, err := actor.DisableServiceAccess("fake-offering", "", "", "")
   890  				Expect(err).To(MatchError(actionerror.ServicePlanVisibilityTypeError{}))
   891  				Expect(warnings).To(ContainElement("other warning"))
   892  			})
   893  		})
   894  	})
   895  })
   896  
   897  func fakeServicePlans() []ccv3.ServicePlanWithSpaceAndOrganization {
   898  	return []ccv3.ServicePlanWithSpaceAndOrganization{
   899  		{
   900  			Name:                "violet",
   901  			ServiceOfferingGUID: "magenta-offering-guid",
   902  			VisibilityType:      "public",
   903  		},
   904  		{
   905  			Name:                "green",
   906  			ServiceOfferingGUID: "cyan-offering-guid",
   907  			VisibilityType:      "space",
   908  			SpaceName:           "some-space",
   909  			OrganizationName:    "some-org",
   910  		},
   911  		{
   912  			Name:                "indigo",
   913  			ServiceOfferingGUID: "key-offering-guid",
   914  			VisibilityType:      "space",
   915  			SpaceName:           "some-space",
   916  			OrganizationName:    "some-org",
   917  		},
   918  		{
   919  			Name:                "red",
   920  			ServiceOfferingGUID: "magenta-offering-guid",
   921  			VisibilityType:      "public",
   922  		},
   923  		{
   924  			Name:                "yellow",
   925  			ServiceOfferingGUID: "yellow-offering-guid",
   926  			VisibilityType:      "organization",
   927  		},
   928  		{
   929  			Name:                "orange",
   930  			ServiceOfferingGUID: "yellow-offering-guid",
   931  			VisibilityType:      "organization",
   932  		},
   933  		{
   934  			Name:                "blue",
   935  			ServiceOfferingGUID: "cyan-offering-guid",
   936  			VisibilityType:      "space",
   937  			SpaceName:           "some-space",
   938  			OrganizationName:    "some-org",
   939  		},
   940  	}
   941  }
   942  
   943  func fakeServiceOfferings() []resources.ServiceOffering {
   944  	return []resources.ServiceOffering{
   945  		{
   946  			GUID:              "cyan-offering-guid",
   947  			Name:              "cyan",
   948  			ServiceBrokerName: "sky-broker",
   949  		},
   950  		{
   951  			GUID:              "magenta-offering-guid",
   952  			Name:              "magenta",
   953  			ServiceBrokerName: "sea-broker",
   954  		},
   955  		{
   956  			GUID:              "yellow-offering-guid",
   957  			Name:              "yellow",
   958  			ServiceBrokerName: "land-broker",
   959  		},
   960  		{
   961  			GUID:              "key-offering-guid",
   962  			Name:              "key",
   963  			ServiceBrokerName: "sky-broker",
   964  		},
   965  	}
   966  }