github.com/ablease/cli@v6.37.1-0.20180613014814-3adbb7d7fb19+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/cf/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  	It("sets the command and buildpack to blank when their values are null in the manifest", func() {
   368  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   369  			"applications": []interface{}{
   370  				generic.NewMap(map[interface{}]interface{}{
   371  					"buildpack": nil,
   372  					"command":   nil,
   373  				}),
   374  			},
   375  		}))
   376  
   377  		apps, err := m.Applications()
   378  		Expect(err).NotTo(HaveOccurred())
   379  		Expect(*apps[0].Command).To(Equal(""))
   380  		Expect(*apps[0].BuildpackURL).To(Equal(""))
   381  	})
   382  
   383  	It("sets the command and buildpack to blank when their values are 'default' in the manifest", func() {
   384  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   385  			"applications": []interface{}{
   386  				generic.NewMap(map[interface{}]interface{}{
   387  					"command":   "default",
   388  					"buildpack": "default",
   389  				}),
   390  			},
   391  		}))
   392  
   393  		apps, err := m.Applications()
   394  		Expect(err).NotTo(HaveOccurred())
   395  		Expect(*apps[0].Command).To(Equal(""))
   396  		Expect(*apps[0].BuildpackURL).To(Equal(""))
   397  	})
   398  
   399  	It("does not set the start command when the manifest doesn't have the 'command' key", func() {
   400  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   401  			"applications": []interface{}{
   402  				map[interface{}]interface{}{},
   403  			},
   404  		}))
   405  
   406  		apps, err := m.Applications()
   407  		Expect(err).NotTo(HaveOccurred())
   408  		Expect(apps[0].Command).To(BeNil())
   409  	})
   410  
   411  	It("can build the applications multiple times", func() {
   412  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   413  			"memory": "254m",
   414  			"applications": []interface{}{
   415  				map[interface{}]interface{}{
   416  					"name": "bitcoin-miner",
   417  				},
   418  				map[interface{}]interface{}{
   419  					"name": "bitcoin-miner",
   420  				},
   421  			},
   422  		}))
   423  
   424  		apps1, err := m.Applications()
   425  		Expect(err).NotTo(HaveOccurred())
   426  
   427  		apps2, err := m.Applications()
   428  		Expect(err).NotTo(HaveOccurred())
   429  		Expect(apps1).To(Equal(apps2))
   430  	})
   431  
   432  	Context("parsing app ports", func() {
   433  		It("parses app ports", func() {
   434  			m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   435  				"applications": []interface{}{
   436  					map[interface{}]interface{}{
   437  						"app-ports": []interface{}{
   438  							8080,
   439  							9090,
   440  						},
   441  					},
   442  				},
   443  			}))
   444  
   445  			apps, err := m.Applications()
   446  			Expect(err).NotTo(HaveOccurred())
   447  
   448  			Expect(apps[0].AppPorts).NotTo(BeNil())
   449  			Expect(*(apps[0].AppPorts)).To(Equal([]int{8080, 9090}))
   450  		})
   451  
   452  		It("handles omitted field", func() {
   453  			m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   454  				"applications": []interface{}{
   455  					map[interface{}]interface{}{},
   456  				},
   457  			}))
   458  
   459  			apps, err := m.Applications()
   460  			Expect(err).NotTo(HaveOccurred())
   461  
   462  			Expect(apps[0].AppPorts).To(BeNil())
   463  		})
   464  
   465  		It("handles mixed arrays", func() {
   466  			m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   467  				"applications": []interface{}{
   468  					map[interface{}]interface{}{
   469  						"app-ports": []interface{}{
   470  							8080,
   471  							"potato",
   472  						},
   473  					},
   474  				},
   475  			}))
   476  
   477  			_, err := m.Applications()
   478  			Expect(err).To(HaveOccurred())
   479  			Expect(err.Error()).To(ContainSubstring("Expected app-ports to be a list of integers."))
   480  		})
   481  
   482  		It("handles non-array values", func() {
   483  			m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   484  				"applications": []interface{}{
   485  					map[interface{}]interface{}{
   486  						"app-ports": "potato",
   487  					},
   488  				},
   489  			}))
   490  
   491  			_, err := m.Applications()
   492  			Expect(err).To(HaveOccurred())
   493  			Expect(err.Error()).To(ContainSubstring("Expected app-ports to be a list of integers."))
   494  		})
   495  	})
   496  
   497  	Context("parsing env vars", func() {
   498  		It("handles values that are not strings", func() {
   499  			m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   500  				"applications": []interface{}{
   501  					generic.NewMap(map[interface{}]interface{}{
   502  						"env": map[interface{}]interface{}{
   503  							"string-key":      "value",
   504  							"int-key":         1,
   505  							"float-key":       11.1,
   506  							"large-int-key":   123456789,
   507  							"large-float-key": 123456789.12345678,
   508  							"bool-key":        false,
   509  						},
   510  					}),
   511  				},
   512  			}))
   513  
   514  			app, err := m.Applications()
   515  			Expect(err).NotTo(HaveOccurred())
   516  
   517  			Expect((*app[0].EnvironmentVars)["string-key"]).To(Equal("value"))
   518  			Expect((*app[0].EnvironmentVars)["int-key"]).To(Equal("1"))
   519  			Expect((*app[0].EnvironmentVars)["float-key"]).To(Equal("11.1"))
   520  			Expect((*app[0].EnvironmentVars)["large-int-key"]).To(Equal("123456789"))
   521  			Expect((*app[0].EnvironmentVars)["large-float-key"]).To(Equal("123456789.12345678"))
   522  			Expect((*app[0].EnvironmentVars)["bool-key"]).To(Equal("false"))
   523  		})
   524  	})
   525  
   526  	Context("parsing services", func() {
   527  		It("can read a list of service instance names", func() {
   528  			m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   529  				"services": []interface{}{"service-1", "service-2"},
   530  			}))
   531  
   532  			app, err := m.Applications()
   533  			Expect(err).NotTo(HaveOccurred())
   534  
   535  			Expect(app[0].ServicesToBind).To(Equal([]string{"service-1", "service-2"}))
   536  		})
   537  	})
   538  
   539  	Context("when routes are provided", func() {
   540  		var manifest *manifest.Manifest
   541  
   542  		Context("when passed 'routes'", func() {
   543  			Context("valid 'routes'", func() {
   544  				BeforeEach(func() {
   545  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   546  						"applications": []interface{}{
   547  							generic.NewMap(map[interface{}]interface{}{
   548  								"routes": []interface{}{
   549  									map[interface{}]interface{}{"route": "route1.example.com"},
   550  									map[interface{}]interface{}{"route": "route2.example.com"},
   551  								},
   552  							}),
   553  						},
   554  					}))
   555  				})
   556  
   557  				It("parses routes into app params", func() {
   558  					apps, err := manifest.Applications()
   559  					Expect(err).NotTo(HaveOccurred())
   560  					Expect(apps).To(HaveLen(1))
   561  
   562  					routes := apps[0].Routes
   563  					Expect(routes).To(HaveLen(2))
   564  					Expect(routes[0].Route).To(Equal("route1.example.com"))
   565  					Expect(routes[1].Route).To(Equal("route2.example.com"))
   566  				})
   567  			})
   568  
   569  			Context("invalid 'routes'", func() {
   570  				Context("'routes' is formatted incorrectly", func() {
   571  					BeforeEach(func() {
   572  						manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   573  							"applications": []interface{}{
   574  								generic.NewMap(map[interface{}]interface{}{
   575  									"routes": []string{},
   576  								}),
   577  							},
   578  						}))
   579  					})
   580  
   581  					It("errors out", func() {
   582  						_, err := manifest.Applications()
   583  						Expect(err).To(HaveOccurred())
   584  						Expect(err.Error()).To(MatchRegexp("should be a list"))
   585  					})
   586  				})
   587  
   588  				Context("an individual 'route' is formatted incorrectly", func() {
   589  					BeforeEach(func() {
   590  						manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   591  							"applications": []interface{}{
   592  								generic.NewMap(map[interface{}]interface{}{
   593  									"routes": []interface{}{
   594  										map[interface{}]interface{}{"routef": "route1.example.com"},
   595  									},
   596  								}),
   597  							},
   598  						}))
   599  					})
   600  
   601  					It("parses routes into app params", func() {
   602  						_, err := manifest.Applications()
   603  						Expect(err).To(HaveOccurred())
   604  						Expect(err.Error()).To(MatchRegexp("each route in 'routes' must have a 'route' property"))
   605  					})
   606  				})
   607  			})
   608  		})
   609  
   610  		Context("when there are no routes", 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  							"buildpack": nil,
   616  							"command":   "echo banana",
   617  						}),
   618  					},
   619  				}))
   620  			})
   621  
   622  			It("sets routes to be nil", func() {
   623  				apps, err := manifest.Applications()
   624  				Expect(err).NotTo(HaveOccurred())
   625  				Expect(apps).To(HaveLen(1))
   626  				Expect(apps[0].Routes).To(BeNil())
   627  			})
   628  		})
   629  
   630  		Context("when no-hostname is not specified in the manifest", func() {
   631  			BeforeEach(func() {
   632  				manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   633  					"applications": []interface{}{
   634  						generic.NewMap(map[interface{}]interface{}{
   635  							"buildpack": nil,
   636  							"command":   "echo banana",
   637  						}),
   638  					},
   639  				}))
   640  			})
   641  
   642  			It("sets no-hostname to be nil", func() {
   643  				apps, err := manifest.Applications()
   644  				Expect(err).NotTo(HaveOccurred())
   645  				Expect(apps).To(HaveLen(1))
   646  				Expect(apps[0].NoHostname).To(BeNil())
   647  			})
   648  		})
   649  
   650  		Context("when no-hostname is specified in the manifest", func() {
   651  			Context("and it is set to true", func() {
   652  				Context("and the value is a boolean", func() {
   653  					BeforeEach(func() {
   654  						manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   655  							"applications": []interface{}{
   656  								generic.NewMap(map[interface{}]interface{}{
   657  									"buildpack":   nil,
   658  									"command":     "echo banana",
   659  									"no-hostname": true,
   660  								}),
   661  							},
   662  						}))
   663  					})
   664  
   665  					It("sets no-hostname to be true", func() {
   666  						apps, err := manifest.Applications()
   667  						Expect(err).NotTo(HaveOccurred())
   668  						Expect(apps).To(HaveLen(1))
   669  						Expect(*apps[0].NoHostname).To(BeTrue())
   670  					})
   671  				})
   672  				Context("and the value is a string", func() {
   673  					BeforeEach(func() {
   674  						manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   675  							"applications": []interface{}{
   676  								generic.NewMap(map[interface{}]interface{}{
   677  									"buildpack":   nil,
   678  									"command":     "echo banana",
   679  									"no-hostname": "true",
   680  								}),
   681  							},
   682  						}))
   683  					})
   684  
   685  					It("sets no-hostname to be true", func() {
   686  						apps, err := manifest.Applications()
   687  						Expect(err).NotTo(HaveOccurred())
   688  						Expect(apps).To(HaveLen(1))
   689  						Expect(*apps[0].NoHostname).To(BeTrue())
   690  					})
   691  				})
   692  			})
   693  			Context("and it is set to false", func() {
   694  				BeforeEach(func() {
   695  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   696  						"applications": []interface{}{
   697  							generic.NewMap(map[interface{}]interface{}{
   698  								"buildpack":   nil,
   699  								"command":     "echo banana",
   700  								"no-hostname": false,
   701  							}),
   702  						},
   703  					}))
   704  				})
   705  				It("sets no-hostname to be false", func() {
   706  					apps, err := manifest.Applications()
   707  					Expect(err).NotTo(HaveOccurred())
   708  					Expect(apps).To(HaveLen(1))
   709  					Expect(*apps[0].NoHostname).To(BeFalse())
   710  				})
   711  			})
   712  		})
   713  	})
   714  
   715  	Context("when the 'docker' property is not specified", func() {
   716  		var manifest *manifest.Manifest
   717  
   718  		BeforeEach(func() {
   719  			manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   720  				"applications": []interface{}{
   721  					generic.NewMap(map[interface{}]interface{}{}),
   722  				},
   723  			}))
   724  		})
   725  
   726  		It("returns a parsed manifest that does not contain any docker fields", func() {
   727  			apps, err := manifest.Applications()
   728  			Expect(err).NotTo(HaveOccurred())
   729  			Expect(apps).To(HaveLen(1))
   730  			Expect(apps[0].DockerImage).To(BeNil())
   731  			Expect(apps[0].DockerUsername).To(BeNil())
   732  		})
   733  	})
   734  
   735  	Context("when the 'docker' property is specified but contains no properties", func() {
   736  		var manifest *manifest.Manifest
   737  
   738  		BeforeEach(func() {
   739  			manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   740  				"applications": []interface{}{
   741  					generic.NewMap(map[interface{}]interface{}{
   742  						"docker": map[interface{}]interface{}{},
   743  					}),
   744  				},
   745  			}))
   746  		})
   747  
   748  		It("returns a parsed manifest that does not contain any docker fields", func() {
   749  			apps, err := manifest.Applications()
   750  			Expect(err).NotTo(HaveOccurred())
   751  			Expect(apps).To(HaveLen(1))
   752  			Expect(apps[0].DockerImage).To(BeNil())
   753  			Expect(apps[0].DockerUsername).To(BeNil())
   754  		})
   755  	})
   756  
   757  	Context("when the 'docker' property is specified", func() {
   758  		var manifest *manifest.Manifest
   759  
   760  		Context("when 'docker' contains an 'image' property", func() {
   761  			Context("when the 'image' property is not a string", func() {
   762  				BeforeEach(func() {
   763  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   764  						"applications": []interface{}{
   765  							generic.NewMap(map[interface{}]interface{}{
   766  								"docker": map[interface{}]interface{}{
   767  									"image": []interface{}{},
   768  								},
   769  							}),
   770  						},
   771  					}))
   772  				})
   773  
   774  				It("returns an error", func() {
   775  					_, err := manifest.Applications()
   776  					Expect(err.Error()).To(MatchRegexp("'docker.image' must be a string"))
   777  				})
   778  			})
   779  
   780  			Context("when the 'image' property is a string", func() {
   781  				BeforeEach(func() {
   782  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   783  						"applications": []interface{}{
   784  							generic.NewMap(map[interface{}]interface{}{
   785  								"docker": map[interface{}]interface{}{
   786  									"image": "docker.com:3333/docker/docker:tag",
   787  								},
   788  							}),
   789  						},
   790  					}))
   791  				})
   792  
   793  				It("sets DockerImage to the 'image' property value", func() {
   794  					apps, err := manifest.Applications()
   795  					Expect(err).NotTo(HaveOccurred())
   796  					Expect(apps).To(HaveLen(1))
   797  					Expect(*apps[0].DockerImage).To(Equal("docker.com:3333/docker/docker:tag"))
   798  				})
   799  			})
   800  		})
   801  
   802  		Context("when 'docker' contains a 'username' property", func() {
   803  			Context("when 'username' is not a string", func() {
   804  				BeforeEach(func() {
   805  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   806  						"applications": []interface{}{
   807  							generic.NewMap(map[interface{}]interface{}{
   808  								"docker": map[interface{}]interface{}{
   809  									"username": []interface{}{},
   810  								},
   811  							}),
   812  						},
   813  					}))
   814  				})
   815  
   816  				It("returns an error", func() {
   817  					_, err := manifest.Applications()
   818  					Expect(err.Error()).To(MatchRegexp("'docker.username' must be a string"))
   819  				})
   820  			})
   821  
   822  			Context("when 'username' is a string", func() {
   823  				BeforeEach(func() {
   824  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   825  						"applications": []interface{}{
   826  							generic.NewMap(map[interface{}]interface{}{
   827  								"docker": map[interface{}]interface{}{
   828  									"username": "some-user",
   829  								},
   830  							}),
   831  						},
   832  					}))
   833  				})
   834  
   835  				It("sets DockerUsername to the 'username' property value", func() {
   836  					apps, err := manifest.Applications()
   837  					Expect(err).NotTo(HaveOccurred())
   838  					Expect(apps).To(HaveLen(1))
   839  					Expect(*apps[0].DockerUsername).To(Equal("some-user"))
   840  				})
   841  			})
   842  		})
   843  	})
   844  })