github.com/cloudfoundry-community/cloudfoundry-cli@v6.44.1-0.20240130060226-cda5ed8e89a5+incompatible/util/manifest/manifest_test.go (about)

     1  package manifest_test
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	"code.cloudfoundry.org/cli/types"
    12  	. "code.cloudfoundry.org/cli/util/manifest"
    13  	"github.com/cloudfoundry/bosh-cli/director/template"
    14  	. "github.com/onsi/ginkgo"
    15  	. "github.com/onsi/ginkgo/extensions/table"
    16  	. "github.com/onsi/gomega"
    17  	. "github.com/onsi/gomega/gstruct"
    18  )
    19  
    20  var _ = Describe("Manifest", func() {
    21  	var manifest string
    22  
    23  	Describe("ReadAndInterpolateManifest", func() {
    24  		var (
    25  			pathToManifest   string
    26  			pathsToVarsFiles []string
    27  			vars             []template.VarKV
    28  			apps             []Application
    29  			executeErr       error
    30  		)
    31  
    32  		BeforeEach(func() {
    33  			tempFile, err := ioutil.TempFile("", "manifest-test-")
    34  			Expect(err).ToNot(HaveOccurred())
    35  			Expect(tempFile.Close()).ToNot(HaveOccurred())
    36  			pathToManifest = tempFile.Name()
    37  			vars = nil
    38  
    39  			pathsToVarsFiles = nil
    40  		})
    41  
    42  		AfterEach(func() {
    43  			Expect(os.RemoveAll(pathToManifest)).ToNot(HaveOccurred())
    44  			for _, path := range pathsToVarsFiles {
    45  				Expect(os.RemoveAll(path)).ToNot(HaveOccurred())
    46  			}
    47  		})
    48  
    49  		JustBeforeEach(func() {
    50  			apps, executeErr = ReadAndInterpolateManifest(pathToManifest, pathsToVarsFiles, vars)
    51  		})
    52  
    53  		When("the manifest contains NO variables that need interpolation", func() {
    54  			BeforeEach(func() {
    55  				manifest = `---
    56  applications:
    57  - name: app-1
    58    buildpack: "some-buildpack"
    59    command: "some-command"
    60    health-check-http-endpoint: "\\some-endpoint"
    61    health-check-type: "http"
    62    instances: 10
    63    disk_quota: 100M
    64    docker:
    65      image: "some-docker-image"
    66      username: "some-docker-username"
    67    memory: 200M
    68    random-route: true
    69    stack: "some-stack"
    70    timeout: 120
    71  - name: "app-2"
    72    buildpack: default
    73    disk_quota: 1G
    74    instances: 0
    75    memory: 2G
    76    routes:
    77    - route: foo.bar.com
    78    - route: baz.qux.com
    79    - route: blep.blah.com/boop
    80    services:
    81    - service_1
    82    - service_2
    83  - name: "app-3"
    84    no-route: true
    85    env:
    86      env_1: 'foo'
    87      env_2: 182837403930483038
    88      env_3: true
    89      env_4: 1.00001
    90  - name: "app-4"
    91    buildpack: null
    92    command: null
    93  - name: "app-5"
    94    domain: "some-domain"
    95    domains:
    96    - domain_1
    97    - domain_2
    98  - name: "app-6"
    99    host: "some-hostname"
   100    hosts:
   101    - hostname_1
   102    - hostname_2
   103    no-hostname: true
   104  - name: "app-7"
   105    routes:
   106    - route: hello.com
   107    - route: bleep.blah.com
   108    random-route: true
   109  `
   110  
   111  				err := ioutil.WriteFile(pathToManifest, []byte(manifest), 0666)
   112  				Expect(err).ToNot(HaveOccurred())
   113  			})
   114  
   115  			When("the manifest does not contain deprecated fields", func() {
   116  				It("returns a merged set of applications", func() {
   117  					Expect(executeErr).ToNot(HaveOccurred())
   118  					Expect(apps).To(HaveLen(7))
   119  
   120  					Expect(apps[0]).To(Equal(Application{
   121  						Name: "app-1",
   122  						Buildpack: types.FilteredString{
   123  							IsSet: true,
   124  							Value: "some-buildpack",
   125  						},
   126  						Command: types.FilteredString{
   127  							IsSet: true,
   128  							Value: "some-command",
   129  						},
   130  						HealthCheckHTTPEndpoint: `\some-endpoint`,
   131  						HealthCheckType:         "http",
   132  						Instances: types.NullInt{
   133  							Value: 10,
   134  							IsSet: true,
   135  						},
   136  						DiskQuota: types.NullByteSizeInMb{
   137  							Value: 100,
   138  							IsSet: true,
   139  						},
   140  						DockerImage:    "some-docker-image",
   141  						DockerUsername: "some-docker-username",
   142  						Memory: types.NullByteSizeInMb{
   143  							Value: 200,
   144  							IsSet: true,
   145  						},
   146  						RandomRoute:        true,
   147  						StackName:          "some-stack",
   148  						HealthCheckTimeout: 120,
   149  					}))
   150  
   151  					Expect(apps[1]).To(Equal(Application{
   152  						Name: "app-2",
   153  						Buildpack: types.FilteredString{
   154  							IsSet: true,
   155  							Value: "",
   156  						},
   157  						DiskQuota: types.NullByteSizeInMb{
   158  							Value: 1024,
   159  							IsSet: true,
   160  						},
   161  						Instances: types.NullInt{
   162  							IsSet: true,
   163  							Value: 0,
   164  						},
   165  						Memory: types.NullByteSizeInMb{
   166  							Value: 2048,
   167  							IsSet: true,
   168  						},
   169  						Routes:   []string{"foo.bar.com", "baz.qux.com", "blep.blah.com/boop"},
   170  						Services: []string{"service_1", "service_2"},
   171  					}))
   172  
   173  					Expect(apps[2]).To(Equal(Application{
   174  						Name: "app-3",
   175  						EnvironmentVariables: map[string]string{
   176  							"env_1": "foo",
   177  							"env_2": "182837403930483038",
   178  							"env_3": "true",
   179  							"env_4": "1.00001",
   180  						},
   181  						NoRoute: true,
   182  					}))
   183  
   184  					Expect(apps[3]).To(Equal(Application{
   185  						Name: "app-4",
   186  						Buildpack: types.FilteredString{
   187  							IsSet: true,
   188  							Value: "",
   189  						},
   190  						Command: types.FilteredString{
   191  							IsSet: true,
   192  							Value: "",
   193  						},
   194  					}))
   195  
   196  					Expect(apps[4].Name).To(Equal("app-5"))
   197  					Expect(apps[4].DeprecatedDomain).ToNot(BeNil())
   198  					Expect(apps[4].DeprecatedDomains).ToNot(BeNil())
   199  
   200  					Expect(apps[5].Name).To(Equal("app-6"))
   201  					Expect(apps[5].DeprecatedHost).ToNot(BeNil())
   202  					Expect(apps[5].DeprecatedHosts).ToNot(BeNil())
   203  					Expect(apps[5].DeprecatedNoHostname).ToNot(BeNil())
   204  
   205  					Expect(apps[6]).To(Equal(Application{
   206  						Name:        "app-7",
   207  						Routes:      []string{"hello.com", "bleep.blah.com"},
   208  						RandomRoute: true,
   209  					}))
   210  				})
   211  			})
   212  
   213  			When("provided deprecated fields", func() {
   214  				When("global fields are provided", func() {
   215  					DescribeTable("raises a GlobalFieldsError",
   216  						func(manifestProperty string, numberOfValues int) {
   217  							tempManifest, err := ioutil.TempFile("", "manifest-test-")
   218  							Expect(err).ToNot(HaveOccurred())
   219  							Expect(tempManifest.Close()).ToNot(HaveOccurred())
   220  							manifestPath := tempManifest.Name()
   221  							defer os.RemoveAll(manifestPath)
   222  
   223  							if numberOfValues == 1 {
   224  								manifest = fmt.Sprintf("---\n%s: value", manifestProperty)
   225  							} else {
   226  								values := []string{"A", "B"}
   227  								manifest = fmt.Sprintf("---\n%s: [%s]", manifestProperty, strings.Join(values, ","))
   228  							}
   229  							err = ioutil.WriteFile(manifestPath, []byte(manifest), 0666)
   230  							Expect(err).ToNot(HaveOccurred())
   231  
   232  							_, err = ReadAndInterpolateManifest(manifestPath, pathsToVarsFiles, vars)
   233  							Expect(err).To(MatchError(GlobalFieldsError{Fields: []string{manifestProperty}}))
   234  						},
   235  
   236  						Entry("global buildpack", "buildpack", 1),
   237  						Entry("global command", "command", 1),
   238  						Entry("global disk quota", "disk_quota", 1),
   239  						Entry("global docker", "docker", 1),
   240  						Entry("global domain", "domain", 1),
   241  						Entry("global domains", "domains", 2),
   242  						Entry("global environment variables", "env", 2),
   243  						Entry("global health check HTTP endpoint", "health-check-http-endpoint", 1),
   244  						Entry("global health check timeout", "timeout", 1),
   245  						Entry("global health check type", "health-check-type", 1),
   246  						Entry("global host", "host", 1),
   247  						Entry("global hosts", "hosts", 2),
   248  						Entry("global instances", "instances", 1),
   249  						Entry("global memory", "memory", 1),
   250  						Entry("global name", "name", 1),
   251  						Entry("global no hostname", "no-hostname", 1),
   252  						Entry("global no route", "no-route", 1),
   253  						Entry("global path", "path", 1),
   254  						Entry("global random-route", "random-route", 1),
   255  						Entry("global routes", "routes", 2),
   256  						Entry("global services", "services", 2),
   257  						Entry("global stack", "stack", 1),
   258  					)
   259  				})
   260  			})
   261  
   262  			When("inheritance is provided", func() {
   263  				BeforeEach(func() {
   264  					manifest = `---
   265  inherit: "./some-inheritance-file"
   266  applications:
   267  - name: "app-1"
   268  `
   269  
   270  					err := ioutil.WriteFile(pathToManifest, []byte(manifest), 0666)
   271  					Expect(err).ToNot(HaveOccurred())
   272  				})
   273  
   274  				It("raises an InheritanceFieldError", func() {
   275  					Expect(executeErr).To(MatchError(InheritanceFieldError{}))
   276  				})
   277  			})
   278  
   279  			When("the manifest specified a single buildpack", func() {
   280  				BeforeEach(func() {
   281  					manifest = `---
   282  applications:
   283  - name: app-1
   284    buildpack: "some-buildpack"
   285    memory: 200M
   286    instances: 10
   287  `
   288  					err := ioutil.WriteFile(pathToManifest, []byte(manifest), 0666)
   289  					Expect(err).ToNot(HaveOccurred())
   290  				})
   291  
   292  				It("returns a merged set of applications", func() {
   293  					Expect(executeErr).ToNot(HaveOccurred())
   294  					Expect(apps).To(HaveLen(1))
   295  
   296  					Expect(apps[0]).To(MatchFields(IgnoreExtras, Fields{
   297  						"Buildpack": Equal(types.FilteredString{
   298  							IsSet: true,
   299  							Value: "some-buildpack",
   300  						}),
   301  						"Buildpacks": BeEmpty(),
   302  					}))
   303  				})
   304  			})
   305  
   306  			When("the manifest contains buildpacks (plural)", func() {
   307  				BeforeEach(func() {
   308  					manifest = `---
   309  applications:
   310  - name: app-1
   311    buildpacks:
   312    - "some-buildpack-1"
   313    - "some-buildpack-2"
   314    memory: 200M
   315    instances: 10
   316  - name: app-2
   317    buildpacks:
   318    - "some-other-buildpack-1"
   319    - "some-other-buildpack-2"
   320    memory: 2048M
   321    instances: 0`
   322  					err := ioutil.WriteFile(pathToManifest, []byte(manifest), 0666)
   323  					Expect(err).ToNot(HaveOccurred())
   324  				})
   325  
   326  				It("returns a merged set of applications", func() {
   327  					Expect(executeErr).ToNot(HaveOccurred())
   328  					Expect(apps).To(HaveLen(2))
   329  
   330  					Expect(apps[0]).To(MatchFields(IgnoreExtras, Fields{
   331  						"Buildpack": Equal(types.FilteredString{
   332  							IsSet: false,
   333  							Value: "",
   334  						}),
   335  						"Buildpacks": ConsistOf("some-buildpack-1", "some-buildpack-2"),
   336  					}))
   337  
   338  					Expect(apps[1]).To(MatchFields(IgnoreExtras, Fields{
   339  						"Buildpack": Equal(types.FilteredString{
   340  							IsSet: false,
   341  							Value: "",
   342  						}),
   343  						"Buildpacks": ConsistOf(
   344  							"some-other-buildpack-1",
   345  							"some-other-buildpack-2",
   346  						),
   347  					}))
   348  				})
   349  			})
   350  
   351  			When("the manifest sets buildpacks to an empty array", func() {
   352  				BeforeEach(func() {
   353  					manifest = `---
   354  applications:
   355  - name: app-1
   356    buildpacks: []
   357    memory: 200M
   358    instances: 10
   359  `
   360  					err := ioutil.WriteFile(pathToManifest, []byte(manifest), 0666)
   361  					Expect(err).ToNot(HaveOccurred())
   362  				})
   363  
   364  				It("returns a merged set of applications", func() {
   365  					Expect(executeErr).ToNot(HaveOccurred())
   366  					Expect(apps).To(HaveLen(1))
   367  
   368  					Expect(apps[0]).To(MatchFields(IgnoreExtras, Fields{
   369  						"Buildpack": Equal(types.FilteredString{
   370  							IsSet: false,
   371  							Value: "",
   372  						}),
   373  						"Buildpacks": Equal([]string{}),
   374  					}))
   375  				})
   376  			})
   377  
   378  			When("the manifest contains an empty buildpacks attribute", func() {
   379  				BeforeEach(func() {
   380  					manifest = `---
   381  applications:
   382  - name: app-1
   383    buildpacks:
   384  `
   385  					err := ioutil.WriteFile(pathToManifest, []byte(manifest), 0666)
   386  					Expect(err).ToNot(HaveOccurred())
   387  				})
   388  
   389  				It("raises an error", func() {
   390  					Expect(executeErr).ToNot(MatchError(new(EmptyBuildpacksError)))
   391  				})
   392  			})
   393  		})
   394  
   395  		When("the manifest contains variables that need interpolation", func() {
   396  			BeforeEach(func() {
   397  				manifest = `---
   398  applications:
   399  - name: ((var1))
   400    instances: ((var2))
   401  `
   402  				err := ioutil.WriteFile(pathToManifest, []byte(manifest), 0666)
   403  				Expect(err).ToNot(HaveOccurred())
   404  			})
   405  
   406  			When("only vars files are provided", func() {
   407  				var (
   408  					varsDir string
   409  				)
   410  
   411  				BeforeEach(func() {
   412  					var err error
   413  					varsDir, err = ioutil.TempDir("", "vars-test")
   414  					Expect(err).ToNot(HaveOccurred())
   415  
   416  					varsFilePath1 := filepath.Join(varsDir, "vars-1")
   417  					err = ioutil.WriteFile(varsFilePath1, []byte("var1: app-1"), 0666)
   418  					Expect(err).ToNot(HaveOccurred())
   419  
   420  					varsFilePath2 := filepath.Join(varsDir, "vars-2")
   421  					err = ioutil.WriteFile(varsFilePath2, []byte("var2: 4"), 0666)
   422  					Expect(err).ToNot(HaveOccurred())
   423  
   424  					pathsToVarsFiles = append(pathsToVarsFiles, varsFilePath1, varsFilePath2)
   425  				})
   426  
   427  				AfterEach(func() {
   428  					Expect(os.RemoveAll(varsDir)).ToNot(HaveOccurred())
   429  				})
   430  
   431  				When("multiple values for the same variable(s) are provided", func() {
   432  					BeforeEach(func() {
   433  						varsFilePath1 := filepath.Join(varsDir, "vars-1")
   434  						err := ioutil.WriteFile(varsFilePath1, []byte("var1: garbageapp\nvar1: app-1\nvar2: 0"), 0666)
   435  						Expect(err).ToNot(HaveOccurred())
   436  
   437  						varsFilePath2 := filepath.Join(varsDir, "vars-2")
   438  						err = ioutil.WriteFile(varsFilePath2, []byte("var2: 4"), 0666)
   439  						Expect(err).ToNot(HaveOccurred())
   440  
   441  						pathsToVarsFiles = append(pathsToVarsFiles, varsFilePath1, varsFilePath2)
   442  					})
   443  
   444  					It("interpolates the placeholder values", func() {
   445  						Expect(executeErr).ToNot(HaveOccurred())
   446  						Expect(apps[0].Name).To(Equal("app-1"))
   447  						Expect(apps[0].Instances).To(Equal(types.NullInt{Value: 4, IsSet: true}))
   448  					})
   449  				})
   450  
   451  				When("the provided files exists and contain valid yaml", func() {
   452  					It("interpolates the placeholder values", func() {
   453  						Expect(executeErr).ToNot(HaveOccurred())
   454  						Expect(apps[0].Name).To(Equal("app-1"))
   455  						Expect(apps[0].Instances).To(Equal(types.NullInt{Value: 4, IsSet: true}))
   456  					})
   457  				})
   458  
   459  				When("a variable in the manifest is not provided in the vars file", func() {
   460  					BeforeEach(func() {
   461  						varsFilePath := filepath.Join(varsDir, "vars-1")
   462  						err := ioutil.WriteFile(varsFilePath, []byte("notvar: foo"), 0666)
   463  						Expect(err).ToNot(HaveOccurred())
   464  
   465  						pathsToVarsFiles = []string{varsFilePath}
   466  					})
   467  
   468  					It("returns an error", func() {
   469  						Expect(executeErr.Error()).To(Equal("Expected to find variables: var1, var2"))
   470  					})
   471  				})
   472  
   473  				When("the provided file path does not exist", func() {
   474  					BeforeEach(func() {
   475  						pathsToVarsFiles = []string{"garbagepath"}
   476  					})
   477  
   478  					It("returns an error", func() {
   479  						Expect(executeErr).To(HaveOccurred())
   480  						Expect(os.IsNotExist(executeErr)).To(BeTrue())
   481  					})
   482  				})
   483  
   484  				When("the provided file is not a valid yaml file", func() {
   485  					BeforeEach(func() {
   486  						varsFilePath := filepath.Join(varsDir, "vars-1")
   487  						err := ioutil.WriteFile(varsFilePath, []byte(": bad"), 0666)
   488  						Expect(err).ToNot(HaveOccurred())
   489  
   490  						pathsToVarsFiles = []string{varsFilePath}
   491  					})
   492  
   493  					It("returns an error", func() {
   494  						Expect(executeErr).To(HaveOccurred())
   495  						Expect(executeErr).To(MatchError(InvalidYAMLError{
   496  							Err: errors.New("yaml: did not find expected key"),
   497  						}))
   498  					})
   499  				})
   500  			})
   501  
   502  			When("only vars are provided", func() {
   503  				BeforeEach(func() {
   504  					vars = []template.VarKV{
   505  						{Name: "var1", Value: "app-1"},
   506  						{Name: "var2", Value: 4},
   507  					}
   508  					manifest = `---
   509  applications:
   510  - name: ((var1))
   511    instances: ((var2))
   512  `
   513  				})
   514  
   515  				It("interpolates the placeholder values", func() {
   516  					Expect(executeErr).ToNot(HaveOccurred())
   517  					Expect(apps[0].Name).To(Equal("app-1"))
   518  					Expect(apps[0].Instances).To(Equal(types.NullInt{Value: 4, IsSet: true}))
   519  				})
   520  			})
   521  
   522  			When("vars and vars files are provided", func() {
   523  				var varsFilePath string
   524  				BeforeEach(func() {
   525  					tmp, err := ioutil.TempFile("", "util-manifest-varsilfe")
   526  					Expect(err).NotTo(HaveOccurred())
   527  					Expect(tmp.Close()).NotTo(HaveOccurred())
   528  
   529  					varsFilePath = tmp.Name()
   530  					err = ioutil.WriteFile(varsFilePath, []byte("var1: app-1\nvar2: 12345"), 0666)
   531  					Expect(err).ToNot(HaveOccurred())
   532  
   533  					pathsToVarsFiles = []string{varsFilePath}
   534  					vars = []template.VarKV{
   535  						{Name: "var2", Value: 4},
   536  					}
   537  				})
   538  
   539  				AfterEach(func() {
   540  					Expect(os.RemoveAll(varsFilePath)).ToNot(HaveOccurred())
   541  				})
   542  
   543  				It("interpolates the placeholder values, prioritizing the vars flag", func() {
   544  					Expect(executeErr).ToNot(HaveOccurred())
   545  					Expect(apps[0].Name).To(Equal("app-1"))
   546  					Expect(apps[0].Instances).To(Equal(types.NullInt{Value: 4, IsSet: true}))
   547  				})
   548  			})
   549  		})
   550  	})
   551  
   552  	Describe("WriteApplicationManifest", func() {
   553  		var (
   554  			application Application
   555  			tmpDir      string
   556  			filePath    string
   557  
   558  			executeErr error
   559  		)
   560  
   561  		BeforeEach(func() {
   562  			var err error
   563  			tmpDir, err = ioutil.TempDir("", "manifest-test-")
   564  			Expect(err).NotTo(HaveOccurred())
   565  			filePath = filepath.Join(tmpDir, "manifest.yml")
   566  		})
   567  
   568  		AfterEach(func() {
   569  			os.RemoveAll(tmpDir)
   570  		})
   571  
   572  		JustBeforeEach(func() {
   573  			executeErr = WriteApplicationManifest(application, filePath)
   574  		})
   575  
   576  		When("all app properties are provided", func() {
   577  			BeforeEach(func() {
   578  				application = Application{
   579  					Name: "app-1",
   580  					Buildpack: types.FilteredString{
   581  						IsSet: true,
   582  						Value: "some-buildpack",
   583  					},
   584  					Buildpacks: []string{"buildpack1", "buildpack2"},
   585  					Command: types.FilteredString{
   586  						IsSet: true,
   587  						Value: "some-command",
   588  					},
   589  					DockerImage:    "some-docker-image",
   590  					DockerUsername: "some-docker-username",
   591  					DockerPassword: "",
   592  					EnvironmentVariables: map[string]string{
   593  						"env_1": "foo",
   594  						"env_2": "182837403930483038",
   595  						"env_3": "true",
   596  						"env_4": "1.00001",
   597  					},
   598  					HealthCheckHTTPEndpoint: `\some-endpoint`,
   599  					HealthCheckType:         "http",
   600  					Instances: types.NullInt{
   601  						Value: 10,
   602  						IsSet: true,
   603  					},
   604  					DiskQuota: types.NullByteSizeInMb{
   605  						Value: 1024,
   606  						IsSet: true,
   607  					},
   608  					Memory: types.NullByteSizeInMb{
   609  						Value: 200,
   610  						IsSet: true,
   611  					},
   612  					NoRoute:            true,
   613  					Routes:             []string{"foo.bar.com", "baz.qux.com", "blep.blah.com/boop"},
   614  					Services:           []string{"service_1", "service_2"},
   615  					StackName:          "some-stack",
   616  					HealthCheckTimeout: 120,
   617  				}
   618  			})
   619  
   620  			It("creates and writes the manifest to the specified filepath", func() {
   621  				Expect(executeErr).NotTo(HaveOccurred())
   622  				manifestBytes, err := ioutil.ReadFile(filePath)
   623  				Expect(err).NotTo(HaveOccurred())
   624  				Expect(string(manifestBytes)).To(Equal(`applications:
   625  - name: app-1
   626    buildpack: some-buildpack
   627    buildpacks:
   628    - buildpack1
   629    - buildpack2
   630    command: some-command
   631    disk_quota: 1G
   632    docker:
   633      image: some-docker-image
   634      username: some-docker-username
   635    env:
   636      env_1: foo
   637      env_2: "182837403930483038"
   638      env_3: "true"
   639      env_4: "1.00001"
   640    health-check-http-endpoint: \some-endpoint
   641    health-check-type: http
   642    instances: 10
   643    memory: 200M
   644    no-route: true
   645    routes:
   646    - route: foo.bar.com
   647    - route: baz.qux.com
   648    - route: blep.blah.com/boop
   649    services:
   650    - service_1
   651    - service_2
   652    stack: some-stack
   653    timeout: 120
   654  `))
   655  			})
   656  		})
   657  
   658  		When("some properties are not provided", func() {
   659  			BeforeEach(func() {
   660  				application = Application{
   661  					Name: "app-1",
   662  				}
   663  			})
   664  
   665  			It("does not save them in manifest", func() {
   666  				Expect(executeErr).NotTo(HaveOccurred())
   667  				manifestBytes, err := ioutil.ReadFile(filePath)
   668  				Expect(err).NotTo(HaveOccurred())
   669  				Expect(string(manifestBytes)).To(Equal(`applications:
   670  - name: app-1
   671  `))
   672  			})
   673  		})
   674  
   675  		When("the file is a relative path", func() {
   676  			var pwd string
   677  
   678  			BeforeEach(func() {
   679  				var err error
   680  				pwd, err = os.Getwd()
   681  				Expect(err).ToNot(HaveOccurred())
   682  
   683  				filePath = "./manifest.yml"
   684  				Expect(os.Chdir(tmpDir)).To(Succeed())
   685  
   686  				application = Application{
   687  					Name: "app-1",
   688  				}
   689  			})
   690  
   691  			AfterEach(func() {
   692  				Expect(os.Chdir(pwd)).To(Succeed())
   693  			})
   694  
   695  			It("writes the file in an expanded path", func() {
   696  				Expect(executeErr).ToNot(HaveOccurred())
   697  				manifestBytes, err := ioutil.ReadFile(filepath.Join(tmpDir, "manifest.yml"))
   698  				Expect(err).ToNot(HaveOccurred())
   699  				Expect(string(manifestBytes)).To(Equal(`applications:
   700  - name: app-1
   701  `))
   702  			})
   703  		})
   704  
   705  		When("the file already exists", func() {
   706  			BeforeEach(func() {
   707  				err := ioutil.WriteFile(filePath, []byte(`{}`), 0644)
   708  				Expect(err).ToNot(HaveOccurred())
   709  				application = Application{
   710  					Name: "app-1",
   711  				}
   712  			})
   713  
   714  			Context("writes the file", func() {
   715  				It("truncates and writes the manifest to specified filepath", func() {
   716  					Expect(executeErr).ToNot(HaveOccurred())
   717  					manifestBytes, err := ioutil.ReadFile(filePath)
   718  					Expect(err).ToNot(HaveOccurred())
   719  					Expect(string(manifestBytes)).To(Equal(`applications:
   720  - name: app-1
   721  `))
   722  				})
   723  			})
   724  		})
   725  	})
   726  })