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