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