Ω(app).Should(BeZero()) }) }) Context("when the app is not found", func() { It("should return the app not found error", func() { app, err := store.GetApp("Marzipan", "Armadillo") Ω(err).Should(Equal(AppNotFoundError)) Ω(app).Should(BeZero()) }) }) Context("when the app directory is empty", func() { It("should return the app not found error", func() { storeAdapter.SetMulti([]storeadapter.StoreNode{{ Key: "/hm/v1/apps/actual/foo-bar/baz", Value: []byte("foo"), }}) storeAdapter.Delete("/hm/v1/apps/actual/foo-bar/baz") app, err := store.GetApp("foo", "bar") Ω(err).Should(Equal(AppNotFoundError)) Ω(app).Should(BeZero()) }) }) }) }) })
}) }) }) Context("when services exists", func() { JustBeforeEach(func() { err := storeAdapter.Create(node) Expect(err).NotTo(HaveOccurred()) finder.Start() Eventually(finder.AllServers).ShouldNot(HaveLen(0)) Expect(finder.PreferredServers()).NotTo(HaveLen(0)) }) It("removes the service", func() { err := storeAdapter.Delete(node.Key) Expect(err).NotTo(HaveOccurred()) Eventually(finder.AllServers).Should(BeEmpty()) Expect(getCallbackCount()).To(Equal(2)) Expect(finder.PreferredServers()).To(BeEmpty()) Expect(getPreferredCount()).To(Equal(1)) }) It("only finds nodes for the doppler server type", func() { router := storeadapter.StoreNode{ Key: "/healthstatus/router/z1/router_z1", Value: []byte("10.99.99.99"), } storeAdapter.Create(router) Consistently(finder.AllServers).Should(Equal(expectedAddress))
}) }) Describe("Recursively deleting empty directories", func() { BeforeEach(func() { storeAdapter.SetMulti([]storeadapter.StoreNode{ {Key: "/hm/v17/pokemon/geodude", Value: []byte("foo")}, {Key: "/hm/v17/deep-pokemon/abra/kadabra/alakazam", Value: []byte{}}, {Key: "/hm/v17/pokemonCount", Value: []byte("151")}, }) }) Context("when the node is a directory", func() { Context("and it is empty", func() { BeforeEach(func() { storeAdapter.Delete("/hm/v17/pokemon/geodude") }) It("shreds it mercilessly", func() { err := store.Compact() Ω(err).ShouldNot(HaveOccurred()) _, err = storeAdapter.Get("/hm/v17/pokemon") Ω(err).Should(Equal(storeadapter.ErrorKeyNotFound)) }) }) Context("and it is non-empty", func() { It("spares it", func() { err := store.Compact() Ω(err).ShouldNot(HaveOccurred())
}) }) }) Context("When an existing service is updated", func() { It("should not notify the channel again", func() { adapter.SetMulti([]storeadapter.StoreNode{buildNode(app2Service1)}) assertNoDataOnChannel(outAddChan) assertNoDataOnChannel(outRemoveChan) }) }) Context("when a service or app should be removed", func() { Context("when an existing app loses one of its services", func() { It("sends that service on the output remove channel", func(done Done) { adapter.Delete(path.Join("/loggregator/services", app1Service2.AppId, app1Service2.Id())) Expect(<-outRemoveChan).To(Equal(app1Service2)) assertNoDataOnChannel(outAddChan) close(done) }) }) Context("when an existing app loses all of its services", func() { It("sends all of the app services on the outgoing remove channel", func(done Done) { adapter.Delete(path.Join("/loggregator/services", app1Service2.AppId)) appServices := drainOutgoingChannel(outRemoveChan, 2) Expect(appServices).To(ContainElement(app1Service1))
}) }) }) Context("When an existing service is updated", func() { It("should not notify the channel again", func() { adapter.SetMulti([]storeadapter.StoreNode{buildNode(app2Service1)}) Expect(outAddChan).To(BeEmpty()) Expect(outRemoveChan).To(BeEmpty()) }) }) Context("when a service or app should be removed", func() { Context("when an existing app loses one of its services", func() { It("sends that service on the output remove channel", func() { err := adapter.Delete(key(app1Service2)) Expect(err).NotTo(HaveOccurred()) var appService appservice.AppService Eventually(outRemoveChan).Should(Receive(&appService)) Expect(appService).To(Equal(app1Service2)) Expect(outAddChan).To(BeEmpty()) }) }) Context("when an existing app loses all of its services", func() { It("sends all of the app services on the outgoing remove channel", func() { adapter.Get(path.Join("/loggregator/services", APP1_ID)) adapter.Delete(path.Join("/loggregator/services", APP1_ID)) appServices := drainOutgoingChannel(outRemoveChan, 2)
dea.GetApp(0).InstanceAtIndex(1).Heartbeat(), )) }() err1 := <-done err2 := <-done Ω(err1).ShouldNot(HaveOccurred()) Ω(err2).ShouldNot(HaveOccurred()) }) }) Context("when something goes wrong and the in-memory cache no longer matches the store", func() { It("should eventually recover", func() { //Delete one of the heartbeats corruptedHeartbeat := dea.GetApp(0).InstanceAtIndex(1).Heartbeat() storeAdapter.Delete("/hm/v1/apps/actual/" + store.AppKey(corruptedHeartbeat.AppGuid, corruptedHeartbeat.AppVersion) + "/" + corruptedHeartbeat.InstanceGuid) //See that it's gone results, err := store.GetInstanceHeartbeats() Ω(err).ShouldNot(HaveOccurred()) Ω(results).Should(HaveLen(1)) //Try to put it back store.SyncHeartbeats(dea.HeartbeatWith( dea.GetApp(0).InstanceAtIndex(1).Heartbeat(), dea.GetApp(1).InstanceAtIndex(3).Heartbeat(), )) //See that we didn't... because it's still in the cache... results, err = store.GetInstanceHeartbeats() Ω(err).ShouldNot(HaveOccurred())