From a15070faf96de5c193aa85aedcae34b28aa10af1 Mon Sep 17 00:00:00 2001 From: Thomas Zurkan Date: Thu, 22 Aug 2019 13:01:46 -0700 Subject: [PATCH 01/10] add a track call --- optimizely/client/client.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/optimizely/client/client.go b/optimizely/client/client.go index c0e5c7e7e..54b72fc8a 100644 --- a/optimizely/client/client.go +++ b/optimizely/client/client.go @@ -133,6 +133,36 @@ func (o *OptimizelyClient) GetEnabledFeatures(userContext entities.UserContext) return enabledFeatures, nil } +func (o *OptimizelyClient) Track(userContext entities.UserContext, eventKey string, eventTags map[string]interface{}) error { + if !o.isValid { + errorMessage := "Optimizely instance is not valid. Failing GetEnabledFeatures." + err := errors.New(errorMessage) + logger.Error(errorMessage, nil) + return err + } + + defer func() { + if r := recover(); r != nil { + errorMessage := fmt.Sprintf(`Optimizely SDK is panicking with the error "%s"`, string(debug.Stack())) + err := errors.New(errorMessage) + logger.Error(errorMessage, err) + } + }() + + event, err := o.configManager.GetConfig().GetEventByKey(eventKey) + + if err != nil { + event. + userEvent := event.CreateConversionUserEvent(o.configManager.GetConfig(), event, userContext, eventTags) + o.eventProcessor.ProcessEvent(userEvent) + } else { + logger.Error("Error getting event", err) + return err + } + + return nil +} + // Close closes the Optimizely instance and stops any ongoing tasks from its children components func (o *OptimizelyClient) Close() { o.cancelFunc() From 2b6fbe032365fe6be51432e8a1f5489923f776f6 Mon Sep 17 00:00:00 2001 From: Thomas Zurkan Date: Thu, 22 Aug 2019 13:05:06 -0700 Subject: [PATCH 02/10] err == nil --- optimizely/client/client.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/optimizely/client/client.go b/optimizely/client/client.go index 54b72fc8a..97319bdfa 100644 --- a/optimizely/client/client.go +++ b/optimizely/client/client.go @@ -151,8 +151,7 @@ func (o *OptimizelyClient) Track(userContext entities.UserContext, eventKey stri event, err := o.configManager.GetConfig().GetEventByKey(eventKey) - if err != nil { - event. + if err == nil { userEvent := event.CreateConversionUserEvent(o.configManager.GetConfig(), event, userContext, eventTags) o.eventProcessor.ProcessEvent(userEvent) } else { From ac116160250391129dea4d205919e3c7ca34bc92 Mon Sep 17 00:00:00 2001 From: Thomas Zurkan Date: Thu, 22 Aug 2019 13:22:10 -0700 Subject: [PATCH 03/10] still need to write a unit test --- optimizely/client/client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/optimizely/client/client.go b/optimizely/client/client.go index 97319bdfa..63f5a817c 100644 --- a/optimizely/client/client.go +++ b/optimizely/client/client.go @@ -149,10 +149,10 @@ func (o *OptimizelyClient) Track(userContext entities.UserContext, eventKey stri } }() - event, err := o.configManager.GetConfig().GetEventByKey(eventKey) + configEvent, err := o.configManager.GetConfig().GetEventByKey(eventKey) if err == nil { - userEvent := event.CreateConversionUserEvent(o.configManager.GetConfig(), event, userContext, eventTags) + userEvent := event.CreateConversionUserEvent(o.configManager.GetConfig(), configEvent, userContext, eventTags) o.eventProcessor.ProcessEvent(userEvent) } else { logger.Error("Error getting event", err) From f7f3a65aaaadfffb048650e3eab4b442bdeb8484 Mon Sep 17 00:00:00 2001 From: Thomas Zurkan Date: Thu, 22 Aug 2019 17:26:22 -0700 Subject: [PATCH 04/10] add track unit test --- optimizely/client/client_test.go | 80 ++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/optimizely/client/client_test.go b/optimizely/client/client_test.go index e282b7125..b708ba024 100644 --- a/optimizely/client/client_test.go +++ b/optimizely/client/client_test.go @@ -18,14 +18,13 @@ package client import ( "errors" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/optimizely/go-sdk/optimizely" "github.com/optimizely/go-sdk/optimizely/decision" "github.com/optimizely/go-sdk/optimizely/entities" + "github.com/optimizely/go-sdk/optimizely/event" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "testing" ) type MockProjectConfig struct { @@ -43,6 +42,11 @@ func (c *MockProjectConfig) GetFeatureList() []entities.Feature { return args.Get(0).([]entities.Feature) } +func (c *MockProjectConfig) GetEventByKey(string) (entities.Event, error) { + args := c.Called() + return args.Get(0).(entities.Event), args.Error(1) +} + type MockProjectConfigManager struct { mock.Mock } @@ -62,6 +66,74 @@ func (m *MockDecisionService) GetFeatureDecision(decisionContext decision.Featur return args.Get(0).(decision.FeatureDecision), args.Error(1) } +type MockProcessor struct { + Events []event.UserEvent +} + +func (f *MockProcessor) ProcessEvent(event event.UserEvent) { + f.Events = append(f.Events, event) +} + +type TestConfig struct { + optimizely.ProjectConfig +} + +func (TestConfig) GetEventByKey(string) (entities.Event, error) { + return entities.Event{ExperimentIds: []string{"15402980349"}, ID: "15368860886", Key: "sample_conversion"}, nil +} + +func (TestConfig) GetFeatureByKey(string) (entities.Feature, error) { + return entities.Feature{}, nil +} + +func (TestConfig) GetProjectID() string { + return "15389410617" +} +func (TestConfig) GetRevision() string { + return "7" +} +func (TestConfig) GetAccountID() string { + return "8362480420" +} +func (TestConfig) GetAnonymizeIP() bool { + return true +} +func (TestConfig) GetAttributeID(key string) string { // returns "" if there is no id + return "" +} +func (TestConfig) GetBotFiltering() bool { + return false +} +func (TestConfig) GetClientName() string { + return "go-sdk" +} +func (TestConfig) GetClientVersion() string { + return "1.0.0" +} + +func TestTrack(t *testing.T) { + mockProcessor := &MockProcessor{} + + mockConfig := new(TestConfig) + mockConfigManager := new(MockProjectConfigManager) + mockConfigManager.On("GetConfig").Return(mockConfig) + mockDecisionService := new(MockDecisionService) + + client := OptimizelyClient{ + configManager: mockConfigManager, + decisionService: mockDecisionService, + eventProcessor: mockProcessor, + isValid: true, + } + + client.Track(entities.UserContext{ID:"1212121", Attributes: map[string]interface{}{}}, "sample_conversion", map[string]interface{}{}) + + assert.True(t, len(mockProcessor.Events) == 1) + assert.True(t, mockProcessor.Events[0].VisitorID == "1212121") + assert.True(t, mockProcessor.Events[0].EventContext.ProjectID == "15389410617") + +} + func TestIsFeatureEnabled(t *testing.T) { testUserContext := entities.UserContext{ID: "test_user_1"} testVariation := entities.Variation{ From d2636bfbaa1ae33b17464eccdcf61512678fb826 Mon Sep 17 00:00:00 2001 From: Thomas Zurkan Date: Thu, 22 Aug 2019 17:32:16 -0700 Subject: [PATCH 05/10] add another test case --- optimizely/client/client.go | 2 +- optimizely/client/client_test.go | 33 +++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/optimizely/client/client.go b/optimizely/client/client.go index 63f5a817c..d21d92e4f 100644 --- a/optimizely/client/client.go +++ b/optimizely/client/client.go @@ -137,7 +137,7 @@ func (o *OptimizelyClient) Track(userContext entities.UserContext, eventKey stri if !o.isValid { errorMessage := "Optimizely instance is not valid. Failing GetEnabledFeatures." err := errors.New(errorMessage) - logger.Error(errorMessage, nil) + logger.Error(errorMessage, err) return err } diff --git a/optimizely/client/client_test.go b/optimizely/client/client_test.go index b708ba024..bbcab1dd2 100644 --- a/optimizely/client/client_test.go +++ b/optimizely/client/client_test.go @@ -78,8 +78,12 @@ type TestConfig struct { optimizely.ProjectConfig } -func (TestConfig) GetEventByKey(string) (entities.Event, error) { - return entities.Event{ExperimentIds: []string{"15402980349"}, ID: "15368860886", Key: "sample_conversion"}, nil +func (TestConfig) GetEventByKey(key string) (entities.Event, error) { + if key == "sample_conversion" { + return entities.Event{ExperimentIds: []string{"15402980349"}, ID: "15368860886", Key: "sample_conversion"}, nil + } + + return entities.Event{}, errors.New("No conversion") } func (TestConfig) GetFeatureByKey(string) (entities.Feature, error) { @@ -126,14 +130,37 @@ func TestTrack(t *testing.T) { isValid: true, } - client.Track(entities.UserContext{ID:"1212121", Attributes: map[string]interface{}{}}, "sample_conversion", map[string]interface{}{}) + err := client.Track(entities.UserContext{ID:"1212121", Attributes: map[string]interface{}{}}, "sample_conversion", map[string]interface{}{}) + assert.Nil(t, err) assert.True(t, len(mockProcessor.Events) == 1) assert.True(t, mockProcessor.Events[0].VisitorID == "1212121") assert.True(t, mockProcessor.Events[0].EventContext.ProjectID == "15389410617") } +func TestTrackFail(t *testing.T) { + mockProcessor := &MockProcessor{} + + mockConfig := new(TestConfig) + mockConfigManager := new(MockProjectConfigManager) + mockConfigManager.On("GetConfig").Return(mockConfig) + mockDecisionService := new(MockDecisionService) + + client := OptimizelyClient{ + configManager: mockConfigManager, + decisionService: mockDecisionService, + eventProcessor: mockProcessor, + isValid: true, + } + + err := client.Track(entities.UserContext{ID:"1212121", Attributes: map[string]interface{}{}}, "bob", map[string]interface{}{}) + + assert.NotNil(t, err) + assert.True(t, len(mockProcessor.Events) == 0) + +} + func TestIsFeatureEnabled(t *testing.T) { testUserContext := entities.UserContext{ID: "test_user_1"} testVariation := entities.Variation{ From 763b5ad91613faee2b5d13a9ff56e930b9ab38be Mon Sep 17 00:00:00 2001 From: Thomas Zurkan Date: Thu, 22 Aug 2019 17:35:13 -0700 Subject: [PATCH 06/10] add better error log message --- optimizely/client/client.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/optimizely/client/client.go b/optimizely/client/client.go index d21d92e4f..f92f826a9 100644 --- a/optimizely/client/client.go +++ b/optimizely/client/client.go @@ -155,7 +155,8 @@ func (o *OptimizelyClient) Track(userContext entities.UserContext, eventKey stri userEvent := event.CreateConversionUserEvent(o.configManager.GetConfig(), configEvent, userContext, eventTags) o.eventProcessor.ProcessEvent(userEvent) } else { - logger.Error("Error getting event", err) + errorMessage := fmt.Sprintf(`Optimizely SDK track: error getting event with key "%s"`, eventKey) + logger.Error(errorMessage, err) return err } From 26e8bf37d4cdd2e99d27cff0886fe43b4b40ea39 Mon Sep 17 00:00:00 2001 From: Thomas Zurkan Date: Thu, 22 Aug 2019 17:49:57 -0700 Subject: [PATCH 07/10] test track with invalid client --- optimizely/client/client_test.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/optimizely/client/client_test.go b/optimizely/client/client_test.go index bbcab1dd2..07273116b 100644 --- a/optimizely/client/client_test.go +++ b/optimizely/client/client_test.go @@ -161,6 +161,28 @@ func TestTrackFail(t *testing.T) { } +func TestTrackInvalid(t *testing.T) { + mockProcessor := &MockProcessor{} + + mockConfig := new(TestConfig) + mockConfigManager := new(MockProjectConfigManager) + mockConfigManager.On("GetConfig").Return(mockConfig) + mockDecisionService := new(MockDecisionService) + + client := OptimizelyClient{ + configManager: mockConfigManager, + decisionService: mockDecisionService, + eventProcessor: mockProcessor, + isValid: false, + } + + err := client.Track(entities.UserContext{ID:"1212121", Attributes: map[string]interface{}{}}, "sample_conversion", map[string]interface{}{}) + + assert.NotNil(t, err) + assert.True(t, len(mockProcessor.Events) == 0) + +} + func TestIsFeatureEnabled(t *testing.T) { testUserContext := entities.UserContext{ID: "test_user_1"} testVariation := entities.Variation{ From 5f56d7ac08ce20eda949c84b5b4b0bfb759c2b05 Mon Sep 17 00:00:00 2001 From: Thomas Zurkan Date: Fri, 23 Aug 2019 10:49:27 -0700 Subject: [PATCH 08/10] track first argument is event key --- optimizely/client/client.go | 2 +- optimizely/client/client_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/optimizely/client/client.go b/optimizely/client/client.go index f92f826a9..6bc2de788 100644 --- a/optimizely/client/client.go +++ b/optimizely/client/client.go @@ -133,7 +133,7 @@ func (o *OptimizelyClient) GetEnabledFeatures(userContext entities.UserContext) return enabledFeatures, nil } -func (o *OptimizelyClient) Track(userContext entities.UserContext, eventKey string, eventTags map[string]interface{}) error { +func (o *OptimizelyClient) Track(eventKey string, userContext entities.UserContext, eventTags map[string]interface{}) error { if !o.isValid { errorMessage := "Optimizely instance is not valid. Failing GetEnabledFeatures." err := errors.New(errorMessage) diff --git a/optimizely/client/client_test.go b/optimizely/client/client_test.go index 07273116b..ea904112b 100644 --- a/optimizely/client/client_test.go +++ b/optimizely/client/client_test.go @@ -130,7 +130,7 @@ func TestTrack(t *testing.T) { isValid: true, } - err := client.Track(entities.UserContext{ID:"1212121", Attributes: map[string]interface{}{}}, "sample_conversion", map[string]interface{}{}) + err := client.Track("sample_conversion", entities.UserContext{ID:"1212121", Attributes: map[string]interface{}{}}, map[string]interface{}{}) assert.Nil(t, err) assert.True(t, len(mockProcessor.Events) == 1) @@ -154,7 +154,7 @@ func TestTrackFail(t *testing.T) { isValid: true, } - err := client.Track(entities.UserContext{ID:"1212121", Attributes: map[string]interface{}{}}, "bob", map[string]interface{}{}) + err := client.Track("bob", entities.UserContext{ID:"1212121", Attributes: map[string]interface{}{}}, map[string]interface{}{}) assert.NotNil(t, err) assert.True(t, len(mockProcessor.Events) == 0) @@ -176,7 +176,7 @@ func TestTrackInvalid(t *testing.T) { isValid: false, } - err := client.Track(entities.UserContext{ID:"1212121", Attributes: map[string]interface{}{}}, "sample_conversion", map[string]interface{}{}) + err := client.Track("sample_conversion", entities.UserContext{ID:"1212121", Attributes: map[string]interface{}{}}, map[string]interface{}{}) assert.NotNil(t, err) assert.True(t, len(mockProcessor.Events) == 0) From c9e5ce5c9c677b965510531516dfbe5756388191 Mon Sep 17 00:00:00 2001 From: Thomas Zurkan Date: Fri, 23 Aug 2019 10:53:37 -0700 Subject: [PATCH 09/10] add comment --- optimizely/client/client.go | 1 + 1 file changed, 1 insertion(+) diff --git a/optimizely/client/client.go b/optimizely/client/client.go index 6bc2de788..3cff3b9c1 100644 --- a/optimizely/client/client.go +++ b/optimizely/client/client.go @@ -133,6 +133,7 @@ func (o *OptimizelyClient) GetEnabledFeatures(userContext entities.UserContext) return enabledFeatures, nil } +// Track take and event key with event tags and if the event is part of the config, send to events backend. func (o *OptimizelyClient) Track(eventKey string, userContext entities.UserContext, eventTags map[string]interface{}) error { if !o.isValid { errorMessage := "Optimizely instance is not valid. Failing GetEnabledFeatures." From e4de9b938336bf0827d2ac393a74221e5cdf3efc Mon Sep 17 00:00:00 2001 From: Thomas Zurkan Date: Fri, 23 Aug 2019 15:45:42 -0700 Subject: [PATCH 10/10] name return parameter for deferred recovery --- optimizely/client/client.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/optimizely/client/client.go b/optimizely/client/client.go index 3cff3b9c1..611295c42 100644 --- a/optimizely/client/client.go +++ b/optimizely/client/client.go @@ -134,10 +134,10 @@ func (o *OptimizelyClient) GetEnabledFeatures(userContext entities.UserContext) } // Track take and event key with event tags and if the event is part of the config, send to events backend. -func (o *OptimizelyClient) Track(eventKey string, userContext entities.UserContext, eventTags map[string]interface{}) error { +func (o *OptimizelyClient) Track(eventKey string, userContext entities.UserContext, eventTags map[string]interface{}) (err error) { if !o.isValid { errorMessage := "Optimizely instance is not valid. Failing GetEnabledFeatures." - err := errors.New(errorMessage) + err = errors.New(errorMessage) logger.Error(errorMessage, err) return err } @@ -145,20 +145,20 @@ func (o *OptimizelyClient) Track(eventKey string, userContext entities.UserConte defer func() { if r := recover(); r != nil { errorMessage := fmt.Sprintf(`Optimizely SDK is panicking with the error "%s"`, string(debug.Stack())) - err := errors.New(errorMessage) + err = errors.New(errorMessage) logger.Error(errorMessage, err) } }() - configEvent, err := o.configManager.GetConfig().GetEventByKey(eventKey) + configEvent, eventError := o.configManager.GetConfig().GetEventByKey(eventKey) - if err == nil { + if eventError == nil { userEvent := event.CreateConversionUserEvent(o.configManager.GetConfig(), configEvent, userContext, eventTags) o.eventProcessor.ProcessEvent(userEvent) } else { errorMessage := fmt.Sprintf(`Optimizely SDK track: error getting event with key "%s"`, eventKey) - logger.Error(errorMessage, err) - return err + logger.Error(errorMessage, eventError) + return eventError } return nil