github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+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  	. "github.com/onsi/ginkgo"
    10  	. "github.com/onsi/gomega"
    11  	. "github.com/onsi/gomega/ghttp"
    12  )
    13  
    14  var _ = Describe("ServiceBroker", func() {
    15  	var client *Client
    16  
    17  	BeforeEach(func() {
    18  		client, _ = NewTestClient()
    19  	})
    20  
    21  	Describe("GetServiceBrokers", func() {
    22  		var (
    23  			serviceBrokers []ServiceBroker
    24  			warnings       Warnings
    25  			executeErr     error
    26  		)
    27  
    28  		JustBeforeEach(func() {
    29  			serviceBrokers, warnings, executeErr = client.GetServiceBrokers()
    30  		})
    31  
    32  		When("service brokers exist", func() {
    33  			BeforeEach(func() {
    34  				response1 := fmt.Sprintf(`
    35  					{
    36  						"pagination": {
    37  							"next": {
    38  								"href": "%s/v3/service_brokers?page=2&per_page=2"
    39  							}
    40  						},
    41  						"resources": [
    42  							{
    43  								"name": "service-broker-name-1",
    44  								"guid": "service-broker-guid-1",
    45  								"url": "service-broker-url-1",
    46  							    "status": "synchronization in progress",
    47  								"relationships": {}
    48  							},
    49  							{
    50  								"name": "service-broker-name-2",
    51  								"guid": "service-broker-guid-2",
    52  								"url": "service-broker-url-2",
    53  								"status": "synchronization failed",
    54  								"relationships": {}
    55  							}
    56  						]
    57  					}`, server.URL())
    58  
    59  				response2 := `
    60  					{
    61  						"pagination": {
    62  							"next": null
    63  						},
    64  						"resources": [
    65  							{
    66  								"name": "service-broker-name-3",
    67  								"guid": "service-broker-guid-3",
    68  								"url": "service-broker-url-3",
    69  								"status": "available",
    70  								"relationships": {}
    71  							}
    72  						]
    73  					}`
    74  
    75  				server.AppendHandlers(
    76  					CombineHandlers(
    77  						VerifyRequest(http.MethodGet, "/v3/service_brokers"),
    78  						RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
    79  					),
    80  				)
    81  				server.AppendHandlers(
    82  					CombineHandlers(
    83  						VerifyRequest(http.MethodGet, "/v3/service_brokers", "page=2&per_page=2"),
    84  						RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"this is another warning"}}),
    85  					),
    86  				)
    87  			})
    88  
    89  			It("returns the queried service-broker and all warnings", func() {
    90  				Expect(executeErr).NotTo(HaveOccurred())
    91  
    92  				Expect(serviceBrokers).To(ConsistOf(
    93  					ServiceBroker{Name: "service-broker-name-1", GUID: "service-broker-guid-1", URL: "service-broker-url-1", Status: "synchronization in progress"},
    94  					ServiceBroker{Name: "service-broker-name-2", GUID: "service-broker-guid-2", URL: "service-broker-url-2", Status: "synchronization failed"},
    95  					ServiceBroker{Name: "service-broker-name-3", GUID: "service-broker-guid-3", URL: "service-broker-url-3", Status: "available"},
    96  				))
    97  				Expect(warnings).To(ConsistOf("this is a warning", "this is another warning"))
    98  			})
    99  		})
   100  
   101  		When("the cloud controller returns errors and warnings", func() {
   102  			BeforeEach(func() {
   103  				response := `{
   104  	  "errors": [
   105  	    {
   106  	      "code": 10008,
   107  	      "detail": "The request is semantically invalid: command presence",
   108  	      "title": "CF-UnprocessableEntity"
   109  	    },
   110  			{
   111  	      "code": 10010,
   112  	      "detail": "Isolation segment not found",
   113  	      "title": "CF-ResourceNotFound"
   114  	    }
   115  	  ]
   116  	}`
   117  				server.AppendHandlers(
   118  					CombineHandlers(
   119  						VerifyRequest(http.MethodGet, "/v3/service_brokers"),
   120  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   121  					),
   122  				)
   123  			})
   124  
   125  			It("returns the error and all warnings", func() {
   126  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   127  					ResponseCode: http.StatusTeapot,
   128  					Errors: []ccerror.V3Error{
   129  						{
   130  							Code:   10008,
   131  							Detail: "The request is semantically invalid: command presence",
   132  							Title:  "CF-UnprocessableEntity",
   133  						},
   134  						{
   135  							Code:   10010,
   136  							Detail: "Isolation segment not found",
   137  							Title:  "CF-ResourceNotFound",
   138  						},
   139  					},
   140  				}))
   141  				Expect(warnings).To(ConsistOf("this is a warning"))
   142  			})
   143  		})
   144  	})
   145  
   146  	Describe("DeleteServiceBroker", func() {
   147  		var (
   148  			warnings          Warnings
   149  			executeErr        error
   150  			serviceBrokerGUID string
   151  			jobURL            JobURL
   152  		)
   153  
   154  		BeforeEach(func() {
   155  			serviceBrokerGUID = "some-service-broker-guid"
   156  		})
   157  
   158  		JustBeforeEach(func() {
   159  			jobURL, warnings, executeErr = client.DeleteServiceBroker(serviceBrokerGUID)
   160  		})
   161  
   162  		When("the Cloud Controller successfully deletes the broker", func() {
   163  			BeforeEach(func() {
   164  				server.AppendHandlers(
   165  					CombineHandlers(
   166  						VerifyRequest(http.MethodDelete, "/v3/service_brokers/some-service-broker-guid"),
   167  						RespondWith(http.StatusOK, "", http.Header{
   168  							"X-Cf-Warnings": {"this is a warning"},
   169  							"Location":      {"some-job-url"},
   170  						}),
   171  					),
   172  				)
   173  			})
   174  
   175  			It("succeeds and returns warnings", func() {
   176  				Expect(executeErr).NotTo(HaveOccurred())
   177  				Expect(warnings).To(ConsistOf("this is a warning"))
   178  				Expect(jobURL).To(Equal(JobURL("some-job-url")))
   179  			})
   180  		})
   181  
   182  		When("the broker is space scoped", func() {
   183  			BeforeEach(func() {
   184  				server.AppendHandlers(
   185  					CombineHandlers(
   186  						VerifyRequest(http.MethodDelete, "/v3/service_brokers/some-service-broker-guid"),
   187  						RespondWith(http.StatusOK, "", http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   188  					),
   189  				)
   190  			})
   191  
   192  			It("succeeds and returns warnings", func() {
   193  				Expect(executeErr).NotTo(HaveOccurred())
   194  				Expect(warnings).To(ConsistOf("this is a warning"))
   195  			})
   196  		})
   197  
   198  		When("the Cloud Controller fails to delete the broker", func() {
   199  			BeforeEach(func() {
   200  				response := `{
   201  	  "errors": [
   202  	    {
   203  	      "code": 10008,
   204  	      "detail": "The request is semantically invalid: command presence",
   205  	      "title": "CF-UnprocessableEntity"
   206  	    },
   207  			{
   208  	      "code": 10010,
   209  	      "detail": "Service broker not found",
   210  	      "title": "CF-ResourceNotFound"
   211  	    }
   212  	  ]
   213  	}`
   214  
   215  				server.AppendHandlers(
   216  					CombineHandlers(
   217  						VerifyRequest(http.MethodDelete, "/v3/service_brokers/some-service-broker-guid"),
   218  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   219  					),
   220  				)
   221  			})
   222  
   223  			It("returns parsed errors and warnings", func() {
   224  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   225  					ResponseCode: http.StatusTeapot,
   226  					Errors: []ccerror.V3Error{
   227  						{
   228  							Code:   10008,
   229  							Detail: "The request is semantically invalid: command presence",
   230  							Title:  "CF-UnprocessableEntity",
   231  						},
   232  						{
   233  							Code:   10010,
   234  							Detail: "Service broker not found",
   235  							Title:  "CF-ResourceNotFound",
   236  						},
   237  					},
   238  				}))
   239  				Expect(warnings).To(ConsistOf("this is a warning"))
   240  			})
   241  		})
   242  	})
   243  
   244  	Describe("CreateServiceBroker", func() {
   245  		const (
   246  			name     = "name"
   247  			url      = "url"
   248  			username = "username"
   249  			password = "password"
   250  		)
   251  
   252  		var (
   253  			jobURL       JobURL
   254  			warnings     Warnings
   255  			executeErr   error
   256  			spaceGUID    string
   257  			expectedBody map[string]interface{}
   258  		)
   259  
   260  		BeforeEach(func() {
   261  			spaceGUID = ""
   262  			expectedBody = map[string]interface{}{
   263  				"name": "name",
   264  				"url":  "url",
   265  				"authentication": map[string]interface{}{
   266  					"type": "basic",
   267  					"credentials": map[string]string{
   268  						"username": "username",
   269  						"password": "password",
   270  					},
   271  				},
   272  			}
   273  		})
   274  
   275  		JustBeforeEach(func() {
   276  			serviceBrokerRequest := ServiceBrokerModel{
   277  				Name:      name,
   278  				URL:       url,
   279  				Username:  username,
   280  				Password:  password,
   281  				SpaceGUID: spaceGUID,
   282  			}
   283  			jobURL, warnings, executeErr = client.CreateServiceBroker(serviceBrokerRequest)
   284  		})
   285  
   286  		When("the Cloud Controller successfully creates the broker", func() {
   287  			BeforeEach(func() {
   288  				server.AppendHandlers(
   289  					CombineHandlers(
   290  						VerifyRequest(http.MethodPost, "/v3/service_brokers"),
   291  						VerifyJSONRepresenting(expectedBody),
   292  						RespondWith(http.StatusOK, "", http.Header{
   293  							"X-Cf-Warnings": {"this is a warning"},
   294  							"Location":      {"some-job-url"},
   295  						}),
   296  					),
   297  				)
   298  			})
   299  
   300  			It("succeeds, returns warnings and job URL", func() {
   301  				Expect(jobURL).To(Equal(JobURL("some-job-url")))
   302  				Expect(executeErr).NotTo(HaveOccurred())
   303  				Expect(warnings).To(ConsistOf("this is a warning"))
   304  			})
   305  		})
   306  
   307  		When("the broker is space scoped", func() {
   308  			BeforeEach(func() {
   309  				spaceGUID = "space-guid"
   310  				expectedBody["relationships"] = map[string]interface{}{
   311  					"space": map[string]interface{}{
   312  						"data": map[string]string{
   313  							"guid": "space-guid",
   314  						},
   315  					},
   316  				}
   317  				server.AppendHandlers(
   318  					CombineHandlers(
   319  						VerifyRequest(http.MethodPost, "/v3/service_brokers"),
   320  						VerifyJSONRepresenting(expectedBody),
   321  						RespondWith(http.StatusOK, "", http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   322  					),
   323  				)
   324  			})
   325  
   326  			It("succeeds and returns warnings", func() {
   327  				Expect(executeErr).NotTo(HaveOccurred())
   328  				Expect(warnings).To(ConsistOf("this is a warning"))
   329  			})
   330  		})
   331  
   332  		When("the Cloud Controller fails to create the broker", func() {
   333  			BeforeEach(func() {
   334  				response := `{
   335  	  "errors": [
   336  	    {
   337  	      "code": 10008,
   338  	      "detail": "The request is semantically invalid: command presence",
   339  	      "title": "CF-UnprocessableEntity"
   340  	    },
   341  			{
   342  	      "code": 10010,
   343  	      "detail": "Isolation segment not found",
   344  	      "title": "CF-ResourceNotFound"
   345  	    }
   346  	  ]
   347  	}`
   348  
   349  				server.AppendHandlers(
   350  					CombineHandlers(
   351  						VerifyRequest(http.MethodPost, "/v3/service_brokers"),
   352  						VerifyJSONRepresenting(expectedBody),
   353  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   354  					),
   355  				)
   356  			})
   357  
   358  			It("returns parsed errors and warnings", func() {
   359  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   360  					ResponseCode: http.StatusTeapot,
   361  					Errors: []ccerror.V3Error{
   362  						{
   363  							Code:   10008,
   364  							Detail: "The request is semantically invalid: command presence",
   365  							Title:  "CF-UnprocessableEntity",
   366  						},
   367  						{
   368  							Code:   10010,
   369  							Detail: "Isolation segment not found",
   370  							Title:  "CF-ResourceNotFound",
   371  						},
   372  					},
   373  				}))
   374  				Expect(warnings).To(ConsistOf("this is a warning"))
   375  			})
   376  		})
   377  	})
   378  
   379  	Describe("UpdateServiceBroker", func() {
   380  		var (
   381  			name, guid, url, username, password string
   382  			jobURL                              JobURL
   383  			warnings                            Warnings
   384  			executeErr                          error
   385  			expectedBody                        map[string]interface{}
   386  		)
   387  
   388  		BeforeEach(func() {
   389  			expectedBody = map[string]interface{}{
   390  				"url": "new-url",
   391  				"authentication": map[string]interface{}{
   392  					"type": "basic",
   393  					"credentials": map[string]string{
   394  						"username": "new-username",
   395  						"password": "new-password",
   396  					},
   397  				},
   398  			}
   399  			name = ""
   400  			guid = "broker-guid"
   401  			url = "new-url"
   402  			username = "new-username"
   403  			password = "new-password"
   404  		})
   405  
   406  		When("the Cloud Controller successfully updates the broker", func() {
   407  			BeforeEach(func() {
   408  				server.AppendHandlers(
   409  					CombineHandlers(
   410  						VerifyRequest(http.MethodPatch, "/v3/service_brokers/"+guid),
   411  						VerifyJSONRepresenting(expectedBody),
   412  						RespondWith(http.StatusOK, "", http.Header{
   413  							"X-Cf-Warnings": {"this is a warning"},
   414  							"Location":      {"some-job-url"},
   415  						}),
   416  					),
   417  				)
   418  			})
   419  
   420  			It("succeeds, returns warnings and job URL", func() {
   421  				jobURL, warnings, executeErr = client.UpdateServiceBroker(
   422  					guid,
   423  					ServiceBrokerModel{
   424  						Name:     name,
   425  						URL:      url,
   426  						Username: username,
   427  						Password: password,
   428  					})
   429  
   430  				Expect(jobURL).To(Equal(JobURL("some-job-url")))
   431  				Expect(executeErr).NotTo(HaveOccurred())
   432  				Expect(warnings).To(ConsistOf("this is a warning"))
   433  			})
   434  		})
   435  
   436  		When("the Cloud Controller fails to update the broker", func() {
   437  			BeforeEach(func() {
   438  				response := `{
   439  	  "errors": [
   440  	    {
   441  	      "code": 10008,
   442  	      "detail": "The request is semantically invalid: command presence",
   443  	      "title": "CF-UnprocessableEntity"
   444  	    },
   445  			{
   446  	      "code": 10010,
   447  	      "detail": "Isolation segment not found",
   448  	      "title": "CF-ResourceNotFound"
   449  	    }
   450  	  ]
   451  	}`
   452  
   453  				server.AppendHandlers(
   454  					CombineHandlers(
   455  						VerifyRequest(http.MethodPatch, "/v3/service_brokers/"+guid),
   456  						VerifyJSONRepresenting(expectedBody),
   457  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   458  					),
   459  				)
   460  			})
   461  
   462  			It("returns parsed errors and warnings", func() {
   463  				jobURL, warnings, executeErr = client.UpdateServiceBroker(guid,
   464  					ServiceBrokerModel{
   465  						Name:     name,
   466  						URL:      url,
   467  						Username: username,
   468  						Password: password,
   469  					})
   470  
   471  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   472  					ResponseCode: http.StatusTeapot,
   473  					Errors: []ccerror.V3Error{
   474  						{
   475  							Code:   10008,
   476  							Detail: "The request is semantically invalid: command presence",
   477  							Title:  "CF-UnprocessableEntity",
   478  						},
   479  						{
   480  							Code:   10010,
   481  							Detail: "Isolation segment not found",
   482  							Title:  "CF-ResourceNotFound",
   483  						},
   484  					},
   485  				}))
   486  				Expect(warnings).To(ConsistOf("this is a warning"))
   487  			})
   488  		})
   489  
   490  		When("only name is provided", func() {
   491  			BeforeEach(func() {
   492  				name = "some-name"
   493  				username = ""
   494  				password = ""
   495  				url = ""
   496  
   497  				expectedBody = map[string]interface{}{
   498  					"name": name,
   499  				}
   500  
   501  				server.AppendHandlers(
   502  					CombineHandlers(
   503  						VerifyRequest(http.MethodPatch, "/v3/service_brokers/"+guid),
   504  						VerifyJSONRepresenting(expectedBody),
   505  						RespondWith(http.StatusOK, "", http.Header{
   506  							"X-Cf-Warnings": {"this is a warning"},
   507  							"Location":      {"some-job-url"},
   508  						}),
   509  					),
   510  				)
   511  			})
   512  
   513  			It("includes only the name in the request body", func() {
   514  				jobURL, warnings, executeErr = client.UpdateServiceBroker(
   515  					guid,
   516  					ServiceBrokerModel{
   517  						Name: name,
   518  					})
   519  				Expect(executeErr).NotTo(HaveOccurred())
   520  			})
   521  		})
   522  
   523  		When("partial authentication credentials are provided", func() {
   524  			It("errors without sending any request", func() {
   525  				_, _, executeErr = client.UpdateServiceBroker(
   526  					guid,
   527  					ServiceBrokerModel{Password: password},
   528  				)
   529  				Expect(executeErr).To(HaveOccurred())
   530  
   531  				_, _, executeErr = client.UpdateServiceBroker(
   532  					guid,
   533  					ServiceBrokerModel{Username: username},
   534  				)
   535  				Expect(executeErr).To(HaveOccurred())
   536  			})
   537  		})
   538  	})
   539  })