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