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