github.com/orange-cloudfoundry/cli@v7.1.0+incompatible/api/cloudcontroller/ccv3/route_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/resources"
    10  	"code.cloudfoundry.org/cli/types"
    11  	. "github.com/onsi/ginkgo"
    12  	. "github.com/onsi/gomega"
    13  	. "github.com/onsi/gomega/ghttp"
    14  )
    15  
    16  var _ = Describe("Route", func() {
    17  	var client *Client
    18  
    19  	BeforeEach(func() {
    20  		client, _ = NewTestClient()
    21  	})
    22  
    23  	Describe("CreateRoute", func() {
    24  		var (
    25  			route      resources.Route
    26  			warnings   Warnings
    27  			executeErr error
    28  			spaceGUID  string
    29  			domainGUID string
    30  			host       string
    31  			path       string
    32  			port       int
    33  			ccv3Route  resources.Route
    34  		)
    35  
    36  		BeforeEach(func() {
    37  			host = ""
    38  			path = ""
    39  			port = 0
    40  		})
    41  
    42  		JustBeforeEach(func() {
    43  			spaceGUID = "space-guid"
    44  			domainGUID = "domain-guid"
    45  			ccv3Route = resources.Route{SpaceGUID: spaceGUID, DomainGUID: domainGUID, Host: host, Path: path, Port: port}
    46  			route, warnings, executeErr = client.CreateRoute(ccv3Route)
    47  		})
    48  
    49  		When("the request succeeds", func() {
    50  			When("no additional flags", func() {
    51  				BeforeEach(func() {
    52  					host = ""
    53  					response := `{
    54    "guid": "some-route-guid",
    55    "relationships": {
    56      "space": {
    57  	  "data": { "guid": "space-guid" }
    58      },
    59      "domain": {
    60  	  "data": { "guid": "domain-guid" }
    61      }
    62    },
    63  	"host": ""
    64  }`
    65  
    66  					expectedBody := `{
    67    "relationships": {
    68    	"space": {
    69        "data": { "guid": "space-guid" }
    70      },
    71      "domain": {
    72  	  "data": { "guid": "domain-guid" }
    73      }
    74    }
    75  }`
    76  
    77  					server.AppendHandlers(
    78  						CombineHandlers(
    79  							VerifyRequest(http.MethodPost, "/v3/routes"),
    80  							VerifyJSON(expectedBody),
    81  							RespondWith(http.StatusCreated, response, http.Header{"X-Cf-Warnings": {"warning-1"}}),
    82  						),
    83  					)
    84  				})
    85  
    86  				It("returns the given route and all warnings", func() {
    87  					Expect(executeErr).ToNot(HaveOccurred())
    88  					Expect(warnings).To(ConsistOf("warning-1"))
    89  
    90  					Expect(route).To(Equal(resources.Route{
    91  						GUID:       "some-route-guid",
    92  						SpaceGUID:  "space-guid",
    93  						DomainGUID: "domain-guid",
    94  					}))
    95  				})
    96  			})
    97  
    98  			When("hostname is passed in", func() {
    99  
   100  				BeforeEach(func() {
   101  					host = "cheesecake"
   102  					response := `{
   103    "guid": "some-route-guid",
   104    "relationships": {
   105      "space": {
   106  			"data": { "guid": "space-guid" }
   107      },
   108      "domain": {
   109  			"data": { "guid": "domain-guid" }
   110      }
   111    },
   112  	"host": "cheesecake"
   113  }`
   114  
   115  					expectedBody := `{
   116    "relationships": {
   117    	"space": {
   118        "data": { "guid": "space-guid" }
   119      },
   120      "domain": {
   121  			"data": { "guid": "domain-guid" }
   122      }
   123    },
   124  	"host": "cheesecake"
   125  }`
   126  
   127  					server.AppendHandlers(
   128  						CombineHandlers(
   129  							VerifyRequest(http.MethodPost, "/v3/routes"),
   130  							VerifyJSON(expectedBody),
   131  							RespondWith(http.StatusCreated, response, http.Header{"X-Cf-Warnings": {"warning-1"}}),
   132  						),
   133  					)
   134  				})
   135  
   136  				It("returns the given route and all warnings", func() {
   137  					Expect(executeErr).ToNot(HaveOccurred())
   138  					Expect(warnings).To(ConsistOf("warning-1"))
   139  
   140  					Expect(route).To(Equal(resources.Route{
   141  						GUID:       "some-route-guid",
   142  						SpaceGUID:  "space-guid",
   143  						DomainGUID: "domain-guid",
   144  						Host:       "cheesecake",
   145  					}))
   146  				})
   147  			})
   148  
   149  			When("path is passed in", func() {
   150  				BeforeEach(func() {
   151  					path = "lion"
   152  
   153  					response := `{
   154  	"guid": "this-route-guid",
   155  	"relationships": {
   156  		"space": {
   157  			"data": {
   158  				"guid": "space-guid"
   159  			}
   160  		},
   161  		"domain": {
   162  			"data": {
   163  				"guid": "domain-guid"
   164  			}
   165  		}
   166  	},
   167  	"path": "lion"
   168  }`
   169  					expectedRequestBody := `{
   170  	"relationships": {
   171  		"space": {
   172  			"data": {
   173  				"guid": "space-guid"
   174  			}
   175  		},
   176  		"domain": {
   177  			"data": {
   178  				"guid": "domain-guid"
   179  			}
   180  		}
   181  	},
   182  	"path": "lion"
   183  }`
   184  
   185  					server.AppendHandlers(
   186  						CombineHandlers(
   187  							VerifyRequest(http.MethodPost, "/v3/routes"),
   188  							VerifyJSON(expectedRequestBody),
   189  							RespondWith(http.StatusCreated, response, http.Header{"X-Cf-Warnings": {"warning-1"}}),
   190  						),
   191  					)
   192  				})
   193  				When("the request succeeds", func() {
   194  					It("returns the given route and all warnings", func() {
   195  						Expect(executeErr).ToNot(HaveOccurred())
   196  						Expect(warnings).To(ConsistOf("warning-1"))
   197  
   198  						Expect(route).To(Equal(resources.Route{
   199  							GUID:       "this-route-guid",
   200  							SpaceGUID:  "space-guid",
   201  							DomainGUID: "domain-guid",
   202  							Path:       "lion",
   203  						}))
   204  					})
   205  				})
   206  			})
   207  
   208  			When("port is passed in", func() {
   209  				BeforeEach(func() {
   210  					port = 1234
   211  
   212  					response := `{
   213  	"guid": "this-route-guid",
   214  	"relationships": {
   215  		"space": {
   216  			"data": {
   217  				"guid": "space-guid"
   218  			}
   219  		},
   220  		"domain": {
   221  			"data": {
   222  				"guid": "domain-guid"
   223  			}
   224  		}
   225  	},
   226  	"port": 1234
   227  }`
   228  					expectedRequestBody := `{
   229  	"relationships": {
   230  		"space": {
   231  			"data": {
   232  				"guid": "space-guid"
   233  			}
   234  		},
   235  		"domain": {
   236  			"data": {
   237  				"guid": "domain-guid"
   238  			}
   239  		}
   240  	},
   241  	"port": 1234
   242  }`
   243  
   244  					server.AppendHandlers(
   245  						CombineHandlers(
   246  							VerifyRequest(http.MethodPost, "/v3/routes"),
   247  							VerifyJSON(expectedRequestBody),
   248  							RespondWith(http.StatusCreated, response, http.Header{"X-Cf-Warnings": {"warning-1"}}),
   249  						),
   250  					)
   251  				})
   252  				When("the request succeeds", func() {
   253  					It("returns the given route and all warnings", func() {
   254  						Expect(executeErr).ToNot(HaveOccurred())
   255  						Expect(warnings).To(ConsistOf("warning-1"))
   256  
   257  						Expect(route).To(Equal(resources.Route{
   258  							GUID:       "this-route-guid",
   259  							SpaceGUID:  "space-guid",
   260  							DomainGUID: "domain-guid",
   261  							Port:       1234,
   262  						}))
   263  					})
   264  				})
   265  			})
   266  		})
   267  
   268  		When("the cloud controller returns errors and warnings", func() {
   269  			BeforeEach(func() {
   270  				response := `{
   271    "errors": [
   272      {
   273        "code": 10008,
   274        "detail": "The request is semantically invalid: command presence",
   275        "title": "CF-UnprocessableEntity"
   276      },
   277  		{
   278        "code": 10010,
   279        "detail": "Isolation segment not found",
   280        "title": "CF-ResourceNotFound"
   281      }
   282    ]
   283  }`
   284  				server.AppendHandlers(
   285  					CombineHandlers(
   286  						VerifyRequest(http.MethodPost, "/v3/routes"),
   287  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   288  					),
   289  				)
   290  			})
   291  
   292  			It("returns the error and all warnings", func() {
   293  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   294  					ResponseCode: http.StatusTeapot,
   295  					Errors: []ccerror.V3Error{
   296  						{
   297  							Code:   10008,
   298  							Detail: "The request is semantically invalid: command presence",
   299  							Title:  "CF-UnprocessableEntity",
   300  						},
   301  						{
   302  							Code:   10010,
   303  							Detail: "Isolation segment not found",
   304  							Title:  "CF-ResourceNotFound",
   305  						},
   306  					},
   307  				}))
   308  				Expect(warnings).To(ConsistOf("this is a warning"))
   309  			})
   310  		})
   311  	})
   312  
   313  	Describe("GetRoutes", func() {
   314  		var (
   315  			query      Query
   316  			routes     []resources.Route
   317  			warnings   Warnings
   318  			executeErr error
   319  		)
   320  
   321  		JustBeforeEach(func() {
   322  			routes, warnings, executeErr = client.GetRoutes(query)
   323  		})
   324  
   325  		When("the request succeeds", func() {
   326  			var (
   327  				response1 string
   328  				response2 string
   329  			)
   330  
   331  			BeforeEach(func() {
   332  				response1 = fmt.Sprintf(`
   333  				{
   334  					"pagination": {
   335  						"next": {
   336  							"href": "%s/v3/routes?page=2"
   337  						}
   338  					},
   339  					"resources": [
   340  						{
   341  							"guid": "route-1-guid",
   342  							"url": "hello",
   343  							"metadata": {
   344  								"labels": {
   345  									"key1": "value1"
   346  								}
   347  							}
   348  						},
   349  						{
   350  							"guid": "route-2-guid",
   351  							"url": "bye"
   352  						}
   353  					]
   354  				}`, server.URL())
   355  
   356  				response2 = `
   357  				{
   358  					"pagination": {
   359  						"next": null
   360  					},
   361  					"resources": [
   362  						{
   363  							"guid": "route-3-guid"
   364  						}
   365  					]
   366  				}`
   367  			})
   368  
   369  			When("not passing any filters", func() {
   370  				BeforeEach(func() {
   371  					query = Query{}
   372  
   373  					server.AppendHandlers(
   374  						CombineHandlers(
   375  							VerifyRequest(http.MethodGet, "/v3/routes"),
   376  							RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"warning-1"}}),
   377  						),
   378  					)
   379  					server.AppendHandlers(
   380  						CombineHandlers(
   381  							VerifyRequest(http.MethodGet, "/v3/routes", "page=2"),
   382  							RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"warning-2"}}),
   383  						),
   384  					)
   385  				})
   386  
   387  				It("returns the given route and all warnings", func() {
   388  					Expect(executeErr).ToNot(HaveOccurred())
   389  					Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   390  
   391  					Expect(routes).To(Equal([]resources.Route{
   392  						{
   393  							GUID: "route-1-guid",
   394  							URL:  "hello",
   395  							Metadata: &resources.Metadata{
   396  								Labels: map[string]types.NullString{
   397  									"key1": types.NewNullString("value1"),
   398  								},
   399  							},
   400  						},
   401  						{
   402  							GUID: "route-2-guid",
   403  							URL:  "bye",
   404  						},
   405  						{
   406  							GUID: "route-3-guid",
   407  						},
   408  					}))
   409  				})
   410  			})
   411  
   412  			When("passing in a query", func() {
   413  				BeforeEach(func() {
   414  					query = Query{Key: "space_guids", Values: []string{"guid1", "guid2"}}
   415  
   416  					server.AppendHandlers(
   417  						CombineHandlers(
   418  							VerifyRequest(http.MethodGet, "/v3/routes", "space_guids=guid1,guid2"),
   419  							RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"warning-1"}}),
   420  						),
   421  					)
   422  					server.AppendHandlers(
   423  						CombineHandlers(
   424  							VerifyRequest(http.MethodGet, "/v3/routes", "page=2", "space_guids=guid1,guid2"),
   425  							RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"warning-2"}}),
   426  						),
   427  					)
   428  				})
   429  
   430  				It("passes query params", func() {
   431  					Expect(executeErr).ToNot(HaveOccurred())
   432  					Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   433  
   434  					Expect(routes).To(Equal([]resources.Route{
   435  						{
   436  							GUID: "route-1-guid",
   437  							URL:  "hello",
   438  							Metadata: &resources.Metadata{
   439  								Labels: map[string]types.NullString{
   440  									"key1": types.NewNullString("value1"),
   441  								},
   442  							},
   443  						},
   444  						{
   445  							GUID: "route-2-guid",
   446  							URL:  "bye",
   447  						},
   448  						{
   449  							GUID: "route-3-guid",
   450  						},
   451  					}))
   452  				})
   453  			})
   454  		})
   455  	})
   456  
   457  	Describe("DeleteRoute", func() {
   458  		var (
   459  			routeGUID    string
   460  			jobURLString string
   461  			jobURL       JobURL
   462  			warnings     Warnings
   463  			executeErr   error
   464  		)
   465  
   466  		JustBeforeEach(func() {
   467  			jobURL, warnings, executeErr = client.DeleteRoute(routeGUID)
   468  		})
   469  
   470  		When("route exists", func() {
   471  			routeGUID = "route-guid"
   472  			jobURLString = "https://api.test.com/v3/jobs/job-guid"
   473  
   474  			BeforeEach(func() {
   475  				server.AppendHandlers(
   476  					CombineHandlers(
   477  						VerifyRequest(http.MethodDelete, "/v3/routes/route-guid"),
   478  						RespondWith(http.StatusAccepted, nil, http.Header{
   479  							"X-Cf-Warnings": {"this is a warning"},
   480  							"Location":      {jobURLString},
   481  						}),
   482  					),
   483  				)
   484  			})
   485  
   486  			It("returns all warnings", func() {
   487  				Expect(executeErr).NotTo(HaveOccurred())
   488  				Expect(jobURL).To(Equal(JobURL(jobURLString)))
   489  				Expect(warnings).To(ConsistOf("this is a warning"))
   490  			})
   491  		})
   492  
   493  		When("the cloud controller returns errors and warnings", func() {
   494  			BeforeEach(func() {
   495  				response := `{
   496  	  "errors": [
   497  	    {
   498  	      "code": 10008,
   499  	      "detail": "The request is semantically invalid: command presence",
   500  	      "title": "CF-UnprocessableEntity"
   501  	    },
   502  			{
   503  	      "code": 10010,
   504  	      "detail": "Isolation segment not found",
   505  	      "title": "CF-ResourceNotFound"
   506  	    }
   507  	  ]
   508  	}`
   509  				server.AppendHandlers(
   510  					CombineHandlers(
   511  						VerifyRequest(http.MethodDelete, "/v3/routes/route-guid"),
   512  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   513  					),
   514  				)
   515  			})
   516  
   517  			It("returns the error and all warnings", func() {
   518  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   519  					ResponseCode: http.StatusTeapot,
   520  					Errors: []ccerror.V3Error{
   521  						{
   522  							Code:   10008,
   523  							Detail: "The request is semantically invalid: command presence",
   524  							Title:  "CF-UnprocessableEntity",
   525  						},
   526  						{
   527  							Code:   10010,
   528  							Detail: "Isolation segment not found",
   529  							Title:  "CF-ResourceNotFound",
   530  						},
   531  					},
   532  				}))
   533  				Expect(warnings).To(ConsistOf("this is a warning"))
   534  			})
   535  		})
   536  	})
   537  
   538  	Describe("MapRoute", func() {
   539  		var (
   540  			routeGUID  = "route-guid"
   541  			appGUID    = "app-guid"
   542  			warnings   Warnings
   543  			executeErr error
   544  		)
   545  
   546  		JustBeforeEach(func() {
   547  			warnings, executeErr = client.MapRoute(routeGUID, appGUID)
   548  		})
   549  
   550  		When("the request is successful", func() {
   551  			BeforeEach(func() {
   552  				expectedBody := fmt.Sprintf(`
   553  					{
   554  						"destinations": [
   555  						 {
   556  							"app": {
   557  								"guid": "%s"
   558  							}
   559  						 }
   560  						]
   561  					}
   562  				`, appGUID)
   563  
   564  				response := `{}`
   565  
   566  				server.AppendHandlers(
   567  					CombineHandlers(
   568  						VerifyRequest(http.MethodPost, "/v3/routes/route-guid/destinations"),
   569  						VerifyJSON(expectedBody),
   570  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"warning-1"}}),
   571  					),
   572  				)
   573  			})
   574  
   575  			It("returns the warnings and no error", func() {
   576  				Expect(executeErr).ToNot(HaveOccurred())
   577  				Expect(warnings).To(ConsistOf("warning-1"))
   578  			})
   579  		})
   580  
   581  		When("the cloud controller returns errors and warnings", func() {
   582  			BeforeEach(func() {
   583  				response := `{
   584    "errors": [
   585      {
   586        "code": 10008,
   587        "detail": "The request is semantically invalid: command presence",
   588        "title": "CF-UnprocessableEntity"
   589      },
   590  		{
   591        "code": 10010,
   592        "detail": "Isolation segment not found",
   593        "title": "CF-ResourceNotFound"
   594      }
   595    ]
   596  }`
   597  				server.AppendHandlers(
   598  					CombineHandlers(
   599  						VerifyRequest(http.MethodPost, "/v3/routes/route-guid/destinations"),
   600  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   601  					),
   602  				)
   603  			})
   604  
   605  			It("returns the error and all warnings", func() {
   606  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   607  					ResponseCode: http.StatusTeapot,
   608  					Errors: []ccerror.V3Error{
   609  						{
   610  							Code:   10008,
   611  							Detail: "The request is semantically invalid: command presence",
   612  							Title:  "CF-UnprocessableEntity",
   613  						},
   614  						{
   615  							Code:   10010,
   616  							Detail: "Isolation segment not found",
   617  							Title:  "CF-ResourceNotFound",
   618  						},
   619  					},
   620  				}))
   621  				Expect(warnings).To(ConsistOf("this is a warning"))
   622  			})
   623  		})
   624  	})
   625  
   626  	Describe("GetRouteDestinations", func() {
   627  		var (
   628  			routeGUID    = "some-route-guid"
   629  			destinations []resources.RouteDestination
   630  			warnings     Warnings
   631  			executeErr   error
   632  		)
   633  
   634  		JustBeforeEach(func() {
   635  			destinations, warnings, executeErr = client.GetRouteDestinations(routeGUID)
   636  		})
   637  
   638  		When("the request succeeds", func() {
   639  			var (
   640  				response string
   641  			)
   642  
   643  			BeforeEach(func() {
   644  				response = `
   645  				{
   646  					"destinations": [
   647  						{
   648  							"guid": "destination-1-guid",
   649  							"app": {
   650  								"guid": "app-1-guid",
   651  								"process": {
   652  									"type": "web"
   653  								}
   654  							}
   655  						},
   656  						{
   657  							"guid": "destination-2-guid",
   658  							"app": {
   659  								"guid": "app-2-guid",
   660  								"process": {
   661  									"type": "worker"
   662  								}
   663  							}
   664  						}
   665  					]
   666  				}`
   667  			})
   668  
   669  			When("the request succeeds", func() {
   670  				BeforeEach(func() {
   671  					server.AppendHandlers(
   672  						CombineHandlers(
   673  							VerifyRequest(http.MethodGet, "/v3/routes/some-route-guid/destinations"),
   674  							RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"warning-1"}}),
   675  						),
   676  					)
   677  				})
   678  
   679  				It("returns destinations and all warnings", func() {
   680  					Expect(executeErr).ToNot(HaveOccurred())
   681  					Expect(warnings).To(ConsistOf("warning-1"))
   682  
   683  					Expect(destinations).To(Equal([]resources.RouteDestination{
   684  						{
   685  							GUID: "destination-1-guid",
   686  							App:  resources.RouteDestinationApp{GUID: "app-1-guid", Process: struct{ Type string }{Type: "web"}},
   687  						},
   688  						{
   689  							GUID: "destination-2-guid",
   690  							App:  resources.RouteDestinationApp{GUID: "app-2-guid", Process: struct{ Type string }{Type: "worker"}},
   691  						},
   692  					}))
   693  				})
   694  			})
   695  		})
   696  	})
   697  
   698  	Describe("UnmapRoute", func() {
   699  		var (
   700  			routeGUID       string
   701  			destinationGUID string
   702  			warnings        Warnings
   703  			executeErr      error
   704  		)
   705  
   706  		JustBeforeEach(func() {
   707  			warnings, executeErr = client.UnmapRoute(routeGUID, destinationGUID)
   708  		})
   709  
   710  		When("route exists", func() {
   711  			routeGUID = "route-guid"
   712  			destinationGUID = "destination-guid"
   713  
   714  			BeforeEach(func() {
   715  				server.AppendHandlers(
   716  					CombineHandlers(
   717  						VerifyRequest(http.MethodDelete, "/v3/routes/route-guid/destinations/destination-guid"),
   718  						RespondWith(http.StatusNoContent, nil, http.Header{
   719  							"X-Cf-Warnings": {"this is a warning"},
   720  						}),
   721  					),
   722  				)
   723  			})
   724  
   725  			It("returns all warnings", func() {
   726  				Expect(executeErr).NotTo(HaveOccurred())
   727  				Expect(warnings).To(ConsistOf("this is a warning"))
   728  			})
   729  		})
   730  
   731  		When("the cloud controller returns errors and warnings", func() {
   732  			BeforeEach(func() {
   733  				response := `{
   734  	  "errors": [
   735  	    {
   736  	      "code": 10008,
   737  	      "detail": "The request is semantically invalid: command presence",
   738  	      "title": "CF-UnprocessableEntity"
   739  	    },
   740  			{
   741  	      "code": 10010,
   742  	      "detail": "Isolation segment not found",
   743  	      "title": "CF-ResourceNotFound"
   744  	    }
   745  	  ]
   746  	}`
   747  				server.AppendHandlers(
   748  					CombineHandlers(
   749  						VerifyRequest(http.MethodDelete, "/v3/routes/route-guid/destinations/destination-guid"),
   750  						RespondWith(http.StatusTeapot, response, http.Header{
   751  							"X-Cf-Warnings": {"this is a warning"},
   752  						}),
   753  					),
   754  				)
   755  			})
   756  
   757  			It("returns the error and all warnings", func() {
   758  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   759  					ResponseCode: http.StatusTeapot,
   760  					Errors: []ccerror.V3Error{
   761  						{
   762  							Code:   10008,
   763  							Detail: "The request is semantically invalid: command presence",
   764  							Title:  "CF-UnprocessableEntity",
   765  						},
   766  						{
   767  							Code:   10010,
   768  							Detail: "Isolation segment not found",
   769  							Title:  "CF-ResourceNotFound",
   770  						},
   771  					},
   772  				}))
   773  				Expect(warnings).To(ConsistOf("this is a warning"))
   774  			})
   775  		})
   776  	})
   777  
   778  	Describe("DeleteOrphanedRoutes", func() {
   779  		var (
   780  			spaceGUID  string
   781  			warnings   Warnings
   782  			executeErr error
   783  			jobURL     JobURL
   784  		)
   785  		JustBeforeEach(func() {
   786  			jobURL, warnings, executeErr = client.DeleteOrphanedRoutes(spaceGUID)
   787  		})
   788  
   789  		When("the API succeeds", func() {
   790  			BeforeEach(func() {
   791  				spaceGUID = "space-guid"
   792  				server.AppendHandlers(
   793  					CombineHandlers(
   794  						VerifyRequest(http.MethodDelete, "/v3/spaces/space-guid/routes", "unmapped=true"),
   795  						RespondWith(
   796  							http.StatusAccepted,
   797  							nil,
   798  							http.Header{"X-Cf-Warnings": {"orphaned-warning"}, "Location": {"job-url"}},
   799  						),
   800  					),
   801  				)
   802  			})
   803  
   804  			It("returns the warnings and a job", func() {
   805  				Expect(executeErr).ToNot(HaveOccurred())
   806  				Expect(warnings).To(ConsistOf("orphaned-warning"))
   807  				Expect(jobURL).To(Equal(JobURL("job-url")))
   808  			})
   809  		})
   810  
   811  		When("the API fails", func() {
   812  			BeforeEach(func() {
   813  				spaceGUID = "space-guid"
   814  				response := `{
   815  	  "errors": [
   816  	    {
   817  	      "code": 10008,
   818  	      "detail": "The request is semantically invalid: command presence",
   819  	      "title": "CF-UnprocessableEntity"
   820  	    },
   821  			{
   822  	      "code": 10010,
   823  	      "detail": "Isolation segment not found",
   824  	      "title": "CF-ResourceNotFound"
   825  	    }
   826  	  ]
   827  	}`
   828  				server.AppendHandlers(
   829  					CombineHandlers(
   830  						VerifyRequest(http.MethodDelete, "/v3/spaces/space-guid/routes", "unmapped=true"),
   831  						RespondWith(
   832  							http.StatusTeapot,
   833  							response,
   834  							http.Header{"X-Cf-Warnings": {"orphaned-warning"}},
   835  						),
   836  					),
   837  				)
   838  			})
   839  
   840  			It("returns the warnings and a job", func() {
   841  
   842  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   843  					ResponseCode: http.StatusTeapot,
   844  					Errors: []ccerror.V3Error{
   845  						{
   846  							Code:   10008,
   847  							Detail: "The request is semantically invalid: command presence",
   848  							Title:  "CF-UnprocessableEntity",
   849  						},
   850  						{
   851  							Code:   10010,
   852  							Detail: "Isolation segment not found",
   853  							Title:  "CF-ResourceNotFound",
   854  						},
   855  					},
   856  				}))
   857  				Expect(warnings).To(ConsistOf("orphaned-warning"))
   858  			})
   859  		})
   860  	})
   861  
   862  	Describe("GetApplicationRoutes", func() {
   863  		var (
   864  			appGUID string
   865  
   866  			routes     []resources.Route
   867  			warnings   Warnings
   868  			executeErr error
   869  		)
   870  
   871  		BeforeEach(func() {
   872  			appGUID = "some-app-guid"
   873  		})
   874  
   875  		JustBeforeEach(func() {
   876  			routes, warnings, executeErr = client.GetApplicationRoutes(appGUID)
   877  		})
   878  
   879  		When("the request succeeds", func() {
   880  			BeforeEach(func() {
   881  				body := `{
   882  	"resources": [
   883  		{
   884  			"guid": "route-guid",
   885  			"host": "host",
   886  			"path": "/path",
   887  			"url": "host.domain.com/path",
   888  			"relationships": {
   889  				"space": {
   890  					"data": {
   891  						"guid": "space-guid"
   892  					}
   893  				},
   894  				"domain": {
   895  					"data": {
   896  						"guid": "domain-guid"
   897  					}
   898  				}
   899  			}
   900  		}, {
   901  			"guid": "route2-guid",
   902  			"host": "",
   903  			"path": "",
   904  			"url": "domain.com",
   905  			"relationships": {
   906  				"space": {
   907  					"data": {
   908  						"guid": "space-guid"
   909  					}
   910  				},
   911  				"domain": {
   912  					"data": {
   913  						"guid": "domain2-guid"
   914  					}
   915  				}
   916  			}
   917  		}
   918  	]
   919  }`
   920  				server.AppendHandlers(
   921  					CombineHandlers(
   922  						VerifyRequest(http.MethodGet, "/v3/apps/some-app-guid/routes"),
   923  						RespondWith(
   924  							http.StatusOK,
   925  							body,
   926  							http.Header{"X-Cf-Warnings": {"get-app-routes-warning"}, "Location": {"job-url"}},
   927  						),
   928  					),
   929  				)
   930  			})
   931  
   932  			It("returns an array of routes", func() {
   933  				Expect(executeErr).NotTo(HaveOccurred())
   934  				Expect(warnings).To(ConsistOf("get-app-routes-warning"))
   935  
   936  				Expect(routes).To(ConsistOf(
   937  					resources.Route{
   938  						GUID:       "route-guid",
   939  						DomainGUID: "domain-guid",
   940  						SpaceGUID:  "space-guid",
   941  						Host:       "host",
   942  						Path:       "/path",
   943  						URL:        "host.domain.com/path",
   944  					},
   945  					resources.Route{
   946  						GUID:       "route2-guid",
   947  						DomainGUID: "domain2-guid",
   948  						SpaceGUID:  "space-guid",
   949  						Host:       "",
   950  						Path:       "",
   951  						URL:        "domain.com",
   952  					},
   953  				))
   954  			})
   955  		})
   956  
   957  		When("there is a cc error", func() {
   958  			BeforeEach(func() {
   959  				response := `{
   960    "errors": [
   961      {
   962        "code": 10008,
   963        "detail": "The request is semantically invalid: command presence",
   964        "title": "CF-UnprocessableEntity"
   965      },
   966  		{
   967        "code": 10010,
   968        "detail": "Isolation segment not found",
   969        "title": "CF-ResourceNotFound"
   970      }
   971    ]
   972  }`
   973  				server.AppendHandlers(
   974  					CombineHandlers(
   975  						VerifyRequest(http.MethodGet, "/v3/apps/some-app-guid/routes"),
   976  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"get-app-routes-warning"}, "Location": {"job-url"}}),
   977  					),
   978  				)
   979  			})
   980  
   981  			It("returns the error", func() {
   982  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   983  					ResponseCode: http.StatusTeapot,
   984  					Errors: []ccerror.V3Error{
   985  						{
   986  							Code:   10008,
   987  							Detail: "The request is semantically invalid: command presence",
   988  							Title:  "CF-UnprocessableEntity",
   989  						},
   990  						{
   991  							Code:   10010,
   992  							Detail: "Isolation segment not found",
   993  							Title:  "CF-ResourceNotFound",
   994  						},
   995  					},
   996  				}))
   997  				Expect(warnings).To(ConsistOf("get-app-routes-warning"))
   998  			})
   999  		})
  1000  	})
  1001  })