github.com/jghiloni/cli@v6.28.1-0.20170628223758-0ce05fe032a2+incompatible/cf/commands/application/push_test.go (about)

     1  package application_test
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"syscall"
     7  
     8  	"code.cloudfoundry.org/cli/cf"
     9  	"code.cloudfoundry.org/cli/cf/actors/actorsfakes"
    10  	"code.cloudfoundry.org/cli/cf/api/apifakes"
    11  	"code.cloudfoundry.org/cli/cf/api/applications/applicationsfakes"
    12  	"code.cloudfoundry.org/cli/cf/api/authentication/authenticationfakes"
    13  	"code.cloudfoundry.org/cli/cf/api/resources"
    14  	"code.cloudfoundry.org/cli/cf/api/stacks/stacksfakes"
    15  	"code.cloudfoundry.org/cli/cf/appfiles/appfilesfakes"
    16  	"code.cloudfoundry.org/cli/cf/commandregistry"
    17  	"code.cloudfoundry.org/cli/cf/commands/application"
    18  	"code.cloudfoundry.org/cli/cf/commands/application/applicationfakes"
    19  	"code.cloudfoundry.org/cli/cf/commands/service/servicefakes"
    20  	"code.cloudfoundry.org/cli/cf/configuration/coreconfig"
    21  	"code.cloudfoundry.org/cli/cf/errors"
    22  	"code.cloudfoundry.org/cli/cf/flags"
    23  	"code.cloudfoundry.org/cli/cf/manifest"
    24  	"code.cloudfoundry.org/cli/cf/manifest/manifestfakes"
    25  	"code.cloudfoundry.org/cli/cf/models"
    26  	"code.cloudfoundry.org/cli/cf/requirements"
    27  	"code.cloudfoundry.org/cli/cf/requirements/requirementsfakes"
    28  	"code.cloudfoundry.org/cli/cf/terminal"
    29  	"code.cloudfoundry.org/cli/cf/trace"
    30  	"code.cloudfoundry.org/cli/util/generic"
    31  	testconfig "code.cloudfoundry.org/cli/util/testhelpers/configuration"
    32  	testterm "code.cloudfoundry.org/cli/util/testhelpers/terminal"
    33  	"code.cloudfoundry.org/cli/util/words/generator/generatorfakes"
    34  	. "github.com/onsi/ginkgo"
    35  	. "github.com/onsi/gomega"
    36  	"github.com/onsi/gomega/gbytes"
    37  )
    38  
    39  var _ = Describe("Push Command", func() {
    40  	var (
    41  		cmd                        application.Push
    42  		ui                         *testterm.FakeUI
    43  		configRepo                 coreconfig.Repository
    44  		manifestRepo               *manifestfakes.FakeRepository
    45  		starter                    *applicationfakes.FakeStarter
    46  		stopper                    *applicationfakes.FakeStopper
    47  		serviceBinder              *servicefakes.OldFakeAppBinder
    48  		appRepo                    *applicationsfakes.FakeRepository
    49  		domainRepo                 *apifakes.FakeDomainRepository
    50  		routeRepo                  *apifakes.FakeRouteRepository
    51  		stackRepo                  *stacksfakes.FakeStackRepository
    52  		serviceRepo                *apifakes.FakeServiceRepository
    53  		wordGenerator              *generatorfakes.FakeWordGenerator
    54  		requirementsFactory        *requirementsfakes.FakeFactory
    55  		authRepo                   *authenticationfakes.FakeRepository
    56  		actor                      *actorsfakes.FakePushActor
    57  		routeActor                 *actorsfakes.FakeRouteActor
    58  		appfiles                   *appfilesfakes.FakeAppFiles
    59  		zipper                     *appfilesfakes.FakeZipper
    60  		deps                       commandregistry.Dependency
    61  		flagContext                flags.FlagContext
    62  		loginReq                   requirements.Passing
    63  		targetedSpaceReq           requirements.Passing
    64  		usageReq                   requirements.Passing
    65  		minVersionReq              requirements.Passing
    66  		OriginalCommandStart       commandregistry.Command
    67  		OriginalCommandStop        commandregistry.Command
    68  		OriginalCommandServiceBind commandregistry.Command
    69  	)
    70  
    71  	BeforeEach(func() {
    72  		//save original command dependences and restore later
    73  		OriginalCommandStart = commandregistry.Commands.FindCommand("start")
    74  		OriginalCommandStop = commandregistry.Commands.FindCommand("stop")
    75  		OriginalCommandServiceBind = commandregistry.Commands.FindCommand("bind-service")
    76  
    77  		requirementsFactory = new(requirementsfakes.FakeFactory)
    78  		loginReq = requirements.Passing{Type: "login"}
    79  		requirementsFactory.NewLoginRequirementReturns(loginReq)
    80  		targetedSpaceReq = requirements.Passing{Type: "targeted space"}
    81  		requirementsFactory.NewTargetedSpaceRequirementReturns(targetedSpaceReq)
    82  		usageReq = requirements.Passing{Type: "usage"}
    83  		requirementsFactory.NewUsageRequirementReturns(usageReq)
    84  		minVersionReq = requirements.Passing{Type: "minVersionReq"}
    85  		requirementsFactory.NewMinAPIVersionRequirementReturns(minVersionReq)
    86  
    87  		ui = &testterm.FakeUI{} //new(terminalfakes.FakeUI)
    88  		configRepo = testconfig.NewRepositoryWithDefaults()
    89  		manifestRepo = new(manifestfakes.FakeRepository)
    90  		wordGenerator = new(generatorfakes.FakeWordGenerator)
    91  		wordGenerator.BabbleReturns("random-host")
    92  		actor = new(actorsfakes.FakePushActor)
    93  		routeActor = new(actorsfakes.FakeRouteActor)
    94  		zipper = new(appfilesfakes.FakeZipper)
    95  		appfiles = new(appfilesfakes.FakeAppFiles)
    96  
    97  		deps = commandregistry.Dependency{
    98  			UI:            ui,
    99  			Config:        configRepo,
   100  			ManifestRepo:  manifestRepo,
   101  			WordGenerator: wordGenerator,
   102  			PushActor:     actor,
   103  			RouteActor:    routeActor,
   104  			AppZipper:     zipper,
   105  			AppFiles:      appfiles,
   106  		}
   107  
   108  		appRepo = new(applicationsfakes.FakeRepository)
   109  		domainRepo = new(apifakes.FakeDomainRepository)
   110  		routeRepo = new(apifakes.FakeRouteRepository)
   111  		serviceRepo = new(apifakes.FakeServiceRepository)
   112  		stackRepo = new(stacksfakes.FakeStackRepository)
   113  		authRepo = new(authenticationfakes.FakeRepository)
   114  		deps.RepoLocator = deps.RepoLocator.SetApplicationRepository(appRepo)
   115  		deps.RepoLocator = deps.RepoLocator.SetDomainRepository(domainRepo)
   116  		deps.RepoLocator = deps.RepoLocator.SetRouteRepository(routeRepo)
   117  		deps.RepoLocator = deps.RepoLocator.SetServiceRepository(serviceRepo)
   118  		deps.RepoLocator = deps.RepoLocator.SetStackRepository(stackRepo)
   119  		deps.RepoLocator = deps.RepoLocator.SetAuthenticationRepository(authRepo)
   120  
   121  		//setup fake commands (counterfeiter) to correctly interact with commandregistry
   122  		starter = new(applicationfakes.FakeStarter)
   123  		starter.SetDependencyStub = func(_ commandregistry.Dependency, _ bool) commandregistry.Command {
   124  			return starter
   125  		}
   126  		starter.MetaDataReturns(commandregistry.CommandMetadata{Name: "start"})
   127  		commandregistry.Register(starter)
   128  
   129  		stopper = new(applicationfakes.FakeStopper)
   130  		stopper.SetDependencyStub = func(_ commandregistry.Dependency, _ bool) commandregistry.Command {
   131  			return stopper
   132  		}
   133  		stopper.MetaDataReturns(commandregistry.CommandMetadata{Name: "stop"})
   134  		commandregistry.Register(stopper)
   135  
   136  		//inject fake commands dependencies into registry
   137  		serviceBinder = new(servicefakes.OldFakeAppBinder)
   138  		commandregistry.Register(serviceBinder)
   139  
   140  		cmd = application.Push{}
   141  		cmd.SetDependency(deps, false)
   142  		flagContext = flags.NewFlagContext(cmd.MetaData().Flags)
   143  	})
   144  
   145  	AfterEach(func() {
   146  		commandregistry.Register(OriginalCommandStart)
   147  		commandregistry.Register(OriginalCommandStop)
   148  		commandregistry.Register(OriginalCommandServiceBind)
   149  	})
   150  
   151  	Describe("Requirements", func() {
   152  		var reqs []requirements.Requirement
   153  
   154  		BeforeEach(func() {
   155  			err := flagContext.Parse("app-name")
   156  			Expect(err).NotTo(HaveOccurred())
   157  
   158  			reqs, err = cmd.Requirements(requirementsFactory, flagContext)
   159  			Expect(err).NotTo(HaveOccurred())
   160  		})
   161  
   162  		It("checks that the user is logged in", func() {
   163  			Expect(requirementsFactory.NewLoginRequirementCallCount()).To(Equal(1))
   164  			Expect(reqs).To(ContainElement(loginReq))
   165  		})
   166  
   167  		It("checks that the space is targeted", func() {
   168  			Expect(requirementsFactory.NewTargetedSpaceRequirementCallCount()).To(Equal(1))
   169  			Expect(reqs).To(ContainElement(targetedSpaceReq))
   170  		})
   171  
   172  		It("checks the number of args", func() {
   173  			Expect(requirementsFactory.NewUsageRequirementCallCount()).To(Equal(1))
   174  			Expect(reqs).To(ContainElement(usageReq))
   175  		})
   176  
   177  		Context("when --route-path is passed in", func() {
   178  			BeforeEach(func() {
   179  				err := flagContext.Parse("app-name", "--route-path", "the-path")
   180  				Expect(err).NotTo(HaveOccurred())
   181  
   182  				reqs, err = cmd.Requirements(requirementsFactory, flagContext)
   183  				Expect(err).NotTo(HaveOccurred())
   184  			})
   185  
   186  			It("returns a minAPIVersionRequirement", func() {
   187  				Expect(requirementsFactory.NewMinAPIVersionRequirementCallCount()).To(Equal(1))
   188  
   189  				option, version := requirementsFactory.NewMinAPIVersionRequirementArgsForCall(0)
   190  				Expect(option).To(Equal("Option '--route-path'"))
   191  				Expect(version).To(Equal(cf.RoutePathMinimumAPIVersion))
   192  
   193  				Expect(reqs).To(ContainElement(minVersionReq))
   194  			})
   195  		})
   196  
   197  		Context("when --app-ports is passed in", func() {
   198  			BeforeEach(func() {
   199  				err := flagContext.Parse("app-name", "--app-ports", "the-app-port")
   200  				Expect(err).NotTo(HaveOccurred())
   201  
   202  				reqs, err = cmd.Requirements(requirementsFactory, flagContext)
   203  				Expect(err).NotTo(HaveOccurred())
   204  			})
   205  
   206  			It("returns a minAPIVersionRequirement", func() {
   207  				Expect(requirementsFactory.NewMinAPIVersionRequirementCallCount()).To(Equal(1))
   208  
   209  				option, version := requirementsFactory.NewMinAPIVersionRequirementArgsForCall(0)
   210  				Expect(option).To(Equal("Option '--app-ports'"))
   211  				Expect(version).To(Equal(cf.MultipleAppPortsMinimumAPIVersion))
   212  
   213  				Expect(reqs).To(ContainElement(minVersionReq))
   214  			})
   215  		})
   216  	})
   217  
   218  	Describe("Execute", func() {
   219  		var (
   220  			executeErr     error
   221  			args           []string
   222  			uiWithContents terminal.UI
   223  			input          *gbytes.Buffer
   224  			output         *gbytes.Buffer
   225  		)
   226  
   227  		BeforeEach(func() {
   228  			input = gbytes.NewBuffer()
   229  			output = gbytes.NewBuffer()
   230  			uiWithContents = terminal.NewUI(input, output, terminal.NewTeePrinter(output), trace.NewWriterPrinter(output, false))
   231  
   232  			domainRepo.FirstOrDefaultStub = func(orgGUID string, name *string) (models.DomainFields, error) {
   233  				if name == nil {
   234  					var foundDomain *models.DomainFields
   235  					domainRepo.ListDomainsForOrg(orgGUID, func(domain models.DomainFields) bool {
   236  						foundDomain = &domain
   237  						return !domain.Shared
   238  					})
   239  
   240  					if foundDomain == nil {
   241  						return models.DomainFields{}, errors.New("Could not find a default domain")
   242  					}
   243  
   244  					return *foundDomain, nil
   245  				}
   246  
   247  				return domainRepo.FindByNameInOrg(*name, orgGUID)
   248  			}
   249  
   250  			domainRepo.ListDomainsForOrgStub = func(orgGUID string, cb func(models.DomainFields) bool) error {
   251  				cb(models.DomainFields{
   252  					Name:   "foo.cf-app.com",
   253  					GUID:   "foo-domain-guid",
   254  					Shared: true,
   255  				})
   256  				return nil
   257  			}
   258  
   259  			actor.ProcessPathStub = func(dirOrZipFile string, cb func(string) error) error {
   260  				return cb(dirOrZipFile)
   261  			}
   262  
   263  			actor.ValidateAppParamsReturns(nil)
   264  
   265  			appfiles.AppFilesInDirReturns(
   266  				[]models.AppFileFields{
   267  					{
   268  						Path: "some-path",
   269  					},
   270  				},
   271  				nil,
   272  			)
   273  
   274  			zipper.ZipReturns(nil)
   275  			zipper.GetZipSizeReturns(9001, nil)
   276  		})
   277  
   278  		AfterEach(func() {
   279  			output.Close()
   280  		})
   281  
   282  		JustBeforeEach(func() {
   283  			cmd.SetDependency(deps, false)
   284  
   285  			err := flagContext.Parse(args...)
   286  			Expect(err).NotTo(HaveOccurred())
   287  
   288  			executeErr = cmd.Execute(flagContext)
   289  		})
   290  
   291  		Context("when pushing a new app", func() {
   292  			BeforeEach(func() {
   293  				m := &manifest.Manifest{
   294  					Path: "manifest.yml",
   295  					Data: generic.NewMap(map[interface{}]interface{}{
   296  						"applications": []interface{}{
   297  							generic.NewMap(map[interface{}]interface{}{
   298  								"name":      "manifest-app-name",
   299  								"memory":    "128MB",
   300  								"instances": 1,
   301  								"host":      "manifest-host",
   302  								"stack":     "custom-stack",
   303  								"timeout":   360,
   304  								"buildpack": "some-buildpack",
   305  								"command":   `JAVA_HOME=$PWD/.openjdk JAVA_OPTS="-Xss995K" ./bin/start.sh run`,
   306  								"path":      filepath.Clean("some/path/from/manifest"),
   307  								"env": generic.NewMap(map[interface{}]interface{}{
   308  									"FOO":  "baz",
   309  									"PATH": "/u/apps/my-app/bin",
   310  								}),
   311  							}),
   312  						},
   313  					}),
   314  				}
   315  				manifestRepo.ReadManifestReturns(m, nil)
   316  
   317  				appRepo.ReadReturns(models.Application{}, errors.NewModelNotFoundError("App", "the-app"))
   318  				appRepo.CreateStub = func(params models.AppParams) (models.Application, error) {
   319  					a := models.Application{}
   320  					a.GUID = *params.Name + "-guid"
   321  					a.Name = *params.Name
   322  					a.State = "stopped"
   323  
   324  					return a, nil
   325  				}
   326  
   327  				args = []string{"app-name"}
   328  			})
   329  
   330  			Context("validating a manifest", func() {
   331  				BeforeEach(func() {
   332  					actor.ValidateAppParamsReturns([]error{
   333  						errors.New("error1"),
   334  						errors.New("error2"),
   335  					})
   336  				})
   337  
   338  				It("returns an properly formatted error", func() {
   339  					Expect(executeErr).To(HaveOccurred())
   340  					Expect(executeErr.Error()).To(MatchRegexp("Invalid application configuration:\nerror1\nerror2"))
   341  				})
   342  			})
   343  
   344  			Context("when given a bad path", func() {
   345  				BeforeEach(func() {
   346  					actor.ProcessPathStub = func(dirOrZipFile string, f func(string) error) error {
   347  						return errors.New("process-path-error")
   348  					}
   349  				})
   350  
   351  				JustBeforeEach(func() {
   352  					err := flagContext.Parse("app-name", "-p", "badpath")
   353  					Expect(err).NotTo(HaveOccurred())
   354  
   355  					executeErr = cmd.Execute(flagContext)
   356  				})
   357  
   358  				It("fails with bad path error", func() {
   359  					Expect(executeErr).To(HaveOccurred())
   360  
   361  					Expect(executeErr.Error()).To(ContainSubstring("Error processing app files: process-path-error"))
   362  				})
   363  			})
   364  
   365  			Context("when the default route for the app already exists", func() {
   366  				var route models.Route
   367  				BeforeEach(func() {
   368  					route = models.Route{
   369  						GUID: "my-route-guid",
   370  						Host: "app-name",
   371  						Domain: models.DomainFields{
   372  							Name:   "foo.cf-app.com",
   373  							GUID:   "foo-domain-guid",
   374  							Shared: true,
   375  						},
   376  					}
   377  					routeActor.FindOrCreateRouteReturns(
   378  						route,
   379  						nil,
   380  					)
   381  				})
   382  
   383  				Context("when binding the app", func() {
   384  					BeforeEach(func() {
   385  						deps.UI = uiWithContents
   386  					})
   387  
   388  					It("binds to existing routes", func() {
   389  						Expect(executeErr).NotTo(HaveOccurred())
   390  
   391  						Expect(routeActor.BindRouteCallCount()).To(Equal(1))
   392  						boundApp, boundRoute := routeActor.BindRouteArgsForCall(0)
   393  						Expect(boundApp.GUID).To(Equal("app-name-guid"))
   394  						Expect(boundRoute).To(Equal(route))
   395  					})
   396  				})
   397  
   398  				Context("when pushing the app", func() {
   399  					BeforeEach(func() {
   400  						actor.GatherFilesReturns([]resources.AppFileResource{}, false, errors.New("failed to get file mode"))
   401  					})
   402  
   403  					It("notifies users about the error actor.GatherFiles() returns", func() {
   404  						Expect(executeErr).To(HaveOccurred())
   405  
   406  						Expect(executeErr.Error()).To(ContainSubstring("failed to get file mode"))
   407  					})
   408  				})
   409  
   410  				Context("when the CC returns 504 Gateway timeout", func() {
   411  					BeforeEach(func() {
   412  						var callCount int
   413  						actor.GatherFilesStub = func(localFiles []models.AppFileFields, appDir string, uploadDir string, useCache bool) ([]resources.AppFileResource, bool, error) {
   414  							callCount += 1
   415  							if callCount == 1 {
   416  								return []resources.AppFileResource{}, false, errors.NewHTTPError(504, "", "")
   417  							} else {
   418  								return []resources.AppFileResource{}, false, nil
   419  							}
   420  						}
   421  					})
   422  
   423  					It("retries without the cache", func() {
   424  						Expect(executeErr).ToNot(HaveOccurred())
   425  
   426  						Expect(actor.GatherFilesCallCount()).To(Equal(2))
   427  
   428  						localFiles, appDir, uploadDir, useCache := actor.GatherFilesArgsForCall(0)
   429  						Expect(useCache).To(Equal(true))
   430  
   431  						localFilesRetry, appDirRetry, uploadDirRetry, useCacheRetry := actor.GatherFilesArgsForCall(1)
   432  						Expect(localFilesRetry).To(Equal(localFiles))
   433  						Expect(appDirRetry).To(Equal(appDir))
   434  						Expect(uploadDirRetry).To(Equal(uploadDir))
   435  						Expect(useCacheRetry).To(Equal(false))
   436  
   437  						Expect(ui.Outputs()).To(ContainElement(
   438  							"Resource matching API timed out; pushing all app files."))
   439  					})
   440  				})
   441  			})
   442  
   443  			Context("when the default route for the app does not exist", func() {
   444  				BeforeEach(func() {
   445  					routeRepo.FindReturns(models.Route{}, errors.NewModelNotFoundError("Org", "couldn't find it"))
   446  				})
   447  
   448  				It("refreshes the auth token (so fresh)", func() {
   449  					Expect(executeErr).NotTo(HaveOccurred())
   450  
   451  					Expect(authRepo.RefreshAuthTokenCallCount()).To(Equal(1))
   452  				})
   453  
   454  				Context("when refreshing the auth token fails", func() {
   455  					BeforeEach(func() {
   456  						authRepo.RefreshAuthTokenReturns("", errors.New("I accidentally the UAA"))
   457  					})
   458  
   459  					It("it returns an error", func() {
   460  						Expect(executeErr).To(HaveOccurred())
   461  
   462  						Expect(executeErr.Error()).To(Equal("I accidentally the UAA"))
   463  					})
   464  				})
   465  
   466  				Context("when multiple domains are specified in manifest", func() {
   467  					var (
   468  						route1 models.Route
   469  						route2 models.Route
   470  						route3 models.Route
   471  						route4 models.Route
   472  					)
   473  
   474  					BeforeEach(func() {
   475  						deps.UI = uiWithContents
   476  						domainRepo.FindByNameInOrgStub = func(name string, owningOrgGUID string) (models.DomainFields, error) {
   477  							return map[string]models.DomainFields{
   478  								"example1.com": {Name: "example1.com", GUID: "example-domain-guid"},
   479  								"example2.com": {Name: "example2.com", GUID: "example-domain-guid"},
   480  							}[name], nil
   481  						}
   482  
   483  						m := &manifest.Manifest{
   484  							Path: "manifest.yml",
   485  							Data: generic.NewMap(map[interface{}]interface{}{
   486  								"applications": []interface{}{
   487  									generic.NewMap(map[interface{}]interface{}{
   488  										"name":      "manifest-app-name",
   489  										"memory":    "128MB",
   490  										"instances": 1,
   491  										"host":      "manifest-host",
   492  										"hosts":     []interface{}{"host2"},
   493  										"domains":   []interface{}{"example1.com", "example2.com"},
   494  										"stack":     "custom-stack",
   495  										"timeout":   360,
   496  										"buildpack": "some-buildpack",
   497  										"command":   `JAVA_HOME=$PWD/.openjdk JAVA_OPTS="-Xss995K" ./bin/start.sh run`,
   498  										"path":      filepath.Clean("some/path/from/manifest"),
   499  										"env": generic.NewMap(map[interface{}]interface{}{
   500  											"FOO":  "baz",
   501  											"PATH": "/u/apps/my-app/bin",
   502  										}),
   503  									}),
   504  								},
   505  							}),
   506  						}
   507  						manifestRepo.ReadManifestReturns(m, nil)
   508  						routeRepo.CreateStub = func(host string, domain models.DomainFields, _ string, _ int, _ bool) (models.Route, error) {
   509  							return models.Route{
   510  								GUID:   "my-route-guid",
   511  								Host:   host,
   512  								Domain: domain,
   513  							}, nil
   514  						}
   515  						args = []string{}
   516  
   517  						route1 = models.Route{
   518  							GUID: "route1-guid",
   519  						}
   520  						route2 = models.Route{
   521  							GUID: "route2-guid",
   522  						}
   523  						route3 = models.Route{
   524  							GUID: "route3-guid",
   525  						}
   526  						route4 = models.Route{
   527  							GUID: "route4-guid",
   528  						}
   529  
   530  						callCount := 0
   531  						routeActor.FindOrCreateRouteStub = func(hostname string, domain models.DomainFields, path string, _ int, useRandomPort bool) (models.Route, error) {
   532  							callCount = callCount + 1
   533  							switch callCount {
   534  							case 1:
   535  								Expect(hostname).To(Equal("host2"))
   536  								Expect(domain.Name).To(Equal("example1.com"))
   537  								Expect(path).To(BeEmpty())
   538  								Expect(useRandomPort).To(BeFalse())
   539  								return route1, nil
   540  							case 2:
   541  								Expect(hostname).To(Equal("manifest-host"))
   542  								Expect(domain.Name).To(Equal("example1.com"))
   543  								Expect(path).To(BeEmpty())
   544  								Expect(useRandomPort).To(BeFalse())
   545  								return route2, nil
   546  							case 3:
   547  								Expect(hostname).To(Equal("host2"))
   548  								Expect(domain.Name).To(Equal("example2.com"))
   549  								Expect(path).To(BeEmpty())
   550  								Expect(useRandomPort).To(BeFalse())
   551  								return route3, nil
   552  							case 4:
   553  								Expect(hostname).To(Equal("manifest-host"))
   554  								Expect(domain.Name).To(Equal("example2.com"))
   555  								Expect(path).To(BeEmpty())
   556  								Expect(useRandomPort).To(BeFalse())
   557  								return route4, nil
   558  							default:
   559  								Fail("should have only been called 4 times")
   560  							}
   561  							panic("should never have gotten this far")
   562  						}
   563  					})
   564  
   565  					It("creates a route for each domain", func() {
   566  						Expect(executeErr).NotTo(HaveOccurred())
   567  
   568  						Expect(routeActor.BindRouteCallCount()).To(Equal(4))
   569  						app, route := routeActor.BindRouteArgsForCall(0)
   570  						Expect(app.Name).To(Equal("manifest-app-name"))
   571  						Expect(route).To(Equal(route1))
   572  
   573  						app, route = routeActor.BindRouteArgsForCall(1)
   574  						Expect(app.Name).To(Equal("manifest-app-name"))
   575  						Expect(route).To(Equal(route2))
   576  
   577  						app, route = routeActor.BindRouteArgsForCall(2)
   578  						Expect(app.Name).To(Equal("manifest-app-name"))
   579  						Expect(route).To(Equal(route3))
   580  
   581  						app, route = routeActor.BindRouteArgsForCall(3)
   582  						Expect(app.Name).To(Equal("manifest-app-name"))
   583  						Expect(route).To(Equal(route4))
   584  					})
   585  
   586  					Context("when overriding the manifest with flags", func() {
   587  						BeforeEach(func() {
   588  							args = []string{"-d", "example1.com"}
   589  							route1 = models.Route{
   590  								GUID: "route1-guid",
   591  							}
   592  							route2 = models.Route{
   593  								GUID: "route2-guid",
   594  							}
   595  
   596  							callCount := 0
   597  							routeActor.FindOrCreateRouteStub = func(hostname string, domain models.DomainFields, path string, _ int, useRandomPort bool) (models.Route, error) {
   598  								callCount = callCount + 1
   599  								switch callCount {
   600  								case 1:
   601  									Expect(hostname).To(Equal("host2"))
   602  									Expect(domain.Name).To(Equal("example1.com"))
   603  									Expect(path).To(BeEmpty())
   604  									Expect(useRandomPort).To(BeFalse())
   605  									return route1, nil
   606  								case 2:
   607  									Expect(hostname).To(Equal("manifest-host"))
   608  									Expect(domain.Name).To(Equal("example1.com"))
   609  									Expect(path).To(BeEmpty())
   610  									Expect(useRandomPort).To(BeFalse())
   611  									return route2, nil
   612  								default:
   613  									Fail("should have only been called 2 times")
   614  								}
   615  								panic("should never have gotten this far")
   616  							}
   617  						})
   618  
   619  						It("`-d` from argument will override the domains", func() {
   620  							Expect(executeErr).NotTo(HaveOccurred())
   621  
   622  							Expect(routeActor.BindRouteCallCount()).To(Equal(2))
   623  							app, route := routeActor.BindRouteArgsForCall(0)
   624  							Expect(app.Name).To(Equal("manifest-app-name"))
   625  							Expect(route).To(Equal(route1))
   626  
   627  							app, route = routeActor.BindRouteArgsForCall(1)
   628  							Expect(app.Name).To(Equal("manifest-app-name"))
   629  							Expect(route).To(Equal(route2))
   630  						})
   631  					})
   632  				})
   633  
   634  				Context("when pushing an app", func() {
   635  					BeforeEach(func() {
   636  						deps.UI = uiWithContents
   637  						routeRepo.CreateStub = func(host string, domain models.DomainFields, _ string, _ int, _ bool) (models.Route, error) {
   638  							return models.Route{
   639  								GUID:   "my-route-guid",
   640  								Host:   host,
   641  								Domain: domain,
   642  							}, nil
   643  						}
   644  						args = []string{"-t", "111", "app-name"}
   645  					})
   646  
   647  					It("doesn't error", func() {
   648  						Expect(executeErr).NotTo(HaveOccurred())
   649  
   650  						totalOutput := terminal.Decolorize(string(output.Contents()))
   651  
   652  						Expect(totalOutput).NotTo(ContainSubstring("FAILED"))
   653  
   654  						params := appRepo.CreateArgsForCall(0)
   655  						Expect(*params.Name).To(Equal("app-name"))
   656  						Expect(*params.SpaceGUID).To(Equal("my-space-guid"))
   657  
   658  						Expect(actor.UploadAppCallCount()).To(Equal(1))
   659  						appGUID, _, _ := actor.UploadAppArgsForCall(0)
   660  						Expect(appGUID).To(Equal("app-name-guid"))
   661  
   662  						Expect(totalOutput).To(ContainSubstring("Creating app app-name in org my-org / space my-space as my-user...\nOK"))
   663  						Expect(totalOutput).To(ContainSubstring("Uploading app-name...\nOK"))
   664  
   665  						Expect(stopper.ApplicationStopCallCount()).To(Equal(0))
   666  
   667  						app, orgName, spaceName := starter.ApplicationStartArgsForCall(0)
   668  						Expect(app.GUID).To(Equal(appGUID))
   669  						Expect(app.Name).To(Equal("app-name"))
   670  						Expect(orgName).To(Equal(configRepo.OrganizationFields().Name))
   671  						Expect(spaceName).To(Equal(configRepo.SpaceFields().Name))
   672  						Expect(starter.SetStartTimeoutInSecondsArgsForCall(0)).To(Equal(111))
   673  					})
   674  				})
   675  
   676  				Context("when there are special characters in the app name", func() {
   677  					Context("when the app name is specified via manifest file", func() {
   678  						BeforeEach(func() {
   679  							m := &manifest.Manifest{
   680  								Path: "manifest.yml",
   681  								Data: generic.NewMap(map[interface{}]interface{}{
   682  									"applications": []interface{}{
   683  										generic.NewMap(map[interface{}]interface{}{
   684  											"name":      "manifest!app-nam#",
   685  											"memory":    "128MB",
   686  											"instances": 1,
   687  											"stack":     "custom-stack",
   688  											"timeout":   360,
   689  											"buildpack": "some-buildpack",
   690  											"command":   `JAVA_HOME=$PWD/.openjdk JAVA_OPTS="-Xss995K" ./bin/start.sh run`,
   691  											"path":      filepath.Clean("some/path/from/manifest"),
   692  											"env": generic.NewMap(map[interface{}]interface{}{
   693  												"FOO":  "baz",
   694  												"PATH": "/u/apps/my-app/bin",
   695  											}),
   696  										}),
   697  									},
   698  								}),
   699  							}
   700  							manifestRepo.ReadManifestReturns(m, nil)
   701  
   702  							args = []string{}
   703  						})
   704  
   705  						It("strips special characters when creating a default route", func() {
   706  							Expect(executeErr).NotTo(HaveOccurred())
   707  
   708  							Expect(routeActor.FindOrCreateRouteCallCount()).To(Equal(1))
   709  							host, _, _, _, _ := routeActor.FindOrCreateRouteArgsForCall(0)
   710  							Expect(host).To(Equal("manifestapp-nam"))
   711  						})
   712  					})
   713  
   714  					Context("when the app name is specified via flag", func() {
   715  						BeforeEach(func() {
   716  							manifestRepo.ReadManifestReturns(manifest.NewEmptyManifest(), nil)
   717  							args = []string{"app@#name"}
   718  						})
   719  
   720  						It("strips special characters when creating a default route", func() {
   721  							Expect(executeErr).NotTo(HaveOccurred())
   722  
   723  							Expect(routeActor.FindOrCreateRouteCallCount()).To(Equal(1))
   724  							host, _, _, _, _ := routeActor.FindOrCreateRouteArgsForCall(0)
   725  							Expect(host).To(Equal("appname"))
   726  						})
   727  					})
   728  				})
   729  
   730  				Context("when flags are provided", func() {
   731  					BeforeEach(func() {
   732  						m := &manifest.Manifest{
   733  							Path: "manifest.yml",
   734  							Data: generic.NewMap(map[interface{}]interface{}{
   735  								"applications": []interface{}{
   736  									generic.NewMap(map[interface{}]interface{}{
   737  										"name":              "manifest!app-nam#",
   738  										"memory":            "128MB",
   739  										"instances":         1,
   740  										"host":              "host-name",
   741  										"domain":            "domain-name",
   742  										"disk_quota":        "1G",
   743  										"stack":             "custom-stack",
   744  										"timeout":           360,
   745  										"health-check-type": "none",
   746  										"app-ports":         []interface{}{3000},
   747  										"buildpack":         "some-buildpack",
   748  										"command":           `JAVA_HOME=$PWD/.openjdk JAVA_OPTS="-Xss995K" ./bin/start.sh run`,
   749  										"path":              filepath.Clean("some/path/from/manifest"),
   750  										"env": generic.NewMap(map[interface{}]interface{}{
   751  											"FOO":  "baz",
   752  											"PATH": "/u/apps/my-app/bin",
   753  										}),
   754  									}),
   755  								},
   756  							}),
   757  						}
   758  						manifestRepo.ReadManifestReturns(m, nil)
   759  
   760  						args = []string{
   761  							"-c", "unicorn -c config/unicorn.rb -D",
   762  							"-d", "bar.cf-app.com",
   763  							"-n", "my-hostname",
   764  							"--route-path", "my-route-path",
   765  							"-k", "4G",
   766  							"-i", "3",
   767  							"-m", "2G",
   768  							"-b", "https://github.com/heroku/heroku-buildpack-play.git",
   769  							"-s", "customLinux",
   770  							"-t", "1",
   771  							"-u", "port",
   772  							"--app-ports", "8080,9000",
   773  							"--no-start",
   774  							"app-name",
   775  						}
   776  					})
   777  
   778  					It("sets the app params from the flags", func() {
   779  						Expect(executeErr).NotTo(HaveOccurred())
   780  
   781  						Expect(appRepo.CreateCallCount()).To(Equal(1))
   782  						appParam := appRepo.CreateArgsForCall(0)
   783  						Expect(*appParam.Command).To(Equal("unicorn -c config/unicorn.rb -D"))
   784  						Expect(appParam.Domains).To(Equal([]string{"bar.cf-app.com"}))
   785  						Expect(appParam.Hosts).To(Equal([]string{"my-hostname"}))
   786  						Expect(*appParam.RoutePath).To(Equal("my-route-path"))
   787  						Expect(*appParam.Name).To(Equal("app-name"))
   788  						Expect(*appParam.InstanceCount).To(Equal(3))
   789  						Expect(*appParam.DiskQuota).To(Equal(int64(4096)))
   790  						Expect(*appParam.Memory).To(Equal(int64(2048)))
   791  						Expect(*appParam.StackName).To(Equal("customLinux"))
   792  						Expect(*appParam.HealthCheckTimeout).To(Equal(1))
   793  						Expect(*appParam.HealthCheckType).To(Equal("port"))
   794  						Expect(*appParam.BuildpackURL).To(Equal("https://github.com/heroku/heroku-buildpack-play.git"))
   795  						Expect(*appParam.AppPorts).To(Equal([]int{8080, 9000}))
   796  						Expect(*appParam.HealthCheckTimeout).To(Equal(1))
   797  					})
   798  				})
   799  
   800  				Context("when an invalid app port is porvided", func() {
   801  					BeforeEach(func() {
   802  						args = []string{"--app-ports", "8080abc", "app-name"}
   803  					})
   804  
   805  					It("returns an error", func() {
   806  						Expect(executeErr).To(HaveOccurred())
   807  
   808  						Expect(executeErr.Error()).To(ContainSubstring("Invalid app port: 8080abc"))
   809  						Expect(executeErr.Error()).To(ContainSubstring("App port must be a number"))
   810  					})
   811  				})
   812  
   813  				Context("when pushing a docker image with --docker-image", func() {
   814  					BeforeEach(func() {
   815  						deps.UI = uiWithContents
   816  						args = []string{"testApp", "--docker-image", "sample/dockerImage"}
   817  					})
   818  
   819  					It("sets diego to true", func() {
   820  						Expect(executeErr).NotTo(HaveOccurred())
   821  
   822  						Expect(appRepo.CreateCallCount()).To(Equal(1))
   823  						params := appRepo.CreateArgsForCall(0)
   824  						Expect(*params.Diego).To(BeTrue())
   825  					})
   826  
   827  					It("sets docker_image", func() {
   828  						Expect(executeErr).NotTo(HaveOccurred())
   829  
   830  						params := appRepo.CreateArgsForCall(0)
   831  						Expect(*params.DockerImage).To(Equal("sample/dockerImage"))
   832  					})
   833  
   834  					It("does not upload appbits", func() {
   835  						Expect(executeErr).NotTo(HaveOccurred())
   836  
   837  						Expect(actor.UploadAppCallCount()).To(Equal(0))
   838  						Expect(terminal.Decolorize(string(output.Contents()))).NotTo(ContainSubstring("Uploading testApp"))
   839  					})
   840  
   841  					Context("when using -o alias", func() {
   842  						BeforeEach(func() {
   843  							args = []string{"testApp", "-o", "sample/dockerImage"}
   844  						})
   845  
   846  						It("sets docker_image", func() {
   847  							Expect(executeErr).NotTo(HaveOccurred())
   848  
   849  							params := appRepo.CreateArgsForCall(0)
   850  							Expect(*params.DockerImage).To(Equal("sample/dockerImage"))
   851  						})
   852  					})
   853  
   854  					Context("when using --docker-username", func() {
   855  						BeforeEach(func() {
   856  							args = append(args, "--docker-username", "some-docker-username")
   857  						})
   858  
   859  						Context("when CF_DOCKER_PASSWORD is set", func() {
   860  							BeforeEach(func() {
   861  								err := os.Setenv("CF_DOCKER_PASSWORD", "some-docker-pass")
   862  								Expect(err).ToNot(HaveOccurred())
   863  							})
   864  
   865  							AfterEach(func() {
   866  								err := os.Unsetenv("CF_DOCKER_PASSWORD")
   867  								Expect(err).ToNot(HaveOccurred())
   868  							})
   869  
   870  							It("it passes the credentials to create call", func() {
   871  								Expect(executeErr).NotTo(HaveOccurred())
   872  
   873  								Expect(output).To(gbytes.Say("Using docker repository password from environment variable CF_DOCKER_PASSWORD."))
   874  
   875  								Expect(appRepo.CreateCallCount()).To(Equal(1))
   876  								params := appRepo.CreateArgsForCall(0)
   877  								Expect(*params.DockerUsername).To(Equal("some-docker-username"))
   878  								Expect(*params.DockerPassword).To(Equal("some-docker-pass"))
   879  							})
   880  						})
   881  
   882  						Context("when CF_DOCKER_PASSWORD is not set", func() {
   883  							BeforeEach(func() {
   884  								Skip("these [mostly] worked prior to the revert in 1d94b2df98b")
   885  							})
   886  
   887  							Context("when the user gets prompted for the docker password", func() {
   888  								Context("when the user inputs the password on the first attempt", func() {
   889  									BeforeEach(func() {
   890  										_, err := input.Write([]byte("some-docker-pass\n"))
   891  										Expect(err).NotTo(HaveOccurred())
   892  									})
   893  
   894  									It("it passes the credentials to create call", func() {
   895  										Expect(executeErr).NotTo(HaveOccurred())
   896  
   897  										Expect(output).To(gbytes.Say("Environment variable CF_DOCKER_PASSWORD not set."))
   898  										Expect(output).To(gbytes.Say("Docker password"))
   899  										Expect(output).ToNot(gbytes.Say("Docker password")) // Only prompt once
   900  
   901  										Expect(appRepo.CreateCallCount()).To(Equal(1))
   902  										params := appRepo.CreateArgsForCall(0)
   903  										Expect(*params.DockerUsername).To(Equal("some-docker-username"))
   904  										Expect(*params.DockerPassword).To(Equal("some-docker-pass"))
   905  									})
   906  								})
   907  
   908  								Context("when the user fails to input the password 3 times", func() {
   909  									BeforeEach(func() {
   910  										_, err := input.Write([]byte("\n\n\n"))
   911  										Expect(err).NotTo(HaveOccurred())
   912  									})
   913  
   914  									It("returns an error", func() {
   915  										Expect(executeErr).To(MatchError("Please provide a password"))
   916  
   917  										Expect(output).To(gbytes.Say("Docker password"))
   918  										Expect(output).To(gbytes.Say("Docker password"))
   919  										Expect(output).To(gbytes.Say("Docker password"))
   920  										Expect(output).ToNot(gbytes.Say("Docker password"))
   921  
   922  										Expect(appRepo.CreateCallCount()).To(Equal(0))
   923  									})
   924  								})
   925  							})
   926  						})
   927  					})
   928  				})
   929  
   930  				Context("when pushing with --docker-username and not --docker-image", func() {
   931  					BeforeEach(func() {
   932  						deps.UI = uiWithContents
   933  						args = []string{"testApp", "--docker-username", "some-docker-username"}
   934  					})
   935  
   936  					It("it returns an error", func() {
   937  						Expect(executeErr).To(MatchError("'--docker-username' requires '--docker-image' to be specified"))
   938  					})
   939  				})
   940  
   941  				Context("when health-check-type '-u' or '--health-check-type' is set", func() {
   942  					Context("when the value is not 'http', 'none', 'port', or 'process'", func() {
   943  						BeforeEach(func() {
   944  							args = []string{"app-name", "-u", "bad-value"}
   945  						})
   946  
   947  						It("returns an error", func() {
   948  							Expect(executeErr).To(HaveOccurred())
   949  
   950  							Expect(executeErr.Error()).To(ContainSubstring("Error: Invalid health-check-type param: bad-value"))
   951  						})
   952  					})
   953  
   954  					Context("when the value is 'http'", func() {
   955  						BeforeEach(func() {
   956  							args = []string{"app-name", "--health-check-type", "http"}
   957  						})
   958  
   959  						It("does not show error", func() {
   960  							Expect(executeErr).NotTo(HaveOccurred())
   961  						})
   962  
   963  						It("sets the HTTP health check endpoint to /", func() {
   964  							params := appRepo.CreateArgsForCall(0)
   965  							Expect(*params.HealthCheckHTTPEndpoint).To(Equal("/"))
   966  						})
   967  					})
   968  
   969  					Context("when the value is 'none'", func() {
   970  						BeforeEach(func() {
   971  							args = []string{"app-name", "--health-check-type", "none"}
   972  						})
   973  
   974  						It("does not show error", func() {
   975  							Expect(executeErr).NotTo(HaveOccurred())
   976  						})
   977  					})
   978  
   979  					Context("when the value is 'port'", func() {
   980  						BeforeEach(func() {
   981  							args = []string{"app-name", "--health-check-type", "port"}
   982  						})
   983  
   984  						It("does not show error", func() {
   985  							Expect(executeErr).NotTo(HaveOccurred())
   986  						})
   987  					})
   988  
   989  					Context("when the value is 'process'", func() {
   990  						BeforeEach(func() {
   991  							args = []string{"app-name", "--health-check-type", "process"}
   992  						})
   993  
   994  						It("does not show error", func() {
   995  							Expect(executeErr).NotTo(HaveOccurred())
   996  						})
   997  					})
   998  				})
   999  
  1000  				Context("with random-route option set", func() {
  1001  					var manifestApp generic.Map
  1002  
  1003  					BeforeEach(func() {
  1004  						manifestApp = generic.NewMap(map[interface{}]interface{}{
  1005  							"name":      "manifest-app-name",
  1006  							"memory":    "128MB",
  1007  							"instances": 1,
  1008  							"domain":    "manifest-example.com",
  1009  							"stack":     "custom-stack",
  1010  							"timeout":   360,
  1011  							"buildpack": "some-buildpack",
  1012  							"command":   `JAVA_HOME=$PWD/.openjdk JAVA_OPTS="-Xss995K" ./bin/start.sh run`,
  1013  							"path":      filepath.Clean("some/path/from/manifest"),
  1014  							"env": generic.NewMap(map[interface{}]interface{}{
  1015  								"FOO":  "baz",
  1016  								"PATH": "/u/apps/my-app/bin",
  1017  							}),
  1018  						})
  1019  						m := &manifest.Manifest{
  1020  							Path: "manifest.yml",
  1021  							Data: generic.NewMap(map[interface{}]interface{}{
  1022  								"applications": []interface{}{manifestApp},
  1023  							}),
  1024  						}
  1025  						manifestRepo.ReadManifestReturns(m, nil)
  1026  					})
  1027  
  1028  					Context("for http routes", func() {
  1029  						Context("when random-route is set as a flag", func() {
  1030  							BeforeEach(func() {
  1031  								args = []string{"--random-route", "app-name"}
  1032  							})
  1033  
  1034  							It("provides a random hostname", func() {
  1035  								Expect(executeErr).NotTo(HaveOccurred())
  1036  
  1037  								Expect(routeActor.FindOrCreateRouteCallCount()).To(Equal(1))
  1038  								host, _, _, _, _ := routeActor.FindOrCreateRouteArgsForCall(0)
  1039  								Expect(host).To(Equal("app-name-random-host"))
  1040  							})
  1041  						})
  1042  
  1043  						Context("when random-route is set in the manifest", func() {
  1044  							BeforeEach(func() {
  1045  								manifestApp.Set("random-route", true)
  1046  								args = []string{"app-name"}
  1047  							})
  1048  
  1049  							It("provides a random hostname", func() {
  1050  								Expect(executeErr).NotTo(HaveOccurred())
  1051  
  1052  								Expect(routeActor.FindOrCreateRouteCallCount()).To(Equal(1))
  1053  								host, _, _, _, _ := routeActor.FindOrCreateRouteArgsForCall(0)
  1054  								Expect(host).To(Equal("app-name-random-host"))
  1055  							})
  1056  						})
  1057  					})
  1058  
  1059  					Context("for tcp routes", func() {
  1060  						var expectedDomain models.DomainFields
  1061  
  1062  						BeforeEach(func() {
  1063  							deps.UI = uiWithContents
  1064  
  1065  							expectedDomain = models.DomainFields{
  1066  								GUID: "some-guid",
  1067  								Name: "some-name",
  1068  								OwningOrganizationGUID: "some-organization-guid",
  1069  								RouterGroupGUID:        "some-router-group-guid",
  1070  								RouterGroupType:        "tcp",
  1071  								Shared:                 true,
  1072  							}
  1073  
  1074  							domainRepo.FindByNameInOrgReturns(
  1075  								expectedDomain,
  1076  								nil,
  1077  							)
  1078  
  1079  							route := models.Route{
  1080  								Domain: expectedDomain,
  1081  								Port:   7777,
  1082  							}
  1083  							routeRepo.CreateReturns(route, nil)
  1084  						})
  1085  
  1086  						Context("when random-route passed as a flag", func() {
  1087  							BeforeEach(func() {
  1088  								args = []string{"--random-route", "app-name"}
  1089  							})
  1090  
  1091  							It("provides a random port and hostname", func() {
  1092  								Expect(executeErr).NotTo(HaveOccurred())
  1093  
  1094  								Expect(routeActor.FindOrCreateRouteCallCount()).To(Equal(1))
  1095  								_, _, _, _, randomPort := routeActor.FindOrCreateRouteArgsForCall(0)
  1096  								Expect(randomPort).To(BeTrue())
  1097  							})
  1098  						})
  1099  
  1100  						Context("when random-route set in the manifest", func() {
  1101  							BeforeEach(func() {
  1102  								manifestApp.Set("random-route", true)
  1103  								args = []string{"app-name"}
  1104  							})
  1105  
  1106  							It("provides a random port and hostname when set in the manifest", func() {
  1107  								Expect(executeErr).NotTo(HaveOccurred())
  1108  
  1109  								Expect(routeActor.FindOrCreateRouteCallCount()).To(Equal(1))
  1110  								_, _, _, _, randomPort := routeActor.FindOrCreateRouteArgsForCall(0)
  1111  								Expect(randomPort).To(BeTrue())
  1112  							})
  1113  						})
  1114  					})
  1115  				})
  1116  
  1117  				Context("when path to an app is set", func() {
  1118  					var expectedLocalFiles []models.AppFileFields
  1119  
  1120  					BeforeEach(func() {
  1121  						expectedLocalFiles = []models.AppFileFields{
  1122  							{
  1123  								Path: "the-path",
  1124  							},
  1125  							{
  1126  								Path: "the-other-path",
  1127  							},
  1128  						}
  1129  						appfiles.AppFilesInDirReturns(expectedLocalFiles, nil)
  1130  						args = []string{"-p", "../some/path-to/an-app/file.zip", "app-with-path"}
  1131  					})
  1132  
  1133  					It("includes the app files in dir", func() {
  1134  						Expect(executeErr).NotTo(HaveOccurred())
  1135  
  1136  						actualLocalFiles, _, _, _ := actor.GatherFilesArgsForCall(0)
  1137  						Expect(actualLocalFiles).To(Equal(expectedLocalFiles))
  1138  					})
  1139  				})
  1140  
  1141  				Context("when there are no app files to process", func() {
  1142  					BeforeEach(func() {
  1143  						deps.UI = uiWithContents
  1144  						appfiles.AppFilesInDirReturns([]models.AppFileFields{}, nil)
  1145  						args = []string{"-p", "../some/path-to/an-app/file.zip", "app-with-path"}
  1146  					})
  1147  
  1148  					It("errors", func() {
  1149  						Expect(executeErr).To(HaveOccurred())
  1150  						Expect(executeErr.Error()).To(ContainSubstring("No app files found in '../some/path-to/an-app/file.zip'"))
  1151  					})
  1152  				})
  1153  
  1154  				Context("when there is an error getting app files", func() {
  1155  					BeforeEach(func() {
  1156  						deps.UI = uiWithContents
  1157  						appfiles.AppFilesInDirReturns([]models.AppFileFields{}, errors.New("some error"))
  1158  						args = []string{"-p", "../some/path-to/an-app/file.zip", "app-with-path"}
  1159  					})
  1160  
  1161  					It("prints a message", func() {
  1162  						Expect(executeErr).To(HaveOccurred())
  1163  						Expect(executeErr.Error()).To(ContainSubstring("Error processing app files in '../some/path-to/an-app/file.zip': some error"))
  1164  					})
  1165  				})
  1166  
  1167  				Context("when an app path is specified with the -p flag", func() {
  1168  					BeforeEach(func() {
  1169  						args = []string{"-p", "../some/path-to/an-app/file.zip", "app-with-path"}
  1170  					})
  1171  
  1172  					It("pushes the contents of the app directory or zip file specified", func() {
  1173  						Expect(executeErr).NotTo(HaveOccurred())
  1174  
  1175  						_, appDir, _, _ := actor.GatherFilesArgsForCall(0)
  1176  						Expect(appDir).To(Equal("../some/path-to/an-app/file.zip"))
  1177  					})
  1178  				})
  1179  
  1180  				Context("when no flags are specified", func() {
  1181  					BeforeEach(func() {
  1182  						m := &manifest.Manifest{
  1183  							Path: "manifest.yml",
  1184  							Data: generic.NewMap(map[interface{}]interface{}{
  1185  								"applications": []interface{}{
  1186  									generic.NewMap(map[interface{}]interface{}{
  1187  										"name":      "manifest-app-name",
  1188  										"memory":    "128MB",
  1189  										"instances": 1,
  1190  										"host":      "manifest-host",
  1191  										"stack":     "custom-stack",
  1192  										"timeout":   360,
  1193  										"buildpack": "some-buildpack",
  1194  										"command":   `JAVA_HOME=$PWD/.openjdk JAVA_OPTS="-Xss995K" ./bin/start.sh run`,
  1195  										"env": generic.NewMap(map[interface{}]interface{}{
  1196  											"FOO":  "baz",
  1197  											"PATH": "/u/apps/my-app/bin",
  1198  										}),
  1199  									}),
  1200  								},
  1201  							}),
  1202  						}
  1203  						manifestRepo.ReadManifestReturns(m, nil)
  1204  						args = []string{"app-with-default-path"}
  1205  					})
  1206  
  1207  					It("pushes the contents of the current working directory by default", func() {
  1208  						Expect(executeErr).NotTo(HaveOccurred())
  1209  
  1210  						dir, _ := os.Getwd()
  1211  						_, appDir, _, _ := actor.GatherFilesArgsForCall(0)
  1212  						Expect(appDir).To(Equal(dir))
  1213  					})
  1214  				})
  1215  
  1216  				Context("when given a bad manifest", func() {
  1217  					BeforeEach(func() {
  1218  						manifestRepo.ReadManifestReturns(manifest.NewEmptyManifest(), errors.New("read manifest error"))
  1219  						args = []string{"-f", "bad/manifest/path"}
  1220  					})
  1221  
  1222  					It("errors", func() {
  1223  						Expect(executeErr).To(HaveOccurred())
  1224  						Expect(executeErr.Error()).To(ContainSubstring("read manifest error"))
  1225  					})
  1226  				})
  1227  
  1228  				Context("when the current directory does not contain a manifest", func() {
  1229  					BeforeEach(func() {
  1230  						deps.UI = uiWithContents
  1231  						manifestRepo.ReadManifestReturns(manifest.NewEmptyManifest(), syscall.ENOENT)
  1232  						args = []string{"--no-route", "app-name"}
  1233  					})
  1234  
  1235  					It("does not fail", func() {
  1236  						Expect(executeErr).NotTo(HaveOccurred())
  1237  						fullOutput := terminal.Decolorize(string(output.Contents()))
  1238  						Expect(fullOutput).To(ContainSubstring("Creating app app-name in org my-org / space my-space as my-user...\nOK"))
  1239  						Expect(fullOutput).To(ContainSubstring("Uploading app-name...\nOK"))
  1240  					})
  1241  				})
  1242  
  1243  				Context("when the current directory does contain a manifest", func() {
  1244  					BeforeEach(func() {
  1245  						deps.UI = uiWithContents
  1246  						m := &manifest.Manifest{
  1247  							Path: "manifest.yml",
  1248  							Data: generic.NewMap(map[interface{}]interface{}{
  1249  								"applications": []interface{}{
  1250  									generic.NewMap(map[interface{}]interface{}{
  1251  										"name":      "manifest-app-name",
  1252  										"memory":    "128MB",
  1253  										"instances": 1,
  1254  										"host":      "manifest-host",
  1255  										"domain":    "manifest-example.com",
  1256  										"stack":     "custom-stack",
  1257  										"timeout":   360,
  1258  										"buildpack": "some-buildpack",
  1259  										"command":   `JAVA_HOME=$PWD/.openjdk JAVA_OPTS="-Xss995K" ./bin/start.sh run`,
  1260  										"path":      filepath.Clean("some/path/from/manifest"),
  1261  										"env": generic.NewMap(map[interface{}]interface{}{
  1262  											"FOO":  "baz",
  1263  											"PATH": "/u/apps/my-app/bin",
  1264  										}),
  1265  									}),
  1266  								},
  1267  							}),
  1268  						}
  1269  						manifestRepo.ReadManifestReturns(m, nil)
  1270  						args = []string{"-p", "some/relative/path"}
  1271  					})
  1272  
  1273  					It("uses the manifest in the current directory by default", func() {
  1274  						Expect(executeErr).NotTo(HaveOccurred())
  1275  
  1276  						Expect(terminal.Decolorize(string(output.Contents()))).To(ContainSubstring("Using manifest file manifest.yml"))
  1277  
  1278  						cwd, _ := os.Getwd()
  1279  						Expect(manifestRepo.ReadManifestArgsForCall(0)).To(Equal(cwd))
  1280  					})
  1281  				})
  1282  
  1283  				Context("when the 'no-manifest'flag is passed", func() {
  1284  					BeforeEach(func() {
  1285  						args = []string{"--no-route", "--no-manifest", "app-name"}
  1286  					})
  1287  
  1288  					It("does not use a manifest", func() {
  1289  						Expect(executeErr).NotTo(HaveOccurred())
  1290  
  1291  						fullOutput := terminal.Decolorize(string(output.Contents()))
  1292  						Expect(fullOutput).NotTo(ContainSubstring("FAILED"))
  1293  						Expect(fullOutput).NotTo(ContainSubstring("hacker-manifesto"))
  1294  
  1295  						Expect(manifestRepo.ReadManifestCallCount()).To(BeZero())
  1296  						params := appRepo.CreateArgsForCall(0)
  1297  						Expect(*params.Name).To(Equal("app-name"))
  1298  					})
  1299  				})
  1300  
  1301  				Context("when the manifest has errors", func() {
  1302  					BeforeEach(func() {
  1303  						manifestRepo.ReadManifestReturns(
  1304  							&manifest.Manifest{
  1305  								Path: "/some-path/",
  1306  							},
  1307  							errors.New("buildpack should not be null"),
  1308  						)
  1309  
  1310  						args = []string{}
  1311  					})
  1312  
  1313  					It("fails when parsing the manifest has errors", func() {
  1314  						Expect(executeErr).To(HaveOccurred())
  1315  						Expect(executeErr.Error()).To(ContainSubstring("Error reading manifest file:\nbuildpack should not be null"))
  1316  					})
  1317  				})
  1318  
  1319  				Context("when the no-route option is set", func() {
  1320  					Context("when provided the --no-route-flag", func() {
  1321  						BeforeEach(func() {
  1322  							domainRepo.FindByNameInOrgReturns(models.DomainFields{
  1323  								Name: "bar.cf-app.com",
  1324  								GUID: "bar-domain-guid",
  1325  							}, nil)
  1326  
  1327  							args = []string{"--no-route", "app-name"}
  1328  						})
  1329  
  1330  						It("does not create a route", func() {
  1331  							Expect(executeErr).NotTo(HaveOccurred())
  1332  							params := appRepo.CreateArgsForCall(0)
  1333  							Expect(*params.Name).To(Equal("app-name"))
  1334  							Expect(routeRepo.CreateCallCount()).To(BeZero())
  1335  						})
  1336  					})
  1337  
  1338  					Context("when no-route is set in the manifest", func() {
  1339  						BeforeEach(func() {
  1340  							deps.UI = uiWithContents
  1341  							workerManifest := &manifest.Manifest{
  1342  								Path: "manifest.yml",
  1343  								Data: generic.NewMap(map[interface{}]interface{}{
  1344  									"applications": []interface{}{
  1345  										generic.NewMap(map[interface{}]interface{}{
  1346  											"name":      "manifest-app-name",
  1347  											"memory":    "128MB",
  1348  											"instances": 1,
  1349  											"host":      "manifest-host",
  1350  											"domain":    "manifest-example.com",
  1351  											"stack":     "custom-stack",
  1352  											"timeout":   360,
  1353  											"buildpack": "some-buildpack",
  1354  											"command":   `JAVA_HOME=$PWD/.openjdk JAVA_OPTS="-Xss995K" ./bin/start.sh run`,
  1355  											"path":      filepath.Clean("some/path/from/manifest"),
  1356  											"env": generic.NewMap(map[interface{}]interface{}{
  1357  												"FOO":  "baz",
  1358  												"PATH": "/u/apps/my-app/bin",
  1359  											}),
  1360  										}),
  1361  									},
  1362  								}),
  1363  							}
  1364  							workerManifest.Data.Get("applications").([]interface{})[0].(generic.Map).Set("no-route", true)
  1365  							manifestRepo.ReadManifestReturns(workerManifest, nil)
  1366  
  1367  							args = []string{"app-name"}
  1368  						})
  1369  
  1370  						It("Does not create a route", func() {
  1371  							Expect(executeErr).NotTo(HaveOccurred())
  1372  							Expect(terminal.Decolorize(string(output.Contents()))).To(ContainSubstring("App app-name is a worker, skipping route creation"))
  1373  							Expect(routeRepo.BindCallCount()).To(BeZero())
  1374  						})
  1375  					})
  1376  				})
  1377  
  1378  				Context("when provided the --no-hostname flag", func() {
  1379  					BeforeEach(func() {
  1380  						domainRepo.ListDomainsForOrgStub = func(orgGUID string, cb func(models.DomainFields) bool) error {
  1381  							cb(models.DomainFields{
  1382  								Name:   "bar.cf-app.com",
  1383  								GUID:   "bar-domain-guid",
  1384  								Shared: true,
  1385  							})
  1386  
  1387  							return nil
  1388  						}
  1389  						routeRepo.FindReturns(models.Route{}, errors.NewModelNotFoundError("Org", "uh oh"))
  1390  
  1391  						args = []string{"--no-hostname", "app-name"}
  1392  					})
  1393  
  1394  					It("maps the root domain route to the app", func() {
  1395  						Expect(executeErr).NotTo(HaveOccurred())
  1396  
  1397  						Expect(routeActor.FindOrCreateRouteCallCount()).To(Equal(1))
  1398  						_, domain, _, _, _ := routeActor.FindOrCreateRouteArgsForCall(0)
  1399  						Expect(domain.GUID).To(Equal("bar-domain-guid"))
  1400  					})
  1401  
  1402  					Context("when using 'routes' in the manifest", func() {
  1403  						BeforeEach(func() {
  1404  							m := &manifest.Manifest{
  1405  								Data: generic.NewMap(map[interface{}]interface{}{
  1406  									"applications": []interface{}{
  1407  										generic.NewMap(map[interface{}]interface{}{
  1408  											"name": "app1",
  1409  											"routes": []interface{}{
  1410  												map[interface{}]interface{}{"route": "app2route1.example.com"},
  1411  											},
  1412  										}),
  1413  									},
  1414  								}),
  1415  							}
  1416  							manifestRepo.ReadManifestReturns(m, nil)
  1417  						})
  1418  
  1419  						It("returns an error", func() {
  1420  							Expect(executeErr).To(HaveOccurred())
  1421  							Expect(executeErr).To(MatchError("Option '--no-hostname' cannot be used with an app manifest containing the 'routes' attribute"))
  1422  						})
  1423  					})
  1424  				})
  1425  
  1426  				Context("with an invalid memory limit", func() {
  1427  					BeforeEach(func() {
  1428  						args = []string{"-m", "abcM", "app-name"}
  1429  					})
  1430  
  1431  					It("fails", func() {
  1432  						Expect(executeErr).To(HaveOccurred())
  1433  						Expect(executeErr.Error()).To(ContainSubstring("Invalid memory limit: abcM"))
  1434  					})
  1435  				})
  1436  
  1437  				Context("when a manifest has many apps", func() {
  1438  					BeforeEach(func() {
  1439  						deps.UI = uiWithContents
  1440  						m := &manifest.Manifest{
  1441  							Data: generic.NewMap(map[interface{}]interface{}{
  1442  								"applications": []interface{}{
  1443  									generic.NewMap(map[interface{}]interface{}{
  1444  										"name":     "app1",
  1445  										"services": []interface{}{"app1-service", "global-service"},
  1446  										"env": generic.NewMap(map[interface{}]interface{}{
  1447  											"SOMETHING": "definitely-something",
  1448  										}),
  1449  									}),
  1450  									generic.NewMap(map[interface{}]interface{}{
  1451  										"name":     "app2",
  1452  										"services": []interface{}{"app2-service", "global-service"},
  1453  										"env": generic.NewMap(map[interface{}]interface{}{
  1454  											"SOMETHING": "nothing",
  1455  										}),
  1456  									}),
  1457  								},
  1458  							}),
  1459  						}
  1460  						manifestRepo.ReadManifestReturns(m, nil)
  1461  						args = []string{}
  1462  					})
  1463  
  1464  					It("pushes each app", func() {
  1465  						Expect(executeErr).NotTo(HaveOccurred())
  1466  
  1467  						totalOutput := terminal.Decolorize(string(output.Contents()))
  1468  						Expect(totalOutput).To(ContainSubstring("Creating app app1"))
  1469  						Expect(totalOutput).To(ContainSubstring("Creating app app2"))
  1470  						Expect(appRepo.CreateCallCount()).To(Equal(2))
  1471  
  1472  						firstApp := appRepo.CreateArgsForCall(0)
  1473  						secondApp := appRepo.CreateArgsForCall(1)
  1474  						Expect(*firstApp.Name).To(Equal("app1"))
  1475  						Expect(*secondApp.Name).To(Equal("app2"))
  1476  
  1477  						envVars := *firstApp.EnvironmentVars
  1478  						Expect(envVars["SOMETHING"]).To(Equal("definitely-something"))
  1479  
  1480  						envVars = *secondApp.EnvironmentVars
  1481  						Expect(envVars["SOMETHING"]).To(Equal("nothing"))
  1482  					})
  1483  
  1484  					Context("when a single app is given as an arg", func() {
  1485  						BeforeEach(func() {
  1486  							args = []string{"app2"}
  1487  						})
  1488  
  1489  						It("pushes that single app", func() {
  1490  							Expect(executeErr).NotTo(HaveOccurred())
  1491  
  1492  							totalOutput := terminal.Decolorize(string(output.Contents()))
  1493  							Expect(totalOutput).To(ContainSubstring("Creating app app2"))
  1494  							Expect(totalOutput).ToNot(ContainSubstring("Creating app app1"))
  1495  							Expect(appRepo.CreateCallCount()).To(Equal(1))
  1496  							params := appRepo.CreateArgsForCall(0)
  1497  							Expect(*params.Name).To(Equal("app2"))
  1498  						})
  1499  					})
  1500  
  1501  					Context("when the given app is not in the manifest", func() {
  1502  						BeforeEach(func() {
  1503  							args = []string{"non-existant-app"}
  1504  						})
  1505  
  1506  						It("fails", func() {
  1507  							Expect(executeErr).To(HaveOccurred())
  1508  							Expect(appRepo.CreateCallCount()).To(BeZero())
  1509  						})
  1510  					})
  1511  				})
  1512  			})
  1513  		})
  1514  
  1515  		Context("re-pushing an existing app", func() {
  1516  			var existingApp models.Application
  1517  
  1518  			BeforeEach(func() {
  1519  				deps.UI = uiWithContents
  1520  				existingApp = models.Application{
  1521  					ApplicationFields: models.ApplicationFields{
  1522  						Name:    "existing-app",
  1523  						GUID:    "existing-app-guid",
  1524  						Command: "unicorn -c config/unicorn.rb -D",
  1525  						EnvironmentVars: map[string]interface{}{
  1526  							"crazy": "pants",
  1527  							"FOO":   "NotYoBaz",
  1528  							"foo":   "manchu",
  1529  						},
  1530  					},
  1531  				}
  1532  				manifestRepo.ReadManifestReturns(manifest.NewEmptyManifest(), nil)
  1533  				appRepo.ReadReturns(existingApp, nil)
  1534  				appRepo.UpdateReturns(existingApp, nil)
  1535  				args = []string{"existing-app"}
  1536  			})
  1537  
  1538  			It("stops the app, achieving a full-downtime deploy!", func() {
  1539  				Expect(executeErr).NotTo(HaveOccurred())
  1540  
  1541  				app, orgName, spaceName := stopper.ApplicationStopArgsForCall(0)
  1542  				Expect(app.GUID).To(Equal(existingApp.GUID))
  1543  				Expect(app.Name).To(Equal("existing-app"))
  1544  				Expect(orgName).To(Equal(configRepo.OrganizationFields().Name))
  1545  				Expect(spaceName).To(Equal(configRepo.SpaceFields().Name))
  1546  
  1547  				Expect(actor.UploadAppCallCount()).To(Equal(1))
  1548  				appGUID, _, _ := actor.UploadAppArgsForCall(0)
  1549  				Expect(appGUID).To(Equal(existingApp.GUID))
  1550  			})
  1551  
  1552  			It("re-uploads the app", func() {
  1553  				Expect(executeErr).NotTo(HaveOccurred())
  1554  
  1555  				totalOutputs := terminal.Decolorize(string(output.Contents()))
  1556  				Expect(totalOutputs).To(ContainSubstring("Uploading existing-app...\nOK"))
  1557  			})
  1558  
  1559  			Context("when the -b flag is provided as 'default'", func() {
  1560  				BeforeEach(func() {
  1561  					args = []string{"-b", "default", "existing-app"}
  1562  				})
  1563  
  1564  				It("resets the app's buildpack", func() {
  1565  					Expect(executeErr).NotTo(HaveOccurred())
  1566  
  1567  					Expect(appRepo.UpdateCallCount()).To(Equal(1))
  1568  					_, params := appRepo.UpdateArgsForCall(0)
  1569  					Expect(*params.BuildpackURL).To(Equal(""))
  1570  				})
  1571  			})
  1572  
  1573  			Context("when the -c flag is provided as 'default'", func() {
  1574  				BeforeEach(func() {
  1575  					args = []string{"-c", "default", "existing-app"}
  1576  				})
  1577  
  1578  				It("resets the app's command", func() {
  1579  					Expect(executeErr).NotTo(HaveOccurred())
  1580  					_, params := appRepo.UpdateArgsForCall(0)
  1581  					Expect(*params.Command).To(Equal(""))
  1582  				})
  1583  			})
  1584  
  1585  			Context("when the -b flag is provided as 'null'", func() {
  1586  				BeforeEach(func() {
  1587  					args = []string{"-b", "null", "existing-app"}
  1588  				})
  1589  
  1590  				It("resets the app's buildpack", func() {
  1591  					Expect(executeErr).NotTo(HaveOccurred())
  1592  					_, params := appRepo.UpdateArgsForCall(0)
  1593  					Expect(*params.BuildpackURL).To(Equal(""))
  1594  				})
  1595  			})
  1596  
  1597  			Context("when the -c flag is provided as 'null'", func() {
  1598  				BeforeEach(func() {
  1599  					args = []string{"-c", "null", "existing-app"}
  1600  				})
  1601  
  1602  				It("resets the app's command", func() {
  1603  					Expect(executeErr).NotTo(HaveOccurred())
  1604  					_, params := appRepo.UpdateArgsForCall(0)
  1605  					Expect(*params.Command).To(Equal(""))
  1606  				})
  1607  			})
  1608  
  1609  			Context("when the manifest provided env variables", func() {
  1610  				BeforeEach(func() {
  1611  					m := &manifest.Manifest{
  1612  						Path: "manifest.yml",
  1613  						Data: generic.NewMap(map[interface{}]interface{}{
  1614  							"applications": []interface{}{
  1615  								generic.NewMap(map[interface{}]interface{}{
  1616  									"name":      "manifest-app-name",
  1617  									"memory":    "128MB",
  1618  									"instances": 1,
  1619  									"host":      "manifest-host",
  1620  									"domain":    "manifest-example.com",
  1621  									"stack":     "custom-stack",
  1622  									"timeout":   360,
  1623  									"buildpack": "some-buildpack",
  1624  									"command":   `JAVA_HOME=$PWD/.openjdk JAVA_OPTS="-Xss995K" ./bin/start.sh run`,
  1625  									"path":      filepath.Clean("some/path/from/manifest"),
  1626  									"env": generic.NewMap(map[interface{}]interface{}{
  1627  										"FOO":  "baz",
  1628  										"PATH": "/u/apps/my-app/bin",
  1629  									}),
  1630  								}),
  1631  							},
  1632  						}),
  1633  					}
  1634  					manifestRepo.ReadManifestReturns(m, nil)
  1635  
  1636  					args = []string{"existing-app"}
  1637  				})
  1638  
  1639  				It("merges env vars from the manifest with those from the server", func() {
  1640  					Expect(executeErr).NotTo(HaveOccurred())
  1641  
  1642  					_, params := appRepo.UpdateArgsForCall(0)
  1643  					updatedAppEnvVars := *params.EnvironmentVars
  1644  					Expect(updatedAppEnvVars["crazy"]).To(Equal("pants"))
  1645  					Expect(updatedAppEnvVars["FOO"]).To(Equal("baz"))
  1646  					Expect(updatedAppEnvVars["foo"]).To(Equal("manchu"))
  1647  					Expect(updatedAppEnvVars["PATH"]).To(Equal("/u/apps/my-app/bin"))
  1648  				})
  1649  			})
  1650  
  1651  			Context("when the app is already stopped", func() {
  1652  				BeforeEach(func() {
  1653  					existingApp.State = "stopped"
  1654  					appRepo.ReadReturns(existingApp, nil)
  1655  					appRepo.UpdateReturns(existingApp, nil)
  1656  					args = []string{"existing-app"}
  1657  				})
  1658  
  1659  				It("does not stop the app", func() {
  1660  					Expect(executeErr).NotTo(HaveOccurred())
  1661  					Expect(stopper.ApplicationStopCallCount()).To(Equal(0))
  1662  				})
  1663  			})
  1664  
  1665  			Context("when the application is pushed with updated parameters", func() {
  1666  				BeforeEach(func() {
  1667  					existingRoute := models.RouteSummary{
  1668  						Host: "existing-app",
  1669  					}
  1670  					existingApp.Routes = []models.RouteSummary{existingRoute}
  1671  					appRepo.ReadReturns(existingApp, nil)
  1672  					appRepo.UpdateReturns(existingApp, nil)
  1673  
  1674  					stackRepo.FindByNameReturns(models.Stack{
  1675  						Name: "differentStack",
  1676  						GUID: "differentStack-guid",
  1677  					}, nil)
  1678  
  1679  					args = []string{
  1680  						"-c", "different start command",
  1681  						"-i", "10",
  1682  						"-m", "1G",
  1683  						"-b", "https://github.com/heroku/heroku-buildpack-different.git",
  1684  						"-s", "differentStack",
  1685  						"existing-app",
  1686  					}
  1687  				})
  1688  
  1689  				It("updates the app", func() {
  1690  					Expect(executeErr).NotTo(HaveOccurred())
  1691  
  1692  					appGUID, params := appRepo.UpdateArgsForCall(0)
  1693  					Expect(appGUID).To(Equal(existingApp.GUID))
  1694  					Expect(*params.Command).To(Equal("different start command"))
  1695  					Expect(*params.InstanceCount).To(Equal(10))
  1696  					Expect(*params.Memory).To(Equal(int64(1024)))
  1697  					Expect(*params.BuildpackURL).To(Equal("https://github.com/heroku/heroku-buildpack-different.git"))
  1698  					Expect(*params.StackGUID).To(Equal("differentStack-guid"))
  1699  				})
  1700  			})
  1701  
  1702  			Context("when the app has a route bound", func() {
  1703  				BeforeEach(func() {
  1704  					domain := models.DomainFields{
  1705  						Name:   "example.com",
  1706  						GUID:   "domain-guid",
  1707  						Shared: true,
  1708  					}
  1709  
  1710  					existingApp.Routes = []models.RouteSummary{{
  1711  						GUID:   "existing-route-guid",
  1712  						Host:   "existing-app",
  1713  						Domain: domain,
  1714  					}}
  1715  
  1716  					appRepo.ReadReturns(existingApp, nil)
  1717  					appRepo.UpdateReturns(existingApp, nil)
  1718  				})
  1719  
  1720  				Context("and no route-related flags are given", func() {
  1721  					Context("and there is no route in the manifest", func() {
  1722  						It("does not add a route to the app", func() {
  1723  							Expect(executeErr).NotTo(HaveOccurred())
  1724  
  1725  							appGUID, _, _ := actor.UploadAppArgsForCall(0)
  1726  							Expect(appGUID).To(Equal("existing-app-guid"))
  1727  							Expect(domainRepo.FindByNameInOrgCallCount()).To(BeZero())
  1728  							Expect(routeRepo.FindCallCount()).To(BeZero())
  1729  							Expect(routeRepo.CreateCallCount()).To(BeZero())
  1730  						})
  1731  					})
  1732  				})
  1733  
  1734  				Context("when --no-route flag is given", func() {
  1735  					BeforeEach(func() {
  1736  						args = []string{"--no-route", "existing-app"}
  1737  					})
  1738  
  1739  					It("removes existing routes that the app is bound to", func() {
  1740  						Expect(executeErr).NotTo(HaveOccurred())
  1741  
  1742  						appGUID, _, _ := actor.UploadAppArgsForCall(0)
  1743  						Expect(appGUID).To(Equal("existing-app-guid"))
  1744  
  1745  						Expect(routeActor.UnbindAllCallCount()).To(Equal(1))
  1746  						app := routeActor.UnbindAllArgsForCall(0)
  1747  						Expect(app.GUID).To(Equal(appGUID))
  1748  
  1749  						Expect(routeActor.FindOrCreateRouteCallCount()).To(BeZero())
  1750  					})
  1751  				})
  1752  
  1753  				Context("when the --no-hostname flag is given", func() {
  1754  					BeforeEach(func() {
  1755  						routeRepo.FindReturns(models.Route{}, errors.NewModelNotFoundError("Org", "existing-app.example.com"))
  1756  						args = []string{"--no-hostname", "existing-app"}
  1757  					})
  1758  
  1759  					It("binds the root domain route to an app with a pre-existing route", func() {
  1760  						Expect(executeErr).NotTo(HaveOccurred())
  1761  
  1762  						Expect(routeActor.FindOrCreateRouteCallCount()).To(Equal(1))
  1763  						hostname, _, _, _, _ := routeActor.FindOrCreateRouteArgsForCall(0)
  1764  						Expect(hostname).To(BeEmpty())
  1765  					})
  1766  				})
  1767  			})
  1768  
  1769  			Context("service instances", func() {
  1770  				BeforeEach(func() {
  1771  					appRepo.CreateStub = func(params models.AppParams) (models.Application, error) {
  1772  						a := models.Application{}
  1773  						a.Name = *params.Name
  1774  						a.GUID = *params.Name + "-guid"
  1775  
  1776  						return a, nil
  1777  					}
  1778  
  1779  					serviceRepo.FindInstanceByNameStub = func(name string) (models.ServiceInstance, error) {
  1780  						return models.ServiceInstance{
  1781  							ServiceInstanceFields: models.ServiceInstanceFields{Name: name},
  1782  						}, nil
  1783  					}
  1784  
  1785  					m := &manifest.Manifest{
  1786  						Data: generic.NewMap(map[interface{}]interface{}{
  1787  							"applications": []interface{}{
  1788  								generic.NewMap(map[interface{}]interface{}{
  1789  									"name":     "app1",
  1790  									"services": []interface{}{"app1-service", "global-service"},
  1791  									"env": generic.NewMap(map[interface{}]interface{}{
  1792  										"SOMETHING": "definitely-something",
  1793  									}),
  1794  								}),
  1795  								generic.NewMap(map[interface{}]interface{}{
  1796  									"name":     "app2",
  1797  									"services": []interface{}{"app2-service", "global-service"},
  1798  									"env": generic.NewMap(map[interface{}]interface{}{
  1799  										"SOMETHING": "nothing",
  1800  									}),
  1801  								}),
  1802  							},
  1803  						}),
  1804  					}
  1805  					manifestRepo.ReadManifestReturns(m, nil)
  1806  
  1807  					args = []string{}
  1808  				})
  1809  
  1810  				Context("when the service is not bound", func() {
  1811  					BeforeEach(func() {
  1812  						appRepo.ReadReturns(models.Application{}, errors.NewModelNotFoundError("App", "the-app"))
  1813  					})
  1814  
  1815  					It("binds service instances to the app", func() {
  1816  						Expect(executeErr).NotTo(HaveOccurred())
  1817  
  1818  						Expect(len(serviceBinder.AppsToBind)).To(Equal(4))
  1819  						Expect(serviceBinder.AppsToBind[0].Name).To(Equal("app1"))
  1820  						Expect(serviceBinder.AppsToBind[1].Name).To(Equal("app1"))
  1821  						Expect(serviceBinder.InstancesToBindTo[0].Name).To(Equal("app1-service"))
  1822  						Expect(serviceBinder.InstancesToBindTo[1].Name).To(Equal("global-service"))
  1823  
  1824  						Expect(serviceBinder.AppsToBind[2].Name).To(Equal("app2"))
  1825  						Expect(serviceBinder.AppsToBind[3].Name).To(Equal("app2"))
  1826  						Expect(serviceBinder.InstancesToBindTo[2].Name).To(Equal("app2-service"))
  1827  						Expect(serviceBinder.InstancesToBindTo[3].Name).To(Equal("global-service"))
  1828  
  1829  						totalOutputs := terminal.Decolorize(string(output.Contents()))
  1830  						Expect(totalOutputs).To(ContainSubstring("Creating app app1 in org my-org / space my-space as my-user...\nOK"))
  1831  						Expect(totalOutputs).To(ContainSubstring("Binding service app1-service to app app1 in org my-org / space my-space as my-user...\nOK"))
  1832  						Expect(totalOutputs).To(ContainSubstring("Binding service global-service to app app1 in org my-org / space my-space as my-user...\nOK"))
  1833  						Expect(totalOutputs).To(ContainSubstring("Creating app app2 in org my-org / space my-space as my-user...\nOK"))
  1834  						Expect(totalOutputs).To(ContainSubstring("Binding service app2-service to app app2 in org my-org / space my-space as my-user...\nOK"))
  1835  						Expect(totalOutputs).To(ContainSubstring("Binding service global-service to app app2 in org my-org / space my-space as my-user...\nOK"))
  1836  					})
  1837  				})
  1838  
  1839  				Context("when the app is already bound to the service", func() {
  1840  					BeforeEach(func() {
  1841  						appRepo.ReadReturns(models.Application{
  1842  							ApplicationFields: models.ApplicationFields{Name: "app-name"},
  1843  						}, nil)
  1844  						serviceBinder.BindApplicationReturns.Error = errors.NewHTTPError(500, errors.ServiceBindingAppServiceTaken, "it don't work")
  1845  					})
  1846  
  1847  					It("gracefully continues", func() {
  1848  						Expect(executeErr).NotTo(HaveOccurred())
  1849  						Expect(len(serviceBinder.AppsToBind)).To(Equal(4))
  1850  					})
  1851  				})
  1852  
  1853  				Context("when the service instance can't be found", func() {
  1854  					BeforeEach(func() {
  1855  						serviceRepo.FindInstanceByNameReturns(models.ServiceInstance{}, errors.New("Error finding instance"))
  1856  					})
  1857  
  1858  					It("fails with an error", func() {
  1859  						Expect(executeErr).To(HaveOccurred())
  1860  						Expect(executeErr.Error()).To(ContainSubstring("Could not find service app1-service to bind to existing-app"))
  1861  					})
  1862  				})
  1863  			})
  1864  
  1865  			Context("checking for bad flags", func() {
  1866  				BeforeEach(func() {
  1867  					appRepo.ReadReturns(models.Application{}, errors.NewModelNotFoundError("App", "the-app"))
  1868  					args = []string{"-t", "FooeyTimeout", "app-name"}
  1869  				})
  1870  
  1871  				It("fails when a non-numeric start timeout is given", func() {
  1872  					Expect(executeErr).To(HaveOccurred())
  1873  					Expect(executeErr.Error()).To(ContainSubstring("Invalid timeout param: FooeyTimeout"))
  1874  				})
  1875  			})
  1876  
  1877  			Context("displaying information about files being uploaded", func() {
  1878  				BeforeEach(func() {
  1879  					appfiles.CountFilesReturns(11)
  1880  					zipper.ZipReturns(nil)
  1881  					zipper.GetZipSizeReturns(6100000, nil)
  1882  					actor.GatherFilesReturns([]resources.AppFileResource{{Path: "path/to/app"}, {Path: "bar"}}, true, nil)
  1883  					args = []string{"appName"}
  1884  				})
  1885  
  1886  				It("displays the information", func() {
  1887  					Expect(executeErr).NotTo(HaveOccurred())
  1888  
  1889  					curDir, err := os.Getwd()
  1890  					Expect(err).NotTo(HaveOccurred())
  1891  
  1892  					totalOutputs := terminal.Decolorize(string(output.Contents()))
  1893  					Expect(totalOutputs).To(ContainSubstring("Uploading app files from: " + curDir))
  1894  					Expect(totalOutputs).To(ContainSubstring("Uploading 5.8M, 11 files\nOK"))
  1895  				})
  1896  			})
  1897  
  1898  			Context("when the app can't be uploaded", func() {
  1899  				BeforeEach(func() {
  1900  					actor.UploadAppReturns(errors.New("Boom!"))
  1901  					args = []string{"app"}
  1902  				})
  1903  
  1904  				It("fails when the app can't be uploaded", func() {
  1905  					Expect(executeErr).To(HaveOccurred())
  1906  					Expect(executeErr.Error()).To(ContainSubstring("Error uploading application"))
  1907  				})
  1908  			})
  1909  
  1910  			Context("when no name and no manifest is given", func() {
  1911  				BeforeEach(func() {
  1912  					manifestRepo.ReadManifestReturns(manifest.NewEmptyManifest(), errors.New("No such manifest"))
  1913  					args = []string{}
  1914  				})
  1915  
  1916  				It("fails", func() {
  1917  					Expect(executeErr).To(HaveOccurred())
  1918  					Expect(executeErr.Error()).To(ContainSubstring("Incorrect Usage. The push command requires an app name. The app name can be supplied as an argument or with a manifest.yml file."))
  1919  				})
  1920  			})
  1921  		})
  1922  
  1923  		Context("when routes are specified in the manifest", func() {
  1924  			Context("and the manifest has more than one app", func() {
  1925  				BeforeEach(func() {
  1926  					m := &manifest.Manifest{
  1927  						Path: "manifest.yml",
  1928  						Data: generic.NewMap(map[interface{}]interface{}{
  1929  							"applications": []interface{}{
  1930  								generic.NewMap(map[interface{}]interface{}{
  1931  									"routes": []interface{}{
  1932  										map[interface{}]interface{}{"route": "app1route1.example.com/path"},
  1933  										map[interface{}]interface{}{"route": "app1route2.example.com:8008"},
  1934  									},
  1935  									"name": "manifest-app-name-1",
  1936  								}),
  1937  								generic.NewMap(map[interface{}]interface{}{
  1938  									"name": "manifest-app-name-2",
  1939  									"routes": []interface{}{
  1940  										map[interface{}]interface{}{"route": "app2route1.example.com"},
  1941  									},
  1942  								}),
  1943  							},
  1944  						}),
  1945  					}
  1946  					manifestRepo.ReadManifestReturns(m, nil)
  1947  
  1948  					appRepo.ReadStub = func(appName string) (models.Application, error) {
  1949  						return models.Application{
  1950  							ApplicationFields: models.ApplicationFields{
  1951  								Name: appName,
  1952  								GUID: appName + "-guid",
  1953  							},
  1954  						}, nil
  1955  					}
  1956  
  1957  					appRepo.UpdateStub = func(appGUID string, appParams models.AppParams) (models.Application, error) {
  1958  						return models.Application{
  1959  							ApplicationFields: models.ApplicationFields{
  1960  								GUID: appGUID,
  1961  							},
  1962  						}, nil
  1963  					}
  1964  				})
  1965  
  1966  				Context("and there are no flags", func() {
  1967  					BeforeEach(func() {
  1968  						args = []string{}
  1969  					})
  1970  
  1971  					It("maps the routes to the specified apps", func() {
  1972  						noHostBool := false
  1973  						emptyAppParams := models.AppParams{
  1974  							NoHostname: &noHostBool,
  1975  						}
  1976  
  1977  						Expect(executeErr).ToNot(HaveOccurred())
  1978  
  1979  						Expect(actor.MapManifestRouteCallCount()).To(Equal(3))
  1980  
  1981  						route, app, appParams := actor.MapManifestRouteArgsForCall(0)
  1982  						Expect(route).To(Equal("app1route1.example.com/path"))
  1983  						Expect(app.ApplicationFields.GUID).To(Equal("manifest-app-name-1-guid"))
  1984  						Expect(appParams).To(Equal(emptyAppParams))
  1985  
  1986  						route, app, appParams = actor.MapManifestRouteArgsForCall(1)
  1987  						Expect(route).To(Equal("app1route2.example.com:8008"))
  1988  						Expect(app.ApplicationFields.GUID).To(Equal("manifest-app-name-1-guid"))
  1989  						Expect(appParams).To(Equal(emptyAppParams))
  1990  
  1991  						route, app, appParams = actor.MapManifestRouteArgsForCall(2)
  1992  						Expect(route).To(Equal("app2route1.example.com"))
  1993  						Expect(app.ApplicationFields.GUID).To(Equal("manifest-app-name-2-guid"))
  1994  						Expect(appParams).To(Equal(emptyAppParams))
  1995  					})
  1996  				})
  1997  
  1998  				Context("and flags other than -f are present", func() {
  1999  					BeforeEach(func() {
  2000  						args = []string{"-n", "hostname-flag"}
  2001  					})
  2002  
  2003  					It("should return an error", func() {
  2004  						Expect(executeErr).To(HaveOccurred())
  2005  						Expect(executeErr.Error()).To(Equal("Incorrect Usage. Command line flags (except -f) cannot be applied when pushing multiple apps from a manifest file."))
  2006  					})
  2007  				})
  2008  			})
  2009  
  2010  			Context("and the manifest has only one app", func() {
  2011  				BeforeEach(func() {
  2012  					m := &manifest.Manifest{
  2013  						Path: "manifest.yml",
  2014  						Data: generic.NewMap(map[interface{}]interface{}{
  2015  							"applications": []interface{}{
  2016  								generic.NewMap(map[interface{}]interface{}{
  2017  									"routes": []interface{}{
  2018  										map[interface{}]interface{}{"route": "app1route1.example.com/path"},
  2019  									},
  2020  									"name": "manifest-app-name-1",
  2021  								}),
  2022  							},
  2023  						}),
  2024  					}
  2025  					manifestRepo.ReadManifestReturns(m, nil)
  2026  
  2027  					appRepo.ReadStub = func(appName string) (models.Application, error) {
  2028  						return models.Application{
  2029  							ApplicationFields: models.ApplicationFields{
  2030  								Name: appName,
  2031  								GUID: appName + "-guid",
  2032  							},
  2033  						}, nil
  2034  					}
  2035  
  2036  					appRepo.UpdateStub = func(appGUID string, appParams models.AppParams) (models.Application, error) {
  2037  						return models.Application{
  2038  							ApplicationFields: models.ApplicationFields{
  2039  								GUID: appGUID,
  2040  							},
  2041  						}, nil
  2042  					}
  2043  				})
  2044  
  2045  				Context("and flags are present", func() {
  2046  					BeforeEach(func() {
  2047  						args = []string{"-n", "flag-value"}
  2048  					})
  2049  
  2050  					It("maps the routes to the apps", func() {
  2051  						noHostBool := false
  2052  						appParamsFromContext := models.AppParams{
  2053  							Hosts:      []string{"flag-value"},
  2054  							NoHostname: &noHostBool,
  2055  						}
  2056  
  2057  						Expect(executeErr).ToNot(HaveOccurred())
  2058  
  2059  						Expect(actor.MapManifestRouteCallCount()).To(Equal(1))
  2060  
  2061  						route, app, appParams := actor.MapManifestRouteArgsForCall(0)
  2062  						Expect(route).To(Equal("app1route1.example.com/path"))
  2063  						Expect(app.ApplicationFields.GUID).To(Equal("manifest-app-name-1-guid"))
  2064  						Expect(appParams).To(Equal(appParamsFromContext))
  2065  					})
  2066  				})
  2067  			})
  2068  		})
  2069  	})
  2070  })