github.com/elopio/cli@v6.21.2-0.20160902224010-ea909d1fdb2f+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 "code.cloudfoundry.org/cli/cf/actors" 12 "code.cloudfoundry.org/cli/cf/actors/actorsfakes" 13 "code.cloudfoundry.org/cli/cf/api/applicationbits/applicationbitsfakes" 14 "code.cloudfoundry.org/cli/cf/api/resources" 15 "code.cloudfoundry.org/cli/cf/appfiles" 16 "code.cloudfoundry.org/cli/cf/appfiles/appfilesfakes" 17 "code.cloudfoundry.org/cli/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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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 267 Describe("UploadApp", func() { 268 It("Simply delegates to the UploadApp function on the app bits repo, which is not worth testing", func() {}) 269 }) 270 271 Describe("ProcessPath", func() { 272 var ( 273 wasCalled bool 274 wasCalledWith string 275 ) 276 277 BeforeEach(func() { 278 zipper := &appfiles.ApplicationZipper{} 279 actor = actors.NewPushActor(appBitsRepo, zipper, appFiles, routeActor) 280 }) 281 282 Context("when given a zip file", func() { 283 var zipFile string 284 285 BeforeEach(func() { 286 zipFile = filepath.Join(fixturesDir, "example-app.zip") 287 }) 288 289 It("extracts the zip when given a zip file", func() { 290 f := func(tempDir string) error { 291 for _, file := range allFiles { 292 actualFilePath := filepath.Join(tempDir, file.Path) 293 _, err := os.Stat(actualFilePath) 294 Expect(err).NotTo(HaveOccurred()) 295 } 296 return nil 297 } 298 err := actor.ProcessPath(zipFile, f) 299 Expect(err).NotTo(HaveOccurred()) 300 }) 301 302 It("calls the provided function with the directory that it extracted to", func() { 303 f := func(tempDir string) error { 304 wasCalled = true 305 wasCalledWith = tempDir 306 return nil 307 } 308 err := actor.ProcessPath(zipFile, f) 309 Expect(err).NotTo(HaveOccurred()) 310 Expect(wasCalled).To(BeTrue()) 311 Expect(wasCalledWith).NotTo(Equal(zipFile)) 312 }) 313 314 It("cleans up the directory that it extracted to", func() { 315 var tempDirWas string 316 f := func(tempDir string) error { 317 tempDirWas = tempDir 318 return nil 319 } 320 err := actor.ProcessPath(zipFile, f) 321 Expect(err).NotTo(HaveOccurred()) 322 _, err = os.Stat(tempDirWas) 323 Expect(err).To(HaveOccurred()) 324 }) 325 326 It("returns an error if the unzipping fails", func() { 327 e := errors.New("some-error") 328 fakezipper.UnzipReturns(e) 329 fakezipper.IsZipFileReturns(true) 330 actor = actors.NewPushActor(appBitsRepo, fakezipper, appFiles, routeActor) 331 332 f := func(_ string) error { 333 return nil 334 } 335 err := actor.ProcessPath(zipFile, f) 336 Expect(err).To(HaveOccurred()) 337 }) 338 }) 339 340 It("calls the provided function with the provided directory", func() { 341 appDir = filepath.Join(fixturesDir, "example-app") 342 f := func(tempDir string) error { 343 wasCalled = true 344 wasCalledWith = tempDir 345 return nil 346 } 347 err := actor.ProcessPath(appDir, f) 348 Expect(err).NotTo(HaveOccurred()) 349 Expect(wasCalled).To(BeTrue()) 350 path, err := filepath.Abs(appDir) 351 Expect(err).NotTo(HaveOccurred()) 352 Expect(wasCalledWith).To(Equal(path)) 353 }) 354 355 It("dereferences the symlink when given a symlink to an app dir", func() { 356 if runtime.GOOS == "windows" { 357 Skip("This should not run on Windows") 358 } 359 360 symlink := filepath.Join(fixturesDir, "example-app-symlink") 361 expectedDir := filepath.Join(fixturesDir, "example-app") // example-app-symlink -> example-app 362 f := func(dir string) error { 363 wasCalled = true 364 wasCalledWith = dir 365 return nil 366 } 367 368 err := actor.ProcessPath(symlink, f) 369 Expect(err).NotTo(HaveOccurred()) 370 Expect(wasCalled).To(BeTrue()) 371 path, err := filepath.Abs(expectedDir) 372 Expect(err).NotTo(HaveOccurred()) 373 Expect(wasCalledWith).To(Equal(path)) 374 }) 375 376 It("calls the provided function with the provided absolute directory", func() { 377 appDir = filepath.Join(fixturesDir, "example-app") 378 absolutePath, err := filepath.Abs(appDir) 379 Expect(err).NotTo(HaveOccurred()) 380 f := func(tempDir string) error { 381 wasCalled = true 382 wasCalledWith = tempDir 383 return nil 384 } 385 err = actor.ProcessPath(absolutePath, f) 386 Expect(err).NotTo(HaveOccurred()) 387 Expect(wasCalled).To(BeTrue()) 388 Expect(wasCalledWith).To(Equal(absolutePath)) 389 }) 390 }) 391 392 Describe("ValidateAppParams", func() { 393 var apps []models.AppParams 394 395 Context("when 'routes' is provided", func() { 396 BeforeEach(func() { 397 appName := "my-app" 398 apps = []models.AppParams{ 399 models.AppParams{ 400 Name: &appName, 401 Routes: []models.ManifestRoute{ 402 models.ManifestRoute{ 403 Route: "route-name.example.com", 404 }, 405 models.ManifestRoute{ 406 Route: "other-route-name.example.com", 407 }, 408 }, 409 }, 410 } 411 }) 412 413 Context("and 'hosts' is provided", func() { 414 BeforeEach(func() { 415 apps[0].Hosts = []string{"host-name"} 416 }) 417 418 It("returns an error", func() { 419 errs := actor.ValidateAppParams(apps) 420 Expect(errs).To(HaveLen(1)) 421 Expect(errs[0].Error()).To(Equal("Application my-app must not be configured with both 'routes' and 'host'/'hosts'")) 422 }) 423 }) 424 425 Context("and 'domains' is provided", func() { 426 BeforeEach(func() { 427 apps[0].Domains = []string{"domain-name"} 428 }) 429 430 It("returns an error", func() { 431 errs := actor.ValidateAppParams(apps) 432 Expect(errs).To(HaveLen(1)) 433 Expect(errs[0].Error()).To(Equal("Application my-app must not be configured with both 'routes' and 'domain'/'domains'")) 434 }) 435 }) 436 437 Context("and 'no-hostname' is provided", func() { 438 BeforeEach(func() { 439 noHostBool := true 440 apps[0].NoHostname = &noHostBool 441 }) 442 443 It("returns an error", func() { 444 errs := actor.ValidateAppParams(apps) 445 Expect(errs).To(HaveLen(1)) 446 Expect(errs[0].Error()).To(Equal("Application my-app must not be configured with both 'routes' and 'no-hostname'")) 447 }) 448 }) 449 450 Context("and 'no-hostname' is not provided", func() { 451 BeforeEach(func() { 452 apps[0].NoHostname = nil 453 }) 454 455 It("returns an error", func() { 456 errs := actor.ValidateAppParams(apps) 457 Expect(errs).To(HaveLen(0)) 458 }) 459 }) 460 }) 461 }) 462 463 Describe("MapManifestRoute", func() { 464 It("passes arguments to route actor", func() { 465 appName := "app-name" 466 app := models.Application{ 467 ApplicationFields: models.ApplicationFields{ 468 Name: appName, 469 GUID: "app-guid", 470 }, 471 } 472 appParamsFromContext := models.AppParams{ 473 Name: &appName, 474 } 475 476 _ = actor.MapManifestRoute("route-name.example.com/testPath", app, appParamsFromContext) 477 actualRoute, actualApp, actualAppParams := routeActor.FindAndBindRouteArgsForCall(0) 478 Expect(actualRoute).To(Equal("route-name.example.com/testPath")) 479 Expect(actualApp).To(Equal(app)) 480 Expect(actualAppParams).To(Equal(appParamsFromContext)) 481 }) 482 }) 483 })