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