github.com/rakutentech/cli@v6.12.5-0.20151006231303-24468b65536e+incompatible/cf/commands/application/push_test.go (about)

     1  package application_test
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"syscall"
     7  
     8  	fakeactors "github.com/cloudfoundry/cli/cf/actors/fakes"
     9  	testApplication "github.com/cloudfoundry/cli/cf/api/applications/fakes"
    10  	testapi "github.com/cloudfoundry/cli/cf/api/fakes"
    11  	"github.com/cloudfoundry/cli/cf/api/resources"
    12  	testStacks "github.com/cloudfoundry/cli/cf/api/stacks/fakes"
    13  	fakeappfiles "github.com/cloudfoundry/cli/cf/app_files/fakes"
    14  	"github.com/cloudfoundry/cli/cf/command_registry"
    15  	"github.com/cloudfoundry/cli/cf/configuration/core_config"
    16  	"github.com/cloudfoundry/cli/cf/errors"
    17  	"github.com/cloudfoundry/cli/cf/manifest"
    18  	"github.com/cloudfoundry/cli/cf/models"
    19  	"github.com/cloudfoundry/cli/generic"
    20  	testcmd "github.com/cloudfoundry/cli/testhelpers/commands"
    21  	testconfig "github.com/cloudfoundry/cli/testhelpers/configuration"
    22  	"github.com/cloudfoundry/cli/testhelpers/maker"
    23  	testmanifest "github.com/cloudfoundry/cli/testhelpers/manifest"
    24  	testreq "github.com/cloudfoundry/cli/testhelpers/requirements"
    25  	testterm "github.com/cloudfoundry/cli/testhelpers/terminal"
    26  	testwords "github.com/cloudfoundry/cli/words/generator/fakes"
    27  	. "github.com/onsi/ginkgo"
    28  	. "github.com/onsi/gomega"
    29  
    30  	. "github.com/cloudfoundry/cli/testhelpers/matchers"
    31  )
    32  
    33  var _ = Describe("Push Command", func() {
    34  	var (
    35  		ui                         *testterm.FakeUI
    36  		configRepo                 core_config.Repository
    37  		manifestRepo               *testmanifest.FakeManifestRepository
    38  		starter                    *testcmd.FakeApplicationStarter
    39  		stopper                    *testcmd.FakeApplicationStopper
    40  		serviceBinder              *testcmd.FakeAppBinder
    41  		appRepo                    *testApplication.FakeApplicationRepository
    42  		domainRepo                 *testapi.FakeDomainRepository
    43  		routeRepo                  *testapi.FakeRouteRepository
    44  		stackRepo                  *testStacks.FakeStackRepository
    45  		serviceRepo                *testapi.FakeServiceRepo
    46  		wordGenerator              *testwords.FakeWordGenerator
    47  		requirementsFactory        *testreq.FakeReqFactory
    48  		authRepo                   *testapi.FakeAuthenticationRepository
    49  		actor                      *fakeactors.FakePushActor
    50  		app_files                  *fakeappfiles.FakeAppFiles
    51  		zipper                     *fakeappfiles.FakeZipper
    52  		OriginalCommandStart       command_registry.Command
    53  		OriginalCommandStop        command_registry.Command
    54  		OriginalCommandServiceBind command_registry.Command
    55  		deps                       command_registry.Dependency
    56  	)
    57  
    58  	updateCommandDependency := func(pluginCall bool) {
    59  		deps.Ui = ui
    60  		deps.Config = configRepo
    61  		deps.ManifestRepo = manifestRepo
    62  		deps.RepoLocator = deps.RepoLocator.SetApplicationRepository(appRepo)
    63  		deps.RepoLocator = deps.RepoLocator.SetDomainRepository(domainRepo)
    64  		deps.RepoLocator = deps.RepoLocator.SetRouteRepository(routeRepo)
    65  		deps.RepoLocator = deps.RepoLocator.SetServiceRepository(serviceRepo)
    66  		deps.RepoLocator = deps.RepoLocator.SetStackRepository(stackRepo)
    67  		deps.RepoLocator = deps.RepoLocator.SetAuthenticationRepository(authRepo)
    68  		deps.WordGenerator = wordGenerator
    69  		deps.PushActor = actor
    70  		deps.AppZipper = zipper
    71  		deps.AppFiles = app_files
    72  
    73  		//inject fake commands dependencies into registry
    74  		command_registry.Register(starter)
    75  		command_registry.Register(stopper)
    76  		command_registry.Register(serviceBinder)
    77  
    78  		command_registry.Commands.SetCommand(command_registry.Commands.FindCommand("push").SetDependency(deps, false))
    79  	}
    80  
    81  	BeforeEach(func() {
    82  		manifestRepo = &testmanifest.FakeManifestRepository{}
    83  
    84  		starter = &testcmd.FakeApplicationStarter{}
    85  		stopper = &testcmd.FakeApplicationStopper{}
    86  		serviceBinder = &testcmd.FakeAppBinder{}
    87  
    88  		//setup fake commands (counterfeiter) to correctly interact with command_registry
    89  		starter.SetDependencyStub = func(_ command_registry.Dependency, _ bool) command_registry.Command {
    90  			return starter
    91  		}
    92  		starter.MetaDataReturns(command_registry.CommandMetadata{Name: "start"})
    93  
    94  		stopper.SetDependencyStub = func(_ command_registry.Dependency, _ bool) command_registry.Command {
    95  			return stopper
    96  		}
    97  		stopper.MetaDataReturns(command_registry.CommandMetadata{Name: "stop"})
    98  
    99  		appRepo = &testApplication.FakeApplicationRepository{}
   100  
   101  		domainRepo = &testapi.FakeDomainRepository{}
   102  		sharedDomain := maker.NewSharedDomainFields(maker.Overrides{"name": "foo.cf-app.com", "guid": "foo-domain-guid"})
   103  		domainRepo.ListDomainsForOrgDomains = []models.DomainFields{sharedDomain}
   104  
   105  		//save original command dependences and restore later
   106  		OriginalCommandStart = command_registry.Commands.FindCommand("start")
   107  		OriginalCommandStop = command_registry.Commands.FindCommand("stop")
   108  		OriginalCommandServiceBind = command_registry.Commands.FindCommand("bind-service")
   109  
   110  		routeRepo = &testapi.FakeRouteRepository{}
   111  		stackRepo = &testStacks.FakeStackRepository{}
   112  		serviceRepo = &testapi.FakeServiceRepo{}
   113  		authRepo = &testapi.FakeAuthenticationRepository{}
   114  		wordGenerator = new(testwords.FakeWordGenerator)
   115  		wordGenerator.BabbleReturns("laughing-cow")
   116  
   117  		ui = new(testterm.FakeUI)
   118  		configRepo = testconfig.NewRepositoryWithDefaults()
   119  
   120  		requirementsFactory = &testreq.FakeReqFactory{LoginSuccess: true, TargetedSpaceSuccess: true}
   121  
   122  		zipper = &fakeappfiles.FakeZipper{}
   123  		app_files = &fakeappfiles.FakeAppFiles{}
   124  		actor = &fakeactors.FakePushActor{}
   125  
   126  	})
   127  
   128  	AfterEach(func() {
   129  		command_registry.Register(OriginalCommandStart)
   130  		command_registry.Register(OriginalCommandStop)
   131  		command_registry.Register(OriginalCommandServiceBind)
   132  	})
   133  
   134  	callPush := func(args ...string) bool {
   135  		return testcmd.RunCliCommand("push", args, requirementsFactory, updateCommandDependency, false)
   136  	}
   137  
   138  	Describe("requirements", func() {
   139  		It("passes when logged in and a space is targeted", func() {
   140  			Expect(callPush()).To(BeTrue())
   141  		})
   142  
   143  		It("fails when not logged in", func() {
   144  			requirementsFactory.LoginSuccess = false
   145  			Expect(callPush()).To(BeFalse())
   146  		})
   147  
   148  		It("fails when a space is not targeted", func() {
   149  			requirementsFactory.TargetedSpaceSuccess = false
   150  			Expect(callPush()).To(BeFalse())
   151  		})
   152  
   153  		// yes, we're aware that the args here should probably be provided in a different order
   154  		// erg: app-name -p some/path some-extra-arg
   155  		// but the test infrastructure for parsing args and flags is sorely lacking
   156  		It("fails when provided too many args", func() {
   157  			Expect(callPush("-p", "path", "too-much", "app-name")).To(BeFalse())
   158  		})
   159  	})
   160  
   161  	Describe("when pushing a new app", func() {
   162  		BeforeEach(func() {
   163  			appRepo.ReadReturns.Error = errors.NewModelNotFoundError("App", "the-app")
   164  
   165  			zipper.ZipReturns(nil)
   166  			zipper.GetZipSizeReturns(9001, nil)
   167  			actor.GatherFilesReturns(nil, true, nil)
   168  			actor.UploadAppReturns(nil)
   169  		})
   170  
   171  		It("does not call Zip() when there is no file to be uploaded", func() {
   172  			actor.GatherFilesReturns(nil, false, nil)
   173  			callPush("my-new-app")
   174  
   175  			Expect(zipper.ZipCallCount()).To(Equal(0))
   176  		})
   177  
   178  		Context("when the default route for the app already exists", func() {
   179  			BeforeEach(func() {
   180  				route := models.Route{}
   181  				route.Guid = "my-route-guid"
   182  				route.Host = "my-new-app"
   183  				route.Domain = domainRepo.ListDomainsForOrgDomains[0]
   184  
   185  				routeRepo.FindByHostAndDomainReturns.Route = route
   186  			})
   187  
   188  			It("binds to existing routes", func() {
   189  				callPush("my-new-app")
   190  
   191  				Expect(routeRepo.CreatedHost).To(BeEmpty())
   192  				Expect(routeRepo.CreatedDomainGuid).To(BeEmpty())
   193  				Expect(routeRepo.FindByHostAndDomainCalledWith.Host).To(Equal("my-new-app"))
   194  				Expect(routeRepo.BoundAppGuid).To(Equal("my-new-app-guid"))
   195  				Expect(routeRepo.BoundRouteGuid).To(Equal("my-route-guid"))
   196  
   197  				Expect(ui.Outputs).To(ContainSubstrings(
   198  					[]string{"Using", "my-new-app.foo.cf-app.com"},
   199  					[]string{"Binding", "my-new-app.foo.cf-app.com"},
   200  					[]string{"OK"},
   201  				))
   202  			})
   203  		})
   204  
   205  		Context("when the default route for the app does not exist", func() {
   206  			BeforeEach(func() {
   207  				routeRepo.FindByHostAndDomainReturns.Error = errors.NewModelNotFoundError("Org", "couldn't find it")
   208  			})
   209  
   210  			It("refreshes the auth token (so fresh)", func() { // so clean
   211  				callPush("fresh-prince")
   212  
   213  				Expect(authRepo.RefreshTokenCalled).To(BeTrue())
   214  			})
   215  
   216  			Context("when refreshing the auth token fails", func() {
   217  				BeforeEach(func() {
   218  					authRepo.RefreshTokenError = errors.New("I accidentally the UAA")
   219  				})
   220  
   221  				It("it displays an error", func() {
   222  					callPush("of-bel-air")
   223  
   224  					Expect(ui.Outputs).ToNot(ContainSubstrings(
   225  						[]string{"Error refreshing auth token"},
   226  					))
   227  					Expect(ui.Outputs).To(ContainSubstrings(
   228  						[]string{"FAILED"},
   229  						[]string{"accidentally the UAA"},
   230  					))
   231  				})
   232  			})
   233  
   234  			Context("when multiple domains are specified in manifest", func() {
   235  
   236  				BeforeEach(func() {
   237  					domainRepo.FindByNameInOrgDomain = []models.DomainFields{
   238  						models.DomainFields{Name: "example1.com", Guid: "example-domain-guid"},
   239  						models.DomainFields{Name: "example2.com", Guid: "example-domain-guid"},
   240  					}
   241  
   242  					manifestRepo.ReadManifestReturns.Manifest = multipleDomainsManifest()
   243  				})
   244  
   245  				It("creates a route for each domain", func() {
   246  					callPush()
   247  
   248  					Expect(ui.Outputs).To(ContainSubstrings(
   249  						[]string{"Creating", "manifest-host.example1.com"},
   250  						[]string{"OK"},
   251  						[]string{"Binding", "manifest-host.example1.com"},
   252  						[]string{"OK"},
   253  						[]string{"Creating", "manifest-host.example2.com"},
   254  						[]string{"OK"},
   255  						[]string{"Binding", "manifest-host.example2.com"},
   256  						[]string{"OK"},
   257  					))
   258  				})
   259  
   260  				It("creates a route for each host on every domains", func() {
   261  					callPush()
   262  
   263  					Expect(ui.Outputs).To(ContainSubstrings(
   264  						[]string{"Creating", "manifest-host.example1.com"},
   265  						[]string{"Binding", "manifest-host.example1.com"},
   266  						[]string{"Creating", "host2.example1.com"},
   267  						[]string{"Binding", "host2.example1.com"},
   268  						[]string{"Creating", "manifest-host.example2.com"},
   269  						[]string{"Binding", "manifest-host.example2.com"},
   270  						[]string{"Creating", "host2.example2.com"},
   271  						[]string{"Binding", "host2.example2.com"},
   272  					))
   273  				})
   274  
   275  				It("`-d` from argument will override the domains in manifest", func() {
   276  					callPush("-d", "example1.com")
   277  
   278  					Expect(ui.Outputs).To(ContainSubstrings(
   279  						[]string{"Creating", "manifest-host.example1.com"},
   280  						[]string{"OK"},
   281  						[]string{"Binding", "manifest-host.example1.com"},
   282  					))
   283  
   284  					Expect(ui.Outputs).ToNot(ContainSubstrings(
   285  						[]string{"Creating", "manifest-host.example2.com"},
   286  					))
   287  				})
   288  
   289  			})
   290  
   291  			It("creates an app", func() {
   292  				callPush("-t", "111", "my-new-app")
   293  				Expect(ui.Outputs).ToNot(ContainSubstrings([]string{"FAILED"}))
   294  
   295  				Expect(*appRepo.CreatedAppParams().Name).To(Equal("my-new-app"))
   296  				Expect(*appRepo.CreatedAppParams().SpaceGuid).To(Equal("my-space-guid"))
   297  
   298  				Expect(routeRepo.FindByHostAndDomainCalledWith.Host).To(Equal("my-new-app"))
   299  				Expect(routeRepo.CreatedHost).To(Equal("my-new-app"))
   300  				Expect(routeRepo.CreatedDomainGuid).To(Equal("foo-domain-guid"))
   301  				Expect(routeRepo.BoundAppGuid).To(Equal("my-new-app-guid"))
   302  				Expect(routeRepo.BoundRouteGuid).To(Equal("my-new-app-route-guid"))
   303  
   304  				appGuid, _, _ := actor.UploadAppArgsForCall(0)
   305  				Expect(appGuid).To(Equal("my-new-app-guid"))
   306  
   307  				Expect(ui.Outputs).To(ContainSubstrings(
   308  					[]string{"Creating app", "my-new-app", "my-org", "my-space"},
   309  					[]string{"OK"},
   310  					[]string{"Creating", "my-new-app.foo.cf-app.com"},
   311  					[]string{"OK"},
   312  					[]string{"Binding", "my-new-app.foo.cf-app.com"},
   313  					[]string{"OK"},
   314  					[]string{"Uploading my-new-app"},
   315  					[]string{"OK"},
   316  				))
   317  
   318  				Expect(stopper.ApplicationStopCallCount()).To(Equal(0))
   319  
   320  				app, orgName, spaceName := starter.ApplicationStartArgsForCall(0)
   321  				Expect(app.Guid).To(Equal(appGuid))
   322  				Expect(app.Name).To(Equal("my-new-app"))
   323  				Expect(orgName).To(Equal(configRepo.OrganizationFields().Name))
   324  				Expect(spaceName).To(Equal(configRepo.SpaceFields().Name))
   325  				Expect(starter.SetStartTimeoutInSecondsArgsForCall(0)).To(Equal(111))
   326  			})
   327  
   328  			It("strips special characters when creating a default route", func() {
   329  				callPush("-t", "111", "Tim's 1st-Crazy__app!")
   330  				Expect(*appRepo.CreatedAppParams().Name).To(Equal("Tim's 1st-Crazy__app!"))
   331  
   332  				Expect(routeRepo.FindByHostAndDomainCalledWith.Host).To(Equal("tims-1st-crazy-app"))
   333  				Expect(routeRepo.CreatedHost).To(Equal("tims-1st-crazy-app"))
   334  
   335  				Expect(ui.Outputs).To(ContainSubstrings(
   336  					[]string{"Creating", "tims-1st-crazy-app.foo.cf-app.com"},
   337  					[]string{"Binding", "tims-1st-crazy-app.foo.cf-app.com"},
   338  				))
   339  				Expect(ui.Outputs).ToNot(ContainSubstrings([]string{"FAILED"}))
   340  			})
   341  
   342  			It("sets the app params from the flags", func() {
   343  				domainRepo.FindByNameInOrgDomain = []models.DomainFields{
   344  					models.DomainFields{
   345  						Name: "bar.cf-app.com",
   346  						Guid: "bar-domain-guid",
   347  					},
   348  				}
   349  				stackRepo.FindByNameReturns(models.Stack{
   350  					Name: "customLinux",
   351  					Guid: "custom-linux-guid",
   352  				}, nil)
   353  
   354  				callPush(
   355  					"-c", "unicorn -c config/unicorn.rb -D",
   356  					"-d", "bar.cf-app.com",
   357  					"-n", "my-hostname",
   358  					"-k", "4G",
   359  					"-i", "3",
   360  					"-m", "2G",
   361  					"-b", "https://github.com/heroku/heroku-buildpack-play.git",
   362  					"-s", "customLinux",
   363  					"-t", "1",
   364  					"--no-start",
   365  					"my-new-app",
   366  				)
   367  
   368  				Expect(ui.Outputs).To(ContainSubstrings(
   369  					[]string{"Using", "customLinux"},
   370  					[]string{"OK"},
   371  					[]string{"Creating app", "my-new-app"},
   372  					[]string{"OK"},
   373  					[]string{"Creating route", "my-hostname.bar.cf-app.com"},
   374  					[]string{"OK"},
   375  					[]string{"Binding", "my-hostname.bar.cf-app.com", "my-new-app"},
   376  					[]string{"Uploading", "my-new-app"},
   377  					[]string{"OK"},
   378  				))
   379  
   380  				Expect(stackRepo.FindByNameArgsForCall(0)).To(Equal("customLinux"))
   381  
   382  				Expect(*appRepo.CreatedAppParams().Name).To(Equal("my-new-app"))
   383  				Expect(*appRepo.CreatedAppParams().Command).To(Equal("unicorn -c config/unicorn.rb -D"))
   384  				Expect(*appRepo.CreatedAppParams().InstanceCount).To(Equal(3))
   385  				Expect(*appRepo.CreatedAppParams().DiskQuota).To(Equal(int64(4096)))
   386  				Expect(*appRepo.CreatedAppParams().Memory).To(Equal(int64(2048)))
   387  				Expect(*appRepo.CreatedAppParams().StackGuid).To(Equal("custom-linux-guid"))
   388  				Expect(*appRepo.CreatedAppParams().HealthCheckTimeout).To(Equal(1))
   389  				Expect(*appRepo.CreatedAppParams().BuildpackUrl).To(Equal("https://github.com/heroku/heroku-buildpack-play.git"))
   390  
   391  				Expect(domainRepo.FindByNameInOrgName).To(Equal("bar.cf-app.com"))
   392  				Expect(domainRepo.FindByNameInOrgGuid).To(Equal("my-org-guid"))
   393  
   394  				Expect(routeRepo.CreatedHost).To(Equal("my-hostname"))
   395  				Expect(routeRepo.CreatedDomainGuid).To(Equal("bar-domain-guid"))
   396  				Expect(routeRepo.BoundAppGuid).To(Equal("my-new-app-guid"))
   397  				Expect(routeRepo.BoundRouteGuid).To(Equal("my-hostname-route-guid"))
   398  
   399  				appGuid, _, _ := actor.UploadAppArgsForCall(0)
   400  				Expect(appGuid).To(Equal("my-new-app-guid"))
   401  
   402  				Expect(starter.ApplicationStartCallCount()).To(Equal(0))
   403  			})
   404  
   405  			Context("when there is a shared domain", func() {
   406  				It("creates a route with the shared domain and maps it to the app", func() {
   407  					privateDomain := models.DomainFields{
   408  						Shared: false,
   409  						Name:   "private.cf-app.com",
   410  						Guid:   "private-domain-guid",
   411  					}
   412  					sharedDomain := models.DomainFields{
   413  						Name:   "shared.cf-app.com",
   414  						Shared: true,
   415  						Guid:   "shared-domain-guid",
   416  					}
   417  
   418  					domainRepo.ListDomainsForOrgDomains = []models.DomainFields{privateDomain, sharedDomain}
   419  
   420  					callPush("-t", "111", "my-new-app")
   421  
   422  					Expect(routeRepo.FindByHostAndDomainCalledWith.Host).To(Equal("my-new-app"))
   423  					Expect(routeRepo.CreatedHost).To(Equal("my-new-app"))
   424  					Expect(routeRepo.CreatedDomainGuid).To(Equal("shared-domain-guid"))
   425  					Expect(routeRepo.BoundAppGuid).To(Equal("my-new-app-guid"))
   426  					Expect(routeRepo.BoundRouteGuid).To(Equal("my-new-app-route-guid"))
   427  
   428  					Expect(ui.Outputs).To(ContainSubstrings(
   429  						[]string{"Creating app", "my-new-app", "my-org", "my-space"},
   430  						[]string{"OK"},
   431  						[]string{"Creating", "my-new-app.shared.cf-app.com"},
   432  						[]string{"OK"},
   433  						[]string{"Binding", "my-new-app.shared.cf-app.com"},
   434  						[]string{"OK"},
   435  						[]string{"Uploading my-new-app"},
   436  						[]string{"OK"},
   437  					))
   438  				})
   439  			})
   440  
   441  			Context("when there is no shared domain but there is a private domain in the targeted org", func() {
   442  				It("creates a route with the private domain and maps it to the app", func() {
   443  					privateDomain := models.DomainFields{
   444  						Shared: false,
   445  						Name:   "private.cf-app.com",
   446  						Guid:   "private-domain-guid",
   447  					}
   448  
   449  					domainRepo.ListDomainsForOrgDomains = []models.DomainFields{privateDomain}
   450  					appRepo.ReadReturns.Error = errors.NewModelNotFoundError("App", "the-app")
   451  
   452  					callPush("-t", "111", "my-new-app")
   453  
   454  					Expect(routeRepo.FindByHostAndDomainCalledWith.Host).To(Equal("my-new-app"))
   455  					Expect(routeRepo.CreatedHost).To(Equal("my-new-app"))
   456  					Expect(routeRepo.CreatedDomainGuid).To(Equal("private-domain-guid"))
   457  					Expect(routeRepo.BoundAppGuid).To(Equal("my-new-app-guid"))
   458  					Expect(routeRepo.BoundRouteGuid).To(Equal("my-new-app-route-guid"))
   459  
   460  					Expect(ui.Outputs).To(ContainSubstrings(
   461  						[]string{"Creating app", "my-new-app", "my-org", "my-space"},
   462  						[]string{"OK"},
   463  						[]string{"Creating", "my-new-app.private.cf-app.com"},
   464  						[]string{"OK"},
   465  						[]string{"Binding", "my-new-app.private.cf-app.com"},
   466  						[]string{"OK"},
   467  						[]string{"Uploading my-new-app"},
   468  						[]string{"OK"},
   469  					))
   470  				})
   471  			})
   472  
   473  			Describe("randomized hostnames", func() {
   474  				var manifestApp generic.Map
   475  
   476  				BeforeEach(func() {
   477  					manifest := singleAppManifest()
   478  					manifestApp = manifest.Data.Get("applications").([]interface{})[0].(generic.Map)
   479  					manifestApp.Delete("host")
   480  					manifestRepo.ReadManifestReturns.Manifest = manifest
   481  				})
   482  
   483  				It("provides a random hostname when the --random-route flag is passed", func() {
   484  					callPush("--random-route", "my-new-app")
   485  					Expect(routeRepo.FindByHostAndDomainCalledWith.Host).To(Equal("my-new-app-laughing-cow"))
   486  				})
   487  
   488  				It("provides a random hostname when the random-route option is set in the manifest", func() {
   489  					manifestApp.Set("random-route", true)
   490  
   491  					callPush("my-new-app")
   492  
   493  					Expect(routeRepo.FindByHostAndDomainCalledWith.Host).To(Equal("my-new-app-laughing-cow"))
   494  				})
   495  			})
   496  
   497  			It("pushes the contents of the app directory or zip file specified using the -p flag", func() {
   498  				callPush("-p", "../some/path-to/an-app/zip-file", "app-with-path")
   499  
   500  				appDir, _ := actor.GatherFilesArgsForCall(0)
   501  				Expect(appDir).To(Equal("../some/path-to/an-app/zip-file"))
   502  			})
   503  
   504  			It("pushes the contents of the current working directory by default", func() {
   505  				callPush("app-with-default-path")
   506  				dir, _ := os.Getwd()
   507  
   508  				appDir, _ := actor.GatherFilesArgsForCall(0)
   509  				Expect(appDir).To(Equal(dir))
   510  			})
   511  
   512  			It("fails when given a bad manifest path", func() {
   513  				manifestRepo.ReadManifestReturns.Manifest = manifest.NewEmptyManifest()
   514  				manifestRepo.ReadManifestReturns.Error = errors.New("read manifest error")
   515  
   516  				callPush("-f", "bad/manifest/path")
   517  
   518  				Expect(ui.Outputs).To(ContainSubstrings(
   519  					[]string{"FAILED"},
   520  					[]string{"read manifest error"},
   521  				))
   522  			})
   523  
   524  			It("does not fail when the current working directory does not contain a manifest", func() {
   525  				manifestRepo.ReadManifestReturns.Manifest = singleAppManifest()
   526  				manifestRepo.ReadManifestReturns.Error = syscall.ENOENT
   527  				manifestRepo.ReadManifestReturns.Manifest.Path = ""
   528  
   529  				callPush("--no-route", "app-name")
   530  
   531  				Expect(ui.Outputs).To(ContainSubstrings(
   532  					[]string{"Creating app", "app-name"},
   533  					[]string{"OK"},
   534  					[]string{"Uploading", "app-name"},
   535  					[]string{"OK"},
   536  				))
   537  				Expect(ui.Outputs).ToNot(ContainSubstrings([]string{"FAILED"}))
   538  			})
   539  
   540  			It("uses the manifest in the current directory by default", func() {
   541  				manifestRepo.ReadManifestReturns.Manifest = singleAppManifest()
   542  				manifestRepo.ReadManifestReturns.Manifest.Path = "manifest.yml"
   543  
   544  				callPush("-p", "some/relative/path")
   545  
   546  				Expect(ui.Outputs).To(ContainSubstrings([]string{"Using manifest file", "manifest.yml"}))
   547  
   548  				cwd, _ := os.Getwd()
   549  				Expect(manifestRepo.ReadManifestArgs.Path).To(Equal(cwd))
   550  			})
   551  
   552  			It("does not use a manifest if the 'no-manifest' flag is passed", func() {
   553  				callPush("--no-route", "--no-manifest", "app-name")
   554  
   555  				Expect(ui.Outputs).ToNot(ContainSubstrings(
   556  					[]string{"FAILED"},
   557  					[]string{"hacker-manifesto"},
   558  				))
   559  
   560  				Expect(manifestRepo.ReadManifestArgs.Path).To(Equal(""))
   561  				Expect(*appRepo.CreatedAppParams().Name).To(Equal("app-name"))
   562  			})
   563  
   564  			It("pushes an app when provided a manifest with one app defined", func() {
   565  				domainRepo.FindByNameInOrgDomain = []models.DomainFields{
   566  					models.DomainFields{
   567  						Name: "manifest-example.com",
   568  						Guid: "bar-domain-guid",
   569  					},
   570  				}
   571  
   572  				manifestRepo.ReadManifestReturns.Manifest = singleAppManifest()
   573  
   574  				callPush()
   575  
   576  				Expect(ui.Outputs).To(ContainSubstrings(
   577  					[]string{"Creating route", "manifest-host.manifest-example.com"},
   578  					[]string{"OK"},
   579  					[]string{"Binding", "manifest-host.manifest-example.com"},
   580  					[]string{"manifest-app-name"},
   581  				))
   582  
   583  				Expect(*appRepo.CreatedAppParams().Name).To(Equal("manifest-app-name"))
   584  				Expect(*appRepo.CreatedAppParams().Memory).To(Equal(int64(128)))
   585  				Expect(*appRepo.CreatedAppParams().InstanceCount).To(Equal(1))
   586  				Expect(*appRepo.CreatedAppParams().StackName).To(Equal("custom-stack"))
   587  				Expect(*appRepo.CreatedAppParams().BuildpackUrl).To(Equal("some-buildpack"))
   588  				Expect(*appRepo.CreatedAppParams().Command).To(Equal("JAVA_HOME=$PWD/.openjdk JAVA_OPTS=\"-Xss995K\" ./bin/start.sh run"))
   589  				// Expect(actor.UploadedDir).To(Equal(filepath.Clean("some/path/from/manifest"))) TODO: Re-enable this once we develop a strategy
   590  
   591  				Expect(*appRepo.CreatedAppParams().EnvironmentVars).To(Equal(map[string]interface{}{
   592  					"PATH": "/u/apps/my-app/bin",
   593  					"FOO":  "baz",
   594  				}))
   595  			})
   596  
   597  			It("pushes an app with multiple routes when multiple hosts are provided", func() {
   598  				domainRepo.FindByNameInOrgDomain = []models.DomainFields{
   599  					models.DomainFields{
   600  						Name: "manifest-example.com",
   601  						Guid: "bar-domain-guid",
   602  					},
   603  				}
   604  
   605  				manifestRepo.ReadManifestReturns.Manifest = multipleHostManifest()
   606  
   607  				callPush()
   608  
   609  				Expect(ui.Outputs).To(ContainSubstrings(
   610  					[]string{"Creating route", "manifest-host-1.manifest-example.com"},
   611  					[]string{"OK"},
   612  					[]string{"Binding", "manifest-host-1.manifest-example.com"},
   613  					[]string{"Creating route", "manifest-host-2.manifest-example.com"},
   614  					[]string{"OK"},
   615  					[]string{"Binding", "manifest-host-2.manifest-example.com"},
   616  					[]string{"manifest-app-name"},
   617  				))
   618  			})
   619  
   620  			It("fails when parsing the manifest has errors", func() {
   621  				manifestRepo.ReadManifestReturns.Manifest = &manifest.Manifest{Path: "/some-path/"}
   622  				manifestRepo.ReadManifestReturns.Error = errors.New("buildpack should not be null")
   623  
   624  				callPush()
   625  
   626  				Expect(ui.Outputs).To(ContainSubstrings(
   627  					[]string{"FAILED"},
   628  					[]string{"Error", "reading", "manifest"},
   629  					[]string{"buildpack should not be null"},
   630  				))
   631  			})
   632  
   633  			It("does not create a route when provided the --no-route flag", func() {
   634  				domainRepo.FindByNameInOrgDomain = []models.DomainFields{
   635  					models.DomainFields{
   636  						Name: "bar.cf-app.com",
   637  						Guid: "bar-domain-guid",
   638  					},
   639  				}
   640  
   641  				callPush("--no-route", "my-new-app")
   642  
   643  				Expect(*appRepo.CreatedAppParams().Name).To(Equal("my-new-app"))
   644  				Expect(routeRepo.CreatedHost).To(Equal(""))
   645  				Expect(routeRepo.CreatedDomainGuid).To(Equal(""))
   646  			})
   647  
   648  			It("maps the root domain route to the app when given the --no-hostname flag", func() {
   649  				domainRepo.ListDomainsForOrgDomains = []models.DomainFields{{
   650  					Name:   "bar.cf-app.com",
   651  					Guid:   "bar-domain-guid",
   652  					Shared: true,
   653  				}}
   654  
   655  				routeRepo.FindByHostAndDomainReturns.Error = errors.NewModelNotFoundError("Org", "uh oh")
   656  
   657  				callPush("--no-hostname", "my-new-app")
   658  
   659  				Expect(*appRepo.CreatedAppParams().Name).To(Equal("my-new-app"))
   660  				Expect(routeRepo.CreatedHost).To(Equal(""))
   661  				Expect(routeRepo.CreatedDomainGuid).To(Equal("bar-domain-guid"))
   662  			})
   663  
   664  			It("Does not create a route when the no-route property is in the manifest", func() {
   665  				workerManifest := singleAppManifest()
   666  				workerManifest.Data.Get("applications").([]interface{})[0].(generic.Map).Set("no-route", true)
   667  				manifestRepo.ReadManifestReturns.Manifest = workerManifest
   668  
   669  				callPush("worker-app")
   670  
   671  				Expect(ui.Outputs).To(ContainSubstrings([]string{"worker-app", "is a worker", "skipping route creation"}))
   672  				Expect(routeRepo.BoundAppGuid).To(Equal(""))
   673  				Expect(routeRepo.BoundRouteGuid).To(Equal(""))
   674  			})
   675  
   676  			It("fails when given an invalid memory limit", func() {
   677  				callPush("-m", "abcM", "my-new-app")
   678  
   679  				Expect(ui.Outputs).To(ContainSubstrings(
   680  					[]string{"FAILED"},
   681  					[]string{"Invalid", "memory limit", "abcM"},
   682  				))
   683  			})
   684  
   685  			Context("when a manifest has many apps", func() {
   686  				BeforeEach(func() {
   687  					manifestRepo.ReadManifestReturns.Manifest = manifestWithServicesAndEnv()
   688  				})
   689  
   690  				It("pushes each app", func() {
   691  					callPush()
   692  
   693  					Expect(ui.Outputs).To(ContainSubstrings(
   694  						[]string{"Creating", "app1"},
   695  						[]string{"Creating", "app2"},
   696  					))
   697  					Expect(len(appRepo.CreateAppParams)).To(Equal(2))
   698  
   699  					firstApp := appRepo.CreateAppParams[0]
   700  					secondApp := appRepo.CreateAppParams[1]
   701  					Expect(*firstApp.Name).To(Equal("app1"))
   702  					Expect(*secondApp.Name).To(Equal("app2"))
   703  
   704  					envVars := *firstApp.EnvironmentVars
   705  					Expect(envVars["SOMETHING"]).To(Equal("definitely-something"))
   706  
   707  					envVars = *secondApp.EnvironmentVars
   708  					Expect(envVars["SOMETHING"]).To(Equal("nothing"))
   709  				})
   710  
   711  				It("pushes a single app when given the name of a single app in the manifest", func() {
   712  					callPush("app2")
   713  
   714  					Expect(ui.Outputs).To(ContainSubstrings([]string{"Creating", "app2"}))
   715  					Expect(ui.Outputs).ToNot(ContainSubstrings([]string{"Creating", "app1"}))
   716  					Expect(len(appRepo.CreateAppParams)).To(Equal(1))
   717  					Expect(*appRepo.CreateAppParams[0].Name).To(Equal("app2"))
   718  				})
   719  
   720  				It("fails when given the name of an app that is not in the manifest", func() {
   721  					callPush("non-existant-app")
   722  
   723  					Expect(ui.Outputs).To(ContainSubstrings([]string{"FAILED"}))
   724  					Expect(len(appRepo.CreateAppParams)).To(Equal(0))
   725  				})
   726  			})
   727  		})
   728  	})
   729  
   730  	Describe("re-pushing an existing app", func() {
   731  		var existingApp models.Application
   732  
   733  		BeforeEach(func() {
   734  			existingApp = models.Application{}
   735  			existingApp.Name = "existing-app"
   736  			existingApp.Guid = "existing-app-guid"
   737  			existingApp.Command = "unicorn -c config/unicorn.rb -D"
   738  			existingApp.EnvironmentVars = map[string]interface{}{
   739  				"crazy": "pants",
   740  				"FOO":   "NotYoBaz",
   741  				"foo":   "manchu",
   742  			}
   743  
   744  			appRepo.ReadReturns.App = existingApp
   745  			appRepo.UpdateAppResult = existingApp
   746  		})
   747  
   748  		It("resets the app's buildpack when the -b flag is provided as 'default'", func() {
   749  			callPush("-b", "default", "existing-app")
   750  			Expect(*appRepo.UpdateParams.BuildpackUrl).To(Equal(""))
   751  		})
   752  
   753  		It("resets the app's command when the -c flag is provided as 'default'", func() {
   754  			callPush("-c", "default", "existing-app")
   755  			Expect(*appRepo.UpdateParams.Command).To(Equal(""))
   756  		})
   757  
   758  		It("resets the app's buildpack when the -b flag is provided as 'null'", func() {
   759  			callPush("-b", "null", "existing-app")
   760  			Expect(*appRepo.UpdateParams.BuildpackUrl).To(Equal(""))
   761  		})
   762  
   763  		It("resets the app's command when the -c flag is provided as 'null'", func() {
   764  			callPush("-c", "null", "existing-app")
   765  			Expect(*appRepo.UpdateParams.Command).To(Equal(""))
   766  		})
   767  
   768  		It("merges env vars from the manifest with those from the server", func() {
   769  			manifestRepo.ReadManifestReturns.Manifest = singleAppManifest()
   770  
   771  			callPush("existing-app")
   772  
   773  			updatedAppEnvVars := *appRepo.UpdateParams.EnvironmentVars
   774  			Expect(updatedAppEnvVars["crazy"]).To(Equal("pants"))
   775  			Expect(updatedAppEnvVars["FOO"]).To(Equal("baz"))
   776  			Expect(updatedAppEnvVars["foo"]).To(Equal("manchu"))
   777  			Expect(updatedAppEnvVars["PATH"]).To(Equal("/u/apps/my-app/bin"))
   778  		})
   779  
   780  		It("stops the app, achieving a full-downtime deploy!", func() {
   781  			appRepo.UpdateAppResult = existingApp
   782  
   783  			callPush("existing-app")
   784  
   785  			app, orgName, spaceName := stopper.ApplicationStopArgsForCall(0)
   786  			Expect(app.Guid).To(Equal(existingApp.Guid))
   787  			Expect(app.Name).To(Equal("existing-app"))
   788  			Expect(orgName).To(Equal(configRepo.OrganizationFields().Name))
   789  			Expect(spaceName).To(Equal(configRepo.SpaceFields().Name))
   790  
   791  			appGuid, _, _ := actor.UploadAppArgsForCall(0)
   792  			Expect(appGuid).To(Equal(existingApp.Guid))
   793  		})
   794  
   795  		It("does not stop the app when it is already stopped", func() {
   796  			existingApp.State = "stopped"
   797  			appRepo.ReadReturns.App = existingApp
   798  			appRepo.UpdateAppResult = existingApp
   799  
   800  			callPush("existing-app")
   801  
   802  			Expect(stopper.ApplicationStopCallCount()).To(Equal(0))
   803  		})
   804  
   805  		It("updates the app", func() {
   806  			existingRoute := models.RouteSummary{}
   807  			existingRoute.Host = "existing-app"
   808  
   809  			existingApp.Routes = []models.RouteSummary{existingRoute}
   810  			appRepo.ReadReturns.App = existingApp
   811  			appRepo.UpdateAppResult = existingApp
   812  
   813  			stackRepo.FindByNameReturns(models.Stack{
   814  				Name: "differentStack",
   815  				Guid: "differentStack-guid",
   816  			}, nil)
   817  
   818  			callPush(
   819  				"-c", "different start command",
   820  				"-i", "10",
   821  				"-m", "1G",
   822  				"-b", "https://github.com/heroku/heroku-buildpack-different.git",
   823  				"-s", "differentStack",
   824  				"existing-app",
   825  			)
   826  
   827  			Expect(appRepo.UpdateAppGuid).To(Equal(existingApp.Guid))
   828  			Expect(*appRepo.UpdateParams.Command).To(Equal("different start command"))
   829  			Expect(*appRepo.UpdateParams.InstanceCount).To(Equal(10))
   830  			Expect(*appRepo.UpdateParams.Memory).To(Equal(int64(1024)))
   831  			Expect(*appRepo.UpdateParams.BuildpackUrl).To(Equal("https://github.com/heroku/heroku-buildpack-different.git"))
   832  			Expect(*appRepo.UpdateParams.StackGuid).To(Equal("differentStack-guid"))
   833  		})
   834  
   835  		It("re-uploads the app", func() {
   836  			callPush("existing-app")
   837  
   838  			Expect(ui.Outputs).To(ContainSubstrings(
   839  				[]string{"Uploading", "existing-app"},
   840  				[]string{"OK"},
   841  			))
   842  		})
   843  
   844  		Describe("when the app has a route bound", func() {
   845  			BeforeEach(func() {
   846  				domain := models.DomainFields{
   847  					Name:   "example.com",
   848  					Guid:   "domain-guid",
   849  					Shared: true,
   850  				}
   851  
   852  				domainRepo.ListDomainsForOrgDomains = []models.DomainFields{domain}
   853  				routeRepo.FindByHostAndDomainReturns.Route = models.Route{
   854  					Host:   "existing-app",
   855  					Domain: domain,
   856  				}
   857  
   858  				existingApp.Routes = []models.RouteSummary{models.RouteSummary{
   859  					Guid:   "existing-route-guid",
   860  					Host:   "existing-app",
   861  					Domain: domain,
   862  				}}
   863  
   864  				appRepo.ReadReturns.App = existingApp
   865  				appRepo.UpdateAppResult = existingApp
   866  			})
   867  
   868  			It("uses the existing route when an app already has it bound", func() {
   869  				callPush("-d", "example.com", "existing-app")
   870  
   871  				Expect(routeRepo.CreatedHost).To(Equal(""))
   872  				Expect(ui.Outputs).ToNot(ContainSubstrings([]string{"Creating route"}))
   873  				Expect(ui.Outputs).To(ContainSubstrings([]string{"Using route", "existing-app", "example.com"}))
   874  			})
   875  
   876  			Context("and no route-related flags are given", func() {
   877  				Context("and there is no route in the manifest", func() {
   878  					It("does not add a route to the app", func() {
   879  						callPush("existing-app")
   880  
   881  						appGuid, _, _ := actor.UploadAppArgsForCall(0)
   882  						Expect(appGuid).To(Equal("existing-app-guid"))
   883  						Expect(domainRepo.FindByNameInOrgName).To(Equal(""))
   884  						Expect(routeRepo.FindByHostAndDomainCalledWith.Domain.Name).To(Equal(""))
   885  						Expect(routeRepo.FindByHostAndDomainCalledWith.Host).To(Equal(""))
   886  						Expect(routeRepo.CreatedHost).To(Equal(""))
   887  						Expect(routeRepo.CreatedDomainGuid).To(Equal(""))
   888  					})
   889  				})
   890  
   891  				Context("and there is a route in the manifest", func() {
   892  					BeforeEach(func() {
   893  						manifestRepo.ReadManifestReturns.Manifest = existingAppManifest()
   894  
   895  						routeRepo.FindByHostAndDomainReturns.Error = errors.NewModelNotFoundError("Org", "uh oh")
   896  
   897  						domainRepo.FindByNameInOrgDomain = []models.DomainFields{
   898  							models.DomainFields{Name: "example.com", Guid: "example-domain-guid"},
   899  						}
   900  					})
   901  
   902  					It("adds the route", func() {
   903  						callPush("existing-app")
   904  						Expect(routeRepo.CreatedHost).To(Equal("new-manifest-host"))
   905  					})
   906  				})
   907  			})
   908  
   909  			It("creates and binds a route when a different domain is specified", func() {
   910  				routeRepo.FindByHostAndDomainReturns.Error = errors.NewModelNotFoundError("Org", "existing-app.newdomain.com")
   911  				domainRepo.FindByNameInOrgDomain = []models.DomainFields{
   912  					models.DomainFields{Guid: "domain-guid", Name: "newdomain.com"},
   913  				}
   914  
   915  				callPush("-d", "newdomain.com", "existing-app")
   916  
   917  				Expect(ui.Outputs).To(ContainSubstrings(
   918  					[]string{"Creating route", "existing-app.newdomain.com"},
   919  					[]string{"OK"},
   920  					[]string{"Binding", "existing-app.newdomain.com"},
   921  				))
   922  
   923  				Expect(domainRepo.FindByNameInOrgName).To(Equal("newdomain.com"))
   924  				Expect(domainRepo.FindByNameInOrgGuid).To(Equal("my-org-guid"))
   925  				Expect(routeRepo.FindByHostAndDomainCalledWith.Domain.Name).To(Equal("newdomain.com"))
   926  				Expect(routeRepo.FindByHostAndDomainCalledWith.Host).To(Equal("existing-app"))
   927  				Expect(routeRepo.CreatedHost).To(Equal("existing-app"))
   928  				Expect(routeRepo.CreatedDomainGuid).To(Equal("domain-guid"))
   929  			})
   930  
   931  			It("creates and binds a route when a different hostname is specified", func() {
   932  				routeRepo.FindByHostAndDomainReturns.Error = errors.NewModelNotFoundError("Org", "new-host.newdomain.com")
   933  
   934  				callPush("-n", "new-host", "existing-app")
   935  
   936  				Expect(ui.Outputs).To(ContainSubstrings(
   937  					[]string{"Creating route", "new-host.example.com"},
   938  					[]string{"OK"},
   939  					[]string{"Binding", "new-host.example.com"},
   940  				))
   941  
   942  				Expect(routeRepo.FindByHostAndDomainCalledWith.Domain.Name).To(Equal("example.com"))
   943  				Expect(routeRepo.FindByHostAndDomainCalledWith.Host).To(Equal("new-host"))
   944  				Expect(routeRepo.CreatedHost).To(Equal("new-host"))
   945  				Expect(routeRepo.CreatedDomainGuid).To(Equal("domain-guid"))
   946  			})
   947  
   948  			It("removes the route when the --no-route flag is given", func() {
   949  				callPush("--no-route", "existing-app")
   950  
   951  				appGuid, _, _ := actor.UploadAppArgsForCall(0)
   952  				Expect(appGuid).To(Equal("existing-app-guid"))
   953  				Expect(domainRepo.FindByNameInOrgName).To(Equal(""))
   954  				Expect(routeRepo.FindByHostAndDomainCalledWith.Domain.Name).To(Equal(""))
   955  				Expect(routeRepo.FindByHostAndDomainCalledWith.Host).To(Equal(""))
   956  				Expect(routeRepo.CreatedHost).To(Equal(""))
   957  				Expect(routeRepo.CreatedDomainGuid).To(Equal(""))
   958  				Expect(routeRepo.UnboundRouteGuid).To(Equal("existing-route-guid"))
   959  				Expect(routeRepo.UnboundAppGuid).To(Equal("existing-app-guid"))
   960  			})
   961  
   962  			It("binds the root domain route to an app with a pre-existing route when the --no-hostname flag is given", func() {
   963  				routeRepo.FindByHostAndDomainReturns.Error = errors.NewModelNotFoundError("Org", "existing-app.example.com")
   964  
   965  				callPush("--no-hostname", "existing-app")
   966  
   967  				Expect(ui.Outputs).To(ContainSubstrings(
   968  					[]string{"Creating route", "example.com"},
   969  					[]string{"OK"},
   970  					[]string{"Binding", "example.com"},
   971  				))
   972  				Expect(ui.Outputs).ToNot(ContainSubstrings([]string{"existing-app.example.com"}))
   973  
   974  				Expect(routeRepo.FindByHostAndDomainCalledWith.Domain.Name).To(Equal("example.com"))
   975  				Expect(routeRepo.FindByHostAndDomainCalledWith.Host).To(Equal(""))
   976  				Expect(routeRepo.CreatedHost).To(Equal(""))
   977  				Expect(routeRepo.CreatedDomainGuid).To(Equal("domain-guid"))
   978  			})
   979  		})
   980  	})
   981  
   982  	Describe("service instances", func() {
   983  		BeforeEach(func() {
   984  			serviceRepo.FindInstanceByNameMap = generic.NewMap(map[interface{}]interface{}{
   985  				"global-service": maker.NewServiceInstance("global-service"),
   986  				"app1-service":   maker.NewServiceInstance("app1-service"),
   987  				"app2-service":   maker.NewServiceInstance("app2-service"),
   988  			})
   989  
   990  			manifestRepo.ReadManifestReturns.Manifest = manifestWithServicesAndEnv()
   991  		})
   992  
   993  		Context("when the service is not bound", func() {
   994  			BeforeEach(func() {
   995  				appRepo.ReadReturns.Error = errors.NewModelNotFoundError("App", "the-app")
   996  			})
   997  
   998  			It("binds service instances to the app", func() {
   999  				callPush()
  1000  				Expect(len(serviceBinder.AppsToBind)).To(Equal(4))
  1001  				Expect(serviceBinder.AppsToBind[0].Name).To(Equal("app1"))
  1002  				Expect(serviceBinder.AppsToBind[1].Name).To(Equal("app1"))
  1003  				Expect(serviceBinder.InstancesToBindTo[0].Name).To(Equal("app1-service"))
  1004  				Expect(serviceBinder.InstancesToBindTo[1].Name).To(Equal("global-service"))
  1005  
  1006  				Expect(serviceBinder.AppsToBind[2].Name).To(Equal("app2"))
  1007  				Expect(serviceBinder.AppsToBind[3].Name).To(Equal("app2"))
  1008  				Expect(serviceBinder.InstancesToBindTo[2].Name).To(Equal("app2-service"))
  1009  				Expect(serviceBinder.InstancesToBindTo[3].Name).To(Equal("global-service"))
  1010  
  1011  				Expect(ui.Outputs).To(ContainSubstrings(
  1012  					[]string{"Creating", "app1"},
  1013  					[]string{"OK"},
  1014  					[]string{"Binding service", "app1-service", "app1", "my-org", "my-space", "my-user"},
  1015  					[]string{"OK"},
  1016  					[]string{"Binding service", "global-service", "app1", "my-org", "my-space", "my-user"},
  1017  					[]string{"OK"},
  1018  					[]string{"Creating", "app2"},
  1019  					[]string{"OK"},
  1020  					[]string{"Binding service", "app2-service", "app2", "my-org", "my-space", "my-user"},
  1021  					[]string{"OK"},
  1022  					[]string{"Binding service", "global-service", "app2", "my-org", "my-space", "my-user"},
  1023  					[]string{"OK"},
  1024  				))
  1025  			})
  1026  		})
  1027  
  1028  		Context("when the app is already bound to the service", func() {
  1029  			BeforeEach(func() {
  1030  				appRepo.ReadReturns.App = maker.NewApp(maker.Overrides{})
  1031  				serviceBinder.BindApplicationReturns.Error = errors.NewHttpError(500, "90003", "it don't work")
  1032  			})
  1033  
  1034  			It("gracefully continues", func() {
  1035  				callPush()
  1036  				Expect(len(serviceBinder.AppsToBind)).To(Equal(4))
  1037  				Expect(ui.Outputs).ToNot(ContainSubstrings([]string{"FAILED"}))
  1038  			})
  1039  		})
  1040  
  1041  		Context("when the service instance can't be found", func() {
  1042  			BeforeEach(func() {
  1043  				//				routeRepo.FindByHostAndDomainReturns.Error = errors.new("can't find service instance")
  1044  				serviceRepo.FindInstanceByNameErr = true
  1045  				manifestRepo.ReadManifestReturns.Manifest = manifestWithServicesAndEnv()
  1046  			})
  1047  
  1048  			It("fails with an error", func() {
  1049  				callPush()
  1050  				Expect(ui.Outputs).To(ContainSubstrings(
  1051  					[]string{"FAILED"},
  1052  					[]string{"Could not find service", "app1-service", "app1"},
  1053  				))
  1054  			})
  1055  		})
  1056  
  1057  	})
  1058  
  1059  	Describe("checking for bad flags", func() {
  1060  		It("fails when a non-numeric start timeout is given", func() {
  1061  			appRepo.ReadReturns.Error = errors.NewModelNotFoundError("App", "the-app")
  1062  
  1063  			callPush("-t", "FooeyTimeout", "my-new-app")
  1064  
  1065  			Expect(ui.Outputs).To(ContainSubstrings(
  1066  				[]string{"FAILED"},
  1067  				[]string{"Invalid", "timeout", "FooeyTimeout"},
  1068  			))
  1069  		})
  1070  	})
  1071  
  1072  	Describe("displaying information about files being uploaded", func() {
  1073  		It("displays information about the files being uploaded", func() {
  1074  			app_files.CountFilesReturns(11)
  1075  			zipper.ZipReturns(nil)
  1076  			zipper.GetZipSizeReturns(6100000, nil)
  1077  			actor.GatherFilesReturns([]resources.AppFileResource{resources.AppFileResource{Path: "path/to/app"}, resources.AppFileResource{Path: "bar"}}, true, nil)
  1078  
  1079  			curDir, err := os.Getwd()
  1080  			Expect(err).NotTo(HaveOccurred())
  1081  
  1082  			callPush("appName")
  1083  			Expect(ui.Outputs).To(ContainSubstrings(
  1084  				[]string{"Uploading", curDir},
  1085  				[]string{"5.8M", "11 files"},
  1086  			))
  1087  		})
  1088  	})
  1089  
  1090  	It("fails when the app can't be uploaded", func() {
  1091  		actor.UploadAppReturns(errors.New("Boom!"))
  1092  
  1093  		callPush("app")
  1094  
  1095  		Expect(ui.Outputs).To(ContainSubstrings(
  1096  			[]string{"Uploading"},
  1097  			[]string{"FAILED"},
  1098  		))
  1099  	})
  1100  
  1101  	Describe("when binding the route fails", func() {
  1102  		BeforeEach(func() {
  1103  			routeRepo.FindByHostAndDomainReturns.Route.Host = "existing-app"
  1104  			routeRepo.FindByHostAndDomainReturns.Route.Domain = models.DomainFields{Name: "foo.cf-app.com"}
  1105  		})
  1106  
  1107  		It("suggests using 'random-route' if the default route is taken", func() {
  1108  			routeRepo.BindErr = errors.NewHttpError(400, errors.INVALID_RELATION, "The URL not available")
  1109  
  1110  			callPush("existing-app")
  1111  
  1112  			Expect(ui.Outputs).To(ContainSubstrings(
  1113  				[]string{"FAILED"},
  1114  				[]string{"existing-app.foo.cf-app.com", "already in use"},
  1115  				[]string{"TIP", "random-route"},
  1116  			))
  1117  		})
  1118  
  1119  		It("does not suggest using 'random-route' for other failures", func() {
  1120  			routeRepo.BindErr = errors.NewHttpError(500, "some-code", "exception happened")
  1121  
  1122  			callPush("existing-app")
  1123  
  1124  			Expect(ui.Outputs).To(ContainSubstrings([]string{"FAILED"}))
  1125  			Expect(ui.Outputs).ToNot(ContainSubstrings([]string{"TIP", "random-route"}))
  1126  		})
  1127  	})
  1128  
  1129  	It("fails when neither a manifest nor a name is given", func() {
  1130  		manifestRepo.ReadManifestReturns.Error = errors.New("No such manifest")
  1131  		callPush()
  1132  		Expect(ui.Outputs).To(ContainSubstrings(
  1133  			[]string{"FAILED"},
  1134  			[]string{"Manifest file is not found"},
  1135  			[]string{"USAGE:"},
  1136  		))
  1137  	})
  1138  })
  1139  
  1140  func existingAppManifest() *manifest.Manifest {
  1141  	return &manifest.Manifest{
  1142  		Path: "manifest.yml",
  1143  		Data: generic.NewMap(map[interface{}]interface{}{
  1144  			"applications": []interface{}{
  1145  				generic.NewMap(map[interface{}]interface{}{
  1146  					"name":      "manifest-app-name",
  1147  					"memory":    "128MB",
  1148  					"instances": 1,
  1149  					"host":      "new-manifest-host",
  1150  					"domain":    "example.com",
  1151  					"stack":     "custom-stack",
  1152  					"timeout":   360,
  1153  					"buildpack": "some-buildpack",
  1154  					"command":   `JAVA_HOME=$PWD/.openjdk JAVA_OPTS="-Xss995K" ./bin/start.sh run`,
  1155  					"path":      filepath.Clean("some/path/from/manifest"),
  1156  					"env": generic.NewMap(map[interface{}]interface{}{
  1157  						"FOO":  "baz",
  1158  						"PATH": "/u/apps/my-app/bin",
  1159  					}),
  1160  				}),
  1161  			},
  1162  		}),
  1163  	}
  1164  }
  1165  
  1166  func multipleHostManifest() *manifest.Manifest {
  1167  	return &manifest.Manifest{
  1168  		Path: "manifest.yml",
  1169  		Data: generic.NewMap(map[interface{}]interface{}{
  1170  			"applications": []interface{}{
  1171  				generic.NewMap(map[interface{}]interface{}{
  1172  					"name":      "manifest-app-name",
  1173  					"memory":    "128MB",
  1174  					"instances": 1,
  1175  					"hosts":     []interface{}{"manifest-host-1", "manifest-host-2"},
  1176  					"domain":    "manifest-example.com",
  1177  					"stack":     "custom-stack",
  1178  					"timeout":   360,
  1179  					"buildpack": "some-buildpack",
  1180  					"command":   `JAVA_HOME=$PWD/.openjdk JAVA_OPTS="-Xss995K" ./bin/start.sh run`,
  1181  					"path":      filepath.Clean("some/path/from/manifest"),
  1182  					"env": generic.NewMap(map[interface{}]interface{}{
  1183  						"FOO":  "baz",
  1184  						"PATH": "/u/apps/my-app/bin",
  1185  					}),
  1186  				}),
  1187  			},
  1188  		}),
  1189  	}
  1190  }
  1191  
  1192  func multipleDomainsManifest() *manifest.Manifest {
  1193  	return &manifest.Manifest{
  1194  		Path: "manifest.yml",
  1195  		Data: generic.NewMap(map[interface{}]interface{}{
  1196  			"applications": []interface{}{
  1197  				generic.NewMap(map[interface{}]interface{}{
  1198  					"name":      "manifest-app-name",
  1199  					"memory":    "128MB",
  1200  					"instances": 1,
  1201  					"host":      "manifest-host",
  1202  					"hosts":     []interface{}{"host2"},
  1203  					"domains":   []interface{}{"example1.com", "example2.com"},
  1204  					"stack":     "custom-stack",
  1205  					"timeout":   360,
  1206  					"buildpack": "some-buildpack",
  1207  					"command":   `JAVA_HOME=$PWD/.openjdk JAVA_OPTS="-Xss995K" ./bin/start.sh run`,
  1208  					"path":      filepath.Clean("some/path/from/manifest"),
  1209  					"env": generic.NewMap(map[interface{}]interface{}{
  1210  						"FOO":  "baz",
  1211  						"PATH": "/u/apps/my-app/bin",
  1212  					}),
  1213  				}),
  1214  			},
  1215  		}),
  1216  	}
  1217  }
  1218  
  1219  func singleAppManifest() *manifest.Manifest {
  1220  	return &manifest.Manifest{
  1221  		Path: "manifest.yml",
  1222  		Data: generic.NewMap(map[interface{}]interface{}{
  1223  			"applications": []interface{}{
  1224  				generic.NewMap(map[interface{}]interface{}{
  1225  					"name":      "manifest-app-name",
  1226  					"memory":    "128MB",
  1227  					"instances": 1,
  1228  					"host":      "manifest-host",
  1229  					"domain":    "manifest-example.com",
  1230  					"stack":     "custom-stack",
  1231  					"timeout":   360,
  1232  					"buildpack": "some-buildpack",
  1233  					"command":   `JAVA_HOME=$PWD/.openjdk JAVA_OPTS="-Xss995K" ./bin/start.sh run`,
  1234  					"path":      filepath.Clean("some/path/from/manifest"),
  1235  					"env": generic.NewMap(map[interface{}]interface{}{
  1236  						"FOO":  "baz",
  1237  						"PATH": "/u/apps/my-app/bin",
  1238  					}),
  1239  				}),
  1240  			},
  1241  		}),
  1242  	}
  1243  }
  1244  
  1245  func manifestWithServicesAndEnv() *manifest.Manifest {
  1246  	return &manifest.Manifest{
  1247  		Data: generic.NewMap(map[interface{}]interface{}{
  1248  			"applications": []interface{}{
  1249  				generic.NewMap(map[interface{}]interface{}{
  1250  					"name":     "app1",
  1251  					"services": []interface{}{"app1-service", "global-service"},
  1252  					"env": generic.NewMap(map[interface{}]interface{}{
  1253  						"SOMETHING": "definitely-something",
  1254  					}),
  1255  				}),
  1256  				generic.NewMap(map[interface{}]interface{}{
  1257  					"name":     "app2",
  1258  					"services": []interface{}{"app2-service", "global-service"},
  1259  					"env": generic.NewMap(map[interface{}]interface{}{
  1260  						"SOMETHING": "nothing",
  1261  					}),
  1262  				}),
  1263  			},
  1264  		}),
  1265  	}
  1266  }