github.com/cloudfoundry-attic/cli-with-i18n@v6.32.1-0.20171002233121-7401370d3b85+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  					"docker": map[interface{}]interface{}{
   296  						"image": "docker.com:3333/docker/docker:some-tag",
   297  					},
   298  				},
   299  			},
   300  		}))
   301  
   302  		apps, err := m.Applications()
   303  		Expect(err).NotTo(HaveOccurred())
   304  		Expect(len(apps)).To(Equal(1))
   305  
   306  		Expect(*apps[0].BuildpackURL).To(Equal("my-buildpack"))
   307  		Expect(*apps[0].DiskQuota).To(Equal(int64(512)))
   308  		Expect(apps[0].Domains).To(ConsistOf([]string{"domain1.test", "domain2.test", "my-domain"}))
   309  		Expect(apps[0].Hosts).To(ConsistOf([]string{"host-1", "host-2", "my-hostname"}))
   310  		Expect(*apps[0].Name).To(Equal("my-app-name"))
   311  		Expect(*apps[0].StackName).To(Equal("my-stack"))
   312  		Expect(*apps[0].HealthCheckType).To(Equal("none"))
   313  		Expect(*apps[0].Memory).To(Equal(int64(256)))
   314  		Expect(*apps[0].InstanceCount).To(Equal(1))
   315  		Expect(*apps[0].HealthCheckTimeout).To(Equal(11))
   316  		Expect(apps[0].NoRoute).To(BeTrue())
   317  		Expect(*apps[0].NoHostname).To(BeTrue())
   318  		Expect(apps[0].UseRandomRoute).To(BeTrue())
   319  		Expect(*apps[0].DockerImage).To(Equal("docker.com:3333/docker/docker:some-tag"))
   320  	})
   321  
   322  	Context("when the health-check-type is 'http'", func() {
   323  		Context("when health-check-http-endpoint IS provided", func() {
   324  			It("sets http-health-check-endpoint to the provided endpoint", func() {
   325  				m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   326  					"applications": []interface{}{
   327  						map[interface{}]interface{}{
   328  							"health-check-type":          "http",
   329  							"health-check-http-endpoint": "/some-endpoint",
   330  						},
   331  					},
   332  				}))
   333  
   334  				apps, err := m.Applications()
   335  				Expect(err).NotTo(HaveOccurred())
   336  				Expect(len(apps)).To(Equal(1))
   337  
   338  				Expect(*apps[0].HealthCheckType).To(Equal("http"))
   339  				Expect(*apps[0].HealthCheckHTTPEndpoint).To(Equal("/some-endpoint"))
   340  			})
   341  		})
   342  	})
   343  
   344  	It("removes duplicated values in 'hosts' and 'domains'", func() {
   345  		m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   346  			"applications": []interface{}{
   347  				map[interface{}]interface{}{
   348  					"domain":  "my-domain",
   349  					"domains": []interface{}{"my-domain", "domain1.test", "domain1.test", "domain2.test"},
   350  					"host":    "my-hostname",
   351  					"hosts":   []interface{}{"my-hostname", "host-1", "host-1", "host-2"},
   352  					"name":    "my-app-name",
   353  				},
   354  			},
   355  		}))
   356  
   357  		apps, err := m.Applications()
   358  		Expect(err).NotTo(HaveOccurred())
   359  		Expect(len(apps)).To(Equal(1))
   360  
   361  		Expect(len(apps[0].Domains)).To(Equal(3))
   362  		Expect(apps[0].Domains).To(ConsistOf([]string{"my-domain", "domain1.test", "domain2.test"}))
   363  		Expect(len(apps[0].Hosts)).To(Equal(3))
   364  		Expect(apps[0].Hosts).To(ConsistOf([]string{"my-hostname", "host-1", "host-2"}))
   365  	})
   366  
   367  	Context("old-style property syntax", func() {
   368  		It("returns an error when the manifest contains non-whitelist properties", func() {
   369  			m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   370  				"applications": []interface{}{
   371  					generic.NewMap(map[interface{}]interface{}{
   372  						"env": generic.NewMap(map[interface{}]interface{}{
   373  							"bar": "many-${some_property-name}-are-cool",
   374  						}),
   375  					}),
   376  				},
   377  			}))
   378  
   379  			_, err := m.Applications()
   380  			Expect(err).To(HaveOccurred())
   381  			Expect(err.Error()).To(ContainSubstring("'${some_property-name}'"))
   382  		})
   383  
   384  		It("replaces the '${random-word} with a combination of 2 random words", func() {
   385  			m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   386  				"applications": []interface{}{
   387  					generic.NewMap(map[interface{}]interface{}{
   388  						"env": generic.NewMap(map[interface{}]interface{}{
   389  							"bar": "prefix_${random-word}_suffix",
   390  							"foo": "some-value",
   391  						}),
   392  					}),
   393  				},
   394  			}))
   395  
   396  			apps, err := m.Applications()
   397  			Expect(err).NotTo(HaveOccurred())
   398  			Expect((*apps[0].EnvironmentVars)["bar"]).To(MatchRegexp(`prefix_\w+-\w+_suffix`))
   399  			Expect((*apps[0].EnvironmentVars)["foo"]).To(Equal("some-value"))
   400  
   401  			apps2, _ := m.Applications()
   402  			Expect((*apps2[0].EnvironmentVars)["bar"]).To(MatchRegexp(`prefix_\w+-\w+_suffix`))
   403  			Expect((*apps2[0].EnvironmentVars)["bar"]).NotTo(Equal((*apps[0].EnvironmentVars)["bar"]))
   404  		})
   405  	})
   406  
   407  	It("sets the command and buildpack to blank when their values are null in the manifest", func() {
   408  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   409  			"applications": []interface{}{
   410  				generic.NewMap(map[interface{}]interface{}{
   411  					"buildpack": nil,
   412  					"command":   nil,
   413  				}),
   414  			},
   415  		}))
   416  
   417  		apps, err := m.Applications()
   418  		Expect(err).NotTo(HaveOccurred())
   419  		Expect(*apps[0].Command).To(Equal(""))
   420  		Expect(*apps[0].BuildpackURL).To(Equal(""))
   421  	})
   422  
   423  	It("sets the command and buildpack to blank when their values are 'default' in the manifest", func() {
   424  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   425  			"applications": []interface{}{
   426  				generic.NewMap(map[interface{}]interface{}{
   427  					"command":   "default",
   428  					"buildpack": "default",
   429  				}),
   430  			},
   431  		}))
   432  
   433  		apps, err := m.Applications()
   434  		Expect(err).NotTo(HaveOccurred())
   435  		Expect(*apps[0].Command).To(Equal(""))
   436  		Expect(*apps[0].BuildpackURL).To(Equal(""))
   437  	})
   438  
   439  	It("does not set the start command when the manifest doesn't have the 'command' key", func() {
   440  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   441  			"applications": []interface{}{
   442  				map[interface{}]interface{}{},
   443  			},
   444  		}))
   445  
   446  		apps, err := m.Applications()
   447  		Expect(err).NotTo(HaveOccurred())
   448  		Expect(apps[0].Command).To(BeNil())
   449  	})
   450  
   451  	It("can build the applications multiple times", func() {
   452  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   453  			"memory": "254m",
   454  			"applications": []interface{}{
   455  				map[interface{}]interface{}{
   456  					"name": "bitcoin-miner",
   457  				},
   458  				map[interface{}]interface{}{
   459  					"name": "bitcoin-miner",
   460  				},
   461  			},
   462  		}))
   463  
   464  		apps1, err := m.Applications()
   465  		Expect(err).NotTo(HaveOccurred())
   466  
   467  		apps2, err := m.Applications()
   468  		Expect(err).NotTo(HaveOccurred())
   469  		Expect(apps1).To(Equal(apps2))
   470  	})
   471  
   472  	Context("parsing app ports", func() {
   473  		It("parses app ports", func() {
   474  			m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   475  				"applications": []interface{}{
   476  					map[interface{}]interface{}{
   477  						"app-ports": []interface{}{
   478  							8080,
   479  							9090,
   480  						},
   481  					},
   482  				},
   483  			}))
   484  
   485  			apps, err := m.Applications()
   486  			Expect(err).NotTo(HaveOccurred())
   487  
   488  			Expect(apps[0].AppPorts).NotTo(BeNil())
   489  			Expect(*(apps[0].AppPorts)).To(Equal([]int{8080, 9090}))
   490  		})
   491  
   492  		It("handles omitted field", func() {
   493  			m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   494  				"applications": []interface{}{
   495  					map[interface{}]interface{}{},
   496  				},
   497  			}))
   498  
   499  			apps, err := m.Applications()
   500  			Expect(err).NotTo(HaveOccurred())
   501  
   502  			Expect(apps[0].AppPorts).To(BeNil())
   503  		})
   504  
   505  		It("handles mixed arrays", func() {
   506  			m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   507  				"applications": []interface{}{
   508  					map[interface{}]interface{}{
   509  						"app-ports": []interface{}{
   510  							8080,
   511  							"potato",
   512  						},
   513  					},
   514  				},
   515  			}))
   516  
   517  			_, err := m.Applications()
   518  			Expect(err).To(HaveOccurred())
   519  			Expect(err.Error()).To(ContainSubstring("Expected app-ports to be a list of integers."))
   520  		})
   521  
   522  		It("handles non-array values", func() {
   523  			m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   524  				"applications": []interface{}{
   525  					map[interface{}]interface{}{
   526  						"app-ports": "potato",
   527  					},
   528  				},
   529  			}))
   530  
   531  			_, err := m.Applications()
   532  			Expect(err).To(HaveOccurred())
   533  			Expect(err.Error()).To(ContainSubstring("Expected app-ports to be a list of integers."))
   534  		})
   535  	})
   536  
   537  	Context("parsing env vars", func() {
   538  		It("handles values that are not strings", func() {
   539  			m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   540  				"applications": []interface{}{
   541  					generic.NewMap(map[interface{}]interface{}{
   542  						"env": map[interface{}]interface{}{
   543  							"string-key":      "value",
   544  							"int-key":         1,
   545  							"float-key":       11.1,
   546  							"large-int-key":   123456789,
   547  							"large-float-key": 123456789.12345678,
   548  							"bool-key":        false,
   549  						},
   550  					}),
   551  				},
   552  			}))
   553  
   554  			app, err := m.Applications()
   555  			Expect(err).NotTo(HaveOccurred())
   556  
   557  			Expect((*app[0].EnvironmentVars)["string-key"]).To(Equal("value"))
   558  			Expect((*app[0].EnvironmentVars)["int-key"]).To(Equal("1"))
   559  			Expect((*app[0].EnvironmentVars)["float-key"]).To(Equal("11.1"))
   560  			Expect((*app[0].EnvironmentVars)["large-int-key"]).To(Equal("123456789"))
   561  			Expect((*app[0].EnvironmentVars)["large-float-key"]).To(Equal("123456789.12345678"))
   562  			Expect((*app[0].EnvironmentVars)["bool-key"]).To(Equal("false"))
   563  		})
   564  	})
   565  
   566  	Context("parsing services", func() {
   567  		It("can read a list of service instance names", func() {
   568  			m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   569  				"services": []interface{}{"service-1", "service-2"},
   570  			}))
   571  
   572  			app, err := m.Applications()
   573  			Expect(err).NotTo(HaveOccurred())
   574  
   575  			Expect(app[0].ServicesToBind).To(Equal([]string{"service-1", "service-2"}))
   576  		})
   577  	})
   578  
   579  	Context("when routes are provided", func() {
   580  		var manifest *manifest.Manifest
   581  
   582  		Context("when passed 'routes'", func() {
   583  			Context("valid 'routes'", func() {
   584  				BeforeEach(func() {
   585  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   586  						"applications": []interface{}{
   587  							generic.NewMap(map[interface{}]interface{}{
   588  								"routes": []interface{}{
   589  									map[interface{}]interface{}{"route": "route1.example.com"},
   590  									map[interface{}]interface{}{"route": "route2.example.com"},
   591  								},
   592  							}),
   593  						},
   594  					}))
   595  				})
   596  
   597  				It("parses routes into app params", func() {
   598  					apps, err := manifest.Applications()
   599  					Expect(err).NotTo(HaveOccurred())
   600  					Expect(apps).To(HaveLen(1))
   601  
   602  					routes := apps[0].Routes
   603  					Expect(routes).To(HaveLen(2))
   604  					Expect(routes[0].Route).To(Equal("route1.example.com"))
   605  					Expect(routes[1].Route).To(Equal("route2.example.com"))
   606  				})
   607  			})
   608  
   609  			Context("invalid 'routes'", func() {
   610  				Context("'routes' is formatted incorrectly", func() {
   611  					BeforeEach(func() {
   612  						manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   613  							"applications": []interface{}{
   614  								generic.NewMap(map[interface{}]interface{}{
   615  									"routes": []string{},
   616  								}),
   617  							},
   618  						}))
   619  					})
   620  
   621  					It("errors out", func() {
   622  						_, err := manifest.Applications()
   623  						Expect(err).To(HaveOccurred())
   624  						Expect(err.Error()).To(MatchRegexp("should be a list"))
   625  					})
   626  				})
   627  
   628  				Context("an individual 'route' is formatted incorrectly", func() {
   629  					BeforeEach(func() {
   630  						manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   631  							"applications": []interface{}{
   632  								generic.NewMap(map[interface{}]interface{}{
   633  									"routes": []interface{}{
   634  										map[interface{}]interface{}{"routef": "route1.example.com"},
   635  									},
   636  								}),
   637  							},
   638  						}))
   639  					})
   640  
   641  					It("parses routes into app params", func() {
   642  						_, err := manifest.Applications()
   643  						Expect(err).To(HaveOccurred())
   644  						Expect(err.Error()).To(MatchRegexp("each route in 'routes' must have a 'route' property"))
   645  					})
   646  				})
   647  			})
   648  		})
   649  
   650  		Context("when there are no routes", func() {
   651  			BeforeEach(func() {
   652  				manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   653  					"applications": []interface{}{
   654  						generic.NewMap(map[interface{}]interface{}{
   655  							"buildpack": nil,
   656  							"command":   "echo banana",
   657  						}),
   658  					},
   659  				}))
   660  			})
   661  
   662  			It("sets routes to be nil", func() {
   663  				apps, err := manifest.Applications()
   664  				Expect(err).NotTo(HaveOccurred())
   665  				Expect(apps).To(HaveLen(1))
   666  				Expect(apps[0].Routes).To(BeNil())
   667  			})
   668  		})
   669  
   670  		Context("when no-hostname is not specified in the manifest", func() {
   671  			BeforeEach(func() {
   672  				manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   673  					"applications": []interface{}{
   674  						generic.NewMap(map[interface{}]interface{}{
   675  							"buildpack": nil,
   676  							"command":   "echo banana",
   677  						}),
   678  					},
   679  				}))
   680  			})
   681  
   682  			It("sets no-hostname to be nil", func() {
   683  				apps, err := manifest.Applications()
   684  				Expect(err).NotTo(HaveOccurred())
   685  				Expect(apps).To(HaveLen(1))
   686  				Expect(apps[0].NoHostname).To(BeNil())
   687  			})
   688  		})
   689  
   690  		Context("when no-hostname is specified in the manifest", func() {
   691  			Context("and it is set to true", func() {
   692  				Context("and the value is a boolean", func() {
   693  					BeforeEach(func() {
   694  						manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   695  							"applications": []interface{}{
   696  								generic.NewMap(map[interface{}]interface{}{
   697  									"buildpack":   nil,
   698  									"command":     "echo banana",
   699  									"no-hostname": true,
   700  								}),
   701  							},
   702  						}))
   703  					})
   704  
   705  					It("sets no-hostname to be true", func() {
   706  						apps, err := manifest.Applications()
   707  						Expect(err).NotTo(HaveOccurred())
   708  						Expect(apps).To(HaveLen(1))
   709  						Expect(*apps[0].NoHostname).To(BeTrue())
   710  					})
   711  				})
   712  				Context("and the value is a string", func() {
   713  					BeforeEach(func() {
   714  						manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   715  							"applications": []interface{}{
   716  								generic.NewMap(map[interface{}]interface{}{
   717  									"buildpack":   nil,
   718  									"command":     "echo banana",
   719  									"no-hostname": "true",
   720  								}),
   721  							},
   722  						}))
   723  					})
   724  
   725  					It("sets no-hostname to be true", func() {
   726  						apps, err := manifest.Applications()
   727  						Expect(err).NotTo(HaveOccurred())
   728  						Expect(apps).To(HaveLen(1))
   729  						Expect(*apps[0].NoHostname).To(BeTrue())
   730  					})
   731  				})
   732  			})
   733  			Context("and it is set to false", func() {
   734  				BeforeEach(func() {
   735  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   736  						"applications": []interface{}{
   737  							generic.NewMap(map[interface{}]interface{}{
   738  								"buildpack":   nil,
   739  								"command":     "echo banana",
   740  								"no-hostname": false,
   741  							}),
   742  						},
   743  					}))
   744  				})
   745  				It("sets no-hostname to be false", func() {
   746  					apps, err := manifest.Applications()
   747  					Expect(err).NotTo(HaveOccurred())
   748  					Expect(apps).To(HaveLen(1))
   749  					Expect(*apps[0].NoHostname).To(BeFalse())
   750  				})
   751  			})
   752  		})
   753  	})
   754  
   755  	Context("when the 'docker' property is not specified", func() {
   756  		var manifest *manifest.Manifest
   757  
   758  		BeforeEach(func() {
   759  			manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   760  				"applications": []interface{}{
   761  					generic.NewMap(map[interface{}]interface{}{}),
   762  				},
   763  			}))
   764  		})
   765  
   766  		It("returns a parsed manifest that does not contain any docker fields", func() {
   767  			apps, err := manifest.Applications()
   768  			Expect(err).NotTo(HaveOccurred())
   769  			Expect(apps).To(HaveLen(1))
   770  			Expect(apps[0].DockerImage).To(BeNil())
   771  			Expect(apps[0].DockerUsername).To(BeNil())
   772  		})
   773  	})
   774  
   775  	Context("when the 'docker' property is specified but contains no properties", func() {
   776  		var manifest *manifest.Manifest
   777  
   778  		BeforeEach(func() {
   779  			manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   780  				"applications": []interface{}{
   781  					generic.NewMap(map[interface{}]interface{}{
   782  						"docker": map[interface{}]interface{}{},
   783  					}),
   784  				},
   785  			}))
   786  		})
   787  
   788  		It("returns a parsed manifest that does not contain any docker fields", func() {
   789  			apps, err := manifest.Applications()
   790  			Expect(err).NotTo(HaveOccurred())
   791  			Expect(apps).To(HaveLen(1))
   792  			Expect(apps[0].DockerImage).To(BeNil())
   793  			Expect(apps[0].DockerUsername).To(BeNil())
   794  		})
   795  	})
   796  
   797  	Context("when the 'docker' property is specified", func() {
   798  		var manifest *manifest.Manifest
   799  
   800  		Context("when 'docker' contains an 'image' property", func() {
   801  			Context("when the 'image' property is not a string", func() {
   802  				BeforeEach(func() {
   803  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   804  						"applications": []interface{}{
   805  							generic.NewMap(map[interface{}]interface{}{
   806  								"docker": map[interface{}]interface{}{
   807  									"image": []interface{}{},
   808  								},
   809  							}),
   810  						},
   811  					}))
   812  				})
   813  
   814  				It("returns an error", func() {
   815  					_, err := manifest.Applications()
   816  					Expect(err.Error()).To(MatchRegexp("'docker.image' must be a string"))
   817  				})
   818  			})
   819  
   820  			Context("when the 'image' property is a string", func() {
   821  				BeforeEach(func() {
   822  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   823  						"applications": []interface{}{
   824  							generic.NewMap(map[interface{}]interface{}{
   825  								"docker": map[interface{}]interface{}{
   826  									"image": "docker.com:3333/docker/docker:tag",
   827  								},
   828  							}),
   829  						},
   830  					}))
   831  				})
   832  
   833  				It("sets DockerImage to the 'image' property value", func() {
   834  					apps, err := manifest.Applications()
   835  					Expect(err).NotTo(HaveOccurred())
   836  					Expect(apps).To(HaveLen(1))
   837  					Expect(*apps[0].DockerImage).To(Equal("docker.com:3333/docker/docker:tag"))
   838  				})
   839  			})
   840  		})
   841  
   842  		Context("when 'docker' contains a 'username' property", func() {
   843  			Context("when 'username' is not a string", func() {
   844  				BeforeEach(func() {
   845  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   846  						"applications": []interface{}{
   847  							generic.NewMap(map[interface{}]interface{}{
   848  								"docker": map[interface{}]interface{}{
   849  									"username": []interface{}{},
   850  								},
   851  							}),
   852  						},
   853  					}))
   854  				})
   855  
   856  				It("returns an error", func() {
   857  					_, err := manifest.Applications()
   858  					Expect(err.Error()).To(MatchRegexp("'docker.username' must be a string"))
   859  				})
   860  			})
   861  
   862  			Context("when 'username' is a string", func() {
   863  				BeforeEach(func() {
   864  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   865  						"applications": []interface{}{
   866  							generic.NewMap(map[interface{}]interface{}{
   867  								"docker": map[interface{}]interface{}{
   868  									"username": "some-user",
   869  								},
   870  							}),
   871  						},
   872  					}))
   873  				})
   874  
   875  				It("sets DockerUsername to the 'username' property value", func() {
   876  					apps, err := manifest.Applications()
   877  					Expect(err).NotTo(HaveOccurred())
   878  					Expect(apps).To(HaveLen(1))
   879  					Expect(*apps[0].DockerUsername).To(Equal("some-user"))
   880  				})
   881  			})
   882  		})
   883  	})
   884  })