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  })