...

Source file src/github.com/launchdarkly/go-server-sdk/v6/ldclient_events_test.go

Documentation: github.com/launchdarkly/go-server-sdk/v6

     1  package ldclient
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	"github.com/launchdarkly/go-server-sdk/v6/internal/sharedtest/mocks"
     8  
     9  	"github.com/launchdarkly/go-sdk-common/v3/ldlog"
    10  	"github.com/launchdarkly/go-sdk-common/v3/ldlogtest"
    11  	"github.com/launchdarkly/go-sdk-common/v3/lduser"
    12  	"github.com/launchdarkly/go-sdk-common/v3/ldvalue"
    13  	ldevents "github.com/launchdarkly/go-sdk-events/v2"
    14  	"github.com/launchdarkly/go-server-sdk/v6/interfaces"
    15  	"github.com/launchdarkly/go-server-sdk/v6/ldcomponents"
    16  	helpers "github.com/launchdarkly/go-test-helpers/v3"
    17  
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  func TestIdentifySendsIdentifyEvent(t *testing.T) {
    23  	client := makeTestClient()
    24  	defer client.Close()
    25  
    26  	user := lduser.NewUser("userKey")
    27  	err := client.Identify(user)
    28  	assert.NoError(t, err)
    29  
    30  	events := client.eventProcessor.(*mocks.CapturingEventProcessor).Events
    31  	assert.Equal(t, 1, len(events))
    32  	e := events[0].(ldevents.IdentifyEventData)
    33  	assert.Equal(t, ldevents.Context(user), e.Context)
    34  }
    35  
    36  func TestIdentifyWithEmptyUserKeySendsNoEvent(t *testing.T) {
    37  	client := makeTestClient()
    38  	defer client.Close()
    39  
    40  	err := client.Identify(lduser.NewUser(""))
    41  	assert.NoError(t, err) // we don't return an error for this, we just log it
    42  
    43  	events := client.eventProcessor.(*mocks.CapturingEventProcessor).Events
    44  	assert.Equal(t, 0, len(events))
    45  }
    46  
    47  func TestTrackEventSendsCustomEvent(t *testing.T) {
    48  	client := makeTestClient()
    49  	defer client.Close()
    50  
    51  	user := lduser.NewUser("userKey")
    52  	key := "eventKey"
    53  	err := client.TrackEvent(key, user)
    54  	assert.NoError(t, err)
    55  
    56  	events := client.eventProcessor.(*mocks.CapturingEventProcessor).Events
    57  	assert.Equal(t, 1, len(events))
    58  	e := events[0].(ldevents.CustomEventData)
    59  	assert.Equal(t, ldevents.Context(user), e.Context)
    60  	assert.Equal(t, key, e.Key)
    61  	assert.Equal(t, ldvalue.Null(), e.Data)
    62  	assert.False(t, e.HasMetric)
    63  }
    64  
    65  func TestTrackDataSendsCustomEventWithData(t *testing.T) {
    66  	client := makeTestClient()
    67  	defer client.Close()
    68  
    69  	user := lduser.NewUser("userKey")
    70  	key := "eventKey"
    71  	data := ldvalue.ArrayOf(ldvalue.String("a"), ldvalue.String("b"))
    72  	err := client.TrackData(key, user, data)
    73  	assert.NoError(t, err)
    74  
    75  	events := client.eventProcessor.(*mocks.CapturingEventProcessor).Events
    76  	assert.Equal(t, 1, len(events))
    77  	e := events[0].(ldevents.CustomEventData)
    78  	assert.Equal(t, ldevents.Context(user), e.Context)
    79  	assert.Equal(t, key, e.Key)
    80  	assert.Equal(t, data, e.Data)
    81  	assert.False(t, e.HasMetric)
    82  }
    83  
    84  func TestTrackMetricSendsCustomEventWithMetricAndData(t *testing.T) {
    85  	client := makeTestClient()
    86  	defer client.Close()
    87  
    88  	user := lduser.NewUser("userKey")
    89  	key := "eventKey"
    90  	data := ldvalue.ArrayOf(ldvalue.String("a"), ldvalue.String("b"))
    91  	metric := float64(1.5)
    92  	err := client.TrackMetric(key, user, metric, data)
    93  	assert.NoError(t, err)
    94  
    95  	events := client.eventProcessor.(*mocks.CapturingEventProcessor).Events
    96  	assert.Equal(t, 1, len(events))
    97  	e := events[0].(ldevents.CustomEventData)
    98  	assert.Equal(t, ldevents.Context(user), e.Context)
    99  	assert.Equal(t, key, e.Key)
   100  	assert.Equal(t, data, e.Data)
   101  	assert.True(t, e.HasMetric)
   102  	assert.Equal(t, metric, e.MetricValue)
   103  }
   104  
   105  func TestTrackWithEmptyUserKeySendsNoEvent(t *testing.T) {
   106  	client := makeTestClient()
   107  	defer client.Close()
   108  
   109  	err := client.TrackEvent("eventkey", lduser.NewUser(""))
   110  	assert.NoError(t, err) // we don't return an error for this, we just log it
   111  
   112  	events := client.eventProcessor.(*mocks.CapturingEventProcessor).Events
   113  	assert.Equal(t, 0, len(events))
   114  }
   115  
   116  func TestTrackMetricWithEmptyUserKeySendsNoEvent(t *testing.T) {
   117  	client := makeTestClient()
   118  	defer client.Close()
   119  
   120  	err := client.TrackMetric("eventKey", lduser.NewUser(""), 2.5, ldvalue.Null())
   121  	assert.NoError(t, err) // we don't return an error for this, we just log it
   122  
   123  	events := client.eventProcessor.(*mocks.CapturingEventProcessor).Events
   124  	assert.Equal(t, 0, len(events))
   125  }
   126  
   127  func TestIdentifyWithEventsDisabledDoesNotCauseError(t *testing.T) {
   128  	mockLog := ldlogtest.NewMockLog()
   129  	client := makeTestClientWithConfig(func(c *Config) {
   130  		c.Events = ldcomponents.NoEvents()
   131  		c.Logging = ldcomponents.Logging().Loggers(mockLog.Loggers)
   132  	})
   133  	defer client.Close()
   134  
   135  	require.NoError(t, client.Identify(lduser.NewUser("")))
   136  
   137  	assert.Len(t, mockLog.GetOutput(ldlog.Warn), 0)
   138  }
   139  
   140  func TestTrackWithEventsDisabledDoesNotCauseError(t *testing.T) {
   141  	mockLog := ldlogtest.NewMockLog()
   142  	client := makeTestClientWithConfig(func(c *Config) {
   143  		c.Events = ldcomponents.NoEvents()
   144  		c.Logging = ldcomponents.Logging().Loggers(mockLog.Loggers)
   145  	})
   146  	defer client.Close()
   147  
   148  	require.NoError(t, client.TrackEvent("eventkey", lduser.NewUser("")))
   149  	require.NoError(t, client.TrackMetric("eventkey", lduser.NewUser(""), 0, ldvalue.Null()))
   150  
   151  	assert.Len(t, mockLog.GetOutput(ldlog.Warn), 0)
   152  }
   153  
   154  func TestWithEventsDisabledDecorator(t *testing.T) {
   155  	doTest := func(name string, fn func(*LDClient) interfaces.LDClientInterface, shouldBeSent bool) {
   156  		t.Run(name, func(t *testing.T) {
   157  			events := &mocks.CapturingEventProcessor{}
   158  			config := Config{
   159  				DataSource: ldcomponents.ExternalUpdatesOnly(),
   160  				Events:     mocks.SingleComponentConfigurer[ldevents.EventProcessor]{Instance: events},
   161  			}
   162  			client, err := MakeCustomClient("", config, 0)
   163  			require.NoError(t, err)
   164  
   165  			ci := fn(client)
   166  			checkEvents := func(action func()) {
   167  				events.Events = nil
   168  				action()
   169  				if shouldBeSent {
   170  					assert.Len(t, events.Events, 1, "should have recorded an event, but did not")
   171  				} else {
   172  					assert.Len(t, events.Events, 0, "should not have recorded an event, but did")
   173  				}
   174  			}
   175  			user := lduser.NewUser("userkey")
   176  			checkEvents(func() { _, _ = ci.BoolVariation("flagkey", user, false) })
   177  			checkEvents(func() { _, _, _ = ci.BoolVariationDetail("flagkey", user, false) })
   178  			checkEvents(func() { _, _ = ci.IntVariation("flagkey", user, 0) })
   179  			checkEvents(func() { _, _, _ = ci.IntVariationDetail("flagkey", user, 0) })
   180  			checkEvents(func() { _, _ = ci.Float64Variation("flagkey", user, 0) })
   181  			checkEvents(func() { _, _, _ = ci.Float64VariationDetail("flagkey", user, 0) })
   182  			checkEvents(func() { _, _ = ci.StringVariation("flagkey", user, "") })
   183  			checkEvents(func() { _, _, _ = ci.StringVariationDetail("flagkey", user, "") })
   184  			checkEvents(func() { _, _ = ci.JSONVariation("flagkey", user, ldvalue.Null()) })
   185  			checkEvents(func() { _, _, _ = ci.JSONVariationDetail("flagkey", user, ldvalue.Null()) })
   186  			checkEvents(func() { ci.Identify(user) })
   187  			checkEvents(func() { ci.TrackEvent("eventkey", user) })
   188  			checkEvents(func() { ci.TrackData("eventkey", user, ldvalue.Bool(true)) })
   189  			checkEvents(func() { ci.TrackMetric("eventkey", user, 1.5, ldvalue.Null()) })
   190  
   191  			state := ci.AllFlagsState(user)
   192  			assert.True(t, state.IsValid())
   193  		})
   194  	}
   195  
   196  	doTest("client.WithEventsDisabled(false)",
   197  		func(c *LDClient) interfaces.LDClientInterface { return c.WithEventsDisabled(false) },
   198  		true)
   199  
   200  	doTest("client.WithEventsDisabled(true)",
   201  		func(c *LDClient) interfaces.LDClientInterface { return c.WithEventsDisabled(true) },
   202  		false)
   203  
   204  	doTest("client.WithEventsDisabled(true).WithEventsDisabled(false)",
   205  		func(c *LDClient) interfaces.LDClientInterface {
   206  			return c.WithEventsDisabled(true).WithEventsDisabled(false)
   207  		},
   208  		true)
   209  
   210  	doTest("client.WithEventsDisabled(true).WithEventsDisabled(true)",
   211  		func(c *LDClient) interfaces.LDClientInterface {
   212  			return c.WithEventsDisabled(true).WithEventsDisabled(true)
   213  		},
   214  		false)
   215  }
   216  
   217  func TestFlushAsync(t *testing.T) {
   218  	g := newGatedEventSender()
   219  	client := makeTestClientWithEventSender(g)
   220  	defer client.Close()
   221  
   222  	client.Identify(evalTestUser)
   223  	client.Flush()
   224  
   225  	helpers.AssertNoMoreValues(t, g.didSendCh, time.Millisecond*50) // didn't do the flush yet
   226  
   227  	g.canSendCh <- struct{}{} // allow the sender to proceed with the fake flush
   228  
   229  	helpers.RequireValue(t, g.didSendCh, time.Millisecond*100) // now the flush has happened
   230  }
   231  
   232  func TestFlushAndWaitSucceeds(t *testing.T) {
   233  	g := newGatedEventSender()
   234  	client := makeTestClientWithEventSender(g)
   235  	defer client.Close()
   236  
   237  	client.Identify(evalTestUser)
   238  
   239  	go func() {
   240  		time.Sleep(time.Millisecond * 20)
   241  		g.canSendCh <- struct{}{}
   242  	}()
   243  
   244  	sent := client.FlushAndWait(time.Millisecond * 500)
   245  	assert.True(t, sent)
   246  
   247  	helpers.RequireValue(t, g.didSendCh, time.Millisecond*50)
   248  }
   249  
   250  func TestFlushAndWaitTimesOut(t *testing.T) {
   251  	g := newGatedEventSender()
   252  	client := makeTestClientWithEventSender(g)
   253  	defer client.Close()
   254  
   255  	client.Identify(evalTestUser)
   256  
   257  	go func() {
   258  		time.Sleep(time.Millisecond * 200)
   259  		g.canSendCh <- struct{}{}
   260  	}()
   261  
   262  	sent := client.FlushAndWait(time.Millisecond * 10)
   263  	assert.False(t, sent)
   264  
   265  	helpers.AssertNoMoreValues(t, g.didSendCh, time.Millisecond*50) // didn't do the flush yet
   266  }
   267  
   268  type gatedEventSender struct {
   269  	canSendCh chan struct{}
   270  	didSendCh chan struct{}
   271  }
   272  
   273  func newGatedEventSender() *gatedEventSender {
   274  	return &gatedEventSender{canSendCh: make(chan struct{}, 100), didSendCh: make(chan struct{}, 100)}
   275  }
   276  
   277  func (g *gatedEventSender) SendEventData(kind ldevents.EventDataKind, data []byte, eventCount int) ldevents.EventSenderResult {
   278  	<-g.canSendCh
   279  	g.didSendCh <- struct{}{}
   280  	return ldevents.EventSenderResult{Success: true}
   281  }
   282  
   283  func makeTestClientWithEventSender(s ldevents.EventSender) *LDClient {
   284  	eventsConfig := ldevents.EventsConfiguration{
   285  		Capacity:              1000,
   286  		EventSender:           s,
   287  		FlushInterval:         time.Hour,
   288  		Loggers:               ldlog.NewDisabledLoggers(),
   289  		UserKeysCapacity:      1000,
   290  		UserKeysFlushInterval: time.Hour,
   291  	}
   292  	ep := ldevents.NewDefaultEventProcessor(eventsConfig)
   293  	return makeTestClientWithConfig(func(c *Config) {
   294  		c.Events = mocks.SingleComponentConfigurer[ldevents.EventProcessor]{Instance: ep}
   295  	})
   296  }
   297  

View as plain text