github.com/wanddynosios/cli/v8@v8.7.9-0.20240221182337-1a92e3a7017f/api/cloudcontroller/ccv3/task_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/api/cloudcontroller/ccv3/constant" 10 "code.cloudfoundry.org/cli/resources" 11 . "github.com/onsi/ginkgo" 12 . "github.com/onsi/gomega" 13 . "github.com/onsi/gomega/ghttp" 14 ) 15 16 var _ = Describe("Task", func() { 17 var client *Client 18 19 BeforeEach(func() { 20 client, _ = NewTestClient() 21 }) 22 23 Describe("CreateApplicationTask", func() { 24 var ( 25 submitTask resources.Task 26 27 task resources.Task 28 warnings Warnings 29 executeErr error 30 ) 31 32 JustBeforeEach(func() { 33 task, warnings, executeErr = client.CreateApplicationTask("some-app-guid", submitTask) 34 }) 35 36 When("the application exists", func() { 37 var response string 38 39 BeforeEach(func() { 40 response = `{ 41 "sequence_id": 3 42 }` 43 }) 44 45 When("the name is empty", func() { 46 BeforeEach(func() { 47 server.AppendHandlers( 48 CombineHandlers( 49 VerifyRequest(http.MethodPost, "/v3/apps/some-app-guid/tasks"), 50 VerifyJSON(`{"command":"some command"}`), 51 RespondWith(http.StatusAccepted, response, http.Header{"X-Cf-Warnings": {"warning"}}), 52 ), 53 ) 54 55 submitTask = resources.Task{Command: "some command"} 56 }) 57 58 It("creates and returns the task and all warnings", func() { 59 Expect(executeErr).ToNot(HaveOccurred()) 60 61 Expect(task).To(Equal(resources.Task{SequenceID: 3})) 62 Expect(warnings).To(ConsistOf("warning")) 63 }) 64 }) 65 66 When("the name is not empty", func() { 67 BeforeEach(func() { 68 server.AppendHandlers( 69 CombineHandlers( 70 VerifyRequest(http.MethodPost, "/v3/apps/some-app-guid/tasks"), 71 VerifyJSON(`{"command":"some command", "name":"some-task-name"}`), 72 RespondWith(http.StatusAccepted, response, http.Header{"X-Cf-Warnings": {"warning"}}), 73 ), 74 ) 75 76 submitTask = resources.Task{Command: "some command", Name: "some-task-name"} 77 }) 78 79 It("creates and returns the task and all warnings", func() { 80 Expect(executeErr).ToNot(HaveOccurred()) 81 82 Expect(task).To(Equal(resources.Task{SequenceID: 3})) 83 Expect(warnings).To(ConsistOf("warning")) 84 }) 85 }) 86 87 When("the disk size is not 0", func() { 88 BeforeEach(func() { 89 response := `{ 90 "disk_in_mb": 123, 91 "sequence_id": 3 92 }` 93 server.AppendHandlers( 94 CombineHandlers( 95 VerifyRequest(http.MethodPost, "/v3/apps/some-app-guid/tasks"), 96 VerifyJSON(`{"command":"some command", "disk_in_mb": 123}`), 97 RespondWith(http.StatusAccepted, response, http.Header{"X-Cf-Warnings": {"warning"}}), 98 ), 99 ) 100 submitTask = resources.Task{Command: "some command", DiskInMB: uint64(123)} 101 }) 102 103 It("creates and returns the task and all warnings with the provided disk size", func() { 104 Expect(executeErr).ToNot(HaveOccurred()) 105 106 Expect(task).To(Equal(resources.Task{DiskInMB: uint64(123), SequenceID: 3})) 107 Expect(warnings).To(ConsistOf("warning")) 108 }) 109 }) 110 111 When("the memory is not 0", func() { 112 BeforeEach(func() { 113 response := `{ 114 "memory_in_mb": 123, 115 "sequence_id": 3 116 }` 117 server.AppendHandlers( 118 CombineHandlers( 119 VerifyRequest(http.MethodPost, "/v3/apps/some-app-guid/tasks"), 120 VerifyJSON(`{"command":"some command", "memory_in_mb": 123}`), 121 RespondWith(http.StatusAccepted, response, http.Header{"X-Cf-Warnings": {"warning"}}), 122 ), 123 ) 124 125 submitTask = resources.Task{Command: "some command", MemoryInMB: uint64(123)} 126 }) 127 128 It("creates and returns the task and all warnings with the provided memory", func() { 129 Expect(executeErr).ToNot(HaveOccurred()) 130 131 Expect(task).To(Equal(resources.Task{MemoryInMB: uint64(123), SequenceID: 3})) 132 Expect(warnings).To(ConsistOf("warning")) 133 }) 134 }) 135 136 }) 137 138 When("the cloud controller returns errors and warnings", func() { 139 BeforeEach(func() { 140 response := `{ 141 "errors": [ 142 { 143 "code": 10008, 144 "detail": "The request is semantically invalid: command presence", 145 "title": "CF-UnprocessableEntity" 146 }, 147 { 148 "code": 10010, 149 "detail": "App not found", 150 "title": "CF-ResourceNotFound" 151 } 152 ] 153 }` 154 server.AppendHandlers( 155 CombineHandlers( 156 VerifyRequest(http.MethodPost, "/v3/apps/some-app-guid/tasks"), 157 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"warning"}}), 158 ), 159 ) 160 161 submitTask = resources.Task{Command: "some command"} 162 }) 163 164 It("returns the errors and all warnings", func() { 165 Expect(executeErr).To(MatchError(ccerror.MultiError{ 166 ResponseCode: http.StatusTeapot, 167 Errors: []ccerror.V3Error{ 168 { 169 Code: 10008, 170 Detail: "The request is semantically invalid: command presence", 171 Title: "CF-UnprocessableEntity", 172 }, 173 { 174 Code: 10010, 175 Detail: "App not found", 176 Title: "CF-ResourceNotFound", 177 }, 178 }, 179 })) 180 Expect(warnings).To(ConsistOf("warning")) 181 }) 182 }) 183 }) 184 185 Describe("GetApplicationTasks", func() { 186 var ( 187 submitQuery Query 188 189 tasks []resources.Task 190 warnings Warnings 191 executeErr error 192 ) 193 194 JustBeforeEach(func() { 195 tasks, warnings, executeErr = client.GetApplicationTasks("some-app-guid", submitQuery) 196 }) 197 198 When("the application exists", func() { 199 BeforeEach(func() { 200 response1 := fmt.Sprintf(`{ 201 "pagination": { 202 "next": { 203 "href": "%s/v3/apps/some-app-guid/tasks?per_page=2&page=2" 204 } 205 }, 206 "resources": [ 207 { 208 "guid": "task-1-guid", 209 "sequence_id": 1, 210 "name": "task-1", 211 "command": "some-command", 212 "state": "SUCCEEDED", 213 "created_at": "2016-11-07T05:59:01Z" 214 }, 215 { 216 "guid": "task-2-guid", 217 "sequence_id": 2, 218 "name": "task-2", 219 "command": "some-command", 220 "state": "FAILED", 221 "created_at": "2016-11-07T06:59:01Z" 222 } 223 ] 224 }`, server.URL()) 225 response2 := `{ 226 "pagination": { 227 "next": null 228 }, 229 "resources": [ 230 { 231 "guid": "task-3-guid", 232 "sequence_id": 3, 233 "name": "task-3", 234 "command": "some-command", 235 "state": "RUNNING", 236 "created_at": "2016-11-07T07:59:01Z" 237 } 238 ] 239 }` 240 server.AppendHandlers( 241 CombineHandlers( 242 VerifyRequest(http.MethodGet, "/v3/apps/some-app-guid/tasks", "per_page=2"), 243 RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"warning-1"}}), 244 ), 245 ) 246 server.AppendHandlers( 247 CombineHandlers( 248 VerifyRequest(http.MethodGet, "/v3/apps/some-app-guid/tasks", "per_page=2&page=2"), 249 RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"warning-2"}}), 250 ), 251 ) 252 253 submitQuery = Query{Key: PerPage, Values: []string{"2"}} 254 }) 255 256 It("returns a list of tasks associated with the application and all warnings", func() { 257 Expect(executeErr).ToNot(HaveOccurred()) 258 259 Expect(tasks).To(ConsistOf( 260 resources.Task{ 261 GUID: "task-1-guid", 262 SequenceID: 1, 263 Name: "task-1", 264 State: constant.TaskSucceeded, 265 CreatedAt: "2016-11-07T05:59:01Z", 266 Command: "some-command", 267 }, 268 resources.Task{ 269 GUID: "task-2-guid", 270 SequenceID: 2, 271 Name: "task-2", 272 State: constant.TaskFailed, 273 CreatedAt: "2016-11-07T06:59:01Z", 274 Command: "some-command", 275 }, 276 resources.Task{ 277 GUID: "task-3-guid", 278 SequenceID: 3, 279 Name: "task-3", 280 State: constant.TaskRunning, 281 CreatedAt: "2016-11-07T07:59:01Z", 282 Command: "some-command", 283 }, 284 )) 285 Expect(warnings).To(ConsistOf("warning-1", "warning-2")) 286 }) 287 }) 288 289 When("the application does not exist", func() { 290 BeforeEach(func() { 291 response := `{ 292 "errors": [ 293 { 294 "code": 10010, 295 "detail": "App not found", 296 "title": "CF-ResourceNotFound" 297 } 298 ] 299 }` 300 server.AppendHandlers( 301 CombineHandlers( 302 VerifyRequest(http.MethodGet, "/v3/apps/some-app-guid/tasks"), 303 RespondWith(http.StatusNotFound, response), 304 ), 305 ) 306 }) 307 308 It("returns a ResourceNotFoundError", func() { 309 Expect(executeErr).To(MatchError(ccerror.ApplicationNotFoundError{})) 310 }) 311 }) 312 313 When("the cloud controller returns errors and warnings", func() { 314 BeforeEach(func() { 315 response := `{ 316 "errors": [ 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 server.AppendHandlers( 330 CombineHandlers( 331 VerifyRequest(http.MethodGet, "/v3/apps/some-app-guid/tasks"), 332 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"warning"}}), 333 ), 334 ) 335 }) 336 337 It("returns the errors and all warnings", func() { 338 Expect(executeErr).To(MatchError(ccerror.MultiError{ 339 ResponseCode: http.StatusTeapot, 340 Errors: []ccerror.V3Error{ 341 { 342 Code: 10008, 343 Detail: "The request is semantically invalid: command presence", 344 Title: "CF-UnprocessableEntity", 345 }, 346 { 347 Code: 10010, 348 Detail: "App not found", 349 Title: "CF-ResourceNotFound", 350 }, 351 }, 352 })) 353 Expect(warnings).To(ConsistOf("warning")) 354 }) 355 }) 356 }) 357 358 Describe("UpdateTaskCancel", func() { 359 var ( 360 task resources.Task 361 warnings Warnings 362 executeErr error 363 ) 364 365 JustBeforeEach(func() { 366 task, warnings, executeErr = client.UpdateTaskCancel("some-task-guid") 367 }) 368 369 When("the request succeeds", func() { 370 BeforeEach(func() { 371 response := `{ 372 "guid": "task-3-guid", 373 "sequence_id": 3, 374 "name": "task-3", 375 "command": "some-command", 376 "state": "CANCELING", 377 "created_at": "2016-11-07T07:59:01Z" 378 }` 379 server.AppendHandlers( 380 CombineHandlers( 381 VerifyRequest(http.MethodPut, "/v3/tasks/some-task-guid/cancel"), 382 RespondWith(http.StatusAccepted, response, http.Header{"X-Cf-Warnings": {"warning"}}), 383 ), 384 ) 385 }) 386 387 It("returns the task and warnings", func() { 388 Expect(executeErr).ToNot(HaveOccurred()) 389 390 Expect(task).To(Equal(resources.Task{ 391 GUID: "task-3-guid", 392 SequenceID: 3, 393 Name: "task-3", 394 Command: "some-command", 395 State: constant.TaskCanceling, 396 CreatedAt: "2016-11-07T07:59:01Z", 397 })) 398 Expect(warnings).To(ConsistOf("warning")) 399 }) 400 }) 401 402 When("the request fails", func() { 403 BeforeEach(func() { 404 response := `{ 405 "errors": [ 406 { 407 "code": 10008, 408 "detail": "The request is semantically invalid: command presence", 409 "title": "CF-UnprocessableEntity" 410 }, 411 { 412 "code": 10010, 413 "detail": "App not found", 414 "title": "CF-ResourceNotFound" 415 } 416 ] 417 }` 418 server.AppendHandlers( 419 CombineHandlers( 420 VerifyRequest(http.MethodPut, "/v3/tasks/some-task-guid/cancel"), 421 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"warning"}}), 422 ), 423 ) 424 }) 425 426 It("returns the errors and all warnings", func() { 427 Expect(executeErr).To(MatchError(ccerror.MultiError{ 428 ResponseCode: http.StatusTeapot, 429 Errors: []ccerror.V3Error{ 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 Expect(warnings).To(ConsistOf("warning")) 443 }) 444 }) 445 }) 446 447 Describe("GetTask", func() { 448 When("the task exists", func() { 449 BeforeEach(func() { 450 response := `{ 451 "guid": "the-task-guid", 452 "sequence_id": 1, 453 "name": "task-1", 454 "command": "some-command", 455 "state": "SUCCEEDED", 456 "created_at": "2016-11-07T05:59:01Z" 457 }` 458 459 server.AppendHandlers( 460 CombineHandlers( 461 VerifyRequest(http.MethodGet, "/v3/tasks/the-task-guid"), 462 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"warning"}}), 463 ), 464 ) 465 }) 466 467 It("returns the task and all warnings", func() { 468 task, warnings, err := client.GetTask("the-task-guid") 469 Expect(err).ToNot(HaveOccurred()) 470 471 expectedTask := resources.Task{ 472 GUID: "the-task-guid", 473 SequenceID: 1, 474 Name: "task-1", 475 State: constant.TaskSucceeded, 476 CreatedAt: "2016-11-07T05:59:01Z", 477 Command: "some-command", 478 } 479 480 Expect(task).To(Equal(expectedTask)) 481 Expect(warnings).To(ConsistOf("warning")) 482 }) 483 }) 484 485 When("the cloud controller returns errors and warnings", func() { 486 BeforeEach(func() { 487 response := `{ 488 "errors": [ 489 { 490 "code": 10008, 491 "detail": "The request is semantically invalid: command presence", 492 "title": "CF-UnprocessableEntity" 493 }, 494 { 495 "code": 10010, 496 "detail": "Task not found", 497 "title": "CF-ResourceNotFound" 498 } 499 ] 500 }` 501 server.AppendHandlers( 502 CombineHandlers( 503 VerifyRequest(http.MethodGet, "/v3/tasks/the-task-guid"), 504 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"warning"}}), 505 ), 506 ) 507 }) 508 509 It("returns the errors and all warnings", func() { 510 _, warnings, err := client.GetTask("the-task-guid") 511 512 Expect(err).To(MatchError(ccerror.MultiError{ 513 ResponseCode: http.StatusTeapot, 514 Errors: []ccerror.V3Error{ 515 { 516 Code: 10008, 517 Detail: "The request is semantically invalid: command presence", 518 Title: "CF-UnprocessableEntity", 519 }, 520 { 521 Code: 10010, 522 Detail: "Task not found", 523 Title: "CF-ResourceNotFound", 524 }, 525 }, 526 })) 527 Expect(warnings).To(ConsistOf("warning")) 528 }) 529 }) 530 }) 531 })