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 }