github.com/cloudfoundry-attic/ltc@v0.0.0-20151123212628-098adc7919fc/app_examiner/app_examiner_test.go (about)

     1  package app_examiner_test
     2  
     3  import (
     4  	"errors"
     5  
     6  	. "github.com/onsi/ginkgo"
     7  	. "github.com/onsi/gomega"
     8  
     9  	"github.com/cloudfoundry-incubator/bbs/models"
    10  	"github.com/cloudfoundry-incubator/ltc/app_examiner"
    11  	"github.com/cloudfoundry-incubator/ltc/app_examiner/fake_noaa_consumer"
    12  	"github.com/cloudfoundry-incubator/ltc/route_helpers"
    13  	"github.com/cloudfoundry-incubator/receptor"
    14  	"github.com/cloudfoundry-incubator/receptor/fake_receptor"
    15  	"github.com/cloudfoundry/sonde-go/events"
    16  )
    17  
    18  var _ = Describe("AppExaminer", func() {
    19  
    20  	var (
    21  		fakeReceptorClient *fake_receptor.FakeClient
    22  		fakeNoaaConsumer   *fake_noaa_consumer.FakeNoaaConsumer
    23  		appExaminer        app_examiner.AppExaminer
    24  	)
    25  
    26  	BeforeEach(func() {
    27  		fakeReceptorClient = &fake_receptor.FakeClient{}
    28  		fakeNoaaConsumer = &fake_noaa_consumer.FakeNoaaConsumer{}
    29  		appExaminer = app_examiner.New(fakeReceptorClient, fakeNoaaConsumer)
    30  	})
    31  
    32  	Describe("ListApps", func() {
    33  		Context("with the receptor returning both desiredlrps and actuallrps", func() {
    34  			BeforeEach(func() {
    35  				desiredLrps := []receptor.DesiredLRPResponse{
    36  					{
    37  						ProcessGuid: "process2-scalingDown",
    38  						Instances:   0,
    39  						DiskMB:      564,
    40  						MemoryMB:    200,
    41  						Routes: route_helpers.Routes{
    42  							AppRoutes: route_helpers.AppRoutes{
    43  								{Hostnames: []string{"ren", "stimpy"}},
    44  							}}.RoutingInfo(),
    45  					},
    46  					{
    47  						ProcessGuid: "process1-scalingUp",
    48  						Instances:   2,
    49  						DiskMB:      256,
    50  						MemoryMB:    100,
    51  						Routes: route_helpers.Routes{
    52  							AppRoutes: route_helpers.AppRoutes{
    53  								{Hostnames: []string{"happy", "joy"}},
    54  							}}.RoutingInfo(),
    55  						EnvironmentVariables: []receptor.EnvironmentVariable{},
    56  						StartTimeout:         30,
    57  						CPUWeight:            94,
    58  						Ports:                []uint16{2378, 67},
    59  						LogGuid:              "asdf-ojf93-9sdcsdk",
    60  						LogSource:            "proc1-log",
    61  						Annotation:           "Best process this side o' the Mississippi.",
    62  					},
    63  				}
    64  				fakeReceptorClient.DesiredLRPsReturns(desiredLrps, nil)
    65  
    66  				actualLrps := []receptor.ActualLRPResponse{
    67  					{ProcessGuid: "process3-stopping", InstanceGuid: "guid4", Index: 1, State: receptor.ActualLRPStateRunning},
    68  					{ProcessGuid: "process1-scalingUp", InstanceGuid: "guid1", Index: 1, State: receptor.ActualLRPStateRunning},
    69  					{ProcessGuid: "process1-scalingUp", InstanceGuid: "guid2", Index: 2, State: receptor.ActualLRPStateClaimed},
    70  					{ProcessGuid: "process2-scalingDown", InstanceGuid: "guid3", Index: 1, State: receptor.ActualLRPStateRunning},
    71  				}
    72  				fakeReceptorClient.ActualLRPsReturns(actualLrps, nil)
    73  			})
    74  
    75  			It("returns a list of alphabetically sorted examined apps", func() {
    76  				appList, err := appExaminer.ListApps()
    77  
    78  				Expect(err).NotTo(HaveOccurred())
    79  				Expect(appList).To(HaveLen(3))
    80  
    81  				process1 := appList[0]
    82  				Expect(process1.ProcessGuid).To(Equal("process1-scalingUp"))
    83  				Expect(process1.DesiredInstances).To(Equal(2))
    84  				Expect(process1.ActualRunningInstances).To(Equal(1))
    85  				Expect(process1.DiskMB).To(Equal(256))
    86  				Expect(process1.MemoryMB).To(Equal(100))
    87  				Expect(process1.Routes).To(Equal(
    88  					route_helpers.Routes{
    89  						AppRoutes: route_helpers.AppRoutes{
    90  							{Hostnames: []string{"happy", "joy"}},
    91  						},
    92  					}))
    93  				Expect(process1.EnvironmentVariables).To(Equal([]app_examiner.EnvironmentVariable{}))
    94  				Expect(process1.StartTimeout).To(Equal(uint(30)))
    95  				Expect(process1.CPUWeight).To(Equal(uint(94)))
    96  				Expect(process1.Ports).To(Equal([]uint16{2378, 67}))
    97  				Expect(process1.LogGuid).To(Equal("asdf-ojf93-9sdcsdk"))
    98  				Expect(process1.LogSource).To(Equal("proc1-log"))
    99  				Expect(process1.Annotation).To(Equal("Best process this side o' the Mississippi."))
   100  
   101  				process2 := appList[1]
   102  				Expect(process2.ProcessGuid).To(Equal("process2-scalingDown"))
   103  				Expect(process2.DesiredInstances).To(Equal(0))
   104  				Expect(process2.ActualRunningInstances).To(Equal(1))
   105  				Expect(process2.DiskMB).To(Equal(564))
   106  				Expect(process2.MemoryMB).To(Equal(200))
   107  				Expect(process2.Routes).To(Equal(
   108  					route_helpers.Routes{
   109  						AppRoutes: route_helpers.AppRoutes{
   110  							{Hostnames: []string{"ren", "stimpy"}},
   111  						},
   112  					}))
   113  
   114  				process3 := appList[2]
   115  				Expect(process3.ProcessGuid).To(Equal("process3-stopping"))
   116  				Expect(process3.DesiredInstances).To(Equal(0))
   117  				Expect(process3.ActualRunningInstances).To(Equal(1))
   118  				Expect(process3.DiskMB).To(Equal(0))
   119  				Expect(process3.MemoryMB).To(Equal(0))
   120  				Expect(process3.Routes).To(BeZero())
   121  			})
   122  
   123  			Context("with tcp-route", func() {
   124  				BeforeEach(func() {
   125  					desiredLrps := []receptor.DesiredLRPResponse{
   126  						receptor.DesiredLRPResponse{
   127  							ProcessGuid: "process2-scalingDown",
   128  							Instances:   0,
   129  							DiskMB:      564,
   130  							MemoryMB:    200,
   131  							Routes: route_helpers.Routes{
   132  								AppRoutes: route_helpers.AppRoutes{
   133  									{Hostnames: []string{"ren", "stimpy"}}},
   134  								TcpRoutes: route_helpers.TcpRoutes{
   135  									{ExternalPort: 51000, Port: 5222},
   136  								},
   137  							}.RoutingInfo(),
   138  						},
   139  						receptor.DesiredLRPResponse{
   140  							ProcessGuid: "process1-scalingUp",
   141  							Instances:   2,
   142  							DiskMB:      256,
   143  							MemoryMB:    100,
   144  							Routes: route_helpers.Routes{
   145  								AppRoutes: route_helpers.AppRoutes{
   146  									{Hostnames: []string{"happy", "joy"}},
   147  								},
   148  								TcpRoutes: route_helpers.TcpRoutes{
   149  									{ExternalPort: 52000, Port: 5222},
   150  								},
   151  							}.RoutingInfo(),
   152  							EnvironmentVariables: []receptor.EnvironmentVariable{},
   153  							StartTimeout:         30,
   154  							CPUWeight:            94,
   155  							Ports:                []uint16{2378, 67},
   156  							LogGuid:              "asdf-ojf93-9sdcsdk",
   157  							LogSource:            "proc1-log",
   158  							Annotation:           "Best process this side o' the Mississippi.",
   159  						},
   160  					}
   161  					fakeReceptorClient.DesiredLRPsReturns(desiredLrps, nil)
   162  
   163  					actualLrps := []receptor.ActualLRPResponse{
   164  						receptor.ActualLRPResponse{ProcessGuid: "process3-stopping", InstanceGuid: "guid4", Index: 1, State: receptor.ActualLRPStateRunning},
   165  						receptor.ActualLRPResponse{ProcessGuid: "process1-scalingUp", InstanceGuid: "guid1", Index: 1, State: receptor.ActualLRPStateRunning},
   166  						receptor.ActualLRPResponse{ProcessGuid: "process1-scalingUp", InstanceGuid: "guid2", Index: 2, State: receptor.ActualLRPStateClaimed},
   167  						receptor.ActualLRPResponse{ProcessGuid: "process2-scalingDown", InstanceGuid: "guid3", Index: 1, State: receptor.ActualLRPStateRunning},
   168  					}
   169  					fakeReceptorClient.ActualLRPsReturns(actualLrps, nil)
   170  				})
   171  
   172  				It("returns a list of alphabetically sorted examined apps with tcp routes", func() {
   173  					appList, err := appExaminer.ListApps()
   174  
   175  					Expect(err).NotTo(HaveOccurred())
   176  					Expect(appList).To(HaveLen(3))
   177  
   178  					process1 := appList[0]
   179  					Expect(process1.ProcessGuid).To(Equal("process1-scalingUp"))
   180  					Expect(process1.DesiredInstances).To(Equal(2))
   181  					Expect(process1.ActualRunningInstances).To(Equal(1))
   182  					Expect(process1.DiskMB).To(Equal(256))
   183  					Expect(process1.MemoryMB).To(Equal(100))
   184  					Expect(process1.Routes).To(Equal(
   185  						route_helpers.Routes{
   186  							AppRoutes: route_helpers.AppRoutes{
   187  								{Hostnames: []string{"happy", "joy"}},
   188  							},
   189  							TcpRoutes: route_helpers.TcpRoutes{
   190  								{
   191  									ExternalPort: 52000,
   192  									Port:         5222,
   193  								},
   194  							},
   195  						}))
   196  					Expect(process1.EnvironmentVariables).To(Equal([]app_examiner.EnvironmentVariable{}))
   197  					Expect(process1.StartTimeout).To(Equal(uint(30)))
   198  					Expect(process1.CPUWeight).To(Equal(uint(94)))
   199  					Expect(process1.Ports).To(Equal([]uint16{2378, 67}))
   200  					Expect(process1.LogGuid).To(Equal("asdf-ojf93-9sdcsdk"))
   201  					Expect(process1.LogSource).To(Equal("proc1-log"))
   202  					Expect(process1.Annotation).To(Equal("Best process this side o' the Mississippi."))
   203  
   204  					process2 := appList[1]
   205  					Expect(process2.ProcessGuid).To(Equal("process2-scalingDown"))
   206  					Expect(process2.DesiredInstances).To(Equal(0))
   207  					Expect(process2.ActualRunningInstances).To(Equal(1))
   208  					Expect(process2.DiskMB).To(Equal(564))
   209  					Expect(process2.MemoryMB).To(Equal(200))
   210  					Expect(process2.Routes).To(Equal(
   211  						route_helpers.Routes{
   212  							AppRoutes: route_helpers.AppRoutes{
   213  								{Hostnames: []string{"ren", "stimpy"}},
   214  							},
   215  							TcpRoutes: route_helpers.TcpRoutes{
   216  								{ExternalPort: 51000, Port: 5222},
   217  							},
   218  						}))
   219  
   220  					process3 := appList[2]
   221  					Expect(process3.ProcessGuid).To(Equal("process3-stopping"))
   222  					Expect(process3.DesiredInstances).To(Equal(0))
   223  					Expect(process3.ActualRunningInstances).To(Equal(1))
   224  					Expect(process3.DiskMB).To(Equal(0))
   225  					Expect(process3.MemoryMB).To(Equal(0))
   226  					Expect(process3.Routes).To(BeZero())
   227  				})
   228  			})
   229  		})
   230  
   231  		Context("when the receptor returns errors", func() {
   232  			It("returns errors from from fetching the DesiredLRPs", func() {
   233  				fakeReceptorClient.DesiredLRPsReturns(nil, errors.New("You should go catch it."))
   234  
   235  				_, err := appExaminer.ListApps()
   236  				Expect(err).To(MatchError("You should go catch it."))
   237  			})
   238  
   239  			It("returns errors from fetching the ActualLRPs", func() {
   240  				fakeReceptorClient.ActualLRPsReturns(nil, errors.New("Receptor is on fire!!"))
   241  
   242  				_, err := appExaminer.ListApps()
   243  				Expect(err).To(MatchError("Receptor is on fire!!"))
   244  			})
   245  		})
   246  	})
   247  
   248  	Describe("ListCells", func() {
   249  		Context("receptor returns actual lrps that are all on existing cells", func() {
   250  			BeforeEach(func() {
   251  				actualLrps := []receptor.ActualLRPResponse{
   252  					receptor.ActualLRPResponse{CellID: "Cell-1", State: receptor.ActualLRPStateRunning},
   253  					receptor.ActualLRPResponse{CellID: "Cell-1", State: receptor.ActualLRPStateRunning},
   254  					receptor.ActualLRPResponse{CellID: "Cell-2", State: receptor.ActualLRPStateClaimed},
   255  					receptor.ActualLRPResponse{CellID: "Cell-2", State: receptor.ActualLRPStateRunning},
   256  				}
   257  				fakeReceptorClient.ActualLRPsReturns(actualLrps, nil)
   258  
   259  				cells := []receptor.CellResponse{
   260  					receptor.CellResponse{CellID: "Cell-1", Zone: "z1", Capacity: receptor.CellCapacity{MemoryMB: 12394, DiskMB: 2349083, Containers: 512}},
   261  					receptor.CellResponse{CellID: "Cell-2", Zone: "z1", Capacity: receptor.CellCapacity{MemoryMB: 12394, DiskMB: 2349083, Containers: 512}},
   262  					receptor.CellResponse{CellID: "Cell-3", Zone: "z2", Capacity: receptor.CellCapacity{MemoryMB: 12394, DiskMB: 2349083, Containers: 512}},
   263  				}
   264  				fakeReceptorClient.CellsReturns(cells, nil)
   265  			})
   266  
   267  			It("returns a list of alphabetically sorted examined cells", func() {
   268  				cellList, err := appExaminer.ListCells()
   269  				Expect(err).NotTo(HaveOccurred())
   270  				Expect(cellList).To(HaveLen(3))
   271  
   272  				cell1 := cellList[0]
   273  				Expect(cell1.CellID).To(Equal("Cell-1"))
   274  				Expect(cell1.RunningInstances).To(Equal(2))
   275  				Expect(cell1.ClaimedInstances).To(Equal(0))
   276  				Expect(cell1.Zone).To(Equal("z1"))
   277  				Expect(cell1.MemoryMB).To(Equal(12394))
   278  				Expect(cell1.DiskMB).To(Equal(2349083))
   279  				Expect(cell1.Containers).To(Equal(512))
   280  
   281  				cell2 := cellList[1]
   282  				Expect(cell2.CellID).To(Equal("Cell-2"))
   283  				Expect(cell2.RunningInstances).To(Equal(1))
   284  				Expect(cell2.ClaimedInstances).To(Equal(1))
   285  				Expect(cell2.Zone).To(Equal("z1"))
   286  
   287  				cell3 := cellList[2]
   288  				Expect(cell3.CellID).To(Equal("Cell-3"))
   289  				Expect(cell3.RunningInstances).To(Equal(0))
   290  				Expect(cell3.ClaimedInstances).To(Equal(0))
   291  				Expect(cell3.Zone).To(Equal("z2"))
   292  			})
   293  		})
   294  
   295  		Context("receptor returns actual lrps that are all on existing cells with Diff Cell IDs", func() {
   296  			BeforeEach(func() {
   297  				actualLrps := []receptor.ActualLRPResponse{
   298  					receptor.ActualLRPResponse{CellID: "lattice-east-01", State: receptor.ActualLRPStateRunning},
   299  					receptor.ActualLRPResponse{CellID: "lattice-east-01", State: receptor.ActualLRPStateRunning},
   300  					receptor.ActualLRPResponse{CellID: "north-2", State: receptor.ActualLRPStateClaimed},
   301  					receptor.ActualLRPResponse{CellID: "north-2", State: receptor.ActualLRPStateRunning},
   302  					receptor.ActualLRPResponse{CellID: "lattice-cell-10", State: receptor.ActualLRPStateRunning},
   303  					receptor.ActualLRPResponse{CellID: "lattice-cell-10", State: receptor.ActualLRPStateRunning},
   304  				}
   305  				fakeReceptorClient.ActualLRPsReturns(actualLrps, nil)
   306  
   307  				cells := []receptor.CellResponse{
   308  					receptor.CellResponse{CellID: "lattice-east-01", Zone: "z1", Capacity: receptor.CellCapacity{MemoryMB: 12394, DiskMB: 2349083, Containers: 512}},
   309  					receptor.CellResponse{CellID: "north-2", Zone: "z1", Capacity: receptor.CellCapacity{MemoryMB: 12394, DiskMB: 2349083, Containers: 512}},
   310  					receptor.CellResponse{CellID: "lattice-random-3", Zone: "z2", Capacity: receptor.CellCapacity{MemoryMB: 12394, DiskMB: 2349083, Containers: 512}},
   311  					receptor.CellResponse{CellID: "lattice-Jamesbond", Zone: "z9", Capacity: receptor.CellCapacity{MemoryMB: 12394, DiskMB: 2349083, Containers: 512}},
   312  					receptor.CellResponse{CellID: "lattice-cell-10", Zone: "z10", Capacity: receptor.CellCapacity{MemoryMB: 12394, DiskMB: 2349083, Containers: 512}},
   313  					receptor.CellResponse{CellID: "lattice-ReallyBig-10000000", Zone: "z11", Capacity: receptor.CellCapacity{MemoryMB: 12394, DiskMB: 2349083, Containers: 512}},
   314  				}
   315  				fakeReceptorClient.CellsReturns(cells, nil)
   316  			})
   317  
   318  			It("returns a list of numarically sorted examined cells", func() {
   319  				cellList, err := appExaminer.ListCells()
   320  				Expect(err).NotTo(HaveOccurred())
   321  				Expect(cellList).To(HaveLen(6))
   322  
   323  				cell1 := cellList[0]
   324  				Expect(cell1.CellID).To(Equal("lattice-east-01"))
   325  				Expect(cell1.RunningInstances).To(Equal(2))
   326  				Expect(cell1.ClaimedInstances).To(Equal(0))
   327  				Expect(cell1.Zone).To(Equal("z1"))
   328  				Expect(cell1.MemoryMB).To(Equal(12394))
   329  				Expect(cell1.DiskMB).To(Equal(2349083))
   330  				Expect(cell1.Containers).To(Equal(512))
   331  
   332  				cell2 := cellList[1]
   333  				Expect(cell2.CellID).To(Equal("north-2"))
   334  				Expect(cell2.RunningInstances).To(Equal(1))
   335  				Expect(cell2.ClaimedInstances).To(Equal(1))
   336  				Expect(cell2.Zone).To(Equal("z1"))
   337  
   338  				cell3 := cellList[2]
   339  				Expect(cell3.CellID).To(Equal("lattice-random-3"))
   340  				Expect(cell3.RunningInstances).To(Equal(0))
   341  				Expect(cell3.ClaimedInstances).To(Equal(0))
   342  				Expect(cell3.Zone).To(Equal("z2"))
   343  
   344  				cell4 := cellList[3]
   345  				Expect(cell4.CellID).To(Equal("lattice-cell-10"))
   346  				Expect(cell4.RunningInstances).To(Equal(2))
   347  				Expect(cell4.ClaimedInstances).To(Equal(0))
   348  				Expect(cell4.Zone).To(Equal("z10"))
   349  
   350  				cell5 := cellList[4]
   351  				Expect(cell5.CellID).To(Equal("lattice-ReallyBig-10000000"))
   352  				Expect(cell5.RunningInstances).To(Equal(0))
   353  				Expect(cell5.ClaimedInstances).To(Equal(0))
   354  				Expect(cell5.Zone).To(Equal("z11"))
   355  
   356  				cell6 := cellList[5]
   357  				Expect(cell6.CellID).To(Equal("lattice-Jamesbond"))
   358  				Expect(cell6.RunningInstances).To(Equal(0))
   359  				Expect(cell6.ClaimedInstances).To(Equal(0))
   360  				Expect(cell6.Zone).To(Equal("z9"))
   361  
   362  			})
   363  		})
   364  
   365  		Context("receptor returns actual lrps, and some of their cells no longer exist", func() {
   366  			BeforeEach(func() {
   367  				actualLrps := []receptor.ActualLRPResponse{
   368  					{CellID: "Cell-0", State: receptor.ActualLRPStateRunning},
   369  					{CellID: "Cell-0", State: receptor.ActualLRPStateClaimed},
   370  					{CellID: "Cell-1", State: receptor.ActualLRPStateRunning},
   371  				}
   372  				fakeReceptorClient.ActualLRPsReturns(actualLrps, nil)
   373  				cells := []receptor.CellResponse{
   374  					{CellID: "Cell-1"},
   375  				}
   376  				fakeReceptorClient.CellsReturns(cells, nil)
   377  			})
   378  
   379  			It("returns a list of alphabetically sorted examined cells", func() {
   380  				cellList, err := appExaminer.ListCells()
   381  				Expect(err).NotTo(HaveOccurred())
   382  				Expect(cellList).To(HaveLen(2))
   383  
   384  				cell0 := cellList[0]
   385  				Expect(cell0.CellID).To(Equal("Cell-0"))
   386  				Expect(cell0.Missing).To(BeTrue())
   387  				Expect(cell0.RunningInstances).To(Equal(1))
   388  				Expect(cell0.ClaimedInstances).To(Equal(1))
   389  			})
   390  		})
   391  
   392  		Context("receptor returns unclaimed actual lrps", func() {
   393  			BeforeEach(func() {
   394  				actualLrps := []receptor.ActualLRPResponse{
   395  					{State: receptor.ActualLRPStateUnclaimed},
   396  					{State: receptor.ActualLRPStateUnclaimed},
   397  				}
   398  				fakeReceptorClient.ActualLRPsReturns(actualLrps, nil)
   399  				fakeReceptorClient.CellsReturns([]receptor.CellResponse{}, nil)
   400  			})
   401  
   402  			It("ignores unclaimed actual lrps", func() {
   403  				cellList, err := appExaminer.ListCells()
   404  				Expect(err).NotTo(HaveOccurred())
   405  				Expect(cellList).To(HaveLen(0))
   406  			})
   407  		})
   408  
   409  		Context("with the receptor returning errors", func() {
   410  			It("returns errors from from fetching the Cells", func() {
   411  				fakeReceptorClient.CellsReturns(nil, errors.New("You should go catch it."))
   412  
   413  				_, err := appExaminer.ListCells()
   414  				Expect(err).To(MatchError("You should go catch it."))
   415  			})
   416  
   417  			It("returns errors from fetching the ActualLRPs", func() {
   418  				fakeReceptorClient.ActualLRPsReturns(nil, errors.New("Receptor is Running."))
   419  
   420  				_, err := appExaminer.ListCells()
   421  				Expect(err).To(MatchError("Receptor is Running."))
   422  			})
   423  
   424  		})
   425  	})
   426  
   427  	Describe("AppStatus", func() {
   428  		var (
   429  			getDesiredLRPResponse           receptor.DesiredLRPResponse
   430  			actualLRPsByProcessGuidResponse []receptor.ActualLRPResponse
   431  			containerMetrics                []*events.ContainerMetric
   432  		)
   433  
   434  		buildContainerMetric := func(applicationId string, instanceIndex int32, cpuPercentage float64, memoryBytes, diskBytes uint64) *events.ContainerMetric {
   435  			return &events.ContainerMetric{
   436  				ApplicationId: &applicationId,
   437  				InstanceIndex: &instanceIndex,
   438  				CpuPercentage: &cpuPercentage,
   439  				MemoryBytes:   &memoryBytes,
   440  				DiskBytes:     &diskBytes,
   441  			}
   442  		}
   443  
   444  		Context("When receptor successfully responds to all requests", func() {
   445  			BeforeEach(func() {
   446  				getDesiredLRPResponse = receptor.DesiredLRPResponse{
   447  					ProcessGuid: "peekaboo-app",
   448  					Domain:      "welp.org",
   449  					RootFS:      "preloaded:rootfs2",
   450  					Instances:   4,
   451  					EnvironmentVariables: []receptor.EnvironmentVariable{
   452  						{Name: "API_TOKEN", Value: "98weufsa"},
   453  						{Name: "PEEKABOO_APP_NICKNAME", Value: "Bugs McGee"},
   454  					},
   455  					StartTimeout: 5,
   456  					DiskMB:       256,
   457  					MemoryMB:     128,
   458  					CPUWeight:    77,
   459  					Ports:        []uint16{8765, 2300},
   460  					Routes: route_helpers.Routes{
   461  						AppRoutes: route_helpers.AppRoutes{
   462  							{Hostnames: []string{"peekaboo-one.example.com", "peekaboo-too.example.com"}},
   463  						},
   464  						TcpRoutes: route_helpers.TcpRoutes{
   465  							{ExternalPort: 52220, Port: 5222},
   466  						},
   467  					}.RoutingInfo(),
   468  					LogGuid:    "9832-ur98j-idsckl",
   469  					LogSource:  "peekaboo-lawgz",
   470  					Annotation: "best. game. ever.",
   471  				}
   472  
   473  				actualLRPsByProcessGuidResponse = []receptor.ActualLRPResponse{
   474  					{
   475  						ProcessGuid:  "peekaboo-app",
   476  						InstanceGuid: "aisu-8dfy8-9dhu",
   477  						CellID:       "cell-3",
   478  						Domain:       "welp.org",
   479  						Index:        1,
   480  						Address:      "212.38.11.83",
   481  						Ports: []receptor.PortMapping{
   482  							{HostPort: 2983, ContainerPort: 2001},
   483  						},
   484  						State:      "CLAIMED",
   485  						Since:      1982,
   486  						CrashCount: 3,
   487  					},
   488  					{
   489  						ProcessGuid:  "peekaboo-app",
   490  						InstanceGuid: "98s98a-xcvcx4-93isl",
   491  						CellID:       "cell-2",
   492  						Domain:       "welp.org",
   493  						Index:        0,
   494  						Address:      "211.94.88.63",
   495  						Ports: []receptor.PortMapping{
   496  							{HostPort: 2786, ContainerPort: 2020},
   497  						},
   498  						State: "RUNNING",
   499  						Since: 2002,
   500  					},
   501  					{
   502  						ProcessGuid:    "peekaboo-app",
   503  						Index:          2,
   504  						State:          "UNCLAIMED",
   505  						PlacementError: "not enough resources. eek.",
   506  					},
   507  					{
   508  						ProcessGuid: "peekaboo-app",
   509  						Index:       3,
   510  						State:       "CRASHED",
   511  						CrashCount:  7,
   512  					},
   513  				}
   514  
   515  				containerMetrics = []*events.ContainerMetric{
   516  					buildContainerMetric("peekaboo-app", 0, 0.018138574, 798729, 32768),
   517  				}
   518  			})
   519  
   520  			It("returns a fully populated AppInfo with instances sorted by index", func() {
   521  				fakeReceptorClient.GetDesiredLRPReturns(getDesiredLRPResponse, nil)
   522  				fakeReceptorClient.ActualLRPsByProcessGuidReturns(actualLRPsByProcessGuidResponse, nil)
   523  				fakeNoaaConsumer.GetContainerMetricsReturns(containerMetrics, nil)
   524  
   525  				appInfo, err := appExaminer.AppStatus("peekaboo-app")
   526  				Expect(err).NotTo(HaveOccurred())
   527  
   528  				Expect(appInfo.ProcessGuid).To(Equal("peekaboo-app"))
   529  				Expect(appInfo.RootFS).To(Equal("preloaded:rootfs2"))
   530  				Expect(appInfo.DesiredInstances).To(Equal(4))
   531  				Expect(appInfo.ActualRunningInstances).To(Equal(1))
   532  				Expect(appInfo.EnvironmentVariables).To(ConsistOf(
   533  					app_examiner.EnvironmentVariable{
   534  						Name:  "API_TOKEN",
   535  						Value: "98weufsa",
   536  					},
   537  					app_examiner.EnvironmentVariable{
   538  						Name:  "PEEKABOO_APP_NICKNAME",
   539  						Value: "Bugs McGee",
   540  					},
   541  				))
   542  				Expect(appInfo.StartTimeout).To(Equal(uint(5)))
   543  				Expect(appInfo.DiskMB).To(Equal(256))
   544  				Expect(appInfo.MemoryMB).To(Equal(128))
   545  				Expect(appInfo.CPUWeight).To(Equal(uint(77)))
   546  				Expect(appInfo.Ports).To(ConsistOf(uint16(8765), uint16(2300)))
   547  				Expect(appInfo.Routes).To(Equal(route_helpers.Routes{
   548  					AppRoutes: route_helpers.AppRoutes{
   549  						{Hostnames: []string{"peekaboo-one.example.com", "peekaboo-too.example.com"}},
   550  					},
   551  					TcpRoutes: route_helpers.TcpRoutes{
   552  						{ExternalPort: 52220, Port: 5222},
   553  					},
   554  				},
   555  				))
   556  				Expect(appInfo.LogGuid).To(Equal("9832-ur98j-idsckl"))
   557  				Expect(appInfo.LogSource).To(Equal("peekaboo-lawgz"))
   558  				Expect(appInfo.Annotation).To(Equal("best. game. ever."))
   559  
   560  				Expect(appInfo.ActualInstances).To(HaveLen(4))
   561  
   562  				instanceZero := appInfo.ActualInstances[0]
   563  				Expect(instanceZero.InstanceGuid).To(Equal("98s98a-xcvcx4-93isl"))
   564  				Expect(instanceZero.CellID).To(Equal("cell-2"))
   565  				Expect(instanceZero.Index).To(Equal(0))
   566  				Expect(instanceZero.Ip).To(Equal("211.94.88.63"))
   567  				Expect(instanceZero.Ports).To(ConsistOf(app_examiner.PortMapping{
   568  					HostPort:      2786,
   569  					ContainerPort: 2020,
   570  				}))
   571  				Expect(instanceZero.State).To(Equal("RUNNING"))
   572  				Expect(instanceZero.Since).To(Equal(int64(2002)))
   573  				Expect(instanceZero.HasMetrics).To(BeTrue())
   574  				Expect(instanceZero.Metrics).To(Equal(app_examiner.InstanceMetrics{
   575  					CpuPercentage: 0.018138574,
   576  					MemoryBytes:   798729,
   577  					DiskBytes:     32768,
   578  				}))
   579  
   580  				instanceOne := appInfo.ActualInstances[1]
   581  				Expect(instanceOne.InstanceGuid).To(Equal("aisu-8dfy8-9dhu"))
   582  				Expect(instanceOne.CellID).To(Equal("cell-3"))
   583  				Expect(instanceOne.Index).To(Equal(1))
   584  				Expect(instanceOne.Ip).To(Equal("212.38.11.83"))
   585  				Expect(instanceOne.Ports).To(ConsistOf(app_examiner.PortMapping{
   586  					HostPort:      2983,
   587  					ContainerPort: 2001,
   588  				}))
   589  				Expect(instanceOne.State).To(Equal("CLAIMED"))
   590  				Expect(instanceOne.Since).To(Equal(int64(1982)))
   591  				Expect(instanceOne.CrashCount).To(Equal(3))
   592  				Expect(instanceOne.HasMetrics).To(BeFalse())
   593  
   594  				instanceTwo := appInfo.ActualInstances[2]
   595  				Expect(instanceTwo.Index).To(Equal(2))
   596  				Expect(instanceTwo.Ports).To(BeEmpty())
   597  				Expect(instanceTwo.State).To(Equal("UNCLAIMED"))
   598  				Expect(instanceTwo.PlacementError).To(Equal("not enough resources. eek."))
   599  				Expect(instanceTwo.HasMetrics).To(BeFalse())
   600  
   601  				instanceThree := appInfo.ActualInstances[3]
   602  				Expect(instanceThree.Index).To(Equal(3))
   603  				Expect(instanceThree.Ports).To(BeEmpty())
   604  				Expect(instanceThree.State).To(Equal("CRASHED"))
   605  				Expect(instanceThree.CrashCount).To(Equal(7))
   606  				Expect(instanceThree.HasMetrics).To(BeFalse())
   607  
   608  				Expect(fakeReceptorClient.GetDesiredLRPCallCount()).To(Equal(1))
   609  				Expect(fakeReceptorClient.GetDesiredLRPArgsForCall(0)).To(Equal("peekaboo-app"))
   610  
   611  				Expect(fakeReceptorClient.ActualLRPsByProcessGuidCallCount()).To(Equal(1))
   612  				Expect(fakeReceptorClient.ActualLRPsByProcessGuidArgsForCall(0)).To(Equal("peekaboo-app"))
   613  
   614  				Expect(fakeNoaaConsumer.GetContainerMetricsCallCount()).To(Equal(1))
   615  				appGuid, token := fakeNoaaConsumer.GetContainerMetricsArgsForCall(0)
   616  				Expect(appGuid).To(Equal("peekaboo-app"))
   617  				Expect(token).To(BeEmpty())
   618  			})
   619  
   620  			Describe("Monitors", func() {
   621  				It("returns AppInfo Monitor for a port monitor", func() {
   622  					getDesiredLRPResponse.Monitor = models.WrapAction(&models.RunAction{
   623  						Path: "/tmp/healthcheck",
   624  						Args: []string{
   625  							"-port",
   626  							"8765",
   627  						},
   628  					})
   629  
   630  					fakeReceptorClient.GetDesiredLRPReturns(getDesiredLRPResponse, nil)
   631  					fakeReceptorClient.ActualLRPsByProcessGuidReturns(actualLRPsByProcessGuidResponse, nil)
   632  					fakeNoaaConsumer.GetContainerMetricsReturns(containerMetrics, nil)
   633  
   634  					appInfo, err := appExaminer.AppStatus("peekaboo-app")
   635  					Expect(err).NotTo(HaveOccurred())
   636  
   637  					Expect(appInfo.Monitor.Port).To(Equal(uint16(8765)))
   638  					Expect(appInfo.Monitor.URI).To(BeEmpty())
   639  					Expect(appInfo.Monitor.Command).To(BeEmpty())
   640  					Expect(appInfo.Monitor.CommandArgs).To(BeEmpty())
   641  				})
   642  
   643  				It("returns AppInfo Monitor for a URL monitor", func() {
   644  					getDesiredLRPResponse.Monitor = models.WrapAction(&models.RunAction{
   645  						Path: "/tmp/healthcheck",
   646  						Args: []string{
   647  							"-port",
   648  							"8765",
   649  							"-uri",
   650  							"/health",
   651  						},
   652  					})
   653  
   654  					fakeReceptorClient.GetDesiredLRPReturns(getDesiredLRPResponse, nil)
   655  					fakeReceptorClient.ActualLRPsByProcessGuidReturns(actualLRPsByProcessGuidResponse, nil)
   656  					fakeNoaaConsumer.GetContainerMetricsReturns(containerMetrics, nil)
   657  
   658  					appInfo, err := appExaminer.AppStatus("peekaboo-app")
   659  					Expect(err).NotTo(HaveOccurred())
   660  
   661  					Expect(appInfo.Monitor.Port).To(Equal(uint16(8765)))
   662  					Expect(appInfo.Monitor.URI).To(Equal("/health"))
   663  					Expect(appInfo.Monitor.Command).To(BeEmpty())
   664  					Expect(appInfo.Monitor.CommandArgs).To(BeEmpty())
   665  				})
   666  
   667  				It("returns AppInfo Monitor for a URL monitor", func() {
   668  					getDesiredLRPResponse.Monitor = models.WrapAction(&models.RunAction{
   669  						Path: "/bin/sh",
   670  						Args: []string{
   671  							"-c",
   672  							"custom-healthcheck -port 8765 -uri /health",
   673  						},
   674  					})
   675  
   676  					fakeReceptorClient.GetDesiredLRPReturns(getDesiredLRPResponse, nil)
   677  					fakeReceptorClient.ActualLRPsByProcessGuidReturns(actualLRPsByProcessGuidResponse, nil)
   678  					fakeNoaaConsumer.GetContainerMetricsReturns(containerMetrics, nil)
   679  
   680  					appInfo, err := appExaminer.AppStatus("peekaboo-app")
   681  					Expect(err).NotTo(HaveOccurred())
   682  
   683  					Expect(appInfo.Monitor.Port).To(BeZero())
   684  					Expect(appInfo.Monitor.URI).To(BeEmpty())
   685  					Expect(appInfo.Monitor.Command).To(Equal("/bin/sh"))
   686  					Expect(appInfo.Monitor.CommandArgs).To(Equal([]string{"-c", "custom-healthcheck -port 8765 -uri /health"}))
   687  				})
   688  			})
   689  
   690  			Context("when desired LRP is not found, but there are actual LRPs for the process GUID (App stopping)", func() {
   691  				It("returns AppInfo that has ActualInstances, but is missing desiredlrp specific data", func() {
   692  					fakeReceptorClient.GetDesiredLRPReturns(receptor.DesiredLRPResponse{}, receptor.Error{
   693  						Type:    receptor.DesiredLRPNotFound,
   694  						Message: "Desired LRP with guid 'peekaboo-app' not found"},
   695  					)
   696  					fakeReceptorClient.ActualLRPsByProcessGuidReturns(actualLRPsByProcessGuidResponse, nil)
   697  					fakeNoaaConsumer.GetContainerMetricsReturns(containerMetrics, nil)
   698  
   699  					appInfo, err := appExaminer.AppStatus("peekaboo-app")
   700  					Expect(err).NotTo(HaveOccurred())
   701  
   702  					Expect(appInfo.ProcessGuid).To(Equal("peekaboo-app"))
   703  					Expect(appInfo.ActualRunningInstances).To(Equal(1))
   704  
   705  					Expect(appInfo.ActualInstances).To(HaveLen(4))
   706  
   707  					instanceZero := appInfo.ActualInstances[0]
   708  					Expect(instanceZero.InstanceGuid).To(Equal("98s98a-xcvcx4-93isl"))
   709  					Expect(instanceZero.CellID).To(Equal("cell-2"))
   710  					Expect(instanceZero.Index).To(Equal(0))
   711  					Expect(instanceZero.Ip).To(Equal("211.94.88.63"))
   712  					Expect(instanceZero.Ports).To(ConsistOf(app_examiner.PortMapping{
   713  						HostPort:      2786,
   714  						ContainerPort: 2020,
   715  					}))
   716  					Expect(instanceZero.State).To(Equal("RUNNING"))
   717  					Expect(instanceZero.Since).To(Equal(int64(2002)))
   718  					Expect(instanceZero.HasMetrics).To(BeTrue())
   719  					Expect(instanceZero.Metrics).To(Equal(app_examiner.InstanceMetrics{
   720  						CpuPercentage: 0.018138574,
   721  						MemoryBytes:   798729,
   722  						DiskBytes:     32768,
   723  					}))
   724  
   725  					instanceOne := appInfo.ActualInstances[1]
   726  					Expect(instanceOne.InstanceGuid).To(Equal("aisu-8dfy8-9dhu"))
   727  					Expect(instanceOne.CellID).To(Equal("cell-3"))
   728  					Expect(instanceOne.Index).To(Equal(1))
   729  					Expect(instanceOne.Ip).To(Equal("212.38.11.83"))
   730  					Expect(instanceOne.Ports).To(ConsistOf(app_examiner.PortMapping{
   731  						HostPort:      2983,
   732  						ContainerPort: 2001,
   733  					}))
   734  					Expect(instanceOne.State).To(Equal("CLAIMED"))
   735  					Expect(instanceOne.Since).To(Equal(int64(1982)))
   736  					Expect(instanceOne.CrashCount).To(Equal(3))
   737  					Expect(instanceOne.HasMetrics).To(BeFalse())
   738  
   739  					instanceTwo := appInfo.ActualInstances[2]
   740  					Expect(instanceTwo.Index).To(Equal(2))
   741  					Expect(instanceTwo.Ports).To(BeEmpty())
   742  					Expect(instanceTwo.State).To(Equal("UNCLAIMED"))
   743  					Expect(instanceTwo.PlacementError).To(Equal("not enough resources. eek."))
   744  					Expect(instanceTwo.HasMetrics).To(BeFalse())
   745  
   746  					instanceThree := appInfo.ActualInstances[3]
   747  					Expect(instanceThree.Index).To(Equal(3))
   748  					Expect(instanceThree.Ports).To(BeEmpty())
   749  					Expect(instanceThree.State).To(Equal("CRASHED"))
   750  					Expect(instanceThree.CrashCount).To(Equal(7))
   751  					Expect(instanceThree.HasMetrics).To(BeFalse())
   752  
   753  					Expect(fakeReceptorClient.GetDesiredLRPCallCount()).To(Equal(1))
   754  					Expect(fakeReceptorClient.ActualLRPsByProcessGuidCallCount()).To(Equal(1))
   755  
   756  					Expect(fakeReceptorClient.GetDesiredLRPArgsForCall(0)).To(Equal("peekaboo-app"))
   757  					Expect(fakeReceptorClient.ActualLRPsByProcessGuidArgsForCall(0)).To(Equal("peekaboo-app"))
   758  
   759  					Expect(fakeNoaaConsumer.GetContainerMetricsCallCount()).To(Equal(1))
   760  					appGuid, token := fakeNoaaConsumer.GetContainerMetricsArgsForCall(0)
   761  					Expect(appGuid).To(Equal("peekaboo-app"))
   762  					Expect(token).To(BeEmpty())
   763  				})
   764  			})
   765  
   766  			It("handles empty desiredLRP with empty actualLRP response", func() {
   767  				fakeReceptorClient.GetDesiredLRPReturns(receptor.DesiredLRPResponse{}, nil)
   768  				fakeReceptorClient.ActualLRPsByProcessGuidReturns(make([]receptor.ActualLRPResponse, 0), nil)
   769  
   770  				_, err := appExaminer.AppStatus("peekaboo-app")
   771  				Expect(err).To(MatchError(app_examiner.AppNotFoundErrorMessage))
   772  
   773  				Expect(fakeReceptorClient.GetDesiredLRPCallCount()).To(Equal(1))
   774  				Expect(fakeReceptorClient.GetDesiredLRPArgsForCall(0)).To(Equal("peekaboo-app"))
   775  
   776  				Expect(fakeReceptorClient.ActualLRPsByProcessGuidCallCount()).To(Equal(1))
   777  				Expect(fakeReceptorClient.ActualLRPsByProcessGuidArgsForCall(0)).To(Equal("peekaboo-app"))
   778  
   779  				Expect(fakeNoaaConsumer.GetContainerMetricsCallCount()).To(Equal(0))
   780  			})
   781  
   782  		})
   783  
   784  		Context("when noaa returns container metrics without an associated actual lrp", func() {
   785  			It("doesn't blow up", func() {
   786  				getDesiredLRPResponse := receptor.DesiredLRPResponse{
   787  					ProcessGuid: "peekaboo-app",
   788  				}
   789  				actualLRPs := []receptor.ActualLRPResponse{
   790  					receptor.ActualLRPResponse{
   791  						ProcessGuid: "peekaboo-app",
   792  						Index:       6,
   793  					},
   794  				}
   795  				containerMetrics := []*events.ContainerMetric{
   796  					buildContainerMetric("peekaboo-app", 42, 0.018138574, 798729, 32768),
   797  				}
   798  				fakeReceptorClient.GetDesiredLRPReturns(getDesiredLRPResponse, nil)
   799  				fakeReceptorClient.ActualLRPsByProcessGuidReturns(actualLRPs, nil)
   800  				fakeNoaaConsumer.GetContainerMetricsReturns(containerMetrics, nil)
   801  
   802  				appInfo, err := appExaminer.AppStatus("peekaboo-app")
   803  				Expect(err).NotTo(HaveOccurred())
   804  				Expect(appInfo).NotTo(BeZero())
   805  				Expect(appInfo.ActualInstances).To(HaveLen(1))
   806  				Expect(appInfo.ActualInstances[0].HasMetrics).To(BeFalse())
   807  			})
   808  		})
   809  
   810  		Context("when the receptor returns errors", func() {
   811  			It("returns errors from from fetching the DesiredLRPs", func() {
   812  				fakeReceptorClient.GetDesiredLRPReturns(receptor.DesiredLRPResponse{}, receptor.Error{
   813  					Type:    receptor.UnknownError,
   814  					Message: "Oops."},
   815  				)
   816  
   817  				_, err := appExaminer.AppStatus("app-to-status")
   818  				Expect(err).To(MatchError("Oops."))
   819  			})
   820  
   821  			It("returns errors from fetching the ActualLRPs", func() {
   822  				fakeReceptorClient.GetDesiredLRPReturns(receptor.DesiredLRPResponse{}, nil)
   823  				fakeReceptorClient.ActualLRPsByProcessGuidReturns(nil, receptor.Error{
   824  					Type:    receptor.UnknownError,
   825  					Message: "ABANDON SHIP!!!!"},
   826  				)
   827  
   828  				_, err := appExaminer.AppStatus("kiss-my-bumper")
   829  				Expect(err).To(MatchError("ABANDON SHIP!!!!"))
   830  			})
   831  
   832  			It("should not panic", func() {
   833  				fakeReceptorClient.GetDesiredLRPReturns(receptor.DesiredLRPResponse{}, errors.New("non-receptor error"))
   834  
   835  				var err error
   836  				Expect(func() { _, err = appExaminer.AppStatus("kiss-my-bumper") }).ShouldNot(Panic())
   837  				Expect(err).To(MatchError("non-receptor error"))
   838  			})
   839  		})
   840  
   841  		Context("when the noaa consumer returns errors", func() {
   842  			It("returns an AppInfo without container metrics", func() {
   843  				desiredLRPs := receptor.DesiredLRPResponse{
   844  					ProcessGuid: "peekaboo-app",
   845  				}
   846  				actualLRPs := []receptor.ActualLRPResponse{
   847  					{ProcessGuid: "peekaboo-app", Index: 6},
   848  				}
   849  				fakeReceptorClient.GetDesiredLRPReturns(desiredLRPs, nil)
   850  				fakeReceptorClient.ActualLRPsByProcessGuidReturns(actualLRPs, nil)
   851  				fakeNoaaConsumer.GetContainerMetricsReturns(nil, errors.New("no metrics 4 you"))
   852  
   853  				appInfo, err := appExaminer.AppStatus("peekaboo-app")
   854  				Expect(err).NotTo(HaveOccurred())
   855  				Expect(appInfo).NotTo(BeZero())
   856  				Expect(appInfo.ActualInstances).To(HaveLen(1))
   857  				Expect(appInfo.ActualInstances[0].HasMetrics).To(BeFalse())
   858  			})
   859  		})
   860  	})
   861  
   862  	Describe("NumOfRunningAppInstances", func() {
   863  		It("returns the number of running instances for a given app guid", func() {
   864  			actualLrpsResponse := []receptor.ActualLRPResponse{
   865  				{ProcessGuid: "americano-app", State: receptor.ActualLRPStateRunning, Index: 1},
   866  				{ProcessGuid: "americano-app", State: receptor.ActualLRPStateRunning, Index: 2},
   867  				{ProcessGuid: "americano-app", State: receptor.ActualLRPStateClaimed, Index: 3},
   868  			}
   869  			fakeReceptorClient.ActualLRPsByProcessGuidReturns(actualLrpsResponse, nil)
   870  
   871  			count, placementError, err := appExaminer.RunningAppInstancesInfo("americano-app")
   872  			Expect(err).NotTo(HaveOccurred())
   873  			Expect(count).To(Equal(2))
   874  			Expect(placementError).To(BeFalse())
   875  
   876  			Expect(fakeReceptorClient.ActualLRPsByProcessGuidCallCount()).To(Equal(1))
   877  			Expect(fakeReceptorClient.ActualLRPsByProcessGuidArgsForCall(0)).To(Equal("americano-app"))
   878  		})
   879  
   880  		It("returns errors from the receptor", func() {
   881  			receptorError := errors.New("receptor did not like that request")
   882  			fakeReceptorClient.ActualLRPsByProcessGuidReturns([]receptor.ActualLRPResponse{}, receptorError)
   883  
   884  			_, _, err := appExaminer.RunningAppInstancesInfo("nescafe-app")
   885  			Expect(err).To(MatchError(receptorError))
   886  		})
   887  
   888  		Context("when there are placement errors on an instance", func() {
   889  			It("returns true for placementError", func() {
   890  				actualLrpsResponse := []receptor.ActualLRPResponse{
   891  					{ProcessGuid: "americano-app", State: receptor.ActualLRPStateRunning, Index: 1},
   892  					{ProcessGuid: "americano-app", State: receptor.ActualLRPStateUnclaimed, Index: 2, PlacementError: "could not place!"},
   893  					{ProcessGuid: "americano-app", State: receptor.ActualLRPStateRunning, Index: 3},
   894  				}
   895  				fakeReceptorClient.ActualLRPsByProcessGuidReturns(actualLrpsResponse, nil)
   896  
   897  				count, placementError, err := appExaminer.RunningAppInstancesInfo("americano-app")
   898  				Expect(err).NotTo(HaveOccurred())
   899  				Expect(count).To(Equal(2))
   900  				Expect(placementError).To(BeTrue())
   901  			})
   902  		})
   903  	})
   904  
   905  	Describe("AppExists", func() {
   906  		It("returns true if the docker app exists", func() {
   907  			actualLRPs := []receptor.ActualLRPResponse{
   908  				{ProcessGuid: "americano-app"},
   909  			}
   910  			fakeReceptorClient.ActualLRPsReturns(actualLRPs, nil)
   911  
   912  			exists, err := appExaminer.AppExists("americano-app")
   913  			Expect(err).NotTo(HaveOccurred())
   914  			Expect(exists).To(BeTrue())
   915  		})
   916  
   917  		It("returns false if the docker app does not exist", func() {
   918  			fakeReceptorClient.ActualLRPsReturns([]receptor.ActualLRPResponse{}, nil)
   919  
   920  			exists, err := appExaminer.AppExists("americano-app")
   921  			Expect(err).NotTo(HaveOccurred())
   922  			Expect(exists).To(BeFalse())
   923  		})
   924  
   925  		Describe("returning errors from the receptor", func() {
   926  			It("returns errors fetching the status", func() {
   927  				fakeReceptorClient.ActualLRPsReturns([]receptor.ActualLRPResponse{}, errors.New("Something Bad"))
   928  
   929  				_, err := appExaminer.AppExists("americano-app")
   930  				Expect(err).To(MatchError("Something Bad"))
   931  			})
   932  		})
   933  	})
   934  })