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  })