github.com/sleungcy/cli@v7.1.0+incompatible/api/cloudcontroller/ccv3/service_offering_test.go (about)

     1  package ccv3_test
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net/http"
     7  
     8  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
     9  	. "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
    10  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/ccv3fakes"
    11  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/internal"
    12  	"code.cloudfoundry.org/cli/resources"
    13  	. "github.com/onsi/ginkgo"
    14  	. "github.com/onsi/gomega"
    15  	. "github.com/onsi/gomega/ghttp"
    16  	. "github.com/onsi/gomega/gstruct"
    17  )
    18  
    19  var _ = Describe("Service Offering", func() {
    20  	Describe("GetServiceOfferings", func() {
    21  		var (
    22  			client *Client
    23  			query  []Query
    24  
    25  			offerings  []resources.ServiceOffering
    26  			warnings   Warnings
    27  			executeErr error
    28  		)
    29  
    30  		BeforeEach(func() {
    31  			client, _ = NewTestClient()
    32  		})
    33  
    34  		JustBeforeEach(func() {
    35  			offerings, warnings, executeErr = client.GetServiceOfferings(query...)
    36  		})
    37  
    38  		When("service offerings exist", func() {
    39  			BeforeEach(func() {
    40  				response1 := fmt.Sprintf(`
    41  					{
    42  						"pagination": {
    43  							"next": {
    44  								"href": "%s/v3/service_offerings?names=myServiceOffering&service_broker_names=myServiceBroker&fields[service_broker]=name,guid&page=2"
    45  							}
    46  						},
    47  						"resources": [
    48  							{
    49  								"guid": "service-offering-1-guid",
    50  								"name": "service-offering-1-name",
    51  								"relationships": {
    52  									"service_broker": {
    53  										"data": {
    54  											"guid": "overview-broker-guid"
    55  										}
    56  									}
    57  								}
    58  							},
    59  							{
    60  								"guid": "service-offering-2-guid",
    61  								"name": "service-offering-2-name",
    62  								"relationships": {
    63  									"service_broker": {
    64  										"data": {
    65  											"guid": "overview-broker-guid"
    66  										}
    67  									}
    68  								}
    69  							}
    70  						],
    71  						"included": {
    72  							"service_brokers": [
    73  								{
    74  									"guid": "overview-broker-guid",
    75  									"name": "overview-broker"
    76  								}
    77  							]	
    78  						}
    79  					}`,
    80  					server.URL())
    81  
    82  				response2 := `
    83  					{
    84  						"pagination": {
    85  							"next": {
    86  								"href": null
    87  							}
    88  						},
    89  						"resources": [
    90  							{
    91  								"guid": "service-offering-3-guid",
    92  								"name": "service-offering-3-name",
    93  								"relationships": {
    94  									"service_broker": {
    95  										"data": {
    96  											"guid": "other-broker-guid"
    97  										}
    98  									}
    99  								}
   100  							}
   101  						],
   102  						"included": {
   103  							"service_brokers": [
   104  								{
   105  									"guid": "other-broker-guid",
   106  									"name": "other-broker"
   107  								}
   108  							]	
   109  						}
   110  					}`
   111  
   112  				server.AppendHandlers(
   113  					CombineHandlers(
   114  						VerifyRequest(http.MethodGet, "/v3/service_offerings", "names=myServiceOffering&service_broker_names=myServiceBroker&fields[service_broker]=name,guid"),
   115  						RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"warning-1"}}),
   116  					),
   117  					CombineHandlers(
   118  						VerifyRequest(http.MethodGet, "/v3/service_offerings", "names=myServiceOffering&service_broker_names=myServiceBroker&fields[service_broker]=name,guid&page=2"),
   119  						RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"warning-2"}}),
   120  					),
   121  				)
   122  
   123  				query = []Query{
   124  					{
   125  						Key:    NameFilter,
   126  						Values: []string{"myServiceOffering"},
   127  					},
   128  					{
   129  						Key:    ServiceBrokerNamesFilter,
   130  						Values: []string{"myServiceBroker"},
   131  					},
   132  				}
   133  			})
   134  
   135  			It("returns a list of service offerings with their associated warnings", func() {
   136  				Expect(executeErr).ToNot(HaveOccurred())
   137  
   138  				Expect(offerings).To(ConsistOf(
   139  					resources.ServiceOffering{
   140  						GUID:              "service-offering-1-guid",
   141  						Name:              "service-offering-1-name",
   142  						ServiceBrokerName: "overview-broker",
   143  						ServiceBrokerGUID: "overview-broker-guid",
   144  					},
   145  					resources.ServiceOffering{
   146  						GUID:              "service-offering-2-guid",
   147  						Name:              "service-offering-2-name",
   148  						ServiceBrokerName: "overview-broker",
   149  						ServiceBrokerGUID: "overview-broker-guid",
   150  					},
   151  					resources.ServiceOffering{
   152  						GUID:              "service-offering-3-guid",
   153  						Name:              "service-offering-3-name",
   154  						ServiceBrokerName: "other-broker",
   155  						ServiceBrokerGUID: "other-broker-guid",
   156  					},
   157  				))
   158  				Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   159  			})
   160  		})
   161  
   162  		When("the cloud controller returns errors and warnings", func() {
   163  			BeforeEach(func() {
   164  				response := `{
   165  					"errors": [
   166  						{
   167  							"code": 42424,
   168  							"detail": "Some detailed error message",
   169  							"title": "CF-SomeErrorTitle"
   170  						},
   171  						{
   172  							"code": 11111,
   173  							"detail": "Some other detailed error message",
   174  							"title": "CF-SomeOtherErrorTitle"
   175  						}
   176  					]
   177  				}`
   178  
   179  				server.AppendHandlers(
   180  					CombineHandlers(
   181  						VerifyRequest(http.MethodGet, "/v3/service_offerings"),
   182  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   183  					),
   184  				)
   185  			})
   186  
   187  			It("returns the error and all warnings", func() {
   188  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   189  					ResponseCode: http.StatusTeapot,
   190  					Errors: []ccerror.V3Error{
   191  						{
   192  							Code:   42424,
   193  							Detail: "Some detailed error message",
   194  							Title:  "CF-SomeErrorTitle",
   195  						},
   196  						{
   197  							Code:   11111,
   198  							Detail: "Some other detailed error message",
   199  							Title:  "CF-SomeOtherErrorTitle",
   200  						},
   201  					},
   202  				}))
   203  				Expect(warnings).To(ConsistOf("this is a warning"))
   204  			})
   205  		})
   206  	})
   207  
   208  	Describe("GetServiceOfferingByNameAndBroker", func() {
   209  		const (
   210  			serviceOfferingName = "myServiceOffering"
   211  		)
   212  
   213  		var (
   214  			client            *Client
   215  			requester         *ccv3fakes.FakeRequester
   216  			serviceBrokerName string
   217  			offering          resources.ServiceOffering
   218  			warnings          Warnings
   219  			executeErr        error
   220  		)
   221  
   222  		BeforeEach(func() {
   223  			requester = new(ccv3fakes.FakeRequester)
   224  			client, _ = NewFakeRequesterTestClient(requester)
   225  
   226  			serviceBrokerName = ""
   227  		})
   228  
   229  		JustBeforeEach(func() {
   230  			offering, warnings, executeErr = client.GetServiceOfferingByNameAndBroker(serviceOfferingName, serviceBrokerName)
   231  		})
   232  
   233  		When("there is a single match", func() {
   234  			BeforeEach(func() {
   235  				requester.MakeListRequestCalls(func(requestParams RequestParams) (IncludedResources, Warnings, error) {
   236  					err := requestParams.AppendToList(resources.ServiceOffering{GUID: "service-offering-guid-1"})
   237  					Expect(err).NotTo(HaveOccurred())
   238  					return IncludedResources{}, Warnings{"this is a warning"}, nil
   239  				})
   240  			})
   241  
   242  			It("makes the correct request", func() {
   243  				Expect(requester.MakeListRequestCallCount()).To(Equal(1))
   244  				Expect(requester.MakeListRequestArgsForCall(0)).To(MatchFields(IgnoreExtras, Fields{
   245  					"RequestName": Equal(internal.GetServiceOfferingsRequest),
   246  					"Query": Equal([]Query{
   247  						{Key: NameFilter, Values: []string{serviceOfferingName}},
   248  						{Key: FieldsServiceBroker, Values: []string{"name", "guid"}},
   249  					}),
   250  					"ResponseBody": Equal(resources.ServiceOffering{}),
   251  				}))
   252  			})
   253  
   254  			It("returns the service offering and warnings", func() {
   255  				Expect(offering).To(Equal(resources.ServiceOffering{GUID: "service-offering-guid-1"}))
   256  				Expect(warnings).To(ConsistOf("this is a warning"))
   257  				Expect(executeErr).NotTo(HaveOccurred())
   258  			})
   259  		})
   260  
   261  		When("there are no matches", func() {
   262  			BeforeEach(func() {
   263  				requester.MakeListRequestCalls(func(requestParams RequestParams) (IncludedResources, Warnings, error) {
   264  					return IncludedResources{}, Warnings{"this is a warning"}, nil
   265  				})
   266  
   267  				serviceBrokerName = "myServiceBroker"
   268  			})
   269  
   270  			It("returns an error and warnings", func() {
   271  				Expect(warnings).To(ConsistOf("this is a warning"))
   272  				Expect(executeErr).To(MatchError(ccerror.ServiceOfferingNotFoundError{
   273  					ServiceOfferingName: serviceOfferingName,
   274  					ServiceBrokerName:   serviceBrokerName,
   275  				}))
   276  			})
   277  		})
   278  
   279  		When("there is more than one match", func() {
   280  			BeforeEach(func() {
   281  				requester.MakeListRequestCalls(func(requestParams RequestParams) (IncludedResources, Warnings, error) {
   282  					err := requestParams.AppendToList(resources.ServiceOffering{
   283  						GUID:              "service-offering-guid-1",
   284  						Name:              serviceOfferingName,
   285  						ServiceBrokerGUID: "broker-1-guid",
   286  					})
   287  					Expect(err).NotTo(HaveOccurred())
   288  					err = requestParams.AppendToList(resources.ServiceOffering{
   289  						GUID:              "service-offering-guid-2",
   290  						Name:              serviceOfferingName,
   291  						ServiceBrokerGUID: "broker-2-guid",
   292  					})
   293  					Expect(err).NotTo(HaveOccurred())
   294  					return IncludedResources{
   295  							ServiceBrokers: []resources.ServiceBroker{
   296  								{GUID: "broker-1-guid", Name: "broker-1"},
   297  								{GUID: "broker-2-guid", Name: "broker-2"},
   298  							}},
   299  						Warnings{"this is a warning"},
   300  						nil
   301  				})
   302  			})
   303  
   304  			It("returns an error and warnings", func() {
   305  				Expect(warnings).To(ConsistOf("this is a warning"))
   306  				Expect(executeErr).To(MatchError(ccerror.ServiceOfferingNameAmbiguityError{
   307  					ServiceOfferingName: serviceOfferingName,
   308  					ServiceBrokerNames:  []string{"broker-1", "broker-2"},
   309  				}))
   310  			})
   311  		})
   312  
   313  		When("the broker name is specified", func() {
   314  			BeforeEach(func() {
   315  				serviceBrokerName = "myServiceBroker"
   316  			})
   317  
   318  			It("makes the correct request", func() {
   319  				Expect(requester.MakeListRequestCallCount()).To(Equal(1))
   320  				Expect(requester.MakeListRequestArgsForCall(0).Query).To(ConsistOf(
   321  					Query{Key: NameFilter, Values: []string{serviceOfferingName}},
   322  					Query{Key: ServiceBrokerNamesFilter, Values: []string{"myServiceBroker"}},
   323  					Query{Key: FieldsServiceBroker, Values: []string{"name", "guid"}},
   324  				))
   325  			})
   326  		})
   327  
   328  		When("the requester returns an error", func() {
   329  			BeforeEach(func() {
   330  				requester.MakeListRequestReturns(IncludedResources{}, Warnings{"this is a warning"}, errors.New("bang"))
   331  			})
   332  
   333  			It("returns an error and warnings", func() {
   334  				Expect(warnings).To(ConsistOf("this is a warning"))
   335  				Expect(executeErr).To(MatchError("bang"))
   336  			})
   337  		})
   338  	})
   339  
   340  	Describe("PurgeServiceOffering", func() {
   341  		const serviceOfferingGUID = "fake-service-offering-guid"
   342  
   343  		var (
   344  			client     *Client
   345  			warnings   Warnings
   346  			executeErr error
   347  		)
   348  
   349  		BeforeEach(func() {
   350  			client, _ = NewTestClient()
   351  		})
   352  
   353  		JustBeforeEach(func() {
   354  			warnings, executeErr = client.PurgeServiceOffering(serviceOfferingGUID)
   355  		})
   356  
   357  		When("the Cloud Controller successfully purges the service offering", func() {
   358  			BeforeEach(func() {
   359  				server.AppendHandlers(
   360  					CombineHandlers(
   361  						VerifyRequest(http.MethodDelete, "/v3/service_offerings/fake-service-offering-guid", "purge=true"),
   362  						RespondWith(http.StatusNoContent, nil, http.Header{
   363  							"X-Cf-Warnings": {"this is a warning"},
   364  						}),
   365  					),
   366  				)
   367  			})
   368  
   369  			It("succeeds and returns warnings", func() {
   370  				Expect(executeErr).NotTo(HaveOccurred())
   371  				Expect(warnings).To(ConsistOf("this is a warning"))
   372  			})
   373  		})
   374  
   375  		When("the Cloud Controller fails to purge the service offering", func() {
   376  			BeforeEach(func() {
   377  				response := `{
   378                                 "errors": [
   379                                   {
   380                                     "code": 10008,
   381  								   "detail": "The request is semantically invalid: command presence",
   382  								   "title": "CF-UnprocessableEntity"
   383  								 },
   384  								 {
   385  								   "code": 10010,
   386  								   "detail": "Service offering not found",
   387  								   "title": "CF-ResourceNotFound"
   388  								 }
   389  							   ]
   390  							 }`
   391  
   392  				server.AppendHandlers(
   393  					CombineHandlers(
   394  						VerifyRequest(http.MethodDelete, "/v3/service_offerings/fake-service-offering-guid", "purge=true"),
   395  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   396  					),
   397  				)
   398  			})
   399  
   400  			It("returns parsed errors and warnings", func() {
   401  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   402  					ResponseCode: http.StatusTeapot,
   403  					Errors: []ccerror.V3Error{
   404  						{
   405  							Code:   10008,
   406  							Detail: "The request is semantically invalid: command presence",
   407  							Title:  "CF-UnprocessableEntity",
   408  						},
   409  						{
   410  							Code:   10010,
   411  							Detail: "Service offering not found",
   412  							Title:  "CF-ResourceNotFound",
   413  						},
   414  					},
   415  				}))
   416  				Expect(warnings).To(ConsistOf("this is a warning"))
   417  			})
   418  		})
   419  	})
   420  
   421  })