github.com/rakutentech/cli@v6.12.5-0.20151006231303-24468b65536e+incompatible/cf/api/applications/applications_test.go (about)

     1  package applications_test
     2  
     3  import (
     4  	"encoding/json"
     5  	"net/http"
     6  	"net/http/httptest"
     7  	"time"
     8  
     9  	testapi "github.com/cloudfoundry/cli/cf/api/fakes"
    10  	"github.com/cloudfoundry/cli/cf/errors"
    11  	"github.com/cloudfoundry/cli/cf/models"
    12  	"github.com/cloudfoundry/cli/cf/net"
    13  	testconfig "github.com/cloudfoundry/cli/testhelpers/configuration"
    14  	testnet "github.com/cloudfoundry/cli/testhelpers/net"
    15  	testterm "github.com/cloudfoundry/cli/testhelpers/terminal"
    16  
    17  	. "github.com/cloudfoundry/cli/cf/api/applications"
    18  	. "github.com/cloudfoundry/cli/testhelpers/matchers"
    19  	. "github.com/onsi/ginkgo"
    20  	. "github.com/onsi/gomega"
    21  )
    22  
    23  var _ = Describe("ApplicationsRepository", func() {
    24  	Describe("finding apps by name", func() {
    25  		It("returns the app when it is found", func() {
    26  			ts, handler, repo := createAppRepo([]testnet.TestRequest{findAppRequest})
    27  			defer ts.Close()
    28  
    29  			app, apiErr := repo.Read("My App")
    30  			Expect(handler).To(HaveAllRequestsCalled())
    31  			Expect(apiErr).NotTo(HaveOccurred())
    32  			Expect(app.Name).To(Equal("My App"))
    33  			Expect(app.Guid).To(Equal("app1-guid"))
    34  			Expect(app.Memory).To(Equal(int64(128)))
    35  			Expect(app.DiskQuota).To(Equal(int64(512)))
    36  			Expect(app.InstanceCount).To(Equal(1))
    37  			Expect(app.EnvironmentVars).To(Equal(map[string]interface{}{"foo": "bar", "baz": "boom"}))
    38  			Expect(app.Routes[0].Host).To(Equal("app1"))
    39  			Expect(app.Routes[0].Domain.Name).To(Equal("cfapps.io"))
    40  			Expect(app.Stack.Name).To(Equal("awesome-stacks-ahoy"))
    41  		})
    42  
    43  		It("returns a failure response when the app is not found", func() {
    44  			request := testapi.NewCloudControllerTestRequest(findAppRequest)
    45  			request.Response = testnet.TestResponse{Status: http.StatusOK, Body: `{"resources": []}`}
    46  
    47  			ts, handler, repo := createAppRepo([]testnet.TestRequest{request})
    48  			defer ts.Close()
    49  
    50  			_, apiErr := repo.Read("My App")
    51  			Expect(handler).To(HaveAllRequestsCalled())
    52  			Expect(apiErr.(*errors.ModelNotFoundError)).NotTo(BeNil())
    53  		})
    54  	})
    55  
    56  	Describe(".GetApp", func() {
    57  		It("returns an application using the given app guid", func() {
    58  			request := testapi.NewCloudControllerTestRequest(testnet.TestRequest{
    59  				Method:   "GET",
    60  				Path:     "/v2/apps/app-guid",
    61  				Response: appModelResponse,
    62  			})
    63  			ts, handler, repo := createAppRepo([]testnet.TestRequest{request})
    64  			defer ts.Close()
    65  			app, err := repo.GetApp("app-guid")
    66  
    67  			Expect(err).ToNot(HaveOccurred())
    68  			Expect(handler).To(HaveAllRequestsCalled())
    69  			Expect(app.Name).To(Equal("My App"))
    70  		})
    71  	})
    72  
    73  	Describe(".ReadFromSpace", func() {
    74  		It("returns an application using the given space guid", func() {
    75  			request := testapi.NewCloudControllerTestRequest(testnet.TestRequest{
    76  				Method:   "GET",
    77  				Path:     "/v2/spaces/another-space-guid/apps?q=name%3AMy+App&inline-relations-depth=1",
    78  				Response: singleAppResponse,
    79  			})
    80  			ts, handler, repo := createAppRepo([]testnet.TestRequest{request})
    81  			defer ts.Close()
    82  			app, err := repo.ReadFromSpace("My App", "another-space-guid")
    83  
    84  			Expect(err).ToNot(HaveOccurred())
    85  			Expect(handler).To(HaveAllRequestsCalled())
    86  			Expect(app.Name).To(Equal("My App"))
    87  		})
    88  	})
    89  
    90  	Describe("creating applications", func() {
    91  		It("makes the right request", func() {
    92  			ts, handler, repo := createAppRepo([]testnet.TestRequest{createApplicationRequest})
    93  			defer ts.Close()
    94  
    95  			params := defaultAppParams()
    96  			createdApp, apiErr := repo.Create(params)
    97  
    98  			Expect(handler).To(HaveAllRequestsCalled())
    99  			Expect(apiErr).NotTo(HaveOccurred())
   100  
   101  			app := models.Application{}
   102  			app.Name = "my-cool-app"
   103  			app.Guid = "my-cool-app-guid"
   104  			Expect(createdApp).To(Equal(app))
   105  		})
   106  
   107  		It("omits fields that are not set", func() {
   108  			request := testapi.NewCloudControllerTestRequest(testnet.TestRequest{
   109  				Method:   "POST",
   110  				Path:     "/v2/apps",
   111  				Matcher:  testnet.RequestBodyMatcher(`{"name":"my-cool-app","instances":3,"memory":2048,"disk_quota":512,"space_guid":"some-space-guid"}`),
   112  				Response: testnet.TestResponse{Status: http.StatusCreated, Body: createApplicationResponse},
   113  			})
   114  
   115  			ts, handler, repo := createAppRepo([]testnet.TestRequest{request})
   116  			defer ts.Close()
   117  
   118  			params := defaultAppParams()
   119  			params.BuildpackUrl = nil
   120  			params.StackGuid = nil
   121  			params.Command = nil
   122  
   123  			_, apiErr := repo.Create(params)
   124  			Expect(handler).To(HaveAllRequestsCalled())
   125  			Expect(apiErr).NotTo(HaveOccurred())
   126  		})
   127  	})
   128  
   129  	Describe("reading environment for an app", func() {
   130  		Context("when the response can be parsed as json", func() {
   131  			var (
   132  				testServer *httptest.Server
   133  				userEnv    *models.Environment
   134  				err        error
   135  				handler    *testnet.TestHandler
   136  				repo       ApplicationRepository
   137  			)
   138  
   139  			AfterEach(func() {
   140  				testServer.Close()
   141  			})
   142  
   143  			Context("when there are system provided env vars", func() {
   144  				BeforeEach(func() {
   145  
   146  					var appEnvRequest = testapi.NewCloudControllerTestRequest(testnet.TestRequest{
   147  						Method: "GET",
   148  						Path:   "/v2/apps/some-cool-app-guid/env",
   149  						Response: testnet.TestResponse{
   150  							Status: http.StatusOK,
   151  							Body: `
   152  {
   153  	 "staging_env_json": {
   154       "STAGING_ENV": "staging_value",
   155  		 "staging": true,
   156  		 "number": 42
   157     },
   158     "running_env_json": {
   159       "RUNNING_ENV": "running_value",
   160  		 "running": false,
   161  		 "number": 37
   162     },
   163     "environment_json": {
   164       "key": "value",
   165  		 "number": 123,
   166  		 "bool": true
   167     },
   168     "system_env_json": {
   169       "VCAP_SERVICES": {
   170  				"system_hash": {
   171            "system_key": "system_value"
   172          }
   173       }
   174     }
   175  }
   176  `,
   177  						}})
   178  
   179  					testServer, handler, repo = createAppRepo([]testnet.TestRequest{appEnvRequest})
   180  					userEnv, err = repo.ReadEnv("some-cool-app-guid")
   181  					Expect(err).ToNot(HaveOccurred())
   182  					Expect(handler).To(HaveAllRequestsCalled())
   183  				})
   184  
   185  				It("returns the user environment, vcap services, running/staging env variables", func() {
   186  					Expect(userEnv.Environment["key"]).To(Equal("value"))
   187  					Expect(userEnv.Environment["number"]).To(Equal(float64(123)))
   188  					Expect(userEnv.Environment["bool"]).To(BeTrue())
   189  					Expect(userEnv.Running["RUNNING_ENV"]).To(Equal("running_value"))
   190  					Expect(userEnv.Running["running"]).To(BeFalse())
   191  					Expect(userEnv.Running["number"]).To(Equal(float64(37)))
   192  					Expect(userEnv.Staging["STAGING_ENV"]).To(Equal("staging_value"))
   193  					Expect(userEnv.Staging["staging"]).To(BeTrue())
   194  					Expect(userEnv.Staging["number"]).To(Equal(float64(42)))
   195  
   196  					vcapServices := userEnv.System["VCAP_SERVICES"]
   197  					data, err := json.Marshal(vcapServices)
   198  
   199  					Expect(err).ToNot(HaveOccurred())
   200  					Expect(string(data)).To(ContainSubstring("\"system_key\":\"system_value\""))
   201  				})
   202  
   203  			})
   204  
   205  			Context("when there are no environment variables", func() {
   206  				BeforeEach(func() {
   207  					var emptyEnvRequest = testapi.NewCloudControllerTestRequest(testnet.TestRequest{
   208  						Method: "GET",
   209  						Path:   "/v2/apps/some-cool-app-guid/env",
   210  						Response: testnet.TestResponse{
   211  							Status: http.StatusOK,
   212  							Body:   `{"system_env_json": {"VCAP_SERVICES": {} }}`,
   213  						}})
   214  
   215  					testServer, handler, repo = createAppRepo([]testnet.TestRequest{emptyEnvRequest})
   216  					userEnv, err = repo.ReadEnv("some-cool-app-guid")
   217  					Expect(err).ToNot(HaveOccurred())
   218  					Expect(handler).To(HaveAllRequestsCalled())
   219  				})
   220  
   221  				It("returns an empty string", func() {
   222  					Expect(len(userEnv.Environment)).To(Equal(0))
   223  					Expect(len(userEnv.System["VCAP_SERVICES"].(map[string]interface{}))).To(Equal(0))
   224  				})
   225  			})
   226  		})
   227  	})
   228  
   229  	Describe("restaging applications", func() {
   230  		It("POSTs to the right URL", func() {
   231  			appRestageRequest := testapi.NewCloudControllerTestRequest(testnet.TestRequest{
   232  				Method: "POST",
   233  				Path:   "/v2/apps/some-cool-app-guid/restage",
   234  				Response: testnet.TestResponse{
   235  					Status: http.StatusOK,
   236  					Body:   "",
   237  				},
   238  			})
   239  
   240  			ts, handler, repo := createAppRepo([]testnet.TestRequest{appRestageRequest})
   241  			defer ts.Close()
   242  
   243  			repo.CreateRestageRequest("some-cool-app-guid")
   244  			Expect(handler).To(HaveAllRequestsCalled())
   245  		})
   246  	})
   247  
   248  	Describe("updating applications", func() {
   249  		It("makes the right request", func() {
   250  			ts, handler, repo := createAppRepo([]testnet.TestRequest{updateApplicationRequest})
   251  			defer ts.Close()
   252  
   253  			app := models.Application{}
   254  			app.Guid = "my-app-guid"
   255  			app.Name = "my-cool-app"
   256  			app.BuildpackUrl = "buildpack-url"
   257  			app.Command = "some-command"
   258  			app.Memory = 2048
   259  			app.InstanceCount = 3
   260  			app.Stack = &models.Stack{Guid: "some-stack-guid"}
   261  			app.SpaceGuid = "some-space-guid"
   262  			app.State = "started"
   263  			app.DiskQuota = 512
   264  			Expect(app.EnvironmentVars).To(BeNil())
   265  
   266  			updatedApp, apiErr := repo.Update(app.Guid, app.ToParams())
   267  
   268  			Expect(handler).To(HaveAllRequestsCalled())
   269  			Expect(apiErr).NotTo(HaveOccurred())
   270  			Expect(updatedApp.Command).To(Equal("some-command"))
   271  			Expect(updatedApp.DetectedStartCommand).To(Equal("detected command"))
   272  			Expect(updatedApp.Name).To(Equal("my-cool-app"))
   273  			Expect(updatedApp.Guid).To(Equal("my-cool-app-guid"))
   274  		})
   275  
   276  		It("sets environment variables", func() {
   277  			request := testapi.NewCloudControllerTestRequest(testnet.TestRequest{
   278  				Method:   "PUT",
   279  				Path:     "/v2/apps/app1-guid",
   280  				Matcher:  testnet.RequestBodyMatcher(`{"environment_json":{"DATABASE_URL":"mysql://example.com/my-db"}}`),
   281  				Response: testnet.TestResponse{Status: http.StatusCreated},
   282  			})
   283  
   284  			ts, handler, repo := createAppRepo([]testnet.TestRequest{request})
   285  			defer ts.Close()
   286  
   287  			envParams := map[string]interface{}{"DATABASE_URL": "mysql://example.com/my-db"}
   288  			params := models.AppParams{EnvironmentVars: &envParams}
   289  
   290  			_, apiErr := repo.Update("app1-guid", params)
   291  
   292  			Expect(handler).To(HaveAllRequestsCalled())
   293  			Expect(apiErr).NotTo(HaveOccurred())
   294  		})
   295  
   296  		It("can remove environment variables", func() {
   297  			request := testapi.NewCloudControllerTestRequest(testnet.TestRequest{
   298  				Method:   "PUT",
   299  				Path:     "/v2/apps/app1-guid",
   300  				Matcher:  testnet.RequestBodyMatcher(`{"environment_json":{}}`),
   301  				Response: testnet.TestResponse{Status: http.StatusCreated},
   302  			})
   303  
   304  			ts, handler, repo := createAppRepo([]testnet.TestRequest{request})
   305  			defer ts.Close()
   306  
   307  			envParams := map[string]interface{}{}
   308  			params := models.AppParams{EnvironmentVars: &envParams}
   309  
   310  			_, apiErr := repo.Update("app1-guid", params)
   311  
   312  			Expect(handler).To(HaveAllRequestsCalled())
   313  			Expect(apiErr).NotTo(HaveOccurred())
   314  		})
   315  	})
   316  
   317  	It("deletes applications", func() {
   318  		deleteApplicationRequest := testapi.NewCloudControllerTestRequest(testnet.TestRequest{
   319  			Method:   "DELETE",
   320  			Path:     "/v2/apps/my-cool-app-guid?recursive=true",
   321  			Response: testnet.TestResponse{Status: http.StatusOK, Body: ""},
   322  		})
   323  
   324  		ts, handler, repo := createAppRepo([]testnet.TestRequest{deleteApplicationRequest})
   325  		defer ts.Close()
   326  
   327  		apiErr := repo.Delete("my-cool-app-guid")
   328  		Expect(handler).To(HaveAllRequestsCalled())
   329  		Expect(apiErr).NotTo(HaveOccurred())
   330  	})
   331  })
   332  
   333  var appModelResponse = testnet.TestResponse{
   334  	Status: http.StatusOK,
   335  	Body: `
   336      {
   337        "metadata": {
   338          "guid": "app1-guid"
   339        },
   340        "entity": {
   341          "name": "My App",
   342          "environment_json": {
   343        		"foo": "bar",
   344        		"baz": "boom"
   345      	},
   346          "memory": 128,
   347          "instances": 1,
   348          "disk_quota": 512,
   349          "state": "STOPPED",
   350          "stack": {
   351  			"metadata": {
   352  				  "guid": "app1-route-guid"
   353  				},
   354  			"entity": {
   355  			  "name": "awesome-stacks-ahoy"
   356  			  }
   357          },
   358          "routes": [
   359        	  {
   360        	    "metadata": {
   361        	      "guid": "app1-route-guid"
   362        	    },
   363        	    "entity": {
   364        	      "host": "app1",
   365        	      "domain": {
   366        	      	"metadata": {
   367        	      	  "guid": "domain1-guid"
   368        	      	},
   369        	      	"entity": {
   370        	      	  "name": "cfapps.io"
   371        	      	}
   372        	      }
   373        	    }
   374        	  }
   375          ]
   376        }
   377      }
   378  `}
   379  
   380  var singleAppResponse = testnet.TestResponse{
   381  	Status: http.StatusOK,
   382  	Body: `
   383  {
   384    "resources": [
   385      {
   386        "metadata": {
   387          "guid": "app1-guid"
   388        },
   389        "entity": {
   390          "name": "My App",
   391          "environment_json": {
   392        		"foo": "bar",
   393        		"baz": "boom"
   394      	},
   395          "memory": 128,
   396          "instances": 1,
   397          "disk_quota": 512,
   398          "state": "STOPPED",
   399          "stack": {
   400  			"metadata": {
   401  				  "guid": "app1-route-guid"
   402  				},
   403  			"entity": {
   404  			  "name": "awesome-stacks-ahoy"
   405  			  }
   406          },
   407          "routes": [
   408        	  {
   409        	    "metadata": {
   410        	      "guid": "app1-route-guid"
   411        	    },
   412        	    "entity": {
   413        	      "host": "app1",
   414        	      "domain": {
   415        	      	"metadata": {
   416        	      	  "guid": "domain1-guid"
   417        	      	},
   418        	      	"entity": {
   419        	      	  "name": "cfapps.io"
   420        	      	}
   421        	      }
   422        	    }
   423        	  }
   424          ]
   425        }
   426      }
   427    ]
   428  }`}
   429  
   430  var findAppRequest = testapi.NewCloudControllerTestRequest(testnet.TestRequest{
   431  	Method:   "GET",
   432  	Path:     "/v2/spaces/my-space-guid/apps?q=name%3AMy+App&inline-relations-depth=1",
   433  	Response: singleAppResponse,
   434  })
   435  
   436  var createApplicationResponse = `
   437  {
   438      "metadata": {
   439          "guid": "my-cool-app-guid"
   440      },
   441      "entity": {
   442          "name": "my-cool-app"
   443      }
   444  }`
   445  
   446  var createApplicationRequest = testapi.NewCloudControllerTestRequest(testnet.TestRequest{
   447  	Method: "POST",
   448  	Path:   "/v2/apps",
   449  	Matcher: testnet.RequestBodyMatcher(`{
   450  		"name":"my-cool-app",
   451  		"instances":3,
   452  		"buildpack":"buildpack-url",
   453  		"memory":2048,
   454  		"disk_quota": 512,
   455  		"space_guid":"some-space-guid",
   456  		"stack_guid":"some-stack-guid",
   457  		"command":"some-command"
   458  	}`),
   459  	Response: testnet.TestResponse{
   460  		Status: http.StatusCreated,
   461  		Body:   createApplicationResponse},
   462  })
   463  
   464  func defaultAppParams() models.AppParams {
   465  	name := "my-cool-app"
   466  	buildpackUrl := "buildpack-url"
   467  	spaceGuid := "some-space-guid"
   468  	stackGuid := "some-stack-guid"
   469  	command := "some-command"
   470  	memory := int64(2048)
   471  	diskQuota := int64(512)
   472  	instanceCount := 3
   473  
   474  	return models.AppParams{
   475  		Name:          &name,
   476  		BuildpackUrl:  &buildpackUrl,
   477  		SpaceGuid:     &spaceGuid,
   478  		StackGuid:     &stackGuid,
   479  		Command:       &command,
   480  		Memory:        &memory,
   481  		DiskQuota:     &diskQuota,
   482  		InstanceCount: &instanceCount,
   483  	}
   484  }
   485  
   486  var updateApplicationResponse = `
   487  {
   488      "metadata": {
   489          "guid": "my-cool-app-guid"
   490      },
   491      "entity": {
   492          "name": "my-cool-app",
   493  				"command": "some-command",
   494  				"detected_start_command": "detected command"
   495      }
   496  }`
   497  
   498  var updateApplicationRequest = testapi.NewCloudControllerTestRequest(testnet.TestRequest{
   499  	Method:  "PUT",
   500  	Path:    "/v2/apps/my-app-guid?inline-relations-depth=1",
   501  	Matcher: testnet.RequestBodyMatcher(`{"name":"my-cool-app","instances":3,"buildpack":"buildpack-url","memory":2048,"disk_quota":512,"space_guid":"some-space-guid","state":"STARTED","stack_guid":"some-stack-guid","command":"some-command"}`),
   502  	Response: testnet.TestResponse{
   503  		Status: http.StatusOK,
   504  		Body:   updateApplicationResponse},
   505  })
   506  
   507  func createAppRepo(requests []testnet.TestRequest) (ts *httptest.Server, handler *testnet.TestHandler, repo ApplicationRepository) {
   508  	ts, handler = testnet.NewServer(requests)
   509  	configRepo := testconfig.NewRepositoryWithDefaults()
   510  	configRepo.SetApiEndpoint(ts.URL)
   511  	gateway := net.NewCloudControllerGateway(configRepo, time.Now, &testterm.FakeUI{})
   512  	repo = NewCloudControllerApplicationRepository(configRepo, gateway)
   513  	return
   514  }