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 }