github.com/jghiloni/cli@v6.28.1-0.20170628223758-0ce05fe032a2+incompatible/cf/manifest/manifest_test.go (about)

     1  package manifest_test
     2  
     3  import (
     4  	"runtime"
     5  	"strings"
     6  
     7  	"code.cloudfoundry.org/cli/cf/manifest"
     8  	"code.cloudfoundry.org/cli/util/generic"
     9  	. "github.com/onsi/ginkgo"
    10  	. "github.com/onsi/gomega"
    11  
    12  	. "code.cloudfoundry.org/cli/util/testhelpers/matchers"
    13  )
    14  
    15  func NewManifest(path string, data generic.Map) (m *manifest.Manifest) {
    16  	return &manifest.Manifest{Path: path, Data: data}
    17  }
    18  
    19  var _ = Describe("Manifests", func() {
    20  	It("merges global properties into each app's properties", func() {
    21  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
    22  			"instances": "3",
    23  			"memory":    "512M",
    24  			"applications": []interface{}{
    25  				map[interface{}]interface{}{
    26  					"name":     "bitcoin-miner",
    27  					"no-route": true,
    28  				},
    29  			},
    30  		}))
    31  
    32  		apps, err := m.Applications()
    33  		Expect(err).NotTo(HaveOccurred())
    34  
    35  		Expect(*apps[0].InstanceCount).To(Equal(3))
    36  		Expect(*apps[0].Memory).To(Equal(int64(512)))
    37  		Expect(apps[0].NoRoute).To(BeTrue())
    38  	})
    39  
    40  	Context("when there is no applications block", func() {
    41  		It("returns a single application with the global properties", func() {
    42  			m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
    43  				"instances": "3",
    44  				"memory":    "512M",
    45  			}))
    46  
    47  			apps, err := m.Applications()
    48  			Expect(err).NotTo(HaveOccurred())
    49  
    50  			Expect(len(apps)).To(Equal(1))
    51  			Expect(*apps[0].InstanceCount).To(Equal(3))
    52  			Expect(*apps[0].Memory).To(Equal(int64(512)))
    53  		})
    54  	})
    55  
    56  	It("returns an error when the memory limit doesn't have a unit", func() {
    57  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
    58  			"instances": "3",
    59  			"memory":    "512",
    60  			"applications": []interface{}{
    61  				map[interface{}]interface{}{
    62  					"name": "bitcoin-miner",
    63  				},
    64  			},
    65  		}))
    66  
    67  		_, err := m.Applications()
    68  		Expect(err).To(HaveOccurred())
    69  		Expect(err.Error()).To(ContainSubstring("Invalid value for 'memory': 512"))
    70  	})
    71  
    72  	It("returns an error when the memory limit is a non-string", func() {
    73  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
    74  			"instances": "3",
    75  			"memory":    128,
    76  			"applications": []interface{}{
    77  				map[interface{}]interface{}{
    78  					"name": "bitcoin-miner",
    79  				},
    80  			},
    81  		}))
    82  
    83  		_, err := m.Applications()
    84  		Expect(err).To(HaveOccurred())
    85  		Expect(err.Error()).To(ContainSubstring("Invalid value for 'memory': 128"))
    86  	})
    87  
    88  	It("sets applications' health check timeouts", func() {
    89  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
    90  			"applications": []interface{}{
    91  				map[interface{}]interface{}{
    92  					"name":    "bitcoin-miner",
    93  					"timeout": "360",
    94  				},
    95  			},
    96  		}))
    97  
    98  		apps, err := m.Applications()
    99  		Expect(err).NotTo(HaveOccurred())
   100  		Expect(*apps[0].HealthCheckTimeout).To(Equal(360))
   101  	})
   102  
   103  	It("allows boolean env var values", func() {
   104  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   105  			"env": generic.NewMap(map[interface{}]interface{}{
   106  				"bar": true,
   107  			}),
   108  		}))
   109  
   110  		_, err := m.Applications()
   111  		Expect(err).ToNot(HaveOccurred())
   112  	})
   113  
   114  	It("allows nil value for global env if env is present in the app", func() {
   115  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   116  			"env": nil,
   117  			"applications": []interface{}{
   118  				map[interface{}]interface{}{
   119  					"name": "bad app",
   120  					"env": map[interface{}]interface{}{
   121  						"foo": "bar",
   122  					},
   123  				},
   124  			},
   125  		}))
   126  
   127  		apps, err := m.Applications()
   128  		Expect(err).NotTo(HaveOccurred())
   129  		Expect(*apps[0].EnvironmentVars).To(Equal(map[string]interface{}{"foo": "bar"}))
   130  	})
   131  
   132  	It("does not allow nil value for env in application", func() {
   133  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   134  			"env": generic.NewMap(map[interface{}]interface{}{
   135  				"foo": "bar",
   136  			}),
   137  			"applications": []interface{}{
   138  				map[interface{}]interface{}{
   139  					"name": "bad app",
   140  					"env":  nil,
   141  				},
   142  			},
   143  		}))
   144  
   145  		_, err := m.Applications()
   146  		Expect(err).To(HaveOccurred())
   147  		Expect(err.Error()).To(ContainSubstring("env should not be null"))
   148  	})
   149  
   150  	It("does not allow nil values for environment variables", func() {
   151  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   152  			"env": generic.NewMap(map[interface{}]interface{}{
   153  				"bar": nil,
   154  			}),
   155  			"applications": []interface{}{
   156  				map[interface{}]interface{}{
   157  					"name": "bad app",
   158  				},
   159  			},
   160  		}))
   161  
   162  		_, err := m.Applications()
   163  		Expect(err).To(HaveOccurred())
   164  		Expect(err.Error()).To(ContainSubstring("env var 'bar' should not be null"))
   165  	})
   166  
   167  	It("returns an empty map when no env was present in the manifest", func() {
   168  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   169  			"applications": []interface{}{
   170  				map[interface{}]interface{}{"name": "no-env-vars"},
   171  			},
   172  		}))
   173  
   174  		apps, err := m.Applications()
   175  		Expect(err).NotTo(HaveOccurred())
   176  		Expect(*apps[0].EnvironmentVars).NotTo(BeNil())
   177  	})
   178  
   179  	It("allows applications to have absolute paths", func() {
   180  		if runtime.GOOS == "windows" {
   181  			m := NewManifest(`C:\some\path\manifest.yml`, generic.NewMap(map[interface{}]interface{}{
   182  				"applications": []interface{}{
   183  					map[interface{}]interface{}{
   184  						"path": `C:\another\path`,
   185  					},
   186  				},
   187  			}))
   188  
   189  			apps, err := m.Applications()
   190  			Expect(err).NotTo(HaveOccurred())
   191  			Expect(*apps[0].Path).To(Equal(`C:\another\path`))
   192  		} else {
   193  			m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   194  				"applications": []interface{}{
   195  					map[interface{}]interface{}{
   196  						"path": "/another/path-segment",
   197  					},
   198  				},
   199  			}))
   200  
   201  			apps, err := m.Applications()
   202  			Expect(err).NotTo(HaveOccurred())
   203  			Expect(*apps[0].Path).To(Equal("/another/path-segment"))
   204  		}
   205  	})
   206  
   207  	It("expands relative app paths based on the manifest's path", func() {
   208  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   209  			"applications": []interface{}{
   210  				map[interface{}]interface{}{
   211  					"path": "../another/path-segment",
   212  				},
   213  			},
   214  		}))
   215  
   216  		apps, err := m.Applications()
   217  		Expect(err).NotTo(HaveOccurred())
   218  		if runtime.GOOS == "windows" {
   219  			Expect(*apps[0].Path).To(Equal("\\some\\another\\path-segment"))
   220  		} else {
   221  			Expect(*apps[0].Path).To(Equal("/some/another/path-segment"))
   222  		}
   223  	})
   224  
   225  	It("returns errors when there are null values", func() {
   226  		m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   227  			"applications": []interface{}{
   228  				map[interface{}]interface{}{
   229  					"disk_quota":   nil,
   230  					"domain":       nil,
   231  					"host":         nil,
   232  					"name":         nil,
   233  					"path":         nil,
   234  					"stack":        nil,
   235  					"memory":       nil,
   236  					"instances":    nil,
   237  					"timeout":      nil,
   238  					"no-route":     nil,
   239  					"no-hostname":  nil,
   240  					"services":     nil,
   241  					"env":          nil,
   242  					"random-route": nil,
   243  				},
   244  			},
   245  		}))
   246  
   247  		_, err := m.Applications()
   248  		Expect(err).To(HaveOccurred())
   249  		errorSlice := strings.Split(err.Error(), "\n")
   250  		manifestKeys := []string{"disk_quota", "domain", "host", "name", "path", "stack",
   251  			"memory", "instances", "timeout", "no-route", "no-hostname", "services", "env", "random-route"}
   252  
   253  		for _, key := range manifestKeys {
   254  			Expect(errorSlice).To(ContainSubstrings([]string{key, "not be null"}))
   255  		}
   256  	})
   257  
   258  	It("returns errors when hosts/domains is not valid slice", func() {
   259  		m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   260  			"applications": []interface{}{
   261  				map[interface{}]interface{}{
   262  					"hosts":   "bad-value",
   263  					"domains": []interface{}{"val1", "val2", false, true},
   264  				},
   265  			},
   266  		}))
   267  
   268  		_, err := m.Applications()
   269  		Expect(err).To(HaveOccurred())
   270  		errorSlice := strings.Split(err.Error(), "\n")
   271  
   272  		Expect(errorSlice).To(ContainSubstrings([]string{"hosts", "to be a list of strings"}))
   273  		Expect(errorSlice).To(ContainSubstrings([]string{"domains", "to be a list of strings"}))
   274  	})
   275  
   276  	It("parses known manifest keys", func() {
   277  		m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   278  			"applications": []interface{}{
   279  				map[interface{}]interface{}{
   280  					"buildpack":         "my-buildpack",
   281  					"disk_quota":        "512M",
   282  					"domain":            "my-domain",
   283  					"domains":           []interface{}{"domain1.test", "domain2.test"},
   284  					"host":              "my-hostname",
   285  					"hosts":             []interface{}{"host-1", "host-2"},
   286  					"name":              "my-app-name",
   287  					"stack":             "my-stack",
   288  					"memory":            "256M",
   289  					"health-check-type": "none",
   290  					"instances":         1,
   291  					"timeout":           11,
   292  					"no-route":          true,
   293  					"no-hostname":       true,
   294  					"random-route":      true,
   295  				},
   296  			},
   297  		}))
   298  
   299  		apps, err := m.Applications()
   300  		Expect(err).NotTo(HaveOccurred())
   301  		Expect(len(apps)).To(Equal(1))
   302  
   303  		Expect(*apps[0].BuildpackURL).To(Equal("my-buildpack"))
   304  		Expect(*apps[0].DiskQuota).To(Equal(int64(512)))
   305  		Expect(apps[0].Domains).To(ConsistOf([]string{"domain1.test", "domain2.test", "my-domain"}))
   306  		Expect(apps[0].Hosts).To(ConsistOf([]string{"host-1", "host-2", "my-hostname"}))
   307  		Expect(*apps[0].Name).To(Equal("my-app-name"))
   308  		Expect(*apps[0].StackName).To(Equal("my-stack"))
   309  		Expect(*apps[0].HealthCheckType).To(Equal("none"))
   310  		Expect(*apps[0].Memory).To(Equal(int64(256)))
   311  		Expect(*apps[0].InstanceCount).To(Equal(1))
   312  		Expect(*apps[0].HealthCheckTimeout).To(Equal(11))
   313  		Expect(apps[0].NoRoute).To(BeTrue())
   314  		Expect(*apps[0].NoHostname).To(BeTrue())
   315  		Expect(apps[0].UseRandomRoute).To(BeTrue())
   316  	})
   317  
   318  	Context("when the health-check-type is 'http'", func() {
   319  		Context("when health-check-http-endpoint IS provided", func() {
   320  			It("sets http-health-check-endpoint to the provided endpoint", func() {
   321  				m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   322  					"applications": []interface{}{
   323  						map[interface{}]interface{}{
   324  							"health-check-type":          "http",
   325  							"health-check-http-endpoint": "/some-endpoint",
   326  						},
   327  					},
   328  				}))
   329  
   330  				apps, err := m.Applications()
   331  				Expect(err).NotTo(HaveOccurred())
   332  				Expect(len(apps)).To(Equal(1))
   333  
   334  				Expect(*apps[0].HealthCheckType).To(Equal("http"))
   335  				Expect(*apps[0].HealthCheckHTTPEndpoint).To(Equal("/some-endpoint"))
   336  			})
   337  		})
   338  	})
   339  
   340  	It("removes duplicated values in 'hosts' and 'domains'", func() {
   341  		m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   342  			"applications": []interface{}{
   343  				map[interface{}]interface{}{
   344  					"domain":  "my-domain",
   345  					"domains": []interface{}{"my-domain", "domain1.test", "domain1.test", "domain2.test"},
   346  					"host":    "my-hostname",
   347  					"hosts":   []interface{}{"my-hostname", "host-1", "host-1", "host-2"},
   348  					"name":    "my-app-name",
   349  				},
   350  			},
   351  		}))
   352  
   353  		apps, err := m.Applications()
   354  		Expect(err).NotTo(HaveOccurred())
   355  		Expect(len(apps)).To(Equal(1))
   356  
   357  		Expect(len(apps[0].Domains)).To(Equal(3))
   358  		Expect(apps[0].Domains).To(ConsistOf([]string{"my-domain", "domain1.test", "domain2.test"}))
   359  		Expect(len(apps[0].Hosts)).To(Equal(3))
   360  		Expect(apps[0].Hosts).To(ConsistOf([]string{"my-hostname", "host-1", "host-2"}))
   361  	})
   362  
   363  	Context("old-style property syntax", func() {
   364  		It("returns an error when the manifest contains non-whitelist properties", func() {
   365  			m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   366  				"applications": []interface{}{
   367  					generic.NewMap(map[interface{}]interface{}{
   368  						"env": generic.NewMap(map[interface{}]interface{}{
   369  							"bar": "many-${some_property-name}-are-cool",
   370  						}),
   371  					}),
   372  				},
   373  			}))
   374  
   375  			_, err := m.Applications()
   376  			Expect(err).To(HaveOccurred())
   377  			Expect(err.Error()).To(ContainSubstring("'${some_property-name}'"))
   378  		})
   379  
   380  		It("replaces the '${random-word} with a combination of 2 random words", func() {
   381  			m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   382  				"applications": []interface{}{
   383  					generic.NewMap(map[interface{}]interface{}{
   384  						"env": generic.NewMap(map[interface{}]interface{}{
   385  							"bar": "prefix_${random-word}_suffix",
   386  							"foo": "some-value",
   387  						}),
   388  					}),
   389  				},
   390  			}))
   391  
   392  			apps, err := m.Applications()
   393  			Expect(err).NotTo(HaveOccurred())
   394  			Expect((*apps[0].EnvironmentVars)["bar"]).To(MatchRegexp(`prefix_\w+-\w+_suffix`))
   395  			Expect((*apps[0].EnvironmentVars)["foo"]).To(Equal("some-value"))
   396  
   397  			apps2, _ := m.Applications()
   398  			Expect((*apps2[0].EnvironmentVars)["bar"]).To(MatchRegexp(`prefix_\w+-\w+_suffix`))
   399  			Expect((*apps2[0].EnvironmentVars)["bar"]).NotTo(Equal((*apps[0].EnvironmentVars)["bar"]))
   400  		})
   401  	})
   402  
   403  	It("sets the command and buildpack to blank when their values are null in the manifest", func() {
   404  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   405  			"applications": []interface{}{
   406  				generic.NewMap(map[interface{}]interface{}{
   407  					"buildpack": nil,
   408  					"command":   nil,
   409  				}),
   410  			},
   411  		}))
   412  
   413  		apps, err := m.Applications()
   414  		Expect(err).NotTo(HaveOccurred())
   415  		Expect(*apps[0].Command).To(Equal(""))
   416  		Expect(*apps[0].BuildpackURL).To(Equal(""))
   417  	})
   418  
   419  	It("sets the command and buildpack to blank when their values are 'default' in the manifest", func() {
   420  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   421  			"applications": []interface{}{
   422  				generic.NewMap(map[interface{}]interface{}{
   423  					"command":   "default",
   424  					"buildpack": "default",
   425  				}),
   426  			},
   427  		}))
   428  
   429  		apps, err := m.Applications()
   430  		Expect(err).NotTo(HaveOccurred())
   431  		Expect(*apps[0].Command).To(Equal(""))
   432  		Expect(*apps[0].BuildpackURL).To(Equal(""))
   433  	})
   434  
   435  	It("does not set the start command when the manifest doesn't have the 'command' key", func() {
   436  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   437  			"applications": []interface{}{
   438  				map[interface{}]interface{}{},
   439  			},
   440  		}))
   441  
   442  		apps, err := m.Applications()
   443  		Expect(err).NotTo(HaveOccurred())
   444  		Expect(apps[0].Command).To(BeNil())
   445  	})
   446  
   447  	It("can build the applications multiple times", func() {
   448  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   449  			"memory": "254m",
   450  			"applications": []interface{}{
   451  				map[interface{}]interface{}{
   452  					"name": "bitcoin-miner",
   453  				},
   454  				map[interface{}]interface{}{
   455  					"name": "bitcoin-miner",
   456  				},
   457  			},
   458  		}))
   459  
   460  		apps1, err := m.Applications()
   461  		Expect(err).NotTo(HaveOccurred())
   462  
   463  		apps2, err := m.Applications()
   464  		Expect(err).NotTo(HaveOccurred())
   465  		Expect(apps1).To(Equal(apps2))
   466  	})
   467  
   468  	Context("parsing app ports", func() {
   469  		It("parses app ports", func() {
   470  			m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   471  				"applications": []interface{}{
   472  					map[interface{}]interface{}{
   473  						"app-ports": []interface{}{
   474  							8080,
   475  							9090,
   476  						},
   477  					},
   478  				},
   479  			}))
   480  
   481  			apps, err := m.Applications()
   482  			Expect(err).NotTo(HaveOccurred())
   483  
   484  			Expect(apps[0].AppPorts).NotTo(BeNil())
   485  			Expect(*(apps[0].AppPorts)).To(Equal([]int{8080, 9090}))
   486  		})
   487  
   488  		It("handles omitted field", func() {
   489  			m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   490  				"applications": []interface{}{
   491  					map[interface{}]interface{}{},
   492  				},
   493  			}))
   494  
   495  			apps, err := m.Applications()
   496  			Expect(err).NotTo(HaveOccurred())
   497  
   498  			Expect(apps[0].AppPorts).To(BeNil())
   499  		})
   500  
   501  		It("handles mixed arrays", func() {
   502  			m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   503  				"applications": []interface{}{
   504  					map[interface{}]interface{}{
   505  						"app-ports": []interface{}{
   506  							8080,
   507  							"potato",
   508  						},
   509  					},
   510  				},
   511  			}))
   512  
   513  			_, err := m.Applications()
   514  			Expect(err).To(HaveOccurred())
   515  			Expect(err.Error()).To(ContainSubstring("Expected app-ports to be a list of integers."))
   516  		})
   517  
   518  		It("handles non-array values", func() {
   519  			m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   520  				"applications": []interface{}{
   521  					map[interface{}]interface{}{
   522  						"app-ports": "potato",
   523  					},
   524  				},
   525  			}))
   526  
   527  			_, err := m.Applications()
   528  			Expect(err).To(HaveOccurred())
   529  			Expect(err.Error()).To(ContainSubstring("Expected app-ports to be a list of integers."))
   530  		})
   531  	})
   532  
   533  	Context("parsing env vars", func() {
   534  		It("handles values that are not strings", func() {
   535  			m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   536  				"applications": []interface{}{
   537  					generic.NewMap(map[interface{}]interface{}{
   538  						"env": map[interface{}]interface{}{
   539  							"string-key":      "value",
   540  							"int-key":         1,
   541  							"float-key":       11.1,
   542  							"large-int-key":   123456789,
   543  							"large-float-key": 123456789.12345678,
   544  							"bool-key":        false,
   545  						},
   546  					}),
   547  				},
   548  			}))
   549  
   550  			app, err := m.Applications()
   551  			Expect(err).NotTo(HaveOccurred())
   552  
   553  			Expect((*app[0].EnvironmentVars)["string-key"]).To(Equal("value"))
   554  			Expect((*app[0].EnvironmentVars)["int-key"]).To(Equal("1"))
   555  			Expect((*app[0].EnvironmentVars)["float-key"]).To(Equal("11.1"))
   556  			Expect((*app[0].EnvironmentVars)["large-int-key"]).To(Equal("123456789"))
   557  			Expect((*app[0].EnvironmentVars)["large-float-key"]).To(Equal("123456789.12345678"))
   558  			Expect((*app[0].EnvironmentVars)["bool-key"]).To(Equal("false"))
   559  		})
   560  	})
   561  
   562  	Context("parsing services", func() {
   563  		It("can read a list of service instance names", func() {
   564  			m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   565  				"services": []interface{}{"service-1", "service-2"},
   566  			}))
   567  
   568  			app, err := m.Applications()
   569  			Expect(err).NotTo(HaveOccurred())
   570  
   571  			Expect(app[0].ServicesToBind).To(Equal([]string{"service-1", "service-2"}))
   572  		})
   573  	})
   574  
   575  	Context("when routes are provided", func() {
   576  		var manifest *manifest.Manifest
   577  
   578  		Context("when passed 'routes'", func() {
   579  			Context("valid 'routes'", func() {
   580  				BeforeEach(func() {
   581  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   582  						"applications": []interface{}{
   583  							generic.NewMap(map[interface{}]interface{}{
   584  								"routes": []interface{}{
   585  									map[interface{}]interface{}{"route": "route1.example.com"},
   586  									map[interface{}]interface{}{"route": "route2.example.com"},
   587  								},
   588  							}),
   589  						},
   590  					}))
   591  				})
   592  
   593  				It("parses routes into app params", func() {
   594  					apps, err := manifest.Applications()
   595  					Expect(err).NotTo(HaveOccurred())
   596  					Expect(apps).To(HaveLen(1))
   597  
   598  					routes := apps[0].Routes
   599  					Expect(routes).To(HaveLen(2))
   600  					Expect(routes[0].Route).To(Equal("route1.example.com"))
   601  					Expect(routes[1].Route).To(Equal("route2.example.com"))
   602  				})
   603  			})
   604  
   605  			Context("invalid 'routes'", func() {
   606  				Context("'routes' is formatted incorrectly", func() {
   607  					BeforeEach(func() {
   608  						manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   609  							"applications": []interface{}{
   610  								generic.NewMap(map[interface{}]interface{}{
   611  									"routes": []string{},
   612  								}),
   613  							},
   614  						}))
   615  					})
   616  
   617  					It("errors out", func() {
   618  						_, err := manifest.Applications()
   619  						Expect(err).To(HaveOccurred())
   620  						Expect(err.Error()).To(MatchRegexp("should be a list"))
   621  					})
   622  				})
   623  
   624  				Context("an individual 'route' is formatted incorrectly", func() {
   625  					BeforeEach(func() {
   626  						manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   627  							"applications": []interface{}{
   628  								generic.NewMap(map[interface{}]interface{}{
   629  									"routes": []interface{}{
   630  										map[interface{}]interface{}{"routef": "route1.example.com"},
   631  									},
   632  								}),
   633  							},
   634  						}))
   635  					})
   636  
   637  					It("parses routes into app params", func() {
   638  						_, err := manifest.Applications()
   639  						Expect(err).To(HaveOccurred())
   640  						Expect(err.Error()).To(MatchRegexp("each route in 'routes' must have a 'route' property"))
   641  					})
   642  				})
   643  			})
   644  		})
   645  
   646  		Context("when there are no routes", func() {
   647  			BeforeEach(func() {
   648  				manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   649  					"applications": []interface{}{
   650  						generic.NewMap(map[interface{}]interface{}{
   651  							"buildpack": nil,
   652  							"command":   "echo banana",
   653  						}),
   654  					},
   655  				}))
   656  			})
   657  
   658  			It("sets routes to be nil", func() {
   659  				apps, err := manifest.Applications()
   660  				Expect(err).NotTo(HaveOccurred())
   661  				Expect(apps).To(HaveLen(1))
   662  				Expect(apps[0].Routes).To(BeNil())
   663  			})
   664  		})
   665  
   666  		Context("when no-hostname is not specified in the manifest", func() {
   667  			BeforeEach(func() {
   668  				manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   669  					"applications": []interface{}{
   670  						generic.NewMap(map[interface{}]interface{}{
   671  							"buildpack": nil,
   672  							"command":   "echo banana",
   673  						}),
   674  					},
   675  				}))
   676  			})
   677  
   678  			It("sets no-hostname to be nil", func() {
   679  				apps, err := manifest.Applications()
   680  				Expect(err).NotTo(HaveOccurred())
   681  				Expect(apps).To(HaveLen(1))
   682  				Expect(apps[0].NoHostname).To(BeNil())
   683  			})
   684  		})
   685  
   686  		Context("when no-hostname is specified in the manifest", func() {
   687  			Context("and it is set to true", func() {
   688  				Context("and the value is a boolean", func() {
   689  					BeforeEach(func() {
   690  						manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   691  							"applications": []interface{}{
   692  								generic.NewMap(map[interface{}]interface{}{
   693  									"buildpack":   nil,
   694  									"command":     "echo banana",
   695  									"no-hostname": true,
   696  								}),
   697  							},
   698  						}))
   699  					})
   700  
   701  					It("sets no-hostname to be true", func() {
   702  						apps, err := manifest.Applications()
   703  						Expect(err).NotTo(HaveOccurred())
   704  						Expect(apps).To(HaveLen(1))
   705  						Expect(*apps[0].NoHostname).To(BeTrue())
   706  					})
   707  				})
   708  				Context("and the value is a string", func() {
   709  					BeforeEach(func() {
   710  						manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   711  							"applications": []interface{}{
   712  								generic.NewMap(map[interface{}]interface{}{
   713  									"buildpack":   nil,
   714  									"command":     "echo banana",
   715  									"no-hostname": "true",
   716  								}),
   717  							},
   718  						}))
   719  					})
   720  
   721  					It("sets no-hostname to be true", func() {
   722  						apps, err := manifest.Applications()
   723  						Expect(err).NotTo(HaveOccurred())
   724  						Expect(apps).To(HaveLen(1))
   725  						Expect(*apps[0].NoHostname).To(BeTrue())
   726  					})
   727  				})
   728  			})
   729  			Context("and it is set to false", func() {
   730  				BeforeEach(func() {
   731  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   732  						"applications": []interface{}{
   733  							generic.NewMap(map[interface{}]interface{}{
   734  								"buildpack":   nil,
   735  								"command":     "echo banana",
   736  								"no-hostname": false,
   737  							}),
   738  						},
   739  					}))
   740  				})
   741  				It("sets no-hostname to be false", func() {
   742  					apps, err := manifest.Applications()
   743  					Expect(err).NotTo(HaveOccurred())
   744  					Expect(apps).To(HaveLen(1))
   745  					Expect(*apps[0].NoHostname).To(BeFalse())
   746  				})
   747  			})
   748  		})
   749  	})
   750  })