diff --git a/.github/variables/go-versions.env b/.github/variables/go-versions.env index 3c277c3..cb4024a 100644 --- a/.github/variables/go-versions.env +++ b/.github/variables/go-versions.env @@ -1,3 +1,3 @@ -latest=1.22 -penultimate=1.21 -min=1.17 +latest=1.24 +penultimate=1.23 +min=1.23 diff --git a/.github/workflows/go-versions.yml b/.github/workflows/go-versions.yml index 86090db..5de11c7 100644 --- a/.github/workflows/go-versions.yml +++ b/.github/workflows/go-versions.yml @@ -49,4 +49,8 @@ jobs: - name: Set Go Version Matrices id: set-matrix run: | - echo "all=[\"${{ steps.set-env.outputs.latest }}\",\"${{ steps.set-env.outputs.penultimate }}\",\"${{ steps.set-env.outputs.min }}\"]" >> $GITHUB_OUTPUT + if [ "${{ steps.set-env.outputs.penultimate }}" == "${{ steps.set-env.outputs.min }}" ]; then + echo "all=[\"${{ steps.set-env.outputs.latest }}\",\"${{ steps.set-env.outputs.penultimate }}\"]" >> $GITHUB_OUTPUT + else + echo "all=[\"${{ steps.set-env.outputs.latest }}\",\"${{ steps.set-env.outputs.penultimate }}\",\"${{ steps.set-env.outputs.min }}\"]" >> $GITHUB_OUTPUT + fi diff --git a/.golangci.yml b/.golangci.yml index a592413..1caf2a0 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,26 +1,17 @@ +version: "2" run: - deadline: 120s tests: false - linters: enable: - bodyclose - dupl - - errcheck - - exportloopref - - goconst - gochecknoglobals - gochecknoinits - goconst - gocritic - gocyclo - godox - - gofmt - - goimports - gosec - - gosimple - - govet - - ineffassign - lll - misspell - nakedret @@ -28,20 +19,31 @@ linters: - prealloc - revive - staticcheck - - stylecheck - - typecheck - unconvert - unparam - - unused - whitespace - fast: false - -linters-settings: - gofmt: - simplify: false - goimports: - local-prefixes: github.com/launchdarkly - + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ issues: - exclude-use-default: false max-same-issues: 1000 +formatters: + enable: + - gofmt + - goimports + settings: + gofmt: + simplify: false + goimports: + local-prefixes: + - gopkg.in/launchdarkly + - github.com/launchdarkly + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/Makefile b/Makefile index c52ae2b..9c7b988 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -GOLANGCI_LINT_VERSION=v1.60.1 +GOLANGCI_LINT_VERSION=v1.64.5 LINTER=./bin/golangci-lint LINTER_VERSION_FILE=./bin/.golangci-lint-version-$(GOLANGCI_LINT_VERSION) diff --git a/contract-tests/go.mod b/contract-tests/go.mod index abd7a70..0900a0b 100644 --- a/contract-tests/go.mod +++ b/contract-tests/go.mod @@ -1,6 +1,6 @@ module github.com/launchdarkly/eventsource/contract-tests -go 1.17 +go 1.23 replace github.com/launchdarkly/eventsource => ../ diff --git a/contract-tests/go.sum b/contract-tests/go.sum index d26315c..acf1872 100644 --- a/contract-tests/go.sum +++ b/contract-tests/go.sum @@ -1,17 +1,10 @@ -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/launchdarkly/go-test-helpers/v2 v2.2.0 h1:L3kGILP/6ewikhzhdNkHy1b5y4zs50LueWenVF0sBbs= -github.com/launchdarkly/go-test-helpers/v2 v2.2.0/go.mod h1:L7+th5govYp5oKU9iN7To5PgznBuIjBPn+ejqKR0avw= +github.com/launchdarkly/go-test-helpers/v3 v3.1.0 h1:E3bxJMzMoA+cJSF3xxtk2/chr1zshl1ZWa0/oR+8bvg= +github.com/launchdarkly/go-test-helpers/v3 v3.1.0/go.mod h1:Ake5+hZFS/DmIGKx/cizhn5W9pGA7pplcR7xCxWiLIo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho= github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/decoder.go b/decoder.go index 4e5ec4a..2e84e0a 100644 --- a/decoder.go +++ b/decoder.go @@ -3,6 +3,7 @@ package eventsource import ( "bufio" "io" + "net/http" "strconv" "strings" "time" @@ -10,6 +11,7 @@ import ( type publication struct { id, event, data, lastEventID string + headers http.Header retry int64 } @@ -22,12 +24,21 @@ func (s *publication) Retry() int64 { return s.retry } // LastEventID is from a separate interface, EventWithLastID func (s *publication) LastEventID() string { return s.lastEventID } +// Headers is from a separate interface, EventWithHeaders +func (s *publication) Headers() http.Header { + if s.headers == nil { + return nil + } + return s.headers.Clone() +} + // A Decoder is capable of reading Events from a stream. type Decoder struct { linesCh <-chan string errorCh <-chan error readTimeout time.Duration lastEventID string + headers http.Header } // DecoderOption is a common interface for optional configuration parameters that can be @@ -48,6 +59,12 @@ func (o lastEventIDDecoderOption) apply(d *Decoder) { d.lastEventID = string(o) } +type headersDecoderOption http.Header + +func (o headersDecoderOption) apply(d *Decoder) { + d.headers = http.Header(o) +} + // DecoderOptionReadTimeout returns an option that sets the read timeout interval for a // Decoder when the Decoder is created. If the Decoder does not receive new data within this // length of time, it will return an error. By default, there is no read timeout. @@ -62,6 +79,13 @@ func DecoderOptionLastEventID(lastEventID string) DecoderOption { return lastEventIDDecoderOption(lastEventID) } +// DecoderOptionHeaders returns an option that sets the Headers property for a +// Decoder when the Decoder is created. This allows access to the HTTP response +// headers for the event. +func DecoderOptionHeaders(headers http.Header) DecoderOption { + return headersDecoderOption(headers) +} + // NewDecoder returns a new Decoder instance that reads events with the given io.Reader. func NewDecoder(r io.Reader) *Decoder { bufReader := bufio.NewReader(newNormaliser(r)) @@ -152,6 +176,7 @@ ReadLoop: } pub.data = strings.TrimSuffix(pub.data, "\n") pub.lastEventID = dec.lastEventID + pub.headers = dec.headers return pub, nil } diff --git a/decoder_test.go b/decoder_test.go index f306e13..a56ac3a 100644 --- a/decoder_test.go +++ b/decoder_test.go @@ -2,6 +2,7 @@ package eventsource import ( "io" + "net/http" "strings" "testing" @@ -57,6 +58,13 @@ func requireLastEventID(t *testing.T, event Event) string { return eventWithID.LastEventID() } +func requireHeaders(t *testing.T, event Event) http.Header { + // necessary because we can't yet add Headers to the basic Event interface; see EventWithHeaders + eventWithHeaders, ok := event.(EventWithHeaders) + require.True(t, ok, "event should have implemented EventWithHeaders") + return eventWithHeaders.Headers() +} + func TestDecoderTracksLastEventID(t *testing.T) { t.Run("uses last ID that is passed in options", func(t *testing.T) { inputData := "data: abc\n\n" @@ -96,8 +104,8 @@ func TestDecoderTracksLastEventID(t *testing.T) { assert.Equal(t, "def", requireLastEventID(t, event3)) }) - t.Run("last ID persists if not overridden", func(t *testing.T) { - inputData := "id: abc\ndata: first\n\ndata: second\n\nid: def\ndata:third\n\n" + t.Run("last ID can be overridden with empty string", func(t *testing.T) { + inputData := "id: abc\ndata: first\n\nid: \ndata: second\n\n" decoder := NewDecoderWithOptions(strings.NewReader(inputData), DecoderOptionLastEventID("my-id")) event1, err := decoder.Decode() @@ -112,32 +120,57 @@ func TestDecoderTracksLastEventID(t *testing.T) { assert.Equal(t, "second", event2.Data()) assert.Equal(t, "", event2.Id()) - assert.Equal(t, "abc", requireLastEventID(t, event2)) + assert.Equal(t, "", requireLastEventID(t, event2)) + }) +} - event3, err := decoder.Decode() +func TestDecoderTracksHeaders(t *testing.T) { + t.Run("event headers is nil if not provided in options", func(t *testing.T) { + inputData := "data: abc\n\n" + + decoder := NewDecoderWithOptions(strings.NewReader(inputData)) + + event, err := decoder.Decode() require.NoError(t, err) - assert.Equal(t, "third", event3.Data()) - assert.Equal(t, "def", event3.Id()) - assert.Equal(t, "def", requireLastEventID(t, event3)) + assert.Equal(t, "abc", event.Data()) + assert.Equal(t, "", event.Id()) + assert.Nil(t, requireHeaders(t, event)) }) - t.Run("last ID can be overridden with empty string", func(t *testing.T) { - inputData := "id: abc\ndata: first\n\nid: \ndata: second\n\n" - decoder := NewDecoderWithOptions(strings.NewReader(inputData), DecoderOptionLastEventID("my-id")) + t.Run("uses headers that are passed in options", func(t *testing.T) { + inputData := "data: abc\n\n" + headers := http.Header{ + "X-Ld-Envid": {"env-id"}, + } - event1, err := decoder.Decode() + decoder := NewDecoderWithOptions(strings.NewReader(inputData), DecoderOptionHeaders(headers)) + + event, err := decoder.Decode() require.NoError(t, err) - assert.Equal(t, "first", event1.Data()) - assert.Equal(t, "abc", event1.Id()) - assert.Equal(t, "abc", requireLastEventID(t, event1)) + assert.Equal(t, "abc", event.Data()) + assert.Equal(t, "", event.Id()) + assert.Equal(t, headers, requireHeaders(t, event)) + }) - event2, err := decoder.Decode() + t.Run("event headers are immutable", func(t *testing.T) { + inputData := "data: abc\n\n" + headers := http.Header{ + "X-Ld-Envid": {"env-id"}, + } + + decoder := NewDecoderWithOptions(strings.NewReader(inputData), DecoderOptionHeaders(headers)) + + event, err := decoder.Decode() require.NoError(t, err) - assert.Equal(t, "second", event2.Data()) - assert.Equal(t, "", event2.Id()) - assert.Equal(t, "", requireLastEventID(t, event2)) + eventHeaders := requireHeaders(t, event) + assert.Equal(t, "abc", event.Data()) + assert.Equal(t, "", event.Id()) + assert.Equal(t, headers, eventHeaders) + + eventHeaders.Add("New-Header", "new-value") + assert.NotContains(t, headers, "New-Header") }) } diff --git a/go.mod b/go.mod index 0188e78..2a71659 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,9 @@ module github.com/launchdarkly/eventsource -go 1.17 +go 1.23 require ( - github.com/launchdarkly/go-test-helpers/v2 v2.2.0 + github.com/launchdarkly/go-test-helpers/v3 v3.1.0 github.com/stretchr/testify v1.6.0 ) diff --git a/go.sum b/go.sum index 5c4b8ff..f6afd00 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,15 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/launchdarkly/go-test-helpers/v2 v2.2.0 h1:L3kGILP/6ewikhzhdNkHy1b5y4zs50LueWenVF0sBbs= -github.com/launchdarkly/go-test-helpers/v2 v2.2.0/go.mod h1:L7+th5govYp5oKU9iN7To5PgznBuIjBPn+ejqKR0avw= +github.com/launchdarkly/go-test-helpers/v3 v3.1.0 h1:E3bxJMzMoA+cJSF3xxtk2/chr1zshl1ZWa0/oR+8bvg= +github.com/launchdarkly/go-test-helpers/v3 v3.1.0/go.mod h1:Ake5+hZFS/DmIGKx/cizhn5W9pGA7pplcR7xCxWiLIo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho= github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/interface.go b/interface.go index ba4a8a0..112dd5a 100644 --- a/interface.go +++ b/interface.go @@ -5,6 +5,8 @@ // If the Repository interface is implemented on the server, events can be replayed in case of a network disconnection. package eventsource +import "net/http" + // Event is the interface for any event received by the client or sent by the server. type Event interface { // Id is an identifier that can be used to allow a client to replay @@ -32,6 +34,19 @@ type EventWithLastID interface { LastEventID() string } +// EventWithHeaders is an additional interface for an event received by the client, +// allowing access to the HTTP response headers. +// +// This is defined as a separate interface for backward compatibility, since this +// feature was added after the Event interface had been defined and adding a method +// to Event would break existing implementations. All events returned by Stream do +// implement this interface, and in a future major version the Event type will be +// changed to always include this field. +type EventWithHeaders interface { + // Headers provides access to the HTTP response headers for the event. + Headers() http.Header +} + // Repository is an interface to be used with Server.Register() allowing clients to replay previous events // through the server, if history is required. type Repository interface { diff --git a/stream.go b/stream.go index 2293960..5d886e0 100644 --- a/stream.go +++ b/stream.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "net/http" "net/url" "sync" @@ -127,9 +126,9 @@ func SubscribeWithRequestAndOptions(request *http.Request, options ...StreamOpti initialRetryTimeoutCh = time.After(configuredOptions.initialRetryTimeout) } for { - r, err := stream.connect() + r, h, err := stream.connect() if err == nil { - go stream.stream(r) + go stream.stream(r, h) return stream, nil } lastError = err @@ -229,7 +228,7 @@ func (stream *Stream) Close() { }) } -func (stream *Stream) connect() (io.ReadCloser, error) { +func (stream *Stream) connect() (io.ReadCloser, http.Header, error) { var err error var resp *http.Response stream.req.Header.Set("Cache-Control", "no-cache") @@ -245,28 +244,28 @@ func (stream *Stream) connect() (io.ReadCloser, error) { // All but the initial connection will need to regenerate the body if stream.connections > 0 && req.GetBody != nil { if req.Body, err = req.GetBody(); err != nil { - return nil, err + return nil, nil, err } } if resp, err = stream.c.Do(&req); err != nil { - return nil, err + return nil, nil, err } stream.connections++ if resp.StatusCode != 200 { - message, _ := ioutil.ReadAll(resp.Body) + message, _ := io.ReadAll(resp.Body) _ = resp.Body.Close() err = SubscriptionError{ Code: resp.StatusCode, Message: string(message), Header: resp.Header, } - return nil, err + return nil, nil, err } - return resp.Body, nil + return resp.Body, resp.Header, nil } -func (stream *Stream) stream(r io.ReadCloser) { +func (stream *Stream) stream(r io.ReadCloser, h http.Header) { retryChan := make(chan struct{}, 1) scheduleRetry := func() { @@ -302,6 +301,7 @@ NewStream: dec := NewDecoderWithOptions(r, DecoderOptionReadTimeout(stream.readTimeout), DecoderOptionLastEventID(stream.lastEventID), + DecoderOptionHeaders(h), ) go func() { for { @@ -358,9 +358,10 @@ NewStream: break NewStream case <-retryChan: var err error - r, err = stream.connect() + r, h, err = stream.connect() if err != nil { r = nil + h = nil if !reportErrorAndMaybeContinue(err) { break NewStream } diff --git a/stream_error_after_subscribe_test.go b/stream_error_after_subscribe_test.go index 33767aa..5e3774b 100644 --- a/stream_error_after_subscribe_test.go +++ b/stream_error_after_subscribe_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/launchdarkly/go-test-helpers/v2/httphelpers" + "github.com/launchdarkly/go-test-helpers/v3/httphelpers" ) func TestStreamErrorsAreSentToErrorsChannel(t *testing.T) { diff --git a/stream_error_during_subscribe_test.go b/stream_error_during_subscribe_test.go index 1c7cc8b..5ce3f1a 100644 --- a/stream_error_during_subscribe_test.go +++ b/stream_error_during_subscribe_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/launchdarkly/go-test-helpers/v2/httphelpers" + "github.com/launchdarkly/go-test-helpers/v3/httphelpers" ) func handlerCausingNetworkError() http.Handler { diff --git a/stream_reading_test.go b/stream_reading_test.go index cbe37db..b438853 100644 --- a/stream_reading_test.go +++ b/stream_reading_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/launchdarkly/go-test-helpers/v2/httphelpers" + "github.com/launchdarkly/go-test-helpers/v3/httphelpers" ) func TestStreamSubscribeEventsChan(t *testing.T) { @@ -23,7 +23,31 @@ func TestStreamSubscribeEventsChan(t *testing.T) { select { case receivedEvent := <-stream.Events: - assert.Equal(t, &publication{id: "123", lastEventID: "123"}, receivedEvent) + receivedWithLastId := receivedEvent.(EventWithLastID) + receivedWithHeaders := receivedEvent.(EventWithHeaders) + assert.Equal(t, "123", receivedEvent.Id()) + assert.Equal(t, "123", receivedWithLastId.LastEventID()) + assert.NotEmpty(t, receivedWithHeaders.Headers()) + case <-time.After(timeToWaitForEvent): + t.Error("Timed out waiting for event") + } +} + +func TestStreamCanReadSpecificHeader(t *testing.T) { + streamHandler, streamControl := httphelpers.SSEHandlerWithEnvironmentID(nil, "env-id") + defer streamControl.Close() + httpServer := httptest.NewServer(streamHandler) + defer httpServer.Close() + + stream := mustSubscribe(t, httpServer.URL) + defer stream.Close() + + streamControl.Send(httphelpers.SSEEvent{ID: "123"}) + + select { + case receivedEvent := <-stream.Events: + receivedWithHeaders := receivedEvent.(EventWithHeaders) + assert.Equal(t, "env-id", receivedWithHeaders.Headers().Get("X-Ld-Envid")) case <-time.After(timeToWaitForEvent): t.Error("Timed out waiting for event") } diff --git a/stream_reconnect_test.go b/stream_reconnect_test.go index db51e4c..a284d93 100644 --- a/stream_reconnect_test.go +++ b/stream_reconnect_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/launchdarkly/go-test-helpers/v2/httphelpers" + "github.com/launchdarkly/go-test-helpers/v3/httphelpers" ) func TestStreamReconnectsIfConnectionIsBroken(t *testing.T) { @@ -49,7 +49,11 @@ func TestStreamReconnectsIfConnectionIsBroken(t *testing.T) { t.Error("Timed out waiting for event") return case receivedEvent := <-stream.Events: - assert.Equal(t, &publication{id: "123", lastEventID: "123"}, receivedEvent) + receivedWithLastId := receivedEvent.(EventWithLastID) + receivedWithHeaders := receivedEvent.(EventWithHeaders) + assert.Equal(t, "123", receivedEvent.Id()) + assert.Equal(t, "123", receivedWithLastId.LastEventID()) + assert.NotEmpty(t, receivedWithHeaders.Headers()) return } } diff --git a/stream_requests_test.go b/stream_requests_test.go index d98fb52..ea800ce 100644 --- a/stream_requests_test.go +++ b/stream_requests_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/launchdarkly/go-test-helpers/v2/httphelpers" + "github.com/launchdarkly/go-test-helpers/v3/httphelpers" ) func TestStreamCanUseCustomClient(t *testing.T) { diff --git a/stream_restart_close_test.go b/stream_restart_close_test.go index 7fab2d7..18135b0 100644 --- a/stream_restart_close_test.go +++ b/stream_restart_close_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/launchdarkly/go-test-helpers/v2/httphelpers" + "github.com/launchdarkly/go-test-helpers/v3/httphelpers" ) func toPublication(e httphelpers.SSEEvent) *publication {