github.com/dcarley/cf-cli@v6.24.1-0.20170220111324-4225ff346898+incompatible/api/cloudcontroller/ccv3/task_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" 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("Task", func() { 16 var client *Client 17 18 BeforeEach(func() { 19 client = NewTestClient() 20 }) 21 22 Describe("NewTask", func() { 23 Context("when the application exists", func() { 24 var response string 25 26 BeforeEach(func() { 27 //TODO: check if latest CC API returns this format 28 response = `{ 29 "sequence_id": 3 30 }` 31 }) 32 33 Context("when the name is empty", func() { 34 BeforeEach(func() { 35 server.AppendHandlers( 36 CombineHandlers( 37 VerifyRequest(http.MethodPost, "/v3/apps/some-app-guid/tasks"), 38 VerifyJSON(`{"command":"some command"}`), 39 RespondWith(http.StatusAccepted, response, http.Header{"X-Cf-Warnings": {"warning"}}), 40 ), 41 ) 42 }) 43 44 It("creates and returns the task and all warnings", func() { 45 task, warnings, err := client.NewTask("some-app-guid", "some command", "", 0, 0) 46 Expect(err).ToNot(HaveOccurred()) 47 48 Expect(task).To(Equal(Task{SequenceID: 3})) 49 Expect(warnings).To(ConsistOf("warning")) 50 }) 51 }) 52 53 Context("when the name is not empty", func() { 54 BeforeEach(func() { 55 server.AppendHandlers( 56 CombineHandlers( 57 VerifyRequest(http.MethodPost, "/v3/apps/some-app-guid/tasks"), 58 VerifyJSON(`{"command":"some command", "name":"some-task-name"}`), 59 RespondWith(http.StatusAccepted, response, http.Header{"X-Cf-Warnings": {"warning"}}), 60 ), 61 ) 62 }) 63 64 It("creates and returns the task and all warnings", func() { 65 task, warnings, err := client.NewTask("some-app-guid", "some command", "some-task-name", 0, 0) 66 Expect(err).ToNot(HaveOccurred()) 67 68 Expect(task).To(Equal(Task{SequenceID: 3})) 69 Expect(warnings).To(ConsistOf("warning")) 70 }) 71 }) 72 73 Context("when the disk size is not 0", func() { 74 BeforeEach(func() { 75 response := `{ 76 "disk_in_mb": 123, 77 "sequence_id": 3 78 }` 79 server.AppendHandlers( 80 CombineHandlers( 81 VerifyRequest(http.MethodPost, "/v3/apps/some-app-guid/tasks"), 82 VerifyJSON(`{"command":"some command", "disk_in_mb": 123}`), 83 RespondWith(http.StatusAccepted, response, http.Header{"X-Cf-Warnings": {"warning"}}), 84 ), 85 ) 86 }) 87 88 It("creates and returns the task and all warnings with the provided disk size", func() { 89 task, warnings, err := client.NewTask("some-app-guid", "some command", "", 0, uint64(123)) 90 Expect(err).ToNot(HaveOccurred()) 91 92 Expect(task).To(Equal(Task{DiskInMB: uint64(123), SequenceID: 3})) 93 Expect(warnings).To(ConsistOf("warning")) 94 }) 95 }) 96 97 Context("when the memory is not 0", func() { 98 BeforeEach(func() { 99 response := `{ 100 "memory_in_mb": 123, 101 "sequence_id": 3 102 }` 103 server.AppendHandlers( 104 CombineHandlers( 105 VerifyRequest(http.MethodPost, "/v3/apps/some-app-guid/tasks"), 106 VerifyJSON(`{"command":"some command", "memory_in_mb": 123}`), 107 RespondWith(http.StatusAccepted, response, http.Header{"X-Cf-Warnings": {"warning"}}), 108 ), 109 ) 110 }) 111 112 It("creates and returns the task and all warnings with the provided memory", func() { 113 task, warnings, err := client.NewTask("some-app-guid", "some command", "", uint64(123), 0) 114 Expect(err).ToNot(HaveOccurred()) 115 116 Expect(task).To(Equal(Task{MemoryInMB: uint64(123), SequenceID: 3})) 117 Expect(warnings).To(ConsistOf("warning")) 118 }) 119 }) 120 121 }) 122 123 Context("when the cloud controller returns errors and warnings", func() { 124 BeforeEach(func() { 125 response := `{ 126 "errors": [ 127 { 128 "code": 10008, 129 "detail": "The request is semantically invalid: command presence", 130 "title": "CF-UnprocessableEntity" 131 }, 132 { 133 "code": 10010, 134 "detail": "App not found", 135 "title": "CF-ResourceNotFound" 136 } 137 ] 138 }` 139 server.AppendHandlers( 140 CombineHandlers( 141 VerifyRequest(http.MethodPost, "/v3/apps/some-app-guid/tasks"), 142 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"warning"}}), 143 ), 144 ) 145 }) 146 147 It("returns the errors and all warnings", func() { 148 _, warnings, err := client.NewTask("some-app-guid", "some command", "", 0, 0) 149 Expect(err).To(MatchError(UnexpectedResponseError{ 150 ResponseCode: http.StatusTeapot, 151 CCErrorResponse: CCErrorResponse{ 152 []CCError{ 153 { 154 Code: 10008, 155 Detail: "The request is semantically invalid: command presence", 156 Title: "CF-UnprocessableEntity", 157 }, 158 { 159 Code: 10010, 160 Detail: "App not found", 161 Title: "CF-ResourceNotFound", 162 }, 163 }, 164 }, 165 })) 166 Expect(warnings).To(ConsistOf("warning")) 167 }) 168 }) 169 }) 170 171 Describe("GetApplicationTasks", func() { 172 Context("when the application exists", func() { 173 BeforeEach(func() { 174 response1 := fmt.Sprintf(`{ 175 "pagination": { 176 "next": { 177 "href": "%s/v3/apps/some-app-guid/tasks?per_page=2&page=2" 178 } 179 }, 180 "resources": [ 181 { 182 "guid": "task-1-guid", 183 "sequence_id": 1, 184 "name": "task-1", 185 "command": "some-command", 186 "state": "SUCCEEDED", 187 "created_at": "2016-11-07T05:59:01Z" 188 }, 189 { 190 "guid": "task-2-guid", 191 "sequence_id": 2, 192 "name": "task-2", 193 "command": "some-command", 194 "state": "FAILED", 195 "created_at": "2016-11-07T06:59:01Z" 196 } 197 ] 198 }`, server.URL()) 199 response2 := `{ 200 "pagination": { 201 "next": null 202 }, 203 "resources": [ 204 { 205 "guid": "task-3-guid", 206 "sequence_id": 3, 207 "name": "task-3", 208 "command": "some-command", 209 "state": "RUNNING", 210 "created_at": "2016-11-07T07:59:01Z" 211 } 212 ] 213 }` 214 server.AppendHandlers( 215 CombineHandlers( 216 VerifyRequest(http.MethodGet, "/v3/apps/some-app-guid/tasks", "per_page=2"), 217 RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"warning-1"}}), 218 ), 219 ) 220 server.AppendHandlers( 221 CombineHandlers( 222 VerifyRequest(http.MethodGet, "/v3/apps/some-app-guid/tasks", "per_page=2&page=2"), 223 RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"warning-2"}}), 224 ), 225 ) 226 }) 227 228 It("returns a list of tasks associated with the application and all warnings", func() { 229 tasks, warnings, err := client.GetApplicationTasks("some-app-guid", url.Values{"per_page": []string{"2"}}) 230 Expect(err).ToNot(HaveOccurred()) 231 232 Expect(tasks).To(ConsistOf( 233 Task{ 234 GUID: "task-1-guid", 235 SequenceID: 1, 236 Name: "task-1", 237 State: "SUCCEEDED", 238 CreatedAt: "2016-11-07T05:59:01Z", 239 Command: "some-command", 240 }, 241 Task{ 242 GUID: "task-2-guid", 243 SequenceID: 2, 244 Name: "task-2", 245 State: "FAILED", 246 CreatedAt: "2016-11-07T06:59:01Z", 247 Command: "some-command", 248 }, 249 Task{ 250 GUID: "task-3-guid", 251 SequenceID: 3, 252 Name: "task-3", 253 State: "RUNNING", 254 CreatedAt: "2016-11-07T07:59:01Z", 255 Command: "some-command", 256 }, 257 )) 258 Expect(warnings).To(ConsistOf("warning-1", "warning-2")) 259 }) 260 }) 261 262 Context("when the application does not exist", func() { 263 BeforeEach(func() { 264 response := `{ 265 "errors": [ 266 { 267 "code": 10010, 268 "detail": "App not found", 269 "title": "CF-ResourceNotFound" 270 } 271 ] 272 }` 273 server.AppendHandlers( 274 CombineHandlers( 275 VerifyRequest(http.MethodGet, "/v3/apps/some-app-guid/tasks"), 276 RespondWith(http.StatusNotFound, response), 277 ), 278 ) 279 }) 280 281 It("returns a ResourceNotFoundError", func() { 282 _, _, err := client.GetApplicationTasks("some-app-guid", nil) 283 Expect(err).To(MatchError(cloudcontroller.ResourceNotFoundError{Message: "App not found"})) 284 }) 285 }) 286 287 Context("when the cloud controller returns errors and warnings", func() { 288 BeforeEach(func() { 289 response := `{ 290 "errors": [ 291 { 292 "code": 10008, 293 "detail": "The request is semantically invalid: command presence", 294 "title": "CF-UnprocessableEntity" 295 }, 296 { 297 "code": 10010, 298 "detail": "App not found", 299 "title": "CF-ResourceNotFound" 300 } 301 ] 302 }` 303 server.AppendHandlers( 304 CombineHandlers( 305 VerifyRequest(http.MethodGet, "/v3/apps/some-app-guid/tasks"), 306 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"warning"}}), 307 ), 308 ) 309 }) 310 311 It("returns the errors and all warnings", func() { 312 _, warnings, err := client.GetApplicationTasks("some-app-guid", nil) 313 Expect(err).To(MatchError(UnexpectedResponseError{ 314 ResponseCode: http.StatusTeapot, 315 CCErrorResponse: CCErrorResponse{ 316 []CCError{ 317 { 318 Code: 10008, 319 Detail: "The request is semantically invalid: command presence", 320 Title: "CF-UnprocessableEntity", 321 }, 322 { 323 Code: 10010, 324 Detail: "App not found", 325 Title: "CF-ResourceNotFound", 326 }, 327 }, 328 }, 329 })) 330 Expect(warnings).To(ConsistOf("warning")) 331 }) 332 }) 333 }) 334 335 Describe("UpdateTask", func() { 336 Context("when the request succeeds", func() { 337 BeforeEach(func() { 338 response := `{ 339 "guid": "task-3-guid", 340 "sequence_id": 3, 341 "name": "task-3", 342 "command": "some-command", 343 "state": "CANCELING", 344 "created_at": "2016-11-07T07:59:01Z" 345 }` 346 server.AppendHandlers( 347 CombineHandlers( 348 VerifyRequest(http.MethodPut, "/v3/tasks/some-task-guid/cancel"), 349 RespondWith(http.StatusAccepted, response, http.Header{"X-Cf-Warnings": {"warning"}}), 350 ), 351 ) 352 }) 353 354 It("returns the task and warnings", func() { 355 task, warnings, err := client.UpdateTask("some-task-guid") 356 Expect(err).ToNot(HaveOccurred()) 357 358 Expect(task).To(Equal(Task{ 359 GUID: "task-3-guid", 360 SequenceID: 3, 361 Name: "task-3", 362 Command: "some-command", 363 State: "CANCELING", 364 CreatedAt: "2016-11-07T07:59:01Z", 365 })) 366 Expect(warnings).To(ConsistOf("warning")) 367 }) 368 }) 369 370 Context("when the request fails", func() { 371 BeforeEach(func() { 372 response := `{ 373 "errors": [ 374 { 375 "code": 10008, 376 "detail": "The request is semantically invalid: command presence", 377 "title": "CF-UnprocessableEntity" 378 }, 379 { 380 "code": 10010, 381 "detail": "App not found", 382 "title": "CF-ResourceNotFound" 383 } 384 ] 385 }` 386 server.AppendHandlers( 387 CombineHandlers( 388 VerifyRequest(http.MethodPut, "/v3/tasks/some-task-guid/cancel"), 389 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"warning"}}), 390 ), 391 ) 392 }) 393 394 It("returns the errors and all warnings", func() { 395 _, warnings, err := client.UpdateTask("some-task-guid") 396 Expect(err).To(MatchError(UnexpectedResponseError{ 397 ResponseCode: http.StatusTeapot, 398 CCErrorResponse: CCErrorResponse{ 399 []CCError{ 400 { 401 Code: 10008, 402 Detail: "The request is semantically invalid: command presence", 403 Title: "CF-UnprocessableEntity", 404 }, 405 { 406 Code: 10010, 407 Detail: "App not found", 408 Title: "CF-ResourceNotFound", 409 }, 410 }, 411 }, 412 })) 413 Expect(warnings).To(ConsistOf("warning")) 414 }) 415 }) 416 }) 417 })