github.com/jghiloni/cli@v6.28.1-0.20170628223758-0ce05fe032a2+incompatible/api/cloudcontroller/ccv3/application_test.go (about) 1 package ccv3_test 2 3 import ( 4 "fmt" 5 "net/http" 6 "net/url" 7 8 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 9 . "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" 10 . "github.com/onsi/ginkgo" 11 . "github.com/onsi/gomega" 12 . "github.com/onsi/gomega/ghttp" 13 ) 14 15 var _ = Describe("Application", func() { 16 var client *Client 17 18 BeforeEach(func() { 19 client = NewTestClient() 20 }) 21 22 Describe("GetApplications", func() { 23 Context("when applications exist", func() { 24 BeforeEach(func() { 25 response1 := fmt.Sprintf(`{ 26 "pagination": { 27 "next": { 28 "href": "%s/v3/apps?space_guids=some-space-guid&names=some-app-name&page=2&per_page=2" 29 } 30 }, 31 "resources": [ 32 { 33 "name": "app-name-1", 34 "guid": "app-guid-1", 35 "lifecycle": { 36 "type": "buildpack", 37 "data": { 38 "buildpacks": ["some-buildpack"], 39 "stack": "some-stack" 40 } 41 } 42 }, 43 { 44 "name": "app-name-2", 45 "guid": "app-guid-2" 46 } 47 ] 48 }`, server.URL()) 49 response2 := `{ 50 "pagination": { 51 "next": null 52 }, 53 "resources": [ 54 { 55 "name": "app-name-3", 56 "guid": "app-guid-3" 57 } 58 ] 59 }` 60 server.AppendHandlers( 61 CombineHandlers( 62 VerifyRequest(http.MethodGet, "/v3/apps", "space_guids=some-space-guid&names=some-app-name"), 63 RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 64 ), 65 ) 66 server.AppendHandlers( 67 CombineHandlers( 68 VerifyRequest(http.MethodGet, "/v3/apps", "space_guids=some-space-guid&names=some-app-name&page=2&per_page=2"), 69 RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"this is another warning"}}), 70 ), 71 ) 72 }) 73 74 It("returns the queried applications and all warnings", func() { 75 apps, warnings, err := client.GetApplications(url.Values{ 76 SpaceGUIDFilter: []string{"some-space-guid"}, 77 NameFilter: []string{"some-app-name"}, 78 }) 79 Expect(err).NotTo(HaveOccurred()) 80 81 Expect(apps).To(ConsistOf( 82 Application{ 83 Name: "app-name-1", 84 GUID: "app-guid-1", 85 }, 86 Application{Name: "app-name-2", GUID: "app-guid-2"}, 87 Application{Name: "app-name-3", GUID: "app-guid-3"}, 88 )) 89 Expect(warnings).To(ConsistOf("this is a warning", "this is another warning")) 90 }) 91 }) 92 93 Context("when the cloud controller returns errors and warnings", func() { 94 BeforeEach(func() { 95 response := `{ 96 "errors": [ 97 { 98 "code": 10008, 99 "detail": "The request is semantically invalid: command presence", 100 "title": "CF-UnprocessableEntity" 101 }, 102 { 103 "code": 10010, 104 "detail": "App not found", 105 "title": "CF-ResourceNotFound" 106 } 107 ] 108 }` 109 server.AppendHandlers( 110 CombineHandlers( 111 VerifyRequest(http.MethodGet, "/v3/apps"), 112 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 113 ), 114 ) 115 }) 116 117 It("returns the error and all warnings", func() { 118 _, warnings, err := client.GetApplications(nil) 119 Expect(err).To(MatchError(ccerror.V3UnexpectedResponseError{ 120 ResponseCode: http.StatusTeapot, 121 V3ErrorResponse: ccerror.V3ErrorResponse{ 122 []ccerror.V3Error{ 123 { 124 Code: 10008, 125 Detail: "The request is semantically invalid: command presence", 126 Title: "CF-UnprocessableEntity", 127 }, 128 { 129 Code: 10010, 130 Detail: "App not found", 131 Title: "CF-ResourceNotFound", 132 }, 133 }, 134 }, 135 })) 136 Expect(warnings).To(ConsistOf("this is a warning")) 137 }) 138 }) 139 }) 140 141 Describe("CreateApplication", func() { 142 Context("when the application successfully is created", func() { 143 BeforeEach(func() { 144 response := `{ 145 "guid": "some-app-guid", 146 "name": "some-app-name" 147 }` 148 149 expectedBody := map[string]interface{}{ 150 "name": "some-app-name", 151 "relationships": map[string]interface{}{ 152 "space": map[string]interface{}{ 153 "data": map[string]string{ 154 "guid": "some-space-guid", 155 }, 156 }, 157 }, 158 } 159 server.AppendHandlers( 160 CombineHandlers( 161 VerifyRequest(http.MethodPost, "/v3/apps"), 162 VerifyJSONRepresenting(expectedBody), 163 RespondWith(http.StatusCreated, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 164 ), 165 ) 166 }) 167 168 It("returns the created app and warnings", func() { 169 app, warnings, err := client.CreateApplication(Application{ 170 Name: "some-app-name", 171 Relationships: Relationships{ 172 SpaceRelationship: Relationship{GUID: "some-space-guid"}, 173 }, 174 }) 175 176 Expect(err).NotTo(HaveOccurred()) 177 Expect(warnings).To(ConsistOf("this is a warning")) 178 179 Expect(app).To(Equal(Application{ 180 Name: "some-app-name", 181 GUID: "some-app-guid", 182 })) 183 }) 184 }) 185 186 Context("when cc returns back an error or warnings", func() { 187 BeforeEach(func() { 188 response := `{ 189 "errors": [ 190 { 191 "code": 10008, 192 "detail": "The request is semantically invalid: command presence", 193 "title": "CF-UnprocessableEntity" 194 }, 195 { 196 "code": 10010, 197 "detail": "App not found", 198 "title": "CF-ResourceNotFound" 199 } 200 ] 201 }` 202 server.AppendHandlers( 203 CombineHandlers( 204 VerifyRequest(http.MethodPost, "/v3/apps"), 205 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 206 ), 207 ) 208 }) 209 210 It("returns the error and all warnings", func() { 211 _, warnings, err := client.CreateApplication(Application{}) 212 Expect(err).To(MatchError(ccerror.V3UnexpectedResponseError{ 213 ResponseCode: http.StatusTeapot, 214 V3ErrorResponse: ccerror.V3ErrorResponse{ 215 []ccerror.V3Error{ 216 { 217 Code: 10008, 218 Detail: "The request is semantically invalid: command presence", 219 Title: "CF-UnprocessableEntity", 220 }, 221 { 222 Code: 10010, 223 Detail: "App not found", 224 Title: "CF-ResourceNotFound", 225 }, 226 }, 227 }, 228 })) 229 Expect(warnings).To(ConsistOf("this is a warning")) 230 }) 231 }) 232 }) 233 234 Describe("SetApplicationDroplet", func() { 235 Context("it sets the droplet", func() { 236 BeforeEach(func() { 237 response := ` 238 { 239 "data": { 240 "guid": "some-droplet-guid" 241 }, 242 "links": { 243 "self": { 244 "href": "https://api.example.org/v3/apps/some-app-guid/relationships/current_droplet" 245 }, 246 "related": { 247 "href": "https://api.example.org/v3/apps/some-app-guid/droplets/current" 248 } 249 } 250 }` 251 requestBody := map[string]interface{}{ 252 "data": map[string]string{ 253 "guid": "some-droplet-guid", 254 }, 255 } 256 257 server.AppendHandlers( 258 CombineHandlers( 259 VerifyRequest(http.MethodPatch, "/v3/apps/some-app-guid/relationships/current_droplet"), 260 VerifyJSONRepresenting(requestBody), 261 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 262 ), 263 ) 264 }) 265 266 It("returns warnings and no error", func() { 267 relationship, warnings, err := client.SetApplicationDroplet("some-app-guid", "some-droplet-guid") 268 Expect(err).ToNot(HaveOccurred()) 269 Expect(warnings).To(ConsistOf("this is a warning")) 270 Expect(relationship.GUID).To(Equal("some-droplet-guid")) 271 }) 272 }) 273 }) 274 Context("when setting the app to the new droplet returns errors and warnings", func() { 275 BeforeEach(func() { 276 response := `{ 277 "errors": [ 278 { 279 "code": 10008, 280 "detail": "The request is semantically invalid: command presence", 281 "title": "CF-UnprocessableEntity" 282 }, 283 { 284 "code": 10010, 285 "detail": "App not found", 286 "title": "CF-ResourceNotFound" 287 } 288 ] 289 }` 290 requestBody := map[string]interface{}{ 291 "data": map[string]string{ 292 "guid": "some-droplet-guid", 293 }, 294 } 295 296 server.AppendHandlers( 297 CombineHandlers( 298 VerifyRequest(http.MethodPatch, "/v3/apps/no-such-app-guid/relationships/current_droplet"), 299 VerifyJSONRepresenting(requestBody), 300 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 301 ), 302 ) 303 304 }) 305 306 It("returns the error and all warnings", func() { 307 _, warnings, err := client.SetApplicationDroplet("no-such-app-guid", "some-droplet-guid") 308 Expect(err).To(MatchError(ccerror.V3UnexpectedResponseError{ 309 ResponseCode: http.StatusTeapot, 310 V3ErrorResponse: ccerror.V3ErrorResponse{ 311 []ccerror.V3Error{ 312 { 313 Code: 10008, 314 Detail: "The request is semantically invalid: command presence", 315 Title: "CF-UnprocessableEntity", 316 }, 317 { 318 Code: 10010, 319 Detail: "App not found", 320 Title: "CF-ResourceNotFound", 321 }, 322 }, 323 }, 324 })) 325 Expect(warnings).To(ConsistOf("this is a warning")) 326 }) 327 }) 328 329 Describe("StopApplication", func() { 330 Context("when the response succeeds", func() { 331 BeforeEach(func() { 332 response := ` 333 { 334 "guid": "some-app-guid", 335 "name": "some-app", 336 "state": "STOPPED" 337 }` 338 server.AppendHandlers( 339 CombineHandlers( 340 VerifyRequest(http.MethodPut, "/v3/apps/some-app-guid/stop"), 341 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 342 ), 343 ) 344 }) 345 346 It("returns warnings and no error", func() { 347 warnings, err := client.StopApplication("some-app-guid") 348 Expect(err).ToNot(HaveOccurred()) 349 Expect(warnings).To(ConsistOf("this is a warning")) 350 }) 351 }) 352 }) 353 354 Context("when stopping the app returns errors and warnings", func() { 355 BeforeEach(func() { 356 response := `{ 357 "errors": [ 358 { 359 "code": 10008, 360 "detail": "The request is semantically invalid: command presence", 361 "title": "CF-UnprocessableEntity" 362 }, 363 { 364 "code": 10010, 365 "detail": "App not found", 366 "title": "CF-ResourceNotFound" 367 } 368 ] 369 }` 370 server.AppendHandlers( 371 CombineHandlers( 372 VerifyRequest(http.MethodPut, "/v3/apps/no-such-app-guid/stop"), 373 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 374 ), 375 ) 376 377 }) 378 379 It("returns the error and all warnings", func() { 380 warnings, err := client.StopApplication("no-such-app-guid") 381 Expect(err).To(MatchError(ccerror.V3UnexpectedResponseError{ 382 ResponseCode: http.StatusTeapot, 383 V3ErrorResponse: ccerror.V3ErrorResponse{ 384 []ccerror.V3Error{ 385 { 386 Code: 10008, 387 Detail: "The request is semantically invalid: command presence", 388 Title: "CF-UnprocessableEntity", 389 }, 390 { 391 Code: 10010, 392 Detail: "App not found", 393 Title: "CF-ResourceNotFound", 394 }, 395 }, 396 }, 397 })) 398 Expect(warnings).To(ConsistOf("this is a warning")) 399 }) 400 }) 401 402 Describe("StartApplication", func() { 403 Context("when the response succeeds", func() { 404 BeforeEach(func() { 405 response := ` 406 { 407 "guid": "some-app-guid", 408 "name": "some-app" 409 }` 410 server.AppendHandlers( 411 CombineHandlers( 412 VerifyRequest(http.MethodPut, "/v3/apps/some-app-guid/start"), 413 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 414 ), 415 ) 416 }) 417 418 It("returns warnings and no error", func() { 419 app, warnings, err := client.StartApplication("some-app-guid") 420 Expect(err).ToNot(HaveOccurred()) 421 Expect(warnings).To(ConsistOf("this is a warning")) 422 Expect(app.GUID).To(Equal("some-app-guid")) 423 }) 424 }) 425 }) 426 Context("when starting the app returns errors and warnings", func() { 427 BeforeEach(func() { 428 response := `{ 429 "errors": [ 430 { 431 "code": 10008, 432 "detail": "The request is semantically invalid: command presence", 433 "title": "CF-UnprocessableEntity" 434 }, 435 { 436 "code": 10010, 437 "detail": "App not found", 438 "title": "CF-ResourceNotFound" 439 } 440 ] 441 }` 442 server.AppendHandlers( 443 CombineHandlers( 444 VerifyRequest(http.MethodPut, "/v3/apps/no-such-app-guid/start"), 445 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 446 ), 447 ) 448 449 }) 450 451 It("returns the error and all warnings", func() { 452 _, warnings, err := client.StartApplication("no-such-app-guid") 453 Expect(err).To(MatchError(ccerror.V3UnexpectedResponseError{ 454 ResponseCode: http.StatusTeapot, 455 V3ErrorResponse: ccerror.V3ErrorResponse{ 456 []ccerror.V3Error{ 457 { 458 Code: 10008, 459 Detail: "The request is semantically invalid: command presence", 460 Title: "CF-UnprocessableEntity", 461 }, 462 { 463 Code: 10010, 464 Detail: "App not found", 465 Title: "CF-ResourceNotFound", 466 }, 467 }, 468 }, 469 })) 470 Expect(warnings).To(ConsistOf("this is a warning")) 471 }) 472 }) 473 })