github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+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/commandregistry/commandregistryfakes"
    18  	"code.cloudfoundry.org/cli/cf/commands/application"
    19  	"code.cloudfoundry.org/cli/cf/commands/application/applicationfakes"
    20  	"code.cloudfoundry.org/cli/cf/commands/service/servicefakes"
    21  	"code.cloudfoundry.org/cli/cf/configuration/coreconfig"
    22  	"code.cloudfoundry.org/cli/cf/errors"
    23  	"code.cloudfoundry.org/cli/cf/flags"
    24  	"code.cloudfoundry.org/cli/cf/manifest"
    25  	"code.cloudfoundry.org/cli/cf/manifest/manifestfakes"
    26  	"code.cloudfoundry.org/cli/cf/models"
    27  	"code.cloudfoundry.org/cli/cf/requirements"
    28  	"code.cloudfoundry.org/cli/cf/requirements/requirementsfakes"
    29  	"code.cloudfoundry.org/cli/cf/terminal"
    30  	"code.cloudfoundry.org/cli/cf/trace"
    31  	"code.cloudfoundry.org/cli/util/generic"
    32  	testconfig "code.cloudfoundry.org/cli/util/testhelpers/configuration"
    33  	testterm "code.cloudfoundry.org/cli/util/testhelpers/terminal"
    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              *commandregistryfakes.FakeRandomWordGenerator
    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(commandregistryfakes.FakeRandomWordGenerator)
    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  							var oldDockerPassword string
   861  
   862  							BeforeEach(func() {
   863  								oldDockerPassword = os.Getenv("CF_DOCKER_PASSWORD")
   864  								Expect(os.Setenv("CF_DOCKER_PASSWORD", "some-docker-pass")).ToNot(HaveOccurred())
   865  							})
   866  
   867  							AfterEach(func() {
   868  								Expect(os.Setenv("CF_DOCKER_PASSWORD", oldDockerPassword)).ToNot(HaveOccurred())
   869  							})
   870  
   871  							It("it passes the credentials to create call", func() {
   872  								Expect(executeErr).NotTo(HaveOccurred())
   873  
   874  								Expect(output).To(gbytes.Say("Using docker repository password from environment variable CF_DOCKER_PASSWORD."))
   875  
   876  								Expect(appRepo.CreateCallCount()).To(Equal(1))
   877  								params := appRepo.CreateArgsForCall(0)
   878  								Expect(*params.DockerUsername).To(Equal("some-docker-username"))
   879  								Expect(*params.DockerPassword).To(Equal("some-docker-pass"))
   880  							})
   881  						})
   882  
   883  						Context("when CF_DOCKER_PASSWORD is not set", func() {
   884  							BeforeEach(func() {
   885  								Skip("these [mostly] worked prior to the revert in 1d94b2df98b")
   886  							})
   887  
   888  							Context("when the user gets prompted for the docker password", func() {
   889  								Context("when the user inputs the password on the first attempt", func() {
   890  									BeforeEach(func() {
   891  										_, err := input.Write([]byte("some-docker-pass\n"))
   892  										Expect(err).NotTo(HaveOccurred())
   893  									})
   894  
   895  									It("it passes the credentials to create call", func() {
   896  										Expect(executeErr).NotTo(HaveOccurred())
   897  
   898  										Expect(output).To(gbytes.Say("Environment variable CF_DOCKER_PASSWORD not set."))
   899  										Expect(output).To(gbytes.Say("Docker password"))
   900  										Expect(output).ToNot(gbytes.Say("Docker password")) // Only prompt once
   901  
   902  										Expect(appRepo.CreateCallCount()).To(Equal(1))
   903  										params := appRepo.CreateArgsForCall(0)
   904  										Expect(*params.DockerUsername).To(Equal("some-docker-username"))
   905  										Expect(*params.DockerPassword).To(Equal("some-docker-pass"))
   906  									})
   907  								})
   908  
   909  								Context("when the user fails to input the password 3 times", func() {
   910  									BeforeEach(func() {
   911  										_, err := input.Write([]byte("\n\n\n"))
   912  										Expect(err).NotTo(HaveOccurred())
   913  									})
   914  
   915  									It("returns an error", func() {
   916  										Expect(executeErr).To(MatchError("Please provide a password"))
   917  
   918  										Expect(output).To(gbytes.Say("Docker password"))
   919  										Expect(output).To(gbytes.Say("Docker password"))
   920  										Expect(output).To(gbytes.Say("Docker password"))
   921  										Expect(output).ToNot(gbytes.Say("Docker password"))
   922  
   923  										Expect(appRepo.CreateCallCount()).To(Equal(0))
   924  									})
   925  								})
   926  							})
   927  						})
   928  					})
   929  				})
   930  
   931  				Context("when the docker password is not set in the env", func() {
   932  					var oldDockerPassword string
   933  
   934  					BeforeEach(func() {
   935  						m := &manifest.Manifest{
   936  							Path: "manifest.yml",
   937  							Data: generic.NewMap(map[interface{}]interface{}{
   938  								"applications": []interface{}{
   939  									map[interface{}]interface{}{
   940  										"docker": map[interface{}]interface{}{
   941  											"username": "user",
   942  										},
   943  									},
   944  								},
   945  							}),
   946  						}
   947  						manifestRepo.ReadManifestReturns(m, nil)
   948  
   949  						oldDockerPassword = os.Getenv("CF_DOCKER_PASSWORD")
   950  						Expect(os.Unsetenv("CF_DOCKER_PASSWORD")).To(Succeed())
   951  					})
   952  
   953  					AfterEach(func() {
   954  						Expect(os.Setenv("CF_DOCKER_PASSWORD", oldDockerPassword)).To(Succeed())
   955  					})
   956  
   957  					Context("when docker username is provided via the manifest only", func() {
   958  						BeforeEach(func() {
   959  							deps.UI = uiWithContents
   960  							args = []string{"testApp", "--docker-image", "some-docker-image"}
   961  						})
   962  
   963  						It("returns an error", func() {
   964  							Expect(executeErr).To(MatchError("No Docker password was provided. Please provide the password by setting the CF_DOCKER_PASSWORD environment variable."))
   965  						})
   966  					})
   967  				})
   968  
   969  				Context("when the docker username is provided via the command line and the docker image is not provided", func() {
   970  					var oldDockerPassword string
   971  
   972  					BeforeEach(func() {
   973  						oldDockerPassword = os.Getenv("CF_DOCKER_PASSWORD")
   974  						Expect(os.Setenv("CF_DOCKER_PASSWORD", "docker-pass")).To(Succeed())
   975  
   976  						deps.UI = uiWithContents
   977  						args = []string{"testApp", "--docker-username", "user"}
   978  					})
   979  
   980  					AfterEach(func() {
   981  						Expect(os.Setenv("CF_DOCKER_PASSWORD", oldDockerPassword)).To(Succeed())
   982  					})
   983  
   984  					It("returns an error", func() {
   985  						Expect(executeErr).To(MatchError("'--docker-username' requires '--docker-image' to be specified"))
   986  					})
   987  				})
   988  
   989  				Context("when docker username is provided via the manifest but docker image is not provided", func() {
   990  					BeforeEach(func() {
   991  						m := &manifest.Manifest{
   992  							Path: "manifest.yml",
   993  							Data: generic.NewMap(map[interface{}]interface{}{
   994  								"applications": []interface{}{
   995  									map[interface{}]interface{}{
   996  										"docker": map[interface{}]interface{}{
   997  											"username": "user",
   998  										},
   999  									},
  1000  								},
  1001  							}),
  1002  						}
  1003  						manifestRepo.ReadManifestReturns(m, nil)
  1004  
  1005  						deps.UI = uiWithContents
  1006  						args = []string{"testApp"}
  1007  					})
  1008  
  1009  					It("returns an error", func() {
  1010  						Expect(executeErr).To(MatchError("'--docker-username' requires '--docker-image' to be specified"))
  1011  					})
  1012  				})
  1013  
  1014  				Context("when health-check-type '-u' or '--health-check-type' is set", func() {
  1015  					Context("when the value is not 'http', 'none', 'port', or 'process'", func() {
  1016  						BeforeEach(func() {
  1017  							args = []string{"app-name", "-u", "bad-value"}
  1018  						})
  1019  
  1020  						It("returns an error", func() {
  1021  							Expect(executeErr).To(HaveOccurred())
  1022  
  1023  							Expect(executeErr.Error()).To(ContainSubstring("Error: Invalid health-check-type param: bad-value"))
  1024  						})
  1025  					})
  1026  
  1027  					Context("when the value is 'http'", func() {
  1028  						BeforeEach(func() {
  1029  							args = []string{"app-name", "--health-check-type", "http"}
  1030  						})
  1031  
  1032  						It("does not show error", func() {
  1033  							Expect(executeErr).NotTo(HaveOccurred())
  1034  						})
  1035  
  1036  						It("sets the HTTP health check endpoint to /", func() {
  1037  							params := appRepo.CreateArgsForCall(0)
  1038  							Expect(*params.HealthCheckHTTPEndpoint).To(Equal("/"))
  1039  						})
  1040  					})
  1041  
  1042  					Context("when the value is 'none'", func() {
  1043  						BeforeEach(func() {
  1044  							args = []string{"app-name", "--health-check-type", "none"}
  1045  						})
  1046  
  1047  						It("does not show error", func() {
  1048  							Expect(executeErr).NotTo(HaveOccurred())
  1049  						})
  1050  					})
  1051  
  1052  					Context("when the value is 'port'", func() {
  1053  						BeforeEach(func() {
  1054  							args = []string{"app-name", "--health-check-type", "port"}
  1055  						})
  1056  
  1057  						It("does not show error", func() {
  1058  							Expect(executeErr).NotTo(HaveOccurred())
  1059  						})
  1060  					})
  1061  
  1062  					Context("when the value is 'process'", func() {
  1063  						BeforeEach(func() {
  1064  							args = []string{"app-name", "--health-check-type", "process"}
  1065  						})
  1066  
  1067  						It("does not show error", func() {
  1068  							Expect(executeErr).NotTo(HaveOccurred())
  1069  						})
  1070  					})
  1071  				})
  1072  
  1073  				Context("with random-route option set", func() {
  1074  					var manifestApp generic.Map
  1075  
  1076  					BeforeEach(func() {
  1077  						manifestApp = generic.NewMap(map[interface{}]interface{}{
  1078  							"name":      "manifest-app-name",
  1079  							"memory":    "128MB",
  1080  							"instances": 1,
  1081  							"domain":    "manifest-example.com",
  1082  							"stack":     "custom-stack",
  1083  							"timeout":   360,
  1084  							"buildpack": "some-buildpack",
  1085  							"command":   `JAVA_HOME=$PWD/.openjdk JAVA_OPTS="-Xss995K" ./bin/start.sh run`,
  1086  							"path":      filepath.Clean("some/path/from/manifest"),
  1087  							"env": generic.NewMap(map[interface{}]interface{}{
  1088  								"FOO":  "baz",
  1089  								"PATH": "/u/apps/my-app/bin",
  1090  							}),
  1091  						})
  1092  						m := &manifest.Manifest{
  1093  							Path: "manifest.yml",
  1094  							Data: generic.NewMap(map[interface{}]interface{}{
  1095  								"applications": []interface{}{manifestApp},
  1096  							}),
  1097  						}
  1098  						manifestRepo.ReadManifestReturns(m, nil)
  1099  					})
  1100  
  1101  					Context("for http routes", func() {
  1102  						Context("when random-route is set as a flag", func() {
  1103  							BeforeEach(func() {
  1104  								args = []string{"--random-route", "app-name"}
  1105  							})
  1106  
  1107  							It("provides a random hostname", func() {
  1108  								Expect(executeErr).NotTo(HaveOccurred())
  1109  
  1110  								Expect(routeActor.FindOrCreateRouteCallCount()).To(Equal(1))
  1111  								host, _, _, _, _ := routeActor.FindOrCreateRouteArgsForCall(0)
  1112  								Expect(host).To(Equal("app-name-random-host"))
  1113  							})
  1114  						})
  1115  
  1116  						Context("when random-route is set in the manifest", func() {
  1117  							BeforeEach(func() {
  1118  								manifestApp.Set("random-route", true)
  1119  								args = []string{"app-name"}
  1120  							})
  1121  
  1122  							It("provides a random hostname", func() {
  1123  								Expect(executeErr).NotTo(HaveOccurred())
  1124  
  1125  								Expect(routeActor.FindOrCreateRouteCallCount()).To(Equal(1))
  1126  								host, _, _, _, _ := routeActor.FindOrCreateRouteArgsForCall(0)
  1127  								Expect(host).To(Equal("app-name-random-host"))
  1128  							})
  1129  						})
  1130  					})
  1131  
  1132  					Context("for tcp routes", func() {
  1133  						var expectedDomain models.DomainFields
  1134  
  1135  						BeforeEach(func() {
  1136  							deps.UI = uiWithContents
  1137  
  1138  							expectedDomain = models.DomainFields{
  1139  								GUID: "some-guid",
  1140  								Name: "some-name",
  1141  								OwningOrganizationGUID: "some-organization-guid",
  1142  								RouterGroupGUID:        "some-router-group-guid",
  1143  								RouterGroupType:        "tcp",
  1144  								Shared:                 true,
  1145  							}
  1146  
  1147  							domainRepo.FindByNameInOrgReturns(
  1148  								expectedDomain,
  1149  								nil,
  1150  							)
  1151  
  1152  							route := models.Route{
  1153  								Domain: expectedDomain,
  1154  								Port:   7777,
  1155  							}
  1156  							routeRepo.CreateReturns(route, nil)
  1157  						})
  1158  
  1159  						Context("when random-route passed as a flag", func() {
  1160  							BeforeEach(func() {
  1161  								args = []string{"--random-route", "app-name"}
  1162  							})
  1163  
  1164  							It("provides a random port and hostname", func() {
  1165  								Expect(executeErr).NotTo(HaveOccurred())
  1166  
  1167  								Expect(routeActor.FindOrCreateRouteCallCount()).To(Equal(1))
  1168  								_, _, _, _, randomPort := routeActor.FindOrCreateRouteArgsForCall(0)
  1169  								Expect(randomPort).To(BeTrue())
  1170  							})
  1171  						})
  1172  
  1173  						Context("when random-route set in the manifest", func() {
  1174  							BeforeEach(func() {
  1175  								manifestApp.Set("random-route", true)
  1176  								args = []string{"app-name"}
  1177  							})
  1178  
  1179  							It("provides a random port and hostname when set in the manifest", func() {
  1180  								Expect(executeErr).NotTo(HaveOccurred())
  1181  
  1182  								Expect(routeActor.FindOrCreateRouteCallCount()).To(Equal(1))
  1183  								_, _, _, _, randomPort := routeActor.FindOrCreateRouteArgsForCall(0)
  1184  								Expect(randomPort).To(BeTrue())
  1185  							})
  1186  						})
  1187  					})
  1188  				})
  1189  
  1190  				Context("when path to an app is set", func() {
  1191  					var expectedLocalFiles []models.AppFileFields
  1192  
  1193  					BeforeEach(func() {
  1194  						expectedLocalFiles = []models.AppFileFields{
  1195  							{
  1196  								Path: "the-path",
  1197  							},
  1198  							{
  1199  								Path: "the-other-path",
  1200  							},
  1201  						}
  1202  						appfiles.AppFilesInDirReturns(expectedLocalFiles, nil)
  1203  						args = []string{"-p", "../some/path-to/an-app/file.zip", "app-with-path"}
  1204  					})
  1205  
  1206  					It("includes the app files in dir", func() {
  1207  						Expect(executeErr).NotTo(HaveOccurred())
  1208  
  1209  						actualLocalFiles, _, _, _ := actor.GatherFilesArgsForCall(0)
  1210  						Expect(actualLocalFiles).To(Equal(expectedLocalFiles))
  1211  					})
  1212  				})
  1213  
  1214  				Context("when there are no app files to process", func() {
  1215  					BeforeEach(func() {
  1216  						deps.UI = uiWithContents
  1217  						appfiles.AppFilesInDirReturns([]models.AppFileFields{}, nil)
  1218  						args = []string{"-p", "../some/path-to/an-app/file.zip", "app-with-path"}
  1219  					})
  1220  
  1221  					It("errors", func() {
  1222  						Expect(executeErr).To(HaveOccurred())
  1223  						Expect(executeErr.Error()).To(ContainSubstring("No app files found in '../some/path-to/an-app/file.zip'"))
  1224  					})
  1225  				})
  1226  
  1227  				Context("when there is an error getting app files", func() {
  1228  					BeforeEach(func() {
  1229  						deps.UI = uiWithContents
  1230  						appfiles.AppFilesInDirReturns([]models.AppFileFields{}, errors.New("some error"))
  1231  						args = []string{"-p", "../some/path-to/an-app/file.zip", "app-with-path"}
  1232  					})
  1233  
  1234  					It("prints a message", func() {
  1235  						Expect(executeErr).To(HaveOccurred())
  1236  						Expect(executeErr.Error()).To(ContainSubstring("Error processing app files in '../some/path-to/an-app/file.zip': some error"))
  1237  					})
  1238  				})
  1239  
  1240  				Context("when an app path is specified with the -p flag", func() {
  1241  					BeforeEach(func() {
  1242  						args = []string{"-p", "../some/path-to/an-app/file.zip", "app-with-path"}
  1243  					})
  1244  
  1245  					It("pushes the contents of the app directory or zip file specified", func() {
  1246  						Expect(executeErr).NotTo(HaveOccurred())
  1247  
  1248  						_, appDir, _, _ := actor.GatherFilesArgsForCall(0)
  1249  						Expect(appDir).To(Equal("../some/path-to/an-app/file.zip"))
  1250  					})
  1251  				})
  1252  
  1253  				Context("when no flags are specified", func() {
  1254  					BeforeEach(func() {
  1255  						m := &manifest.Manifest{
  1256  							Path: "manifest.yml",
  1257  							Data: generic.NewMap(map[interface{}]interface{}{
  1258  								"applications": []interface{}{
  1259  									generic.NewMap(map[interface{}]interface{}{
  1260  										"name":      "manifest-app-name",
  1261  										"memory":    "128MB",
  1262  										"instances": 1,
  1263  										"host":      "manifest-host",
  1264  										"stack":     "custom-stack",
  1265  										"timeout":   360,
  1266  										"buildpack": "some-buildpack",
  1267  										"command":   `JAVA_HOME=$PWD/.openjdk JAVA_OPTS="-Xss995K" ./bin/start.sh run`,
  1268  										"env": generic.NewMap(map[interface{}]interface{}{
  1269  											"FOO":  "baz",
  1270  											"PATH": "/u/apps/my-app/bin",
  1271  										}),
  1272  									}),
  1273  								},
  1274  							}),
  1275  						}
  1276  						manifestRepo.ReadManifestReturns(m, nil)
  1277  						args = []string{"app-with-default-path"}
  1278  					})
  1279  
  1280  					It("pushes the contents of the current working directory by default", func() {
  1281  						Expect(executeErr).NotTo(HaveOccurred())
  1282  
  1283  						dir, _ := os.Getwd()
  1284  						_, appDir, _, _ := actor.GatherFilesArgsForCall(0)
  1285  						Expect(appDir).To(Equal(dir))
  1286  					})
  1287  				})
  1288  
  1289  				Context("when given a bad manifest", func() {
  1290  					BeforeEach(func() {
  1291  						manifestRepo.ReadManifestReturns(manifest.NewEmptyManifest(), errors.New("read manifest error"))
  1292  						args = []string{"-f", "bad/manifest/path"}
  1293  					})
  1294  
  1295  					It("errors", func() {
  1296  						Expect(executeErr).To(HaveOccurred())
  1297  						Expect(executeErr.Error()).To(ContainSubstring("read manifest error"))
  1298  					})
  1299  				})
  1300  
  1301  				Context("when the current directory does not contain a manifest", func() {
  1302  					BeforeEach(func() {
  1303  						deps.UI = uiWithContents
  1304  						manifestRepo.ReadManifestReturns(manifest.NewEmptyManifest(), syscall.ENOENT)
  1305  						args = []string{"--no-route", "app-name"}
  1306  					})
  1307  
  1308  					It("does not fail", func() {
  1309  						Expect(executeErr).NotTo(HaveOccurred())
  1310  						fullOutput := terminal.Decolorize(string(output.Contents()))
  1311  						Expect(fullOutput).To(ContainSubstring("Creating app app-name in org my-org / space my-space as my-user...\nOK"))
  1312  						Expect(fullOutput).To(ContainSubstring("Uploading app-name...\nOK"))
  1313  					})
  1314  				})
  1315  
  1316  				Context("when the current directory does contain a manifest", func() {
  1317  					BeforeEach(func() {
  1318  						deps.UI = uiWithContents
  1319  						m := &manifest.Manifest{
  1320  							Path: "manifest.yml",
  1321  							Data: generic.NewMap(map[interface{}]interface{}{
  1322  								"applications": []interface{}{
  1323  									generic.NewMap(map[interface{}]interface{}{
  1324  										"name":      "manifest-app-name",
  1325  										"memory":    "128MB",
  1326  										"instances": 1,
  1327  										"host":      "manifest-host",
  1328  										"domain":    "manifest-example.com",
  1329  										"stack":     "custom-stack",
  1330  										"timeout":   360,
  1331  										"buildpack": "some-buildpack",
  1332  										"command":   `JAVA_HOME=$PWD/.openjdk JAVA_OPTS="-Xss995K" ./bin/start.sh run`,
  1333  										"path":      filepath.Clean("some/path/from/manifest"),
  1334  										"env": generic.NewMap(map[interface{}]interface{}{
  1335  											"FOO":  "baz",
  1336  											"PATH": "/u/apps/my-app/bin",
  1337  										}),
  1338  									}),
  1339  								},
  1340  							}),
  1341  						}
  1342  						manifestRepo.ReadManifestReturns(m, nil)
  1343  						args = []string{"-p", "some/relative/path"}
  1344  					})
  1345  
  1346  					It("uses the manifest in the current directory by default", func() {
  1347  						Expect(executeErr).NotTo(HaveOccurred())
  1348  
  1349  						Expect(terminal.Decolorize(string(output.Contents()))).To(ContainSubstring("Using manifest file manifest.yml"))
  1350  
  1351  						cwd, _ := os.Getwd()
  1352  						Expect(manifestRepo.ReadManifestArgsForCall(0)).To(Equal(cwd))
  1353  					})
  1354  				})
  1355  
  1356  				Context("when the 'no-manifest'flag is passed", func() {
  1357  					BeforeEach(func() {
  1358  						args = []string{"--no-route", "--no-manifest", "app-name"}
  1359  					})
  1360  
  1361  					It("does not use a manifest", func() {
  1362  						Expect(executeErr).NotTo(HaveOccurred())
  1363  
  1364  						fullOutput := terminal.Decolorize(string(output.Contents()))
  1365  						Expect(fullOutput).NotTo(ContainSubstring("FAILED"))
  1366  						Expect(fullOutput).NotTo(ContainSubstring("hacker-manifesto"))
  1367  
  1368  						Expect(manifestRepo.ReadManifestCallCount()).To(BeZero())
  1369  						params := appRepo.CreateArgsForCall(0)
  1370  						Expect(*params.Name).To(Equal("app-name"))
  1371  					})
  1372  				})
  1373  
  1374  				Context("when the manifest has errors", func() {
  1375  					BeforeEach(func() {
  1376  						manifestRepo.ReadManifestReturns(
  1377  							&manifest.Manifest{
  1378  								Path: "/some-path/",
  1379  							},
  1380  							errors.New("buildpack should not be null"),
  1381  						)
  1382  
  1383  						args = []string{}
  1384  					})
  1385  
  1386  					It("fails when parsing the manifest has errors", func() {
  1387  						Expect(executeErr).To(HaveOccurred())
  1388  						Expect(executeErr.Error()).To(ContainSubstring("Error reading manifest file:\nbuildpack should not be null"))
  1389  					})
  1390  				})
  1391  
  1392  				Context("when the no-route option is set", func() {
  1393  					Context("when provided the --no-route-flag", func() {
  1394  						BeforeEach(func() {
  1395  							domainRepo.FindByNameInOrgReturns(models.DomainFields{
  1396  								Name: "bar.cf-app.com",
  1397  								GUID: "bar-domain-guid",
  1398  							}, nil)
  1399  
  1400  							args = []string{"--no-route", "app-name"}
  1401  						})
  1402  
  1403  						It("does not create a route", func() {
  1404  							Expect(executeErr).NotTo(HaveOccurred())
  1405  							params := appRepo.CreateArgsForCall(0)
  1406  							Expect(*params.Name).To(Equal("app-name"))
  1407  							Expect(routeRepo.CreateCallCount()).To(BeZero())
  1408  						})
  1409  					})
  1410  
  1411  					Context("when no-route is set in the manifest", func() {
  1412  						BeforeEach(func() {
  1413  							deps.UI = uiWithContents
  1414  							workerManifest := &manifest.Manifest{
  1415  								Path: "manifest.yml",
  1416  								Data: generic.NewMap(map[interface{}]interface{}{
  1417  									"applications": []interface{}{
  1418  										generic.NewMap(map[interface{}]interface{}{
  1419  											"name":      "manifest-app-name",
  1420  											"memory":    "128MB",
  1421  											"instances": 1,
  1422  											"host":      "manifest-host",
  1423  											"domain":    "manifest-example.com",
  1424  											"stack":     "custom-stack",
  1425  											"timeout":   360,
  1426  											"buildpack": "some-buildpack",
  1427  											"command":   `JAVA_HOME=$PWD/.openjdk JAVA_OPTS="-Xss995K" ./bin/start.sh run`,
  1428  											"path":      filepath.Clean("some/path/from/manifest"),
  1429  											"env": generic.NewMap(map[interface{}]interface{}{
  1430  												"FOO":  "baz",
  1431  												"PATH": "/u/apps/my-app/bin",
  1432  											}),
  1433  										}),
  1434  									},
  1435  								}),
  1436  							}
  1437  							workerManifest.Data.Get("applications").([]interface{})[0].(generic.Map).Set("no-route", true)
  1438  							manifestRepo.ReadManifestReturns(workerManifest, nil)
  1439  
  1440  							args = []string{"app-name"}
  1441  						})
  1442  
  1443  						It("Does not create a route", func() {
  1444  							Expect(executeErr).NotTo(HaveOccurred())
  1445  							Expect(terminal.Decolorize(string(output.Contents()))).To(ContainSubstring("App app-name is a worker, skipping route creation"))
  1446  							Expect(routeRepo.BindCallCount()).To(BeZero())
  1447  						})
  1448  					})
  1449  				})
  1450  
  1451  				Context("when provided the --no-hostname flag", func() {
  1452  					BeforeEach(func() {
  1453  						domainRepo.ListDomainsForOrgStub = func(orgGUID string, cb func(models.DomainFields) bool) error {
  1454  							cb(models.DomainFields{
  1455  								Name:   "bar.cf-app.com",
  1456  								GUID:   "bar-domain-guid",
  1457  								Shared: true,
  1458  							})
  1459  
  1460  							return nil
  1461  						}
  1462  						routeRepo.FindReturns(models.Route{}, errors.NewModelNotFoundError("Org", "uh oh"))
  1463  
  1464  						args = []string{"--no-hostname", "app-name"}
  1465  					})
  1466  
  1467  					It("maps the root domain route to the app", func() {
  1468  						Expect(executeErr).NotTo(HaveOccurred())
  1469  
  1470  						Expect(routeActor.FindOrCreateRouteCallCount()).To(Equal(1))
  1471  						_, domain, _, _, _ := routeActor.FindOrCreateRouteArgsForCall(0)
  1472  						Expect(domain.GUID).To(Equal("bar-domain-guid"))
  1473  					})
  1474  
  1475  					Context("when using 'routes' in the manifest", func() {
  1476  						BeforeEach(func() {
  1477  							m := &manifest.Manifest{
  1478  								Data: generic.NewMap(map[interface{}]interface{}{
  1479  									"applications": []interface{}{
  1480  										generic.NewMap(map[interface{}]interface{}{
  1481  											"name": "app1",
  1482  											"routes": []interface{}{
  1483  												map[interface{}]interface{}{"route": "app2route1.example.com"},
  1484  											},
  1485  										}),
  1486  									},
  1487  								}),
  1488  							}
  1489  							manifestRepo.ReadManifestReturns(m, nil)
  1490  						})
  1491  
  1492  						It("returns an error", func() {
  1493  							Expect(executeErr).To(HaveOccurred())
  1494  							Expect(executeErr).To(MatchError("Option '--no-hostname' cannot be used with an app manifest containing the 'routes' attribute"))
  1495  						})
  1496  					})
  1497  				})
  1498  
  1499  				Context("with an invalid memory limit", func() {
  1500  					BeforeEach(func() {
  1501  						args = []string{"-m", "abcM", "app-name"}
  1502  					})
  1503  
  1504  					It("fails", func() {
  1505  						Expect(executeErr).To(HaveOccurred())
  1506  						Expect(executeErr.Error()).To(ContainSubstring("Invalid memory limit: abcM"))
  1507  					})
  1508  				})
  1509  
  1510  				Context("when a manifest has many apps", func() {
  1511  					BeforeEach(func() {
  1512  						deps.UI = uiWithContents
  1513  						m := &manifest.Manifest{
  1514  							Data: generic.NewMap(map[interface{}]interface{}{
  1515  								"applications": []interface{}{
  1516  									generic.NewMap(map[interface{}]interface{}{
  1517  										"name":     "app1",
  1518  										"services": []interface{}{"app1-service", "global-service"},
  1519  										"env": generic.NewMap(map[interface{}]interface{}{
  1520  											"SOMETHING": "definitely-something",
  1521  										}),
  1522  									}),
  1523  									generic.NewMap(map[interface{}]interface{}{
  1524  										"name":     "app2",
  1525  										"services": []interface{}{"app2-service", "global-service"},
  1526  										"env": generic.NewMap(map[interface{}]interface{}{
  1527  											"SOMETHING": "nothing",
  1528  										}),
  1529  									}),
  1530  								},
  1531  							}),
  1532  						}
  1533  						manifestRepo.ReadManifestReturns(m, nil)
  1534  						args = []string{}
  1535  					})
  1536  
  1537  					It("pushes each app", func() {
  1538  						Expect(executeErr).NotTo(HaveOccurred())
  1539  
  1540  						totalOutput := terminal.Decolorize(string(output.Contents()))
  1541  						Expect(totalOutput).To(ContainSubstring("Creating app app1"))
  1542  						Expect(totalOutput).To(ContainSubstring("Creating app app2"))
  1543  						Expect(appRepo.CreateCallCount()).To(Equal(2))
  1544  
  1545  						firstApp := appRepo.CreateArgsForCall(0)
  1546  						secondApp := appRepo.CreateArgsForCall(1)
  1547  						Expect(*firstApp.Name).To(Equal("app1"))
  1548  						Expect(*secondApp.Name).To(Equal("app2"))
  1549  
  1550  						envVars := *firstApp.EnvironmentVars
  1551  						Expect(envVars["SOMETHING"]).To(Equal("definitely-something"))
  1552  
  1553  						envVars = *secondApp.EnvironmentVars
  1554  						Expect(envVars["SOMETHING"]).To(Equal("nothing"))
  1555  					})
  1556  
  1557  					Context("when a single app is given as an arg", func() {
  1558  						BeforeEach(func() {
  1559  							args = []string{"app2"}
  1560  						})
  1561  
  1562  						It("pushes that single app", func() {
  1563  							Expect(executeErr).NotTo(HaveOccurred())
  1564  
  1565  							totalOutput := terminal.Decolorize(string(output.Contents()))
  1566  							Expect(totalOutput).To(ContainSubstring("Creating app app2"))
  1567  							Expect(totalOutput).ToNot(ContainSubstring("Creating app app1"))
  1568  							Expect(appRepo.CreateCallCount()).To(Equal(1))
  1569  							params := appRepo.CreateArgsForCall(0)
  1570  							Expect(*params.Name).To(Equal("app2"))
  1571  						})
  1572  					})
  1573  
  1574  					Context("when the given app is not in the manifest", func() {
  1575  						BeforeEach(func() {
  1576  							args = []string{"non-existant-app"}
  1577  						})
  1578  
  1579  						It("fails", func() {
  1580  							Expect(executeErr).To(HaveOccurred())
  1581  							Expect(appRepo.CreateCallCount()).To(BeZero())
  1582  						})
  1583  					})
  1584  				})
  1585  			})
  1586  		})
  1587  
  1588  		Context("re-pushing an existing app", func() {
  1589  			var existingApp models.Application
  1590  
  1591  			BeforeEach(func() {
  1592  				deps.UI = uiWithContents
  1593  				existingApp = models.Application{
  1594  					ApplicationFields: models.ApplicationFields{
  1595  						Name:    "existing-app",
  1596  						GUID:    "existing-app-guid",
  1597  						Command: "unicorn -c config/unicorn.rb -D",
  1598  						EnvironmentVars: map[string]interface{}{
  1599  							"crazy": "pants",
  1600  							"FOO":   "NotYoBaz",
  1601  							"foo":   "manchu",
  1602  						},
  1603  					},
  1604  				}
  1605  				manifestRepo.ReadManifestReturns(manifest.NewEmptyManifest(), nil)
  1606  				appRepo.ReadReturns(existingApp, nil)
  1607  				appRepo.UpdateReturns(existingApp, nil)
  1608  				args = []string{"existing-app"}
  1609  			})
  1610  
  1611  			It("stops the app, achieving a full-downtime deploy!", func() {
  1612  				Expect(executeErr).NotTo(HaveOccurred())
  1613  
  1614  				app, orgName, spaceName := stopper.ApplicationStopArgsForCall(0)
  1615  				Expect(app.GUID).To(Equal(existingApp.GUID))
  1616  				Expect(app.Name).To(Equal("existing-app"))
  1617  				Expect(orgName).To(Equal(configRepo.OrganizationFields().Name))
  1618  				Expect(spaceName).To(Equal(configRepo.SpaceFields().Name))
  1619  
  1620  				Expect(actor.UploadAppCallCount()).To(Equal(1))
  1621  				appGUID, _, _ := actor.UploadAppArgsForCall(0)
  1622  				Expect(appGUID).To(Equal(existingApp.GUID))
  1623  			})
  1624  
  1625  			It("re-uploads the app", func() {
  1626  				Expect(executeErr).NotTo(HaveOccurred())
  1627  
  1628  				totalOutputs := terminal.Decolorize(string(output.Contents()))
  1629  				Expect(totalOutputs).To(ContainSubstring("Uploading existing-app...\nOK"))
  1630  			})
  1631  
  1632  			Context("when the -b flag is provided as 'default'", func() {
  1633  				BeforeEach(func() {
  1634  					args = []string{"-b", "default", "existing-app"}
  1635  				})
  1636  
  1637  				It("resets the app's buildpack", func() {
  1638  					Expect(executeErr).NotTo(HaveOccurred())
  1639  
  1640  					Expect(appRepo.UpdateCallCount()).To(Equal(1))
  1641  					_, params := appRepo.UpdateArgsForCall(0)
  1642  					Expect(*params.BuildpackURL).To(Equal(""))
  1643  				})
  1644  			})
  1645  
  1646  			Context("when the -c flag is provided as 'default'", func() {
  1647  				BeforeEach(func() {
  1648  					args = []string{"-c", "default", "existing-app"}
  1649  				})
  1650  
  1651  				It("resets the app's command", func() {
  1652  					Expect(executeErr).NotTo(HaveOccurred())
  1653  					_, params := appRepo.UpdateArgsForCall(0)
  1654  					Expect(*params.Command).To(Equal(""))
  1655  				})
  1656  			})
  1657  
  1658  			Context("when the -b flag is provided as 'null'", func() {
  1659  				BeforeEach(func() {
  1660  					args = []string{"-b", "null", "existing-app"}
  1661  				})
  1662  
  1663  				It("resets the app's buildpack", func() {
  1664  					Expect(executeErr).NotTo(HaveOccurred())
  1665  					_, params := appRepo.UpdateArgsForCall(0)
  1666  					Expect(*params.BuildpackURL).To(Equal(""))
  1667  				})
  1668  			})
  1669  
  1670  			Context("when the -c flag is provided as 'null'", func() {
  1671  				BeforeEach(func() {
  1672  					args = []string{"-c", "null", "existing-app"}
  1673  				})
  1674  
  1675  				It("resets the app's command", func() {
  1676  					Expect(executeErr).NotTo(HaveOccurred())
  1677  					_, params := appRepo.UpdateArgsForCall(0)
  1678  					Expect(*params.Command).To(Equal(""))
  1679  				})
  1680  			})
  1681  
  1682  			Context("when the manifest provided env variables", func() {
  1683  				BeforeEach(func() {
  1684  					m := &manifest.Manifest{
  1685  						Path: "manifest.yml",
  1686  						Data: generic.NewMap(map[interface{}]interface{}{
  1687  							"applications": []interface{}{
  1688  								generic.NewMap(map[interface{}]interface{}{
  1689  									"name":      "manifest-app-name",
  1690  									"memory":    "128MB",
  1691  									"instances": 1,
  1692  									"host":      "manifest-host",
  1693  									"domain":    "manifest-example.com",
  1694  									"stack":     "custom-stack",
  1695  									"timeout":   360,
  1696  									"buildpack": "some-buildpack",
  1697  									"command":   `JAVA_HOME=$PWD/.openjdk JAVA_OPTS="-Xss995K" ./bin/start.sh run`,
  1698  									"path":      filepath.Clean("some/path/from/manifest"),
  1699  									"env": generic.NewMap(map[interface{}]interface{}{
  1700  										"FOO":  "baz",
  1701  										"PATH": "/u/apps/my-app/bin",
  1702  									}),
  1703  								}),
  1704  							},
  1705  						}),
  1706  					}
  1707  					manifestRepo.ReadManifestReturns(m, nil)
  1708  
  1709  					args = []string{"existing-app"}
  1710  				})
  1711  
  1712  				It("merges env vars from the manifest with those from the server", func() {
  1713  					Expect(executeErr).NotTo(HaveOccurred())
  1714  
  1715  					_, params := appRepo.UpdateArgsForCall(0)
  1716  					updatedAppEnvVars := *params.EnvironmentVars
  1717  					Expect(updatedAppEnvVars["crazy"]).To(Equal("pants"))
  1718  					Expect(updatedAppEnvVars["FOO"]).To(Equal("baz"))
  1719  					Expect(updatedAppEnvVars["foo"]).To(Equal("manchu"))
  1720  					Expect(updatedAppEnvVars["PATH"]).To(Equal("/u/apps/my-app/bin"))
  1721  				})
  1722  			})
  1723  
  1724  			Context("when the app is already stopped", func() {
  1725  				BeforeEach(func() {
  1726  					existingApp.State = "stopped"
  1727  					appRepo.ReadReturns(existingApp, nil)
  1728  					appRepo.UpdateReturns(existingApp, nil)
  1729  					args = []string{"existing-app"}
  1730  				})
  1731  
  1732  				It("does not stop the app", func() {
  1733  					Expect(executeErr).NotTo(HaveOccurred())
  1734  					Expect(stopper.ApplicationStopCallCount()).To(Equal(0))
  1735  				})
  1736  			})
  1737  
  1738  			Context("when the application is pushed with updated parameters", func() {
  1739  				BeforeEach(func() {
  1740  					existingRoute := models.RouteSummary{
  1741  						Host: "existing-app",
  1742  					}
  1743  					existingApp.Routes = []models.RouteSummary{existingRoute}
  1744  					appRepo.ReadReturns(existingApp, nil)
  1745  					appRepo.UpdateReturns(existingApp, nil)
  1746  
  1747  					stackRepo.FindByNameReturns(models.Stack{
  1748  						Name: "differentStack",
  1749  						GUID: "differentStack-guid",
  1750  					}, nil)
  1751  
  1752  					args = []string{
  1753  						"-c", "different start command",
  1754  						"-i", "10",
  1755  						"-m", "1G",
  1756  						"-b", "https://github.com/heroku/heroku-buildpack-different.git",
  1757  						"-s", "differentStack",
  1758  						"existing-app",
  1759  					}
  1760  				})
  1761  
  1762  				It("updates the app", func() {
  1763  					Expect(executeErr).NotTo(HaveOccurred())
  1764  
  1765  					appGUID, params := appRepo.UpdateArgsForCall(0)
  1766  					Expect(appGUID).To(Equal(existingApp.GUID))
  1767  					Expect(*params.Command).To(Equal("different start command"))
  1768  					Expect(*params.InstanceCount).To(Equal(10))
  1769  					Expect(*params.Memory).To(Equal(int64(1024)))
  1770  					Expect(*params.BuildpackURL).To(Equal("https://github.com/heroku/heroku-buildpack-different.git"))
  1771  					Expect(*params.StackGUID).To(Equal("differentStack-guid"))
  1772  				})
  1773  			})
  1774  
  1775  			Context("when the app has a route bound", func() {
  1776  				BeforeEach(func() {
  1777  					domain := models.DomainFields{
  1778  						Name:   "example.com",
  1779  						GUID:   "domain-guid",
  1780  						Shared: true,
  1781  					}
  1782  
  1783  					existingApp.Routes = []models.RouteSummary{{
  1784  						GUID:   "existing-route-guid",
  1785  						Host:   "existing-app",
  1786  						Domain: domain,
  1787  					}}
  1788  
  1789  					appRepo.ReadReturns(existingApp, nil)
  1790  					appRepo.UpdateReturns(existingApp, nil)
  1791  				})
  1792  
  1793  				Context("and no route-related flags are given", func() {
  1794  					Context("and there is no route in the manifest", func() {
  1795  						It("does not add a route to the app", func() {
  1796  							Expect(executeErr).NotTo(HaveOccurred())
  1797  
  1798  							appGUID, _, _ := actor.UploadAppArgsForCall(0)
  1799  							Expect(appGUID).To(Equal("existing-app-guid"))
  1800  							Expect(domainRepo.FindByNameInOrgCallCount()).To(BeZero())
  1801  							Expect(routeRepo.FindCallCount()).To(BeZero())
  1802  							Expect(routeRepo.CreateCallCount()).To(BeZero())
  1803  						})
  1804  					})
  1805  				})
  1806  
  1807  				Context("when --no-route flag is given", func() {
  1808  					BeforeEach(func() {
  1809  						args = []string{"--no-route", "existing-app"}
  1810  					})
  1811  
  1812  					It("removes existing routes that the app is bound to", func() {
  1813  						Expect(executeErr).NotTo(HaveOccurred())
  1814  
  1815  						appGUID, _, _ := actor.UploadAppArgsForCall(0)
  1816  						Expect(appGUID).To(Equal("existing-app-guid"))
  1817  
  1818  						Expect(routeActor.UnbindAllCallCount()).To(Equal(1))
  1819  						app := routeActor.UnbindAllArgsForCall(0)
  1820  						Expect(app.GUID).To(Equal(appGUID))
  1821  
  1822  						Expect(routeActor.FindOrCreateRouteCallCount()).To(BeZero())
  1823  					})
  1824  				})
  1825  
  1826  				Context("when the --no-hostname flag is given", func() {
  1827  					BeforeEach(func() {
  1828  						routeRepo.FindReturns(models.Route{}, errors.NewModelNotFoundError("Org", "existing-app.example.com"))
  1829  						args = []string{"--no-hostname", "existing-app"}
  1830  					})
  1831  
  1832  					It("binds the root domain route to an app with a pre-existing route", func() {
  1833  						Expect(executeErr).NotTo(HaveOccurred())
  1834  
  1835  						Expect(routeActor.FindOrCreateRouteCallCount()).To(Equal(1))
  1836  						hostname, _, _, _, _ := routeActor.FindOrCreateRouteArgsForCall(0)
  1837  						Expect(hostname).To(BeEmpty())
  1838  					})
  1839  				})
  1840  			})
  1841  
  1842  			Context("service instances", func() {
  1843  				BeforeEach(func() {
  1844  					appRepo.CreateStub = func(params models.AppParams) (models.Application, error) {
  1845  						a := models.Application{}
  1846  						a.Name = *params.Name
  1847  						a.GUID = *params.Name + "-guid"
  1848  
  1849  						return a, nil
  1850  					}
  1851  
  1852  					serviceRepo.FindInstanceByNameStub = func(name string) (models.ServiceInstance, error) {
  1853  						return models.ServiceInstance{
  1854  							ServiceInstanceFields: models.ServiceInstanceFields{Name: name},
  1855  						}, nil
  1856  					}
  1857  
  1858  					m := &manifest.Manifest{
  1859  						Data: generic.NewMap(map[interface{}]interface{}{
  1860  							"applications": []interface{}{
  1861  								generic.NewMap(map[interface{}]interface{}{
  1862  									"name":     "app1",
  1863  									"services": []interface{}{"app1-service", "global-service"},
  1864  									"env": generic.NewMap(map[interface{}]interface{}{
  1865  										"SOMETHING": "definitely-something",
  1866  									}),
  1867  								}),
  1868  								generic.NewMap(map[interface{}]interface{}{
  1869  									"name":     "app2",
  1870  									"services": []interface{}{"app2-service", "global-service"},
  1871  									"env": generic.NewMap(map[interface{}]interface{}{
  1872  										"SOMETHING": "nothing",
  1873  									}),
  1874  								}),
  1875  							},
  1876  						}),
  1877  					}
  1878  					manifestRepo.ReadManifestReturns(m, nil)
  1879  
  1880  					args = []string{}
  1881  				})
  1882  
  1883  				Context("when the service is not bound", func() {
  1884  					BeforeEach(func() {
  1885  						appRepo.ReadReturns(models.Application{}, errors.NewModelNotFoundError("App", "the-app"))
  1886  					})
  1887  
  1888  					It("binds service instances to the app", func() {
  1889  						Expect(executeErr).NotTo(HaveOccurred())
  1890  
  1891  						Expect(len(serviceBinder.AppsToBind)).To(Equal(4))
  1892  						Expect(serviceBinder.AppsToBind[0].Name).To(Equal("app1"))
  1893  						Expect(serviceBinder.AppsToBind[1].Name).To(Equal("app1"))
  1894  						Expect(serviceBinder.InstancesToBindTo[0].Name).To(Equal("app1-service"))
  1895  						Expect(serviceBinder.InstancesToBindTo[1].Name).To(Equal("global-service"))
  1896  
  1897  						Expect(serviceBinder.AppsToBind[2].Name).To(Equal("app2"))
  1898  						Expect(serviceBinder.AppsToBind[3].Name).To(Equal("app2"))
  1899  						Expect(serviceBinder.InstancesToBindTo[2].Name).To(Equal("app2-service"))
  1900  						Expect(serviceBinder.InstancesToBindTo[3].Name).To(Equal("global-service"))
  1901  
  1902  						totalOutputs := terminal.Decolorize(string(output.Contents()))
  1903  						Expect(totalOutputs).To(ContainSubstring("Creating app app1 in org my-org / space my-space as my-user...\nOK"))
  1904  						Expect(totalOutputs).To(ContainSubstring("Binding service app1-service to app app1 in org my-org / space my-space as my-user...\nOK"))
  1905  						Expect(totalOutputs).To(ContainSubstring("Binding service global-service to app app1 in org my-org / space my-space as my-user...\nOK"))
  1906  						Expect(totalOutputs).To(ContainSubstring("Creating app app2 in org my-org / space my-space as my-user...\nOK"))
  1907  						Expect(totalOutputs).To(ContainSubstring("Binding service app2-service to app app2 in org my-org / space my-space as my-user...\nOK"))
  1908  						Expect(totalOutputs).To(ContainSubstring("Binding service global-service to app app2 in org my-org / space my-space as my-user...\nOK"))
  1909  					})
  1910  				})
  1911  
  1912  				Context("when the app is already bound to the service", func() {
  1913  					BeforeEach(func() {
  1914  						appRepo.ReadReturns(models.Application{
  1915  							ApplicationFields: models.ApplicationFields{Name: "app-name"},
  1916  						}, nil)
  1917  						serviceBinder.BindApplicationReturns.Error = errors.NewHTTPError(500, errors.ServiceBindingAppServiceTaken, "it don't work")
  1918  					})
  1919  
  1920  					It("gracefully continues", func() {
  1921  						Expect(executeErr).NotTo(HaveOccurred())
  1922  						Expect(len(serviceBinder.AppsToBind)).To(Equal(4))
  1923  					})
  1924  				})
  1925  
  1926  				Context("when the service instance can't be found", func() {
  1927  					BeforeEach(func() {
  1928  						serviceRepo.FindInstanceByNameReturns(models.ServiceInstance{}, errors.New("Error finding instance"))
  1929  					})
  1930  
  1931  					It("fails with an error", func() {
  1932  						Expect(executeErr).To(HaveOccurred())
  1933  						Expect(executeErr.Error()).To(ContainSubstring("Could not find service app1-service to bind to existing-app"))
  1934  					})
  1935  				})
  1936  			})
  1937  
  1938  			Context("checking for bad flags", func() {
  1939  				BeforeEach(func() {
  1940  					appRepo.ReadReturns(models.Application{}, errors.NewModelNotFoundError("App", "the-app"))
  1941  					args = []string{"-t", "FooeyTimeout", "app-name"}
  1942  				})
  1943  
  1944  				It("fails when a non-numeric start timeout is given", func() {
  1945  					Expect(executeErr).To(HaveOccurred())
  1946  					Expect(executeErr.Error()).To(ContainSubstring("Invalid timeout param: FooeyTimeout"))
  1947  				})
  1948  			})
  1949  
  1950  			Context("displaying information about files being uploaded", func() {
  1951  				BeforeEach(func() {
  1952  					appfiles.CountFilesReturns(11)
  1953  					zipper.ZipReturns(nil)
  1954  					zipper.GetZipSizeReturns(6100000, nil)
  1955  					actor.GatherFilesReturns([]resources.AppFileResource{{Path: "path/to/app"}, {Path: "bar"}}, true, nil)
  1956  					args = []string{"appName"}
  1957  				})
  1958  
  1959  				It("displays the information", func() {
  1960  					Expect(executeErr).NotTo(HaveOccurred())
  1961  
  1962  					curDir, err := os.Getwd()
  1963  					Expect(err).NotTo(HaveOccurred())
  1964  
  1965  					totalOutputs := terminal.Decolorize(string(output.Contents()))
  1966  					Expect(totalOutputs).To(ContainSubstring("Uploading app files from: " + curDir))
  1967  					Expect(totalOutputs).To(ContainSubstring("Uploading 5.8M, 11 files\nOK"))
  1968  				})
  1969  			})
  1970  
  1971  			Context("when the app can't be uploaded", func() {
  1972  				BeforeEach(func() {
  1973  					actor.UploadAppReturns(errors.New("Boom!"))
  1974  					args = []string{"app"}
  1975  				})
  1976  
  1977  				It("fails when the app can't be uploaded", func() {
  1978  					Expect(executeErr).To(HaveOccurred())
  1979  					Expect(executeErr.Error()).To(ContainSubstring("Error uploading application"))
  1980  				})
  1981  			})
  1982  
  1983  			Context("when no name and no manifest is given", func() {
  1984  				BeforeEach(func() {
  1985  					manifestRepo.ReadManifestReturns(manifest.NewEmptyManifest(), errors.New("No such manifest"))
  1986  					args = []string{}
  1987  				})
  1988  
  1989  				It("fails", func() {
  1990  					Expect(executeErr).To(HaveOccurred())
  1991  					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."))
  1992  				})
  1993  			})
  1994  		})
  1995  
  1996  		Context("when routes are specified in the manifest", func() {
  1997  			Context("and the manifest has more than one app", func() {
  1998  				BeforeEach(func() {
  1999  					m := &manifest.Manifest{
  2000  						Path: "manifest.yml",
  2001  						Data: generic.NewMap(map[interface{}]interface{}{
  2002  							"applications": []interface{}{
  2003  								generic.NewMap(map[interface{}]interface{}{
  2004  									"routes": []interface{}{
  2005  										map[interface{}]interface{}{"route": "app1route1.example.com/path"},
  2006  										map[interface{}]interface{}{"route": "app1route2.example.com:8008"},
  2007  									},
  2008  									"name": "manifest-app-name-1",
  2009  								}),
  2010  								generic.NewMap(map[interface{}]interface{}{
  2011  									"name": "manifest-app-name-2",
  2012  									"routes": []interface{}{
  2013  										map[interface{}]interface{}{"route": "app2route1.example.com"},
  2014  									},
  2015  								}),
  2016  							},
  2017  						}),
  2018  					}
  2019  					manifestRepo.ReadManifestReturns(m, nil)
  2020  
  2021  					appRepo.ReadStub = func(appName string) (models.Application, error) {
  2022  						return models.Application{
  2023  							ApplicationFields: models.ApplicationFields{
  2024  								Name: appName,
  2025  								GUID: appName + "-guid",
  2026  							},
  2027  						}, nil
  2028  					}
  2029  
  2030  					appRepo.UpdateStub = func(appGUID string, appParams models.AppParams) (models.Application, error) {
  2031  						return models.Application{
  2032  							ApplicationFields: models.ApplicationFields{
  2033  								GUID: appGUID,
  2034  							},
  2035  						}, nil
  2036  					}
  2037  				})
  2038  
  2039  				Context("and there are no flags", func() {
  2040  					BeforeEach(func() {
  2041  						args = []string{}
  2042  					})
  2043  
  2044  					It("maps the routes to the specified apps", func() {
  2045  						noHostBool := false
  2046  						emptyAppParams := models.AppParams{
  2047  							NoHostname: &noHostBool,
  2048  						}
  2049  
  2050  						Expect(executeErr).ToNot(HaveOccurred())
  2051  
  2052  						Expect(actor.MapManifestRouteCallCount()).To(Equal(3))
  2053  
  2054  						route, app, appParams := actor.MapManifestRouteArgsForCall(0)
  2055  						Expect(route).To(Equal("app1route1.example.com/path"))
  2056  						Expect(app.ApplicationFields.GUID).To(Equal("manifest-app-name-1-guid"))
  2057  						Expect(appParams).To(Equal(emptyAppParams))
  2058  
  2059  						route, app, appParams = actor.MapManifestRouteArgsForCall(1)
  2060  						Expect(route).To(Equal("app1route2.example.com:8008"))
  2061  						Expect(app.ApplicationFields.GUID).To(Equal("manifest-app-name-1-guid"))
  2062  						Expect(appParams).To(Equal(emptyAppParams))
  2063  
  2064  						route, app, appParams = actor.MapManifestRouteArgsForCall(2)
  2065  						Expect(route).To(Equal("app2route1.example.com"))
  2066  						Expect(app.ApplicationFields.GUID).To(Equal("manifest-app-name-2-guid"))
  2067  						Expect(appParams).To(Equal(emptyAppParams))
  2068  					})
  2069  				})
  2070  
  2071  				Context("and flags other than -f are present", func() {
  2072  					BeforeEach(func() {
  2073  						args = []string{"-n", "hostname-flag"}
  2074  					})
  2075  
  2076  					It("should return an error", func() {
  2077  						Expect(executeErr).To(HaveOccurred())
  2078  						Expect(executeErr.Error()).To(Equal("Incorrect Usage. Command line flags (except -f) cannot be applied when pushing multiple apps from a manifest file."))
  2079  					})
  2080  				})
  2081  			})
  2082  
  2083  			Context("and the manifest has only one app", func() {
  2084  				BeforeEach(func() {
  2085  					m := &manifest.Manifest{
  2086  						Path: "manifest.yml",
  2087  						Data: generic.NewMap(map[interface{}]interface{}{
  2088  							"applications": []interface{}{
  2089  								generic.NewMap(map[interface{}]interface{}{
  2090  									"routes": []interface{}{
  2091  										map[interface{}]interface{}{"route": "app1route1.example.com/path"},
  2092  									},
  2093  									"name": "manifest-app-name-1",
  2094  								}),
  2095  							},
  2096  						}),
  2097  					}
  2098  					manifestRepo.ReadManifestReturns(m, nil)
  2099  
  2100  					appRepo.ReadStub = func(appName string) (models.Application, error) {
  2101  						return models.Application{
  2102  							ApplicationFields: models.ApplicationFields{
  2103  								Name: appName,
  2104  								GUID: appName + "-guid",
  2105  							},
  2106  						}, nil
  2107  					}
  2108  
  2109  					appRepo.UpdateStub = func(appGUID string, appParams models.AppParams) (models.Application, error) {
  2110  						return models.Application{
  2111  							ApplicationFields: models.ApplicationFields{
  2112  								GUID: appGUID,
  2113  							},
  2114  						}, nil
  2115  					}
  2116  				})
  2117  
  2118  				Context("and flags are present", func() {
  2119  					BeforeEach(func() {
  2120  						args = []string{"-n", "flag-value"}
  2121  					})
  2122  
  2123  					It("maps the routes to the apps", func() {
  2124  						noHostBool := false
  2125  						appParamsFromContext := models.AppParams{
  2126  							Hosts:      []string{"flag-value"},
  2127  							NoHostname: &noHostBool,
  2128  						}
  2129  
  2130  						Expect(executeErr).ToNot(HaveOccurred())
  2131  
  2132  						Expect(actor.MapManifestRouteCallCount()).To(Equal(1))
  2133  
  2134  						route, app, appParams := actor.MapManifestRouteArgsForCall(0)
  2135  						Expect(route).To(Equal("app1route1.example.com/path"))
  2136  						Expect(app.ApplicationFields.GUID).To(Equal("manifest-app-name-1-guid"))
  2137  						Expect(appParams).To(Equal(appParamsFromContext))
  2138  					})
  2139  				})
  2140  			})
  2141  		})
  2142  	})
  2143  })