github.com/liamawhite/cli-with-i18n@v6.32.1-0.20171122084555-dede0a5c3448+incompatible/cf/actors/push_test.go (about) 1 package actors_test 2 3 import ( 4 "errors" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "runtime" 10 11 "github.com/liamawhite/cli-with-i18n/cf/actors" 12 "github.com/liamawhite/cli-with-i18n/cf/actors/actorsfakes" 13 "github.com/liamawhite/cli-with-i18n/cf/api/applicationbits/applicationbitsfakes" 14 "github.com/liamawhite/cli-with-i18n/cf/api/resources" 15 "github.com/liamawhite/cli-with-i18n/cf/appfiles" 16 "github.com/liamawhite/cli-with-i18n/cf/appfiles/appfilesfakes" 17 "github.com/liamawhite/cli-with-i18n/cf/models" 18 . "github.com/onsi/ginkgo" 19 . "github.com/onsi/gomega" 20 ) 21 22 var _ = Describe("Push Actor", func() { 23 var ( 24 appBitsRepo *applicationbitsfakes.FakeApplicationBitsRepository 25 appFiles *appfilesfakes.FakeAppFiles 26 fakezipper *appfilesfakes.FakeZipper 27 routeActor *actorsfakes.FakeRouteActor 28 actor actors.PushActor 29 fixturesDir string 30 appDir string 31 allFiles []models.AppFileFields 32 presentFiles []resources.AppFileResource 33 ) 34 35 BeforeEach(func() { 36 appBitsRepo = new(applicationbitsfakes.FakeApplicationBitsRepository) 37 appFiles = new(appfilesfakes.FakeAppFiles) 38 fakezipper = new(appfilesfakes.FakeZipper) 39 routeActor = new(actorsfakes.FakeRouteActor) 40 actor = actors.NewPushActor(appBitsRepo, fakezipper, appFiles, routeActor) 41 fixturesDir = filepath.Join("..", "..", "fixtures", "applications") 42 allFiles = []models.AppFileFields{ 43 {Path: "example-app/.cfignore"}, 44 {Path: "example-app/app.rb"}, 45 {Path: "example-app/config.ru"}, 46 {Path: "example-app/Gemfile"}, 47 {Path: "example-app/Gemfile.lock"}, 48 {Path: "example-app/ignore-me"}, 49 {Path: "example-app/manifest.yml"}, 50 } 51 }) 52 53 Describe("GatherFiles", func() { 54 var tmpDir string 55 56 BeforeEach(func() { 57 presentFiles = []resources.AppFileResource{ 58 {Path: "example-app/ignore-me"}, 59 } 60 61 appDir = filepath.Join(fixturesDir, "example-app.zip") 62 appBitsRepo.GetApplicationFilesReturns(presentFiles, nil) 63 var err error 64 tmpDir, err = ioutil.TempDir("", "gather-files") 65 Expect(err).NotTo(HaveOccurred()) 66 }) 67 68 AfterEach(func() { 69 os.RemoveAll(tmpDir) 70 }) 71 72 Context("when we cannot reach CC", func() { 73 var expectedErr error 74 75 BeforeEach(func() { 76 expectedErr = errors.New("error") 77 appBitsRepo.GetApplicationFilesReturns(nil, expectedErr) 78 }) 79 80 It("returns an error if we cannot reach the cc", func() { 81 _, _, err := actor.GatherFiles(allFiles, appDir, tmpDir, true) 82 Expect(err).To(HaveOccurred()) 83 Expect(err).To(Equal(expectedErr)) 84 }) 85 }) 86 87 Context("when we cannot copy the app files", func() { 88 var expectedErr error 89 90 BeforeEach(func() { 91 expectedErr = errors.New("error") 92 appFiles.CopyFilesReturns(expectedErr) 93 }) 94 95 It("returns an error", func() { 96 _, _, err := actor.GatherFiles(allFiles, appDir, tmpDir, true) 97 Expect(err).To(HaveOccurred()) 98 Expect(err).To(Equal(expectedErr)) 99 }) 100 }) 101 102 Context("when using .cfignore", func() { 103 BeforeEach(func() { 104 appDir = filepath.Join(fixturesDir, "exclude-a-default-cfignore") 105 // Ignore app files for this test as .cfignore is not one of them 106 appBitsRepo.GetApplicationFilesReturns(nil, nil) 107 }) 108 109 It("copies the .cfignore file to the upload directory", func() { 110 _, _, err := actor.GatherFiles(allFiles, appDir, tmpDir, true) 111 Expect(err).NotTo(HaveOccurred()) 112 113 _, err = os.Stat(filepath.Join(tmpDir, ".cfignore")) 114 Expect(os.IsNotExist(err)).To(BeFalse()) 115 }) 116 }) 117 118 It("returns files to upload with file mode unchanged on non-Windows platforms", func() { 119 if runtime.GOOS == "windows" { 120 Skip("This does not run on windows") 121 } 122 123 info, err := os.Lstat(filepath.Join(fixturesDir, "example-app/ignore-me")) 124 Expect(err).NotTo(HaveOccurred()) 125 126 expectedFileMode := fmt.Sprintf("%#o", info.Mode()) 127 128 actualFiles, _, err := actor.GatherFiles(allFiles, fixturesDir, tmpDir, true) 129 Expect(err).NotTo(HaveOccurred()) 130 131 expectedFiles := []resources.AppFileResource{ 132 { 133 Path: "example-app/ignore-me", 134 Mode: expectedFileMode, 135 }, 136 } 137 138 Expect(actualFiles).To(Equal(expectedFiles)) 139 }) 140 141 It("returns files to upload with file mode always being executable on Windows platforms", func() { 142 if runtime.GOOS != "windows" { 143 Skip("This runs only on windows") 144 } 145 146 info, err := os.Lstat(filepath.Join(fixturesDir, "example-app/ignore-me")) 147 Expect(err).NotTo(HaveOccurred()) 148 149 expectedFileMode := fmt.Sprintf("%#o", info.Mode()|0700) 150 151 actualFiles, _, err := actor.GatherFiles(allFiles, fixturesDir, tmpDir, true) 152 Expect(err).NotTo(HaveOccurred()) 153 154 expectedFiles := []resources.AppFileResource{ 155 { 156 Path: "example-app/ignore-me", 157 Mode: expectedFileMode, 158 }, 159 } 160 161 Expect(actualFiles).To(Equal(expectedFiles)) 162 }) 163 164 Context("when there are no remote files", func() { 165 BeforeEach(func() { 166 appBitsRepo.GetApplicationFilesReturns([]resources.AppFileResource{}, nil) 167 }) 168 169 It("returns true for hasFileToUpload", func() { 170 _, hasFileToUpload, err := actor.GatherFiles(allFiles, fixturesDir, tmpDir, true) 171 Expect(err).NotTo(HaveOccurred()) 172 Expect(hasFileToUpload).To(BeTrue()) 173 }) 174 175 It("copies all local files to the upload dir", func() { 176 expectedFiles := []models.AppFileFields{ 177 {Path: "example-app/.cfignore"}, 178 {Path: "example-app/app.rb"}, 179 {Path: "example-app/config.ru"}, 180 {Path: "example-app/Gemfile"}, 181 {Path: "example-app/Gemfile.lock"}, 182 {Path: "example-app/ignore-me"}, 183 {Path: "example-app/manifest.yml"}, 184 } 185 _, _, err := actor.GatherFiles(allFiles, fixturesDir, tmpDir, true) 186 Expect(err).NotTo(HaveOccurred()) 187 188 Expect(appFiles.CopyFilesCallCount()).To(Equal(1)) 189 filesToUpload, fromDir, toDir := appFiles.CopyFilesArgsForCall(0) 190 Expect(filesToUpload).To(Equal(expectedFiles)) 191 Expect(fromDir).To(Equal(fixturesDir)) 192 Expect(toDir).To(Equal(tmpDir)) 193 }) 194 }) 195 196 Context("when there are local files that aren't matched", func() { 197 var remoteFiles []resources.AppFileResource 198 199 BeforeEach(func() { 200 remoteFiles = []resources.AppFileResource{ 201 {Path: "example-app/manifest.yml"}, 202 } 203 204 appBitsRepo.GetApplicationFilesReturns(remoteFiles, nil) 205 }) 206 207 It("returns true for hasFileToUpload", func() { 208 _, hasFileToUpload, err := actor.GatherFiles(allFiles, fixturesDir, tmpDir, true) 209 Expect(err).NotTo(HaveOccurred()) 210 Expect(hasFileToUpload).To(BeTrue()) 211 }) 212 213 It("copies unmatched local files to the upload dir", func() { 214 expectedFiles := []models.AppFileFields{ 215 {Path: "example-app/.cfignore"}, 216 {Path: "example-app/app.rb"}, 217 {Path: "example-app/config.ru"}, 218 {Path: "example-app/Gemfile"}, 219 {Path: "example-app/Gemfile.lock"}, 220 {Path: "example-app/ignore-me"}, 221 } 222 _, _, err := actor.GatherFiles(allFiles, fixturesDir, tmpDir, true) 223 Expect(err).NotTo(HaveOccurred()) 224 225 Expect(appFiles.CopyFilesCallCount()).To(Equal(1)) 226 filesToUpload, fromDir, toDir := appFiles.CopyFilesArgsForCall(0) 227 Expect(filesToUpload).To(Equal(expectedFiles)) 228 Expect(fromDir).To(Equal(fixturesDir)) 229 Expect(toDir).To(Equal(tmpDir)) 230 }) 231 }) 232 233 Context("when local and remote files are equivalent", func() { 234 BeforeEach(func() { 235 remoteFiles := []resources.AppFileResource{ 236 {Path: "example-app/.cfignore", Mode: "0644"}, 237 {Path: "example-app/app.rb", Mode: "0755"}, 238 {Path: "example-app/config.ru", Mode: "0644"}, 239 {Path: "example-app/Gemfile", Mode: "0644"}, 240 {Path: "example-app/Gemfile.lock", Mode: "0644"}, 241 {Path: "example-app/ignore-me", Mode: "0666"}, 242 {Path: "example-app/manifest.yml", Mode: "0644"}, 243 } 244 245 appBitsRepo.GetApplicationFilesReturns(remoteFiles, nil) 246 }) 247 248 It("returns false for hasFileToUpload", func() { 249 _, hasFileToUpload, err := actor.GatherFiles(allFiles, fixturesDir, tmpDir, true) 250 Expect(err).NotTo(HaveOccurred()) 251 Expect(hasFileToUpload).To(BeFalse()) 252 }) 253 254 It("copies nothing to the upload dir", func() { 255 _, _, err := actor.GatherFiles(allFiles, fixturesDir, tmpDir, true) 256 Expect(err).NotTo(HaveOccurred()) 257 258 Expect(appFiles.CopyFilesCallCount()).To(Equal(1)) 259 filesToUpload, fromDir, toDir := appFiles.CopyFilesArgsForCall(0) 260 Expect(filesToUpload).To(BeEmpty()) 261 Expect(fromDir).To(Equal(fixturesDir)) 262 Expect(toDir).To(Equal(tmpDir)) 263 }) 264 }) 265 266 Context("when told not to use the remote cache", func() { 267 It("does not use the remote cache", func() { 268 actor.GatherFiles(allFiles, fixturesDir, tmpDir, false) 269 Expect(appBitsRepo.GetApplicationFilesCallCount()).To(Equal(0)) 270 }) 271 }) 272 }) 273 274 Describe("UploadApp", func() { 275 It("Simply delegates to the UploadApp function on the app bits repo, which is not worth testing", func() {}) 276 }) 277 278 Describe("ProcessPath", func() { 279 var ( 280 wasCalled bool 281 wasCalledWith string 282 ) 283 284 BeforeEach(func() { 285 zipper := &appfiles.ApplicationZipper{} 286 actor = actors.NewPushActor(appBitsRepo, zipper, appFiles, routeActor) 287 }) 288 289 Context("when given a zip file", func() { 290 var zipFile string 291 292 BeforeEach(func() { 293 zipFile = filepath.Join(fixturesDir, "example-app.zip") 294 }) 295 296 It("extracts the zip when given a zip file", func() { 297 f := func(tempDir string) error { 298 for _, file := range allFiles { 299 actualFilePath := filepath.Join(tempDir, file.Path) 300 _, err := os.Stat(actualFilePath) 301 Expect(err).NotTo(HaveOccurred()) 302 } 303 return nil 304 } 305 err := actor.ProcessPath(zipFile, f) 306 Expect(err).NotTo(HaveOccurred()) 307 }) 308 309 It("calls the provided function with the directory that it extracted to", func() { 310 f := func(tempDir string) error { 311 wasCalled = true 312 wasCalledWith = tempDir 313 return nil 314 } 315 err := actor.ProcessPath(zipFile, f) 316 Expect(err).NotTo(HaveOccurred()) 317 Expect(wasCalled).To(BeTrue()) 318 Expect(wasCalledWith).NotTo(Equal(zipFile)) 319 }) 320 321 It("cleans up the directory that it extracted to", func() { 322 var tempDirWas string 323 f := func(tempDir string) error { 324 tempDirWas = tempDir 325 return nil 326 } 327 err := actor.ProcessPath(zipFile, f) 328 Expect(err).NotTo(HaveOccurred()) 329 _, err = os.Stat(tempDirWas) 330 Expect(err).To(HaveOccurred()) 331 }) 332 333 It("returns an error if the unzipping fails", func() { 334 e := errors.New("some-error") 335 fakezipper.UnzipReturns(e) 336 fakezipper.IsZipFileReturns(true) 337 actor = actors.NewPushActor(appBitsRepo, fakezipper, appFiles, routeActor) 338 339 f := func(_ string) error { 340 return nil 341 } 342 err := actor.ProcessPath(zipFile, f) 343 Expect(err).To(HaveOccurred()) 344 }) 345 }) 346 347 It("calls the provided function with the provided directory", func() { 348 appDir = filepath.Join(fixturesDir, "example-app") 349 f := func(tempDir string) error { 350 wasCalled = true 351 wasCalledWith = tempDir 352 return nil 353 } 354 err := actor.ProcessPath(appDir, f) 355 Expect(err).NotTo(HaveOccurred()) 356 Expect(wasCalled).To(BeTrue()) 357 358 path, err := filepath.Abs(appDir) 359 Expect(err).NotTo(HaveOccurred()) 360 361 path, err = filepath.EvalSymlinks(path) 362 Expect(err).NotTo(HaveOccurred()) 363 364 Expect(wasCalledWith).To(Equal(path)) 365 }) 366 367 It("dereferences the symlink when given a symlink to an app dir", func() { 368 if runtime.GOOS == "windows" { 369 Skip("This should not run on Windows") 370 } 371 372 symlink := filepath.Join(fixturesDir, "example-app-symlink") 373 expectedDir := filepath.Join(fixturesDir, "example-app") // example-app-symlink -> example-app 374 f := func(dir string) error { 375 wasCalled = true 376 wasCalledWith = dir 377 return nil 378 } 379 380 err := actor.ProcessPath(symlink, f) 381 Expect(err).NotTo(HaveOccurred()) 382 Expect(wasCalled).To(BeTrue()) 383 path, err := filepath.Abs(expectedDir) 384 Expect(err).NotTo(HaveOccurred()) 385 Expect(wasCalledWith).To(Equal(path)) 386 }) 387 388 It("calls the provided function with the provided absolute directory", func() { 389 appDir = filepath.Join(fixturesDir, "example-app") 390 absolutePath, err := filepath.Abs(appDir) 391 Expect(err).NotTo(HaveOccurred()) 392 f := func(tempDir string) error { 393 wasCalled = true 394 wasCalledWith = tempDir 395 return nil 396 } 397 err = actor.ProcessPath(absolutePath, f) 398 Expect(err).NotTo(HaveOccurred()) 399 400 absolutePath, err = filepath.EvalSymlinks(absolutePath) 401 Expect(err).NotTo(HaveOccurred()) 402 403 Expect(wasCalled).To(BeTrue()) 404 Expect(wasCalledWith).To(Equal(absolutePath)) 405 }) 406 }) 407 408 Describe("ValidateAppParams", func() { 409 var apps []models.AppParams 410 411 Context("when HealthCheckType is not http", func() { 412 Context("when HealthCheckHTTPEndpoint is provided", func() { 413 BeforeEach(func() { 414 healthCheckType := "port" 415 endpoint := "/some-endpoint" 416 apps = []models.AppParams{ 417 models.AppParams{ 418 HealthCheckType: &healthCheckType, 419 HealthCheckHTTPEndpoint: &endpoint, 420 }, 421 } 422 }) 423 It("displays error", func() { 424 errs := actor.ValidateAppParams(apps) 425 Expect(errs).To(HaveLen(1)) 426 Expect(errs[0].Error()).To(Equal("Health check type must be 'http' to set a health check HTTP endpoint.")) 427 }) 428 }) 429 }) 430 431 Context("when docker and buildpack is provided", func() { 432 BeforeEach(func() { 433 appName := "my-app" 434 dockerImage := "some-image" 435 buildpackURL := "some-build-pack.url" 436 apps = []models.AppParams{ 437 models.AppParams{ 438 Name: &appName, 439 BuildpackURL: &buildpackURL, 440 DockerImage: &dockerImage, 441 }, 442 } 443 }) 444 It("displays error", func() { 445 errs := actor.ValidateAppParams(apps) 446 Expect(errs).To(HaveLen(1)) 447 Expect(errs[0].Error()).To(Equal("Application my-app must not be configured with both 'buildpack' and 'docker'")) 448 }) 449 }) 450 451 Context("when docker and path is provided", func() { 452 BeforeEach(func() { 453 appName := "my-app" 454 dockerImage := "some-image" 455 path := "some-path" 456 apps = []models.AppParams{ 457 models.AppParams{ 458 Name: &appName, 459 DockerImage: &dockerImage, 460 Path: &path, 461 }, 462 } 463 }) 464 It("displays error", func() { 465 errs := actor.ValidateAppParams(apps) 466 Expect(errs).To(HaveLen(1)) 467 Expect(errs[0].Error()).To(Equal("Application my-app must not be configured with both 'docker' and 'path'")) 468 }) 469 }) 470 471 Context("when 'routes' is provided", func() { 472 BeforeEach(func() { 473 appName := "my-app" 474 apps = []models.AppParams{ 475 models.AppParams{ 476 Name: &appName, 477 Routes: []models.ManifestRoute{ 478 models.ManifestRoute{ 479 Route: "route-name.example.com", 480 }, 481 models.ManifestRoute{ 482 Route: "other-route-name.example.com", 483 }, 484 }, 485 }, 486 } 487 }) 488 489 Context("and 'hosts' is provided", func() { 490 BeforeEach(func() { 491 apps[0].Hosts = []string{"host-name"} 492 }) 493 494 It("returns an error", func() { 495 errs := actor.ValidateAppParams(apps) 496 Expect(errs).To(HaveLen(1)) 497 Expect(errs[0].Error()).To(Equal("Application my-app must not be configured with both 'routes' and 'host'/'hosts'")) 498 }) 499 }) 500 501 Context("and 'domains' is provided", func() { 502 BeforeEach(func() { 503 apps[0].Domains = []string{"domain-name"} 504 }) 505 506 It("returns an error", func() { 507 errs := actor.ValidateAppParams(apps) 508 Expect(errs).To(HaveLen(1)) 509 Expect(errs[0].Error()).To(Equal("Application my-app must not be configured with both 'routes' and 'domain'/'domains'")) 510 }) 511 }) 512 513 Context("and 'no-hostname' is provided", func() { 514 BeforeEach(func() { 515 noHostBool := true 516 apps[0].NoHostname = &noHostBool 517 }) 518 519 It("returns an error", func() { 520 errs := actor.ValidateAppParams(apps) 521 Expect(errs).To(HaveLen(1)) 522 Expect(errs[0].Error()).To(Equal("Application my-app must not be configured with both 'routes' and 'no-hostname'")) 523 }) 524 }) 525 526 Context("and 'no-hostname' is not provided", func() { 527 BeforeEach(func() { 528 apps[0].NoHostname = nil 529 }) 530 531 It("returns an error", func() { 532 errs := actor.ValidateAppParams(apps) 533 Expect(errs).To(HaveLen(0)) 534 }) 535 }) 536 }) 537 }) 538 539 Describe("MapManifestRoute", func() { 540 It("passes arguments to route actor", func() { 541 appName := "app-name" 542 app := models.Application{ 543 ApplicationFields: models.ApplicationFields{ 544 Name: appName, 545 GUID: "app-guid", 546 }, 547 } 548 appParamsFromContext := models.AppParams{ 549 Name: &appName, 550 } 551 552 _ = actor.MapManifestRoute("route-name.example.com/testPath", app, appParamsFromContext) 553 actualRoute, actualApp, actualAppParams := routeActor.FindAndBindRouteArgsForCall(0) 554 Expect(actualRoute).To(Equal("route-name.example.com/testPath")) 555 Expect(actualApp).To(Equal(app)) 556 Expect(actualAppParams).To(Equal(appParamsFromContext)) 557 }) 558 }) 559 })