From e010f6888ae26702fd23aa89c2c2d6d21475c7a4 Mon Sep 17 00:00:00 2001 From: Joshua Carroll Date: Wed, 6 Feb 2019 23:55:24 +0000 Subject: [PATCH 1/2] Add ExpectedStop() to Stream --- twitter/streams.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/twitter/streams.go b/twitter/streams.go index e6affd5..9d1e386 100644 --- a/twitter/streams.go +++ b/twitter/streams.go @@ -155,6 +155,7 @@ type Stream struct { done chan struct{} group *sync.WaitGroup body io.Closer + expected bool } // newStream creates a Stream and starts a goroutine to retry connecting and @@ -175,6 +176,7 @@ func newStream(client *http.Client, req *http.Request, expBackoff, aggExpBackoff // Stop signals retry and receiver to stop, closes the Messages channel, and // blocks until done. func (s *Stream) Stop() { + s.expected = true close(s.done) // Scanner does not have a Stop() or take a done channel, so for low volume // streams Scan() blocks until the next keep-alive. Close the resp.Body to @@ -186,6 +188,12 @@ func (s *Stream) Stop() { s.group.Wait() } +// ExpectedStop indicates whether Stream halting was due to an expected Stop() +// or some error condition. +func (s *Stream) ExpectedStop() bool { + return s.expected +} + // retry retries making the given http.Request and receiving the response // according to the Twitter backoff policies. Callers should invoke in a // goroutine since backoffs sleep between retries. From 0e4fd64d08f27d1a97f1164cefc3392fd8f02fee Mon Sep 17 00:00:00 2001 From: Joshua Carroll Date: Thu, 7 Feb 2019 15:36:35 +0000 Subject: [PATCH 2/2] Expose more test scaffolding for use in implementing libraries --- twitter/test_utils.go | 57 +++++++++++++++++++++++++++++++++++++++++ twitter/twitter_test.go | 32 ----------------------- 2 files changed, 57 insertions(+), 32 deletions(-) create mode 100644 twitter/test_utils.go diff --git a/twitter/test_utils.go b/twitter/test_utils.go new file mode 100644 index 0000000..8b869af --- /dev/null +++ b/twitter/test_utils.go @@ -0,0 +1,57 @@ +package twitter + +import ( + "net/http" + "net/http/httptest" + "net/url" +) + +// NewTestStream creates a Stream for testing with a provided input Messages channel. +// It is safe to call Stop() once on the provided *Stream, as with a normal Stream. +func NewTestStream(messages chan interface{}) *Stream { + return &Stream{ + Messages: messages, + done: make(chan struct{}), + } +} + +// NewTestServer exposes testServer for test scaffolding in libraries that use go-twitter +// it takes a map of path:functions to set the ServeMux. +func NewTestServer(handlers map[string]func(w http.ResponseWriter, r *http.Request)) (*http.Client, *httptest.Server) { + client, mux, server := testServer() + for path, handler := range handlers { + mux.HandleFunc(path, handler) + } + return client, server +} + +// testServer returns an http Client, ServeMux, and Server. The client proxies +// requests to the server and handlers can be registered on the mux to handle +// requests. The caller must close the test server. +func testServer() (*http.Client, *http.ServeMux, *httptest.Server) { + mux := http.NewServeMux() + server := httptest.NewServer(mux) + transport := &RewriteTransport{&http.Transport{ + Proxy: func(req *http.Request) (*url.URL, error) { + return url.Parse(server.URL) + }, + }} + client := &http.Client{Transport: transport} + return client, mux, server +} + +// RewriteTransport rewrites https requests to http to avoid TLS cert issues +// during testing. +type RewriteTransport struct { + Transport http.RoundTripper +} + +// RoundTrip rewrites the request scheme to http and calls through to the +// composed RoundTripper or if it is nil, to the http.DefaultTransport. +func (t *RewriteTransport) RoundTrip(req *http.Request) (*http.Response, error) { + req.URL.Scheme = "http" + if t.Transport == nil { + return http.DefaultTransport.RoundTrip(req) + } + return t.Transport.RoundTrip(req) +} diff --git a/twitter/twitter_test.go b/twitter/twitter_test.go index 4b7ca29..d89d622 100644 --- a/twitter/twitter_test.go +++ b/twitter/twitter_test.go @@ -2,7 +2,6 @@ package twitter import ( "net/http" - "net/http/httptest" "net/url" "testing" "time" @@ -12,37 +11,6 @@ import ( var defaultTestTimeout = time.Second * 1 -// testServer returns an http Client, ServeMux, and Server. The client proxies -// requests to the server and handlers can be registered on the mux to handle -// requests. The caller must close the test server. -func testServer() (*http.Client, *http.ServeMux, *httptest.Server) { - mux := http.NewServeMux() - server := httptest.NewServer(mux) - transport := &RewriteTransport{&http.Transport{ - Proxy: func(req *http.Request) (*url.URL, error) { - return url.Parse(server.URL) - }, - }} - client := &http.Client{Transport: transport} - return client, mux, server -} - -// RewriteTransport rewrites https requests to http to avoid TLS cert issues -// during testing. -type RewriteTransport struct { - Transport http.RoundTripper -} - -// RoundTrip rewrites the request scheme to http and calls through to the -// composed RoundTripper or if it is nil, to the http.DefaultTransport. -func (t *RewriteTransport) RoundTrip(req *http.Request) (*http.Response, error) { - req.URL.Scheme = "http" - if t.Transport == nil { - return http.DefaultTransport.RoundTrip(req) - } - return t.Transport.RoundTrip(req) -} - func assertMethod(t *testing.T, expectedMethod string, req *http.Request) { assert.Equal(t, expectedMethod, req.Method) }