...

Source file src/github.com/launchdarkly/go-sdk-events/v2/event_sender_test.go

Documentation: github.com/launchdarkly/go-sdk-events/v2

     1  package ldevents
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"net/http/httptest"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/launchdarkly/go-sdk-common/v3/ldlog"
    11  	"github.com/launchdarkly/go-sdk-common/v3/ldtime"
    12  
    13  	"github.com/launchdarkly/go-test-helpers/v3/httphelpers"
    14  
    15  	"github.com/stretchr/testify/assert"
    16  )
    17  
    18  type errorInfo struct {
    19  	status int
    20  }
    21  
    22  func (ei errorInfo) Handler() http.Handler {
    23  	if ei.status > 0 {
    24  		return httphelpers.HandlerWithStatus(ei.status)
    25  	}
    26  	return httphelpers.BrokenConnectionHandler()
    27  }
    28  
    29  func (ei errorInfo) String() string {
    30  	if ei.status > 0 {
    31  		return fmt.Sprintf("error %d", ei.status)
    32  	}
    33  	return "network error"
    34  }
    35  
    36  func TestDataIsSentToAnalyticsURI(t *testing.T) {
    37  	es, requestsCh := makeEventSenderWithRequestSink()
    38  
    39  	result := es.SendEventData(AnalyticsEventDataKind, arbitraryJSONData, 1)
    40  	assert.True(t, result.Success)
    41  
    42  	assert.Equal(t, 1, len(requestsCh))
    43  	r := <-requestsCh
    44  	assert.Equal(t, fakeEventsURI, r.Request.URL.String())
    45  	assert.Equal(t, arbitraryJSONData, r.Body)
    46  }
    47  
    48  func TestDataIsSentToDiagnosticURI(t *testing.T) {
    49  	es, requestsCh := makeEventSenderWithRequestSink()
    50  
    51  	result := es.SendEventData(DiagnosticEventDataKind, arbitraryJSONData, 1)
    52  	assert.True(t, result.Success)
    53  
    54  	assert.Equal(t, 1, len(requestsCh))
    55  	r := <-requestsCh
    56  	assert.Equal(t, fakeDiagnosticURI, r.Request.URL.String())
    57  	assert.Equal(t, arbitraryJSONData, r.Body)
    58  }
    59  
    60  func TestUnknownDataKindIsIgnored(t *testing.T) {
    61  	es, requestsCh := makeEventSenderWithRequestSink()
    62  
    63  	result := es.SendEventData(EventDataKind("not valid"), arbitraryJSONData, 1)
    64  	assert.False(t, result.Success)
    65  	assert.False(t, result.MustShutDown)
    66  	assert.Len(t, requestsCh, 0)
    67  }
    68  
    69  func TestAnalyticsEventsHaveSchemaAndPayloadIDHeaders(t *testing.T) {
    70  	es, requestsCh := makeEventSenderWithRequestSink()
    71  
    72  	es.SendEventData(AnalyticsEventDataKind, arbitraryJSONData, 1)
    73  	es.SendEventData(AnalyticsEventDataKind, arbitraryJSONData, 1)
    74  
    75  	assert.Equal(t, 2, len(requestsCh))
    76  	r0 := <-requestsCh
    77  	r1 := <-requestsCh
    78  
    79  	assert.Equal(t, currentEventSchema, r0.Request.Header.Get(eventSchemaHeader))
    80  	assert.Equal(t, currentEventSchema, r1.Request.Header.Get(eventSchemaHeader))
    81  
    82  	id0 := r0.Request.Header.Get(payloadIDHeader)
    83  	id1 := r1.Request.Header.Get(payloadIDHeader)
    84  	assert.NotEqual(t, "", id0)
    85  	assert.NotEqual(t, "", id1)
    86  	assert.NotEqual(t, id0, id1)
    87  }
    88  
    89  func TestDiagnosticEventsDoNotHaveSchemaOrPayloadID(t *testing.T) {
    90  	es, requestsCh := makeEventSenderWithRequestSink()
    91  
    92  	es.SendEventData(DiagnosticEventDataKind, arbitraryJSONData, 1)
    93  
    94  	assert.Equal(t, 1, len(requestsCh))
    95  	r := <-requestsCh
    96  	assert.Equal(t, "", r.Request.Header.Get(eventSchemaHeader))
    97  	assert.Equal(t, "", r.Request.Header.Get(payloadIDHeader))
    98  }
    99  
   100  func TestEventSenderParsesTimeFromServer(t *testing.T) {
   101  	expectedTime := ldtime.UnixMillisFromTime(time.Date(1940, time.February, 15, 12, 13, 14, 0, time.UTC))
   102  	headers := make(http.Header)
   103  	headers.Set("Date", "Thu, 15 Feb 1940 12:13:14 GMT")
   104  	handler := httphelpers.HandlerWithResponse(202, headers, nil)
   105  	es := makeEventSenderWithHTTPClient(httphelpers.ClientFromHandler(handler))
   106  
   107  	result := es.SendEventData(AnalyticsEventDataKind, arbitraryJSONData, 1)
   108  	assert.True(t, result.Success)
   109  	assert.Equal(t, expectedTime, result.TimeFromServer)
   110  }
   111  
   112  func TestEventSenderRetriesOnRecoverableError(t *testing.T) {
   113  	errs := []errorInfo{{400}, {408}, {429}, {500}, {503}, {0}}
   114  	for _, errorInfo := range errs {
   115  		t.Run(fmt.Sprintf("Retries once after %s", errorInfo), func(t *testing.T) {
   116  			handler, requestsCh := httphelpers.RecordingHandler(
   117  				httphelpers.SequentialHandler(
   118  					errorInfo.Handler(),                // fails once
   119  					httphelpers.HandlerWithStatus(202), // then succeeds
   120  				),
   121  			)
   122  			es := makeEventSenderWithHTTPClient(httphelpers.ClientFromHandler(handler))
   123  
   124  			result := es.SendEventData(AnalyticsEventDataKind, arbitraryJSONData, 1)
   125  
   126  			assert.True(t, result.Success)
   127  			assert.False(t, result.MustShutDown)
   128  
   129  			assert.Equal(t, 2, len(requestsCh))
   130  			r0 := <-requestsCh
   131  			r1 := <-requestsCh
   132  			assert.Equal(t, arbitraryJSONData, r0.Body)
   133  			assert.Equal(t, arbitraryJSONData, r1.Body)
   134  			id0 := r0.Request.Header.Get(payloadIDHeader)
   135  			assert.NotEqual(t, "", id0)
   136  			assert.Equal(t, id0, r1.Request.Header.Get(payloadIDHeader))
   137  		})
   138  
   139  		t.Run(fmt.Sprintf("Does not retry more than once after %s", errorInfo), func(t *testing.T) {
   140  			handler, requestsCh := httphelpers.RecordingHandler(
   141  				httphelpers.SequentialHandler(
   142  					errorInfo.Handler(),                // fails once
   143  					errorInfo.Handler(),                // fails again
   144  					httphelpers.HandlerWithStatus(202), // then would succeed, if we did a 3rd request
   145  				),
   146  			)
   147  			es := makeEventSenderWithHTTPClient(httphelpers.ClientFromHandler(handler))
   148  
   149  			result := es.SendEventData(AnalyticsEventDataKind, arbitraryJSONData, 1)
   150  
   151  			assert.False(t, result.Success)
   152  			assert.False(t, result.MustShutDown)
   153  
   154  			assert.Equal(t, 2, len(requestsCh))
   155  			r0 := <-requestsCh
   156  			r1 := <-requestsCh
   157  			assert.Equal(t, arbitraryJSONData, r0.Body)
   158  			assert.Equal(t, arbitraryJSONData, r1.Body)
   159  			id0 := r0.Request.Header.Get(payloadIDHeader)
   160  			assert.NotEqual(t, "", id0)
   161  			assert.Equal(t, id0, r1.Request.Header.Get(payloadIDHeader))
   162  		})
   163  	}
   164  }
   165  
   166  func TestEventSenderFailsOnUnrecoverableError(t *testing.T) {
   167  	errs := []errorInfo{{401}, {403}}
   168  	for _, errorInfo := range errs {
   169  		t.Run(fmt.Sprintf("Fails permanently after %s", errorInfo), func(t *testing.T) {
   170  			handler, requestsCh := httphelpers.RecordingHandler(
   171  				httphelpers.SequentialHandler(
   172  					errorInfo.Handler(),                // fails once
   173  					httphelpers.HandlerWithStatus(202), // then succeeds
   174  				),
   175  			)
   176  			es := makeEventSenderWithHTTPClient(httphelpers.ClientFromHandler(handler))
   177  
   178  			result := es.SendEventData(AnalyticsEventDataKind, arbitraryJSONData, 1)
   179  
   180  			assert.False(t, result.Success)
   181  			assert.True(t, result.MustShutDown)
   182  
   183  			assert.Equal(t, 1, len(requestsCh))
   184  			r := <-requestsCh
   185  			assert.Equal(t, arbitraryJSONData, r.Body)
   186  		})
   187  	}
   188  }
   189  
   190  func TestServerSideSenderSetsURIsFromBase(t *testing.T) {
   191  	handler, requestsCh := httphelpers.RecordingHandler(httphelpers.HandlerWithStatus(202))
   192  	client := httphelpers.ClientFromHandler(handler)
   193  	es := NewServerSideEventSender(EventSenderConfiguration{Client: client, BaseURI: fakeBaseURI, Loggers: ldlog.NewDisabledLoggers()},
   194  		sdkKey)
   195  
   196  	es.SendEventData(AnalyticsEventDataKind, arbitraryJSONData, 1)
   197  	es.SendEventData(DiagnosticEventDataKind, arbitraryJSONData, 1)
   198  
   199  	assert.Equal(t, 2, len(requestsCh))
   200  	r0 := <-requestsCh
   201  	r1 := <-requestsCh
   202  	assert.Equal(t, fakeEventsURI, r0.Request.URL.String())
   203  	assert.Equal(t, fakeDiagnosticURI, r1.Request.URL.String())
   204  }
   205  
   206  func TestServerSideSenderHasDefaultBaseURI(t *testing.T) {
   207  	handler, requestsCh := httphelpers.RecordingHandler(httphelpers.HandlerWithStatus(202))
   208  	client := httphelpers.ClientFromHandler(handler)
   209  	es := NewServerSideEventSender(
   210  		EventSenderConfiguration{
   211  			Client:  client,
   212  			Loggers: ldlog.NewDisabledLoggers(),
   213  		},
   214  		sdkKey,
   215  	)
   216  
   217  	es.SendEventData(AnalyticsEventDataKind, arbitraryJSONData, 1)
   218  	es.SendEventData(DiagnosticEventDataKind, arbitraryJSONData, 1)
   219  
   220  	assert.Equal(t, 2, len(requestsCh))
   221  	r0 := <-requestsCh
   222  	r1 := <-requestsCh
   223  	assert.Equal(t, "https://events.launchdarkly.com/bulk", r0.Request.URL.String())
   224  	assert.Equal(t, "https://events.launchdarkly.com/diagnostic", r1.Request.URL.String())
   225  }
   226  
   227  func TestServerSideSenderAddsAuthorizationHeader(t *testing.T) {
   228  	handler, requestsCh := httphelpers.RecordingHandler(httphelpers.HandlerWithStatus(202))
   229  	client := httphelpers.ClientFromHandler(handler)
   230  	extraHeaders := make(http.Header)
   231  	extraHeaders.Set("my-header", "my-value")
   232  	es := NewServerSideEventSender(
   233  		EventSenderConfiguration{
   234  			Client:      client,
   235  			BaseURI:     fakeBaseURI,
   236  			BaseHeaders: func() http.Header { return extraHeaders },
   237  			Loggers:     ldlog.NewDisabledLoggers(),
   238  		},
   239  		sdkKey,
   240  	)
   241  
   242  	es.SendEventData(AnalyticsEventDataKind, arbitraryJSONData, 1)
   243  
   244  	assert.Equal(t, 1, len(requestsCh))
   245  	r := <-requestsCh
   246  	assert.Equal(t, sdkKey, r.Request.Header.Get("Authorization"))
   247  	assert.Equal(t, "my-value", r.Request.Header.Get("my-header"))
   248  }
   249  
   250  func TestSchemaVersionCannotBeOverriddenWithServerSideSender(t *testing.T) {
   251  	handler, requestsCh := httphelpers.RecordingHandler(httphelpers.HandlerForMethod("POST", httphelpers.HandlerWithStatus(202), nil))
   252  	client := httphelpers.ClientFromHandler(handler)
   253  
   254  	config := EventSenderConfiguration{Client: client, SchemaVersion: 99}
   255  	es := makeEventSenderWithConfig(config)
   256  
   257  	es.SendEventData(AnalyticsEventDataKind, arbitraryJSONData, 1)
   258  
   259  	r := <-requestsCh
   260  	assert.Equal(t, currentEventSchema, r.Request.Header.Get(eventSchemaHeader))
   261  }
   262  
   263  func TestSchemaVersionCanBeOverriddenWithDirectSend(t *testing.T) {
   264  	handler, requestsCh := httphelpers.RecordingHandler(httphelpers.HandlerForMethod("POST", httphelpers.HandlerWithStatus(202), nil))
   265  	client := httphelpers.ClientFromHandler(handler)
   266  
   267  	config := EventSenderConfiguration{Client: client, SchemaVersion: 99}
   268  
   269  	_ = SendEventDataWithRetry(config, AnalyticsEventDataKind, "", arbitraryJSONData, 1)
   270  
   271  	r := <-requestsCh
   272  	assert.Equal(t, "/bulk", r.Request.URL.Path)
   273  	assert.Equal(t, "99", r.Request.Header.Get(eventSchemaHeader))
   274  }
   275  
   276  func TestSendEventDataCanUseDefaultHTTPClient(t *testing.T) {
   277  	handler, requestsCh := httphelpers.RecordingHandler(httphelpers.HandlerForMethod("POST", httphelpers.HandlerWithStatus(202), nil))
   278  	server := httptest.NewServer(handler)
   279  	defer server.Close()
   280  
   281  	config := EventSenderConfiguration{BaseURI: server.URL}
   282  
   283  	_ = SendEventDataWithRetry(config, AnalyticsEventDataKind, "", arbitraryJSONData, 1)
   284  
   285  	r := <-requestsCh
   286  	assert.Equal(t, "/bulk", r.Request.URL.Path)
   287  	assert.Equal(t, string(arbitraryJSONData), string(r.Body))
   288  }
   289  
   290  func TestSendEventDataCanOverrideURI(t *testing.T) {
   291  	handler, requestsCh := httphelpers.RecordingHandler(httphelpers.HandlerForMethod("POST", httphelpers.HandlerWithStatus(202), nil))
   292  	server := httptest.NewServer(handler)
   293  	defer server.Close()
   294  
   295  	config := EventSenderConfiguration{BaseURI: server.URL}
   296  
   297  	_ = SendEventDataWithRetry(config, AnalyticsEventDataKind, "/other/path", arbitraryJSONData, 1)
   298  	_ = SendEventDataWithRetry(config, AnalyticsEventDataKind, "other/path", arbitraryJSONData, 1)
   299  
   300  	r1, r2 := <-requestsCh, <-requestsCh
   301  	assert.Equal(t, "/other/path", r1.Request.URL.Path)
   302  	assert.Equal(t, "/other/path", r2.Request.URL.Path)
   303  }
   304  
   305  func makeEventSenderWithConfig(config EventSenderConfiguration) EventSender {
   306  	config.BaseURI = fakeBaseURI
   307  	config.Loggers = ldlog.NewDisabledLoggers()
   308  	if config.RetryDelay == 0 {
   309  		config.RetryDelay = briefRetryDelay
   310  	}
   311  	return NewServerSideEventSender(config, sdkKey)
   312  }
   313  
   314  func makeEventSenderWithHTTPClient(client *http.Client) EventSender {
   315  	return makeEventSenderWithConfig(EventSenderConfiguration{Client: client})
   316  }
   317  
   318  func makeEventSenderWithRequestSink() (EventSender, <-chan httphelpers.HTTPRequestInfo) {
   319  	handler, requestsCh := httphelpers.RecordingHandler(httphelpers.HandlerForMethod("POST", httphelpers.HandlerWithStatus(202), nil))
   320  	client := httphelpers.ClientFromHandler(handler)
   321  	return makeEventSenderWithHTTPClient(client), requestsCh
   322  }
   323  

View as plain text