github.com/franc20/ayesa_sap@v7.0.0-beta.28.0.20200124003224-302d4d52fa6c+incompatible/api/cloudcontroller/ccv3/service_broker_test.go (about)

     1  package ccv3_test
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  
     7  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
     8  	. "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
     9  	"code.cloudfoundry.org/cli/types"
    10  	. "github.com/onsi/ginkgo"
    11  	. "github.com/onsi/gomega"
    12  	. "github.com/onsi/gomega/ghttp"
    13  )
    14  
    15  var _ = Describe("ServiceBroker", func() {
    16  	var client *Client
    17  
    18  	BeforeEach(func() {
    19  		client, _ = NewTestClient()
    20  	})
    21  
    22  	Describe("GetServiceBrokers", func() {
    23  		var (
    24  			query          []Query
    25  			serviceBrokers []ServiceBroker
    26  			warnings       Warnings
    27  			executeErr     error
    28  		)
    29  
    30  		JustBeforeEach(func() {
    31  			serviceBrokers, warnings, executeErr = client.GetServiceBrokers(query...)
    32  		})
    33  
    34  		When("there are no service brokers", func() {
    35  			BeforeEach(func() {
    36  				response := `
    37  					{
    38  						"pagination": {
    39  							"next": null
    40  						},
    41  						"resources": []
    42  					}`
    43  
    44  				server.AppendHandlers(
    45  					CombineHandlers(
    46  						VerifyRequest(http.MethodGet, "/v3/service_brokers"),
    47  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
    48  					),
    49  				)
    50  			})
    51  
    52  			It("returns an empty list", func() {
    53  				Expect(executeErr).NotTo(HaveOccurred())
    54  				Expect(serviceBrokers).To(HaveLen(0))
    55  				Expect(warnings).To(ConsistOf("this is a warning"))
    56  			})
    57  		})
    58  
    59  		When("there is a service broker", func() {
    60  			BeforeEach(func() {
    61  				response := `
    62  					{
    63  						"pagination": {
    64  							"next": null
    65  						},
    66  						"resources": [
    67  							{
    68  								"name": "service-broker-name-1",
    69  								"guid": "service-broker-guid-1",
    70  								"url": "service-broker-url-1",
    71  							    "status": "synchronization in progress"
    72  							}
    73  						]
    74  					}`
    75  
    76  				server.AppendHandlers(
    77  					CombineHandlers(
    78  						VerifyRequest(http.MethodGet, "/v3/service_brokers"),
    79  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is another warning"}}),
    80  					),
    81  				)
    82  			})
    83  
    84  			It("returns the service broker", func() {
    85  				Expect(executeErr).NotTo(HaveOccurred())
    86  				Expect(serviceBrokers).To(ConsistOf(ServiceBroker{
    87  					Name:     "service-broker-name-1",
    88  					GUID:     "service-broker-guid-1",
    89  					URL:      "service-broker-url-1",
    90  					Status:   "synchronization in progress",
    91  					Metadata: nil,
    92  				}))
    93  				Expect(warnings).To(ConsistOf("this is another warning"))
    94  			})
    95  		})
    96  
    97  		When("the service broker has labels", func() {
    98  			BeforeEach(func() {
    99  				response := `
   100  					{
   101  						"pagination": {
   102  							"next": null
   103  						},
   104  						"resources": [
   105  							{
   106  								"name": "service-broker-name-1",
   107  								"guid": "service-broker-guid-1",
   108  								"url": "service-broker-url-1",
   109  							    "status": "synchronization in progress",
   110  								"metadata": {
   111  									"labels": {
   112  										"some-key":"some-value",
   113  										"other-key":"other-value"
   114  									}
   115  								}
   116  							}
   117  						]
   118  					}`
   119  
   120  				server.AppendHandlers(
   121  					CombineHandlers(
   122  						VerifyRequest(http.MethodGet, "/v3/service_brokers"),
   123  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is another warning"}}),
   124  					),
   125  				)
   126  			})
   127  
   128  			It("returns the service broker with the labels in Metadata", func() {
   129  				Expect(executeErr).NotTo(HaveOccurred())
   130  				Expect(serviceBrokers).To(ConsistOf(ServiceBroker{
   131  					Name:   "service-broker-name-1",
   132  					GUID:   "service-broker-guid-1",
   133  					URL:    "service-broker-url-1",
   134  					Status: "synchronization in progress",
   135  					Metadata: &Metadata{
   136  						Labels: map[string]types.NullString{
   137  							"some-key":  types.NewNullString("some-value"),
   138  							"other-key": types.NewNullString("other-value"),
   139  						},
   140  					},
   141  				}))
   142  			})
   143  		})
   144  
   145  		When("there is more than one page of service brokers", func() {
   146  			BeforeEach(func() {
   147  				response1 := fmt.Sprintf(`
   148  					{
   149  						"pagination": {
   150  							"next": {
   151  								"href": "%s/v3/service_brokers?page=2&per_page=2"
   152  							}
   153  						},
   154  						"resources": [
   155  							{
   156  								"name": "service-broker-name-1",
   157  								"guid": "service-broker-guid-1",
   158  								"url": "service-broker-url-1",
   159  							    "status": "synchronization in progress",
   160  								"relationships": {}
   161  							},
   162  							{
   163  								"name": "service-broker-name-2",
   164  								"guid": "service-broker-guid-2",
   165  								"url": "service-broker-url-2",
   166  								"status": "synchronization failed",
   167  								"relationships": {}
   168  							}
   169  						]
   170  					}`, server.URL())
   171  
   172  				response2 := `
   173  					{
   174  						"pagination": {
   175  							"next": null
   176  						},
   177  						"resources": [
   178  							{
   179  								"name": "service-broker-name-3",
   180  								"guid": "service-broker-guid-3",
   181  								"url": "service-broker-url-3",
   182  								"status": "available",
   183  								"relationships": {}
   184  							}
   185  						]
   186  					}`
   187  
   188  				server.AppendHandlers(
   189  					CombineHandlers(
   190  						VerifyRequest(http.MethodGet, "/v3/service_brokers"),
   191  						RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   192  					),
   193  				)
   194  				server.AppendHandlers(
   195  					CombineHandlers(
   196  						VerifyRequest(http.MethodGet, "/v3/service_brokers", "page=2&per_page=2"),
   197  						RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"this is another warning"}}),
   198  					),
   199  				)
   200  			})
   201  
   202  			It("returns the queried service-broker and all warnings", func() {
   203  				Expect(executeErr).NotTo(HaveOccurred())
   204  
   205  				Expect(serviceBrokers).To(ConsistOf(
   206  					ServiceBroker{Name: "service-broker-name-1", GUID: "service-broker-guid-1", URL: "service-broker-url-1", Status: "synchronization in progress"},
   207  					ServiceBroker{Name: "service-broker-name-2", GUID: "service-broker-guid-2", URL: "service-broker-url-2", Status: "synchronization failed"},
   208  					ServiceBroker{Name: "service-broker-name-3", GUID: "service-broker-guid-3", URL: "service-broker-url-3", Status: "available"},
   209  				))
   210  				Expect(warnings).To(ConsistOf("this is a warning", "this is another warning"))
   211  			})
   212  		})
   213  
   214  		When("a filter is specified", func() {
   215  			BeforeEach(func() {
   216  				query = []Query{
   217  					{
   218  						Key:    NameFilter,
   219  						Values: []string{"special-unicorn-broker"},
   220  					},
   221  				}
   222  
   223  				response := `
   224  					{
   225  						"pagination": {
   226  							"next": null
   227  						},
   228  						"resources": [
   229  							{
   230  								"name": "special-unicorn-broker",
   231  								"guid": "service-broker-guid-1",
   232  								"url": "service-broker-url-1",
   233  							    "status": "synchronization in progress"
   234  							}
   235  						]
   236  					}`
   237  
   238  				server.AppendHandlers(
   239  					CombineHandlers(
   240  						VerifyRequest(http.MethodGet, "/v3/service_brokers", "names=special-unicorn-broker"),
   241  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is another warning"}}),
   242  					),
   243  				)
   244  			})
   245  
   246  			It("passes the filter in the query and returns the result", func() {
   247  				Expect(executeErr).NotTo(HaveOccurred())
   248  				Expect(serviceBrokers[0].Name).To(Equal("special-unicorn-broker"))
   249  				Expect(warnings).To(ConsistOf("this is another warning"))
   250  			})
   251  		})
   252  
   253  		When("the cloud controller returns errors and warnings", func() {
   254  			BeforeEach(func() {
   255  				response := `{
   256  	  "errors": [
   257  	    {
   258  	      "code": 10008,
   259  	      "detail": "The request is semantically invalid: command presence",
   260  	      "title": "CF-UnprocessableEntity"
   261  	    },
   262  			{
   263  	      "code": 10010,
   264  	      "detail": "Isolation segment not found",
   265  	      "title": "CF-ResourceNotFound"
   266  	    }
   267  	  ]
   268  	}`
   269  				server.AppendHandlers(
   270  					CombineHandlers(
   271  						VerifyRequest(http.MethodGet, "/v3/service_brokers"),
   272  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   273  					),
   274  				)
   275  			})
   276  
   277  			It("returns the error and all warnings", func() {
   278  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   279  					ResponseCode: http.StatusTeapot,
   280  					Errors: []ccerror.V3Error{
   281  						{
   282  							Code:   10008,
   283  							Detail: "The request is semantically invalid: command presence",
   284  							Title:  "CF-UnprocessableEntity",
   285  						},
   286  						{
   287  							Code:   10010,
   288  							Detail: "Isolation segment not found",
   289  							Title:  "CF-ResourceNotFound",
   290  						},
   291  					},
   292  				}))
   293  				Expect(warnings).To(ConsistOf("this is a warning"))
   294  			})
   295  		})
   296  	})
   297  
   298  	Describe("DeleteServiceBroker", func() {
   299  		var (
   300  			warnings          Warnings
   301  			executeErr        error
   302  			serviceBrokerGUID string
   303  			jobURL            JobURL
   304  		)
   305  
   306  		BeforeEach(func() {
   307  			serviceBrokerGUID = "some-service-broker-guid"
   308  		})
   309  
   310  		JustBeforeEach(func() {
   311  			jobURL, warnings, executeErr = client.DeleteServiceBroker(serviceBrokerGUID)
   312  		})
   313  
   314  		When("the Cloud Controller successfully deletes the broker", func() {
   315  			BeforeEach(func() {
   316  				server.AppendHandlers(
   317  					CombineHandlers(
   318  						VerifyRequest(http.MethodDelete, "/v3/service_brokers/some-service-broker-guid"),
   319  						RespondWith(http.StatusOK, "", http.Header{
   320  							"X-Cf-Warnings": {"this is a warning"},
   321  							"Location":      {"some-job-url"},
   322  						}),
   323  					),
   324  				)
   325  			})
   326  
   327  			It("succeeds and returns warnings", func() {
   328  				Expect(executeErr).NotTo(HaveOccurred())
   329  				Expect(warnings).To(ConsistOf("this is a warning"))
   330  				Expect(jobURL).To(Equal(JobURL("some-job-url")))
   331  			})
   332  		})
   333  
   334  		When("the broker is space scoped", func() {
   335  			BeforeEach(func() {
   336  				server.AppendHandlers(
   337  					CombineHandlers(
   338  						VerifyRequest(http.MethodDelete, "/v3/service_brokers/some-service-broker-guid"),
   339  						RespondWith(http.StatusOK, "", http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   340  					),
   341  				)
   342  			})
   343  
   344  			It("succeeds and returns warnings", func() {
   345  				Expect(executeErr).NotTo(HaveOccurred())
   346  				Expect(warnings).To(ConsistOf("this is a warning"))
   347  			})
   348  		})
   349  
   350  		When("the Cloud Controller fails to delete the broker", func() {
   351  			BeforeEach(func() {
   352  				response := `{
   353  	  "errors": [
   354  	    {
   355  	      "code": 10008,
   356  	      "detail": "The request is semantically invalid: command presence",
   357  	      "title": "CF-UnprocessableEntity"
   358  	    },
   359  			{
   360  	      "code": 10010,
   361  	      "detail": "Service broker not found",
   362  	      "title": "CF-ResourceNotFound"
   363  	    }
   364  	  ]
   365  	}`
   366  
   367  				server.AppendHandlers(
   368  					CombineHandlers(
   369  						VerifyRequest(http.MethodDelete, "/v3/service_brokers/some-service-broker-guid"),
   370  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   371  					),
   372  				)
   373  			})
   374  
   375  			It("returns parsed errors and warnings", func() {
   376  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   377  					ResponseCode: http.StatusTeapot,
   378  					Errors: []ccerror.V3Error{
   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 broker not found",
   387  							Title:  "CF-ResourceNotFound",
   388  						},
   389  					},
   390  				}))
   391  				Expect(warnings).To(ConsistOf("this is a warning"))
   392  			})
   393  		})
   394  	})
   395  
   396  	Describe("CreateServiceBroker", func() {
   397  		const (
   398  			name     = "name"
   399  			url      = "url"
   400  			username = "username"
   401  			password = "password"
   402  		)
   403  
   404  		var (
   405  			jobURL       JobURL
   406  			warnings     Warnings
   407  			executeErr   error
   408  			spaceGUID    string
   409  			expectedBody map[string]interface{}
   410  		)
   411  
   412  		BeforeEach(func() {
   413  			spaceGUID = ""
   414  			expectedBody = map[string]interface{}{
   415  				"name": "name",
   416  				"url":  "url",
   417  				"authentication": map[string]interface{}{
   418  					"type": "basic",
   419  					"credentials": map[string]string{
   420  						"username": "username",
   421  						"password": "password",
   422  					},
   423  				},
   424  			}
   425  		})
   426  
   427  		JustBeforeEach(func() {
   428  			serviceBrokerRequest := ServiceBrokerModel{
   429  				Name:      name,
   430  				URL:       url,
   431  				Username:  username,
   432  				Password:  password,
   433  				SpaceGUID: spaceGUID,
   434  			}
   435  			jobURL, warnings, executeErr = client.CreateServiceBroker(serviceBrokerRequest)
   436  		})
   437  
   438  		When("the Cloud Controller successfully creates the broker", func() {
   439  			BeforeEach(func() {
   440  				server.AppendHandlers(
   441  					CombineHandlers(
   442  						VerifyRequest(http.MethodPost, "/v3/service_brokers"),
   443  						VerifyJSONRepresenting(expectedBody),
   444  						RespondWith(http.StatusOK, "", http.Header{
   445  							"X-Cf-Warnings": {"this is a warning"},
   446  							"Location":      {"some-job-url"},
   447  						}),
   448  					),
   449  				)
   450  			})
   451  
   452  			It("succeeds, returns warnings and job URL", func() {
   453  				Expect(jobURL).To(Equal(JobURL("some-job-url")))
   454  				Expect(executeErr).NotTo(HaveOccurred())
   455  				Expect(warnings).To(ConsistOf("this is a warning"))
   456  			})
   457  		})
   458  
   459  		When("the broker is space scoped", func() {
   460  			BeforeEach(func() {
   461  				spaceGUID = "space-guid"
   462  				expectedBody["relationships"] = map[string]interface{}{
   463  					"space": map[string]interface{}{
   464  						"data": map[string]string{
   465  							"guid": "space-guid",
   466  						},
   467  					},
   468  				}
   469  				server.AppendHandlers(
   470  					CombineHandlers(
   471  						VerifyRequest(http.MethodPost, "/v3/service_brokers"),
   472  						VerifyJSONRepresenting(expectedBody),
   473  						RespondWith(http.StatusOK, "", http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   474  					),
   475  				)
   476  			})
   477  
   478  			It("succeeds and returns warnings", func() {
   479  				Expect(executeErr).NotTo(HaveOccurred())
   480  				Expect(warnings).To(ConsistOf("this is a warning"))
   481  			})
   482  		})
   483  
   484  		When("the Cloud Controller fails to create the broker", func() {
   485  			BeforeEach(func() {
   486  				response := `{
   487  	  "errors": [
   488  	    {
   489  	      "code": 10008,
   490  	      "detail": "The request is semantically invalid: command presence",
   491  	      "title": "CF-UnprocessableEntity"
   492  	    },
   493  			{
   494  	      "code": 10010,
   495  	      "detail": "Isolation segment not found",
   496  	      "title": "CF-ResourceNotFound"
   497  	    }
   498  	  ]
   499  	}`
   500  
   501  				server.AppendHandlers(
   502  					CombineHandlers(
   503  						VerifyRequest(http.MethodPost, "/v3/service_brokers"),
   504  						VerifyJSONRepresenting(expectedBody),
   505  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   506  					),
   507  				)
   508  			})
   509  
   510  			It("returns parsed errors and warnings", func() {
   511  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   512  					ResponseCode: http.StatusTeapot,
   513  					Errors: []ccerror.V3Error{
   514  						{
   515  							Code:   10008,
   516  							Detail: "The request is semantically invalid: command presence",
   517  							Title:  "CF-UnprocessableEntity",
   518  						},
   519  						{
   520  							Code:   10010,
   521  							Detail: "Isolation segment not found",
   522  							Title:  "CF-ResourceNotFound",
   523  						},
   524  					},
   525  				}))
   526  				Expect(warnings).To(ConsistOf("this is a warning"))
   527  			})
   528  		})
   529  	})
   530  
   531  	Describe("UpdateServiceBroker", func() {
   532  		var (
   533  			name, guid, url, username, password string
   534  			jobURL                              JobURL
   535  			warnings                            Warnings
   536  			executeErr                          error
   537  			expectedBody                        map[string]interface{}
   538  		)
   539  
   540  		BeforeEach(func() {
   541  			expectedBody = map[string]interface{}{
   542  				"url": "new-url",
   543  				"authentication": map[string]interface{}{
   544  					"type": "basic",
   545  					"credentials": map[string]string{
   546  						"username": "new-username",
   547  						"password": "new-password",
   548  					},
   549  				},
   550  			}
   551  			name = ""
   552  			guid = "broker-guid"
   553  			url = "new-url"
   554  			username = "new-username"
   555  			password = "new-password"
   556  		})
   557  
   558  		When("the Cloud Controller successfully updates the broker", func() {
   559  			BeforeEach(func() {
   560  				server.AppendHandlers(
   561  					CombineHandlers(
   562  						VerifyRequest(http.MethodPatch, "/v3/service_brokers/"+guid),
   563  						VerifyJSONRepresenting(expectedBody),
   564  						RespondWith(http.StatusOK, "", http.Header{
   565  							"X-Cf-Warnings": {"this is a warning"},
   566  							"Location":      {"some-job-url"},
   567  						}),
   568  					),
   569  				)
   570  			})
   571  
   572  			It("succeeds, returns warnings and job URL", func() {
   573  				jobURL, warnings, executeErr = client.UpdateServiceBroker(
   574  					guid,
   575  					ServiceBrokerModel{
   576  						Name:     name,
   577  						URL:      url,
   578  						Username: username,
   579  						Password: password,
   580  					})
   581  
   582  				Expect(jobURL).To(Equal(JobURL("some-job-url")))
   583  				Expect(executeErr).NotTo(HaveOccurred())
   584  				Expect(warnings).To(ConsistOf("this is a warning"))
   585  			})
   586  		})
   587  
   588  		When("the Cloud Controller fails to update the broker", func() {
   589  			BeforeEach(func() {
   590  				response := `{
   591  	  "errors": [
   592  	    {
   593  	      "code": 10008,
   594  	      "detail": "The request is semantically invalid: command presence",
   595  	      "title": "CF-UnprocessableEntity"
   596  	    },
   597  			{
   598  	      "code": 10010,
   599  	      "detail": "Isolation segment not found",
   600  	      "title": "CF-ResourceNotFound"
   601  	    }
   602  	  ]
   603  	}`
   604  
   605  				server.AppendHandlers(
   606  					CombineHandlers(
   607  						VerifyRequest(http.MethodPatch, "/v3/service_brokers/"+guid),
   608  						VerifyJSONRepresenting(expectedBody),
   609  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   610  					),
   611  				)
   612  			})
   613  
   614  			It("returns parsed errors and warnings", func() {
   615  				jobURL, warnings, executeErr = client.UpdateServiceBroker(guid,
   616  					ServiceBrokerModel{
   617  						Name:     name,
   618  						URL:      url,
   619  						Username: username,
   620  						Password: password,
   621  					})
   622  
   623  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   624  					ResponseCode: http.StatusTeapot,
   625  					Errors: []ccerror.V3Error{
   626  						{
   627  							Code:   10008,
   628  							Detail: "The request is semantically invalid: command presence",
   629  							Title:  "CF-UnprocessableEntity",
   630  						},
   631  						{
   632  							Code:   10010,
   633  							Detail: "Isolation segment not found",
   634  							Title:  "CF-ResourceNotFound",
   635  						},
   636  					},
   637  				}))
   638  				Expect(warnings).To(ConsistOf("this is a warning"))
   639  			})
   640  		})
   641  
   642  		When("only name is provided", func() {
   643  			BeforeEach(func() {
   644  				name = "some-name"
   645  				username = ""
   646  				password = ""
   647  				url = ""
   648  
   649  				expectedBody = map[string]interface{}{
   650  					"name": name,
   651  				}
   652  
   653  				server.AppendHandlers(
   654  					CombineHandlers(
   655  						VerifyRequest(http.MethodPatch, "/v3/service_brokers/"+guid),
   656  						VerifyJSONRepresenting(expectedBody),
   657  						RespondWith(http.StatusOK, "", http.Header{
   658  							"X-Cf-Warnings": {"this is a warning"},
   659  							"Location":      {"some-job-url"},
   660  						}),
   661  					),
   662  				)
   663  			})
   664  
   665  			It("includes only the name in the request body", func() {
   666  				jobURL, warnings, executeErr = client.UpdateServiceBroker(
   667  					guid,
   668  					ServiceBrokerModel{
   669  						Name: name,
   670  					})
   671  				Expect(executeErr).NotTo(HaveOccurred())
   672  			})
   673  		})
   674  
   675  		When("partial authentication credentials are provided", func() {
   676  			It("errors without sending any request", func() {
   677  				_, _, executeErr = client.UpdateServiceBroker(
   678  					guid,
   679  					ServiceBrokerModel{Password: password},
   680  				)
   681  				Expect(executeErr).To(HaveOccurred())
   682  
   683  				_, _, executeErr = client.UpdateServiceBroker(
   684  					guid,
   685  					ServiceBrokerModel{Username: username},
   686  				)
   687  				Expect(executeErr).To(HaveOccurred())
   688  				Expect(executeErr).To(MatchError("Incorrect usage: both username and password must be defined in order to do an update"))
   689  			})
   690  		})
   691  	})
   692  })