package leafnodes_test import ( . "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/internal/leafnodes" . "github.com/onsi/gomega" "net/http" "github.com/onsi/gomega/ghttp" "time" "github.com/onsi/ginkgo/internal/codelocation" Failer "github.com/onsi/ginkgo/internal/failer" "github.com/onsi/ginkgo/types" ) var _ = Describe("SynchronizedBeforeSuiteNode", func() { var failer *Failer.Failer var node SuiteNode var codeLocation types.CodeLocation var innerCodeLocation types.CodeLocation var outcome bool var server *ghttp.Server BeforeEach(func() { server = ghttp.NewServer() codeLocation = codelocation.New(0) innerCodeLocation = codelocation.New(0) failer = Failer.New() }) AfterEach(func() { server.Close() }) newNode := func(bodyA interface{}, bodyB interface{}) SuiteNode { return NewSynchronizedBeforeSuiteNode(bodyA, bodyB, codeLocation, time.Millisecond, failer) } Describe("when not running in parallel", func() { Context("when all is well", func() { var data []byte BeforeEach(func() { data = nil node = newNode(func() []byte { return []byte("my data") }, func(d []byte) { data = d }) outcome = node.Run(1, 1, server.URL()) }) It("should run A, then B passing the output from A to B", func() { Ω(data).Should(Equal([]byte("my data"))) }) It("should report success", func() { Ω(outcome).Should(BeTrue()) Ω(node.Passed()).Should(BeTrue()) Ω(node.Summary().State).Should(Equal(types.SpecStatePassed)) }) }) Context("when A fails", func() { var ranB bool BeforeEach(func() { ranB = false node = newNode(func() []byte { failer.Fail("boom", innerCodeLocation) return nil }, func([]byte) { ranB = true }) outcome = node.Run(1, 1, server.URL()) }) It("should not run B", func() { Ω(ranB).Should(BeFalse()) }) It("should report failure", func() { Ω(outcome).Should(BeFalse()) Ω(node.Passed()).Should(BeFalse()) Ω(node.Summary().State).Should(Equal(types.SpecStateFailed)) }) }) Context("when B fails", func() { BeforeEach(func() { node = newNode(func() []byte { return nil }, func([]byte) { failer.Fail("boom", innerCodeLocation) }) outcome = node.Run(1, 1, server.URL()) }) It("should report failure", func() { Ω(outcome).Should(BeFalse()) Ω(node.Passed()).Should(BeFalse()) Ω(node.Summary().State).Should(Equal(types.SpecStateFailed)) }) }) Context("when A times out", func() { var ranB bool BeforeEach(func() { ranB = false node = newNode(func(Done) []byte { time.Sleep(time.Second) return nil }, func([]byte) { ranB = true }) outcome = node.Run(1, 1, server.URL()) }) It("should not run B", func() { Ω(ranB).Should(BeFalse()) }) It("should report failure", func() { Ω(outcome).Should(BeFalse()) Ω(node.Passed()).Should(BeFalse()) Ω(node.Summary().State).Should(Equal(types.SpecStateTimedOut)) }) }) Context("when B times out", func() { BeforeEach(func() { node = newNode(func() []byte { return nil }, func([]byte, Done) { time.Sleep(time.Second) }) outcome = node.Run(1, 1, server.URL()) }) It("should report failure", func() { Ω(outcome).Should(BeFalse()) Ω(node.Passed()).Should(BeFalse()) Ω(node.Summary().State).Should(Equal(types.SpecStateTimedOut)) }) }) }) Describe("when running in parallel", func() { var ranB bool var parallelNode, parallelTotal int BeforeEach(func() { ranB = false parallelNode, parallelTotal = 1, 3 }) Context("as the first node, it runs A", func() { var expectedState types.RemoteBeforeSuiteData BeforeEach(func() { parallelNode, parallelTotal = 1, 3 }) JustBeforeEach(func() { server.AppendHandlers(ghttp.CombineHandlers( ghttp.VerifyRequest("POST", "/BeforeSuiteState"), ghttp.VerifyJSONRepresenting(expectedState), )) outcome = node.Run(parallelNode, parallelTotal, server.URL()) }) Context("when A succeeds", func() { BeforeEach(func() { expectedState = types.RemoteBeforeSuiteData{Data: []byte("my data"), State: types.RemoteBeforeSuiteStatePassed} node = newNode(func() []byte { return []byte("my data") }, func([]byte) { ranB = true }) }) It("should post about A succeeding", func() { Ω(server.ReceivedRequests()).Should(HaveLen(1)) }) It("should run B", func() { Ω(ranB).Should(BeTrue()) }) It("should report success", func() { Ω(outcome).Should(BeTrue()) }) }) Context("when A fails", func() { BeforeEach(func() { expectedState = types.RemoteBeforeSuiteData{Data: nil, State: types.RemoteBeforeSuiteStateFailed} node = newNode(func() []byte { panic("BAM") }, func([]byte) { ranB = true }) }) It("should post about A failing", func() { Ω(server.ReceivedRequests()).Should(HaveLen(1)) }) It("should not run B", func() { Ω(ranB).Should(BeFalse()) }) It("should report failure", func() { Ω(outcome).Should(BeFalse()) }) }) }) Context("as the Nth node", func() { var statusCode int var response interface{} var ranA bool var bData []byte BeforeEach(func() { ranA = false bData = nil statusCode = http.StatusOK server.AppendHandlers(ghttp.CombineHandlers( ghttp.VerifyRequest("GET", "/BeforeSuiteState"), ghttp.RespondWith(http.StatusOK, string((types.RemoteBeforeSuiteData{Data: nil, State: types.RemoteBeforeSuiteStatePending}).ToJSON())), ), ghttp.CombineHandlers( ghttp.VerifyRequest("GET", "/BeforeSuiteState"), ghttp.RespondWith(http.StatusOK, string((types.RemoteBeforeSuiteData{Data: nil, State: types.RemoteBeforeSuiteStatePending}).ToJSON())), ), ghttp.CombineHandlers( ghttp.VerifyRequest("GET", "/BeforeSuiteState"), ghttp.RespondWithJSONEncodedPtr(&statusCode, &response), )) node = newNode(func() []byte { ranA = true return nil }, func(data []byte) { bData = data }) parallelNode, parallelTotal = 2, 3 }) Context("when A on node1 succeeds", func() { BeforeEach(func() { response = types.RemoteBeforeSuiteData{Data: []byte("my data"), State: types.RemoteBeforeSuiteStatePassed} outcome = node.Run(parallelNode, parallelTotal, server.URL()) }) It("should not run A", func() { Ω(ranA).Should(BeFalse()) }) It("should poll for A", func() { Ω(server.ReceivedRequests()).Should(HaveLen(3)) }) It("should run B when the polling succeeds", func() { Ω(bData).Should(Equal([]byte("my data"))) }) It("should succeed", func() { Ω(outcome).Should(BeTrue()) Ω(node.Passed()).Should(BeTrue()) }) }) Context("when A on node1 fails", func() { BeforeEach(func() { response = types.RemoteBeforeSuiteData{Data: []byte("my data"), State: types.RemoteBeforeSuiteStateFailed} outcome = node.Run(parallelNode, parallelTotal, server.URL()) }) It("should not run A", func() { Ω(ranA).Should(BeFalse()) }) It("should poll for A", func() { Ω(server.ReceivedRequests()).Should(HaveLen(3)) }) It("should not run B", func() { Ω(bData).Should(BeNil()) }) It("should fail", func() { Ω(outcome).Should(BeFalse()) Ω(node.Passed()).Should(BeFalse()) summary := node.Summary() Ω(summary.State).Should(Equal(types.SpecStateFailed)) Ω(summary.Failure.Message).Should(Equal("BeforeSuite on Node 1 failed")) Ω(summary.Failure.Location).Should(Equal(codeLocation)) Ω(summary.Failure.ComponentType).Should(Equal(types.SpecComponentTypeBeforeSuite)) Ω(summary.Failure.ComponentIndex).Should(Equal(0)) Ω(summary.Failure.ComponentCodeLocation).Should(Equal(codeLocation)) }) }) Context("when node1 disappears", func() { BeforeEach(func() { response = types.RemoteBeforeSuiteData{Data: []byte("my data"), State: types.RemoteBeforeSuiteStateDisappeared} outcome = node.Run(parallelNode, parallelTotal, server.URL()) }) It("should not run A", func() { Ω(ranA).Should(BeFalse()) }) It("should poll for A", func() { Ω(server.ReceivedRequests()).Should(HaveLen(3)) }) It("should not run B", func() { Ω(bData).Should(BeNil()) }) It("should fail", func() { Ω(outcome).Should(BeFalse()) Ω(node.Passed()).Should(BeFalse()) summary := node.Summary() Ω(summary.State).Should(Equal(types.SpecStateFailed)) Ω(summary.Failure.Message).Should(Equal("Node 1 disappeared before completing BeforeSuite")) Ω(summary.Failure.Location).Should(Equal(codeLocation)) Ω(summary.Failure.ComponentType).Should(Equal(types.SpecComponentTypeBeforeSuite)) Ω(summary.Failure.ComponentIndex).Should(Equal(0)) Ω(summary.Failure.ComponentCodeLocation).Should(Equal(codeLocation)) }) }) }) }) Describe("construction", func() { Describe("the first function", func() { Context("when the first function returns a byte array", func() { Context("and takes nothing", func() { It("should be fine", func() { Ω(func() { newNode(func() []byte { return nil }, func([]byte) {}) }).ShouldNot(Panic()) }) }) Context("and takes a done function", func() { It("should be fine", func() { Ω(func() { newNode(func(Done) []byte { return nil }, func([]byte) {}) }).ShouldNot(Panic()) }) }) Context("and takes more than one thing", func() { It("should panic", func() { Ω(func() { newNode(func(Done, Done) []byte { return nil }, func([]byte) {}) }).Should(Panic()) }) }) Context("and takes something else", func() { It("should panic", func() { Ω(func() { newNode(func(bool) []byte { return nil }, func([]byte) {}) }).Should(Panic()) }) }) }) Context("when the first function does not return a byte array", func() { It("should panic", func() { Ω(func() { newNode(func() {}, func([]byte) {}) }).Should(Panic()) Ω(func() { newNode(func() []int { return nil }, func([]byte) {}) }).Should(Panic()) }) }) }) Describe("the second function", func() { Context("when the second function takes a byte array", func() { It("should be fine", func() { Ω(func() { newNode(func() []byte { return nil }, func([]byte) {}) }).ShouldNot(Panic()) }) }) Context("when it also takes a done channel", func() { It("should be fine", func() { Ω(func() { newNode(func() []byte { return nil }, func([]byte, Done) {}) }).ShouldNot(Panic()) }) }) Context("if it takes anything else", func() { It("should panic", func() { Ω(func() { newNode(func() []byte { return nil }, func([]byte, chan bool) {}) }).Should(Panic()) Ω(func() { newNode(func() []byte { return nil }, func(string) {}) }).Should(Panic()) }) }) Context("if it takes nothing at all", func() { It("should panic", func() { Ω(func() { newNode(func() []byte { return nil }, func() {}) }).Should(Panic()) }) }) Context("if it returns something", func() { It("should panic", func() { Ω(func() { newNode(func() []byte { return nil }, func([]byte) []byte { return nil }) }).Should(Panic()) }) }) }) }) })