github.com/chenbh/concourse/v6@v6.4.2/fly/integration/watch_test.go (about)

     1  package integration_test
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"os/exec"
     8  
     9  	. "github.com/onsi/ginkgo"
    10  	. "github.com/onsi/gomega"
    11  	"github.com/onsi/gomega/gbytes"
    12  	"github.com/onsi/gomega/gexec"
    13  	"github.com/onsi/gomega/ghttp"
    14  	"github.com/vito/go-sse/sse"
    15  
    16  	"github.com/chenbh/concourse/v6/atc"
    17  	"github.com/chenbh/concourse/v6/atc/event"
    18  )
    19  
    20  var _ = Describe("Watching", func() {
    21  	var streaming chan struct{}
    22  	var events chan atc.Event
    23  
    24  	BeforeEach(func() {
    25  		streaming = make(chan struct{})
    26  		events = make(chan atc.Event)
    27  	})
    28  
    29  	eventsHandler := func() http.HandlerFunc {
    30  		return ghttp.CombineHandlers(
    31  			ghttp.VerifyRequest("GET", "/api/v1/builds/3/events"),
    32  			func(w http.ResponseWriter, r *http.Request) {
    33  				flusher := w.(http.Flusher)
    34  
    35  				w.Header().Add("Content-Type", "text/event-stream; charset=utf-8")
    36  				w.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate")
    37  				w.Header().Add("Connection", "keep-alive")
    38  
    39  				w.WriteHeader(http.StatusOK)
    40  
    41  				flusher.Flush()
    42  
    43  				close(streaming)
    44  
    45  				id := 0
    46  
    47  				for e := range events {
    48  					payload, err := json.Marshal(event.Message{Event: e})
    49  					Expect(err).NotTo(HaveOccurred())
    50  
    51  					event := sse.Event{
    52  						ID:   fmt.Sprintf("%d", id),
    53  						Name: "event",
    54  						Data: payload,
    55  					}
    56  
    57  					err = event.Write(w)
    58  					Expect(err).NotTo(HaveOccurred())
    59  
    60  					flusher.Flush()
    61  
    62  					id++
    63  				}
    64  
    65  				err := sse.Event{
    66  					Name: "end",
    67  				}.Write(w)
    68  				Expect(err).NotTo(HaveOccurred())
    69  			},
    70  		)
    71  	}
    72  
    73  	watch := func(args ...string) {
    74  		watchWithArgs := append([]string{"watch"}, args...)
    75  
    76  		flyCmd := exec.Command(flyPath, append([]string{"-t", targetName}, watchWithArgs...)...)
    77  
    78  		sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
    79  		Expect(err).NotTo(HaveOccurred())
    80  
    81  		Eventually(streaming).Should(BeClosed())
    82  
    83  		events <- event.Log{Payload: "sup"}
    84  
    85  		Eventually(sess.Out).Should(gbytes.Say("sup"))
    86  
    87  		close(events)
    88  
    89  		<-sess.Exited
    90  		Expect(sess.ExitCode()).To(Equal(0))
    91  	}
    92  
    93  	Context("with no arguments", func() {
    94  		BeforeEach(func() {
    95  			atcServer.AppendHandlers(
    96  				ghttp.CombineHandlers(
    97  					ghttp.VerifyRequest("GET", "/api/v1/builds"),
    98  					ghttp.RespondWithJSONEncoded(200, []atc.Build{
    99  						{ID: 4, Name: "1", Status: "started", JobName: "some-job"},
   100  						{ID: 3, Name: "3", Status: "started"},
   101  						{ID: 2, Name: "2", Status: "started"},
   102  						{ID: 1, Name: "1", Status: "finished"},
   103  					}),
   104  				),
   105  				eventsHandler(),
   106  			)
   107  		})
   108  
   109  		It("watches the most recent one-off build", func() {
   110  			watch()
   111  		})
   112  	})
   113  
   114  	Context("with a build ID and no job", func() {
   115  		BeforeEach(func() {
   116  			atcServer.AppendHandlers(
   117  				eventsHandler(),
   118  			)
   119  		})
   120  
   121  		It("Watches the given build id", func() {
   122  			watch("--build", "3")
   123  		})
   124  
   125  		It("Watches the given direct build URL", func() {
   126  			watch("--url", atcServer.URL()+"/builds/3")
   127  		})
   128  	})
   129  
   130  	Context("with a specific job and pipeline", func() {
   131  		Context("when the job has no builds", func() {
   132  			BeforeEach(func() {
   133  				atcServer.AppendHandlers(
   134  					ghttp.CombineHandlers(
   135  						ghttp.VerifyRequest("GET", "/api/v1/teams/main/pipelines/some-pipeline/jobs/some-job"),
   136  						ghttp.RespondWithJSONEncoded(200, atc.Job{}),
   137  					),
   138  					eventsHandler(),
   139  				)
   140  			})
   141  
   142  			It("returns an error and exits", func() {
   143  				flyCmd := exec.Command(flyPath, "-t", targetName, "watch", "--job", "some-pipeline/some-job")
   144  				sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
   145  				Expect(err).NotTo(HaveOccurred())
   146  
   147  				Eventually(sess.Err).Should(gbytes.Say("job has no builds"))
   148  				<-sess.Exited
   149  				Expect(sess.ExitCode()).To(Equal(1))
   150  			})
   151  		})
   152  
   153  		Context("when the job has a next build", func() {
   154  			BeforeEach(func() {
   155  				didStream := make(chan struct{})
   156  				streaming = didStream
   157  
   158  				atcServer.AppendHandlers(
   159  					ghttp.CombineHandlers(
   160  						ghttp.VerifyRequest("GET", "/api/v1/teams/main/pipelines/some-pipeline/jobs/some-job"),
   161  						ghttp.RespondWithJSONEncoded(200, atc.Job{
   162  							NextBuild: &atc.Build{
   163  								ID:      3,
   164  								Name:    "3",
   165  								Status:  "started",
   166  								JobName: "some-job",
   167  							},
   168  							FinishedBuild: &atc.Build{
   169  								ID:      2,
   170  								Name:    "2",
   171  								Status:  "failed",
   172  								JobName: "some-job",
   173  							},
   174  						}),
   175  					),
   176  					eventsHandler(),
   177  				)
   178  			})
   179  
   180  			It("watches the job's next build", func() {
   181  				watch("--job", "some-pipeline/some-job")
   182  			})
   183  
   184  			It("watches the job's next build URL", func() {
   185  				watch("--url", atcServer.URL()+"/teams/main/pipelines/some-pipeline/jobs/some-job")
   186  			})
   187  		})
   188  
   189  		Context("when the job only has a finished build", func() {
   190  			BeforeEach(func() {
   191  				atcServer.AppendHandlers(
   192  					ghttp.CombineHandlers(
   193  						ghttp.VerifyRequest("GET", "/api/v1/teams/main/pipelines/main/jobs/some-job"),
   194  						ghttp.RespondWithJSONEncoded(200, atc.Job{
   195  							NextBuild: nil,
   196  							FinishedBuild: &atc.Build{
   197  								ID:      3,
   198  								Name:    "3",
   199  								Status:  "failed",
   200  								JobName: "some-job",
   201  							},
   202  						}),
   203  					),
   204  					eventsHandler(),
   205  				)
   206  			})
   207  
   208  			It("watches the job's finished build", func() {
   209  				watch("--job", "main/some-job")
   210  			})
   211  
   212  			It("watches the job's finished build URL", func() {
   213  				watch("--url", atcServer.URL()+"/teams/main/pipelines/main/jobs/some-job")
   214  			})
   215  		})
   216  
   217  		Context("with a specific build of the job", func() {
   218  			BeforeEach(func() {
   219  				atcServer.AppendHandlers(
   220  					ghttp.CombineHandlers(
   221  						ghttp.VerifyRequest("GET", "/api/v1/teams/main/pipelines/main/jobs/some-job/builds/3"),
   222  						ghttp.RespondWithJSONEncoded(200, atc.Build{
   223  							ID:      3,
   224  							Name:    "3",
   225  							Status:  "failed",
   226  							JobName: "some-job",
   227  						}),
   228  					),
   229  					eventsHandler(),
   230  				)
   231  			})
   232  
   233  			It("watches the given build", func() {
   234  				watch("--job", "main/some-job", "--build", "3")
   235  			})
   236  
   237  			It("watches the given build URL", func() {
   238  				watch("--url", atcServer.URL()+"/teams/main/pipelines/main/jobs/some-job/builds/3")
   239  			})
   240  		})
   241  	})
   242  })