Skip to content

Commit d604829

Browse files
author
Michael Ng
authored
refac(client): Revise how the client handles and returns errors. (#151)
1 parent 37d1916 commit d604829

File tree

3 files changed

+85
-61
lines changed

3 files changed

+85
-61
lines changed

.travis.yml

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ install:
66
- eval "$(gimme)"
77
stages:
88
- 'Lint'
9-
- 'Integration tests'
9+
# - 'Integration tests'
1010
- 'Unit test'
1111
jobs:
1212
include:
@@ -16,19 +16,19 @@ jobs:
1616
- curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(go env GOPATH)/bin v1.19.0
1717
script:
1818
- $GOPATH/bin/golangci-lint run --out-format=tab --tests=false pkg/...
19-
- stage: 'Integration Tests'
20-
merge_mode: replace
21-
env: SDK=go SDK_BRANCH=$TRAVIS_PULL_REQUEST_BRANCH
22-
cache: false
23-
language: minimal
24-
install: skip
25-
before_script:
26-
- mkdir $HOME/travisci-tools && pushd $HOME/travisci-tools && git init && git pull https://$CI_USER_TOKEN@github.com/optimizely/travisci-tools.git && popd
27-
script:
28-
# TODO: Remove sohail/gosdkonly branch specification here, after
29-
# we can run FSC tests on master: https://optimizely.atlassian.net/browse/OASIS-5425
30-
- $HOME/travisci-tools/trigger-script-with-status-update.sh sohail/gosdkonly
31-
after_success: travis_terminate 0
19+
# - stage: 'Integration Tests'
20+
# merge_mode: replace
21+
# env: SDK=go SDK_BRANCH=$TRAVIS_PULL_REQUEST_BRANCH
22+
# cache: false
23+
# language: minimal
24+
# install: skip
25+
# before_script:
26+
# - mkdir $HOME/travisci-tools && pushd $HOME/travisci-tools && git init && git pull https://$CI_USER_TOKEN@github.com/optimizely/travisci-tools.git && popd
27+
# script:
28+
# # TODO: Remove sohail/gosdkonly branch specification here, after
29+
# # we can run FSC tests on master: https://optimizely.atlassian.net/browse/OASIS-5425
30+
# - $HOME/travisci-tools/trigger-script-with-status-update.sh sohail/gosdkonly
31+
# after_success: travis_terminate 0
3232
- &test
3333
stage: 'Unit test'
3434
env: GIMME_GO_VERSION=master GIMME_OS=linux GIMME_ARCH=amd64

pkg/client/client.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -320,9 +320,9 @@ func (o *OptimizelyClient) Track(eventKey string, userContext entities.UserConte
320320
configEvent, e := projectConfig.GetEventByKey(eventKey)
321321

322322
if e != nil {
323-
errorMessage := fmt.Sprintf(`optimizely SDK track: error getting event with key "%s"`, eventKey)
324-
logger.Error(errorMessage, e)
325-
return e
323+
errorMessage := fmt.Sprintf(`Unable to get event for key "%s": %s`, eventKey, e)
324+
logger.Warning(errorMessage)
325+
return nil
326326
}
327327

328328
userEvent := event.CreateConversionUserEvent(projectConfig, configEvent, userContext, eventTags)
@@ -359,8 +359,8 @@ func (o *OptimizelyClient) getFeatureDecision(featureKey string, userContext ent
359359

360360
feature, e := projectConfig.GetFeatureByKey(featureKey)
361361
if e != nil {
362-
logger.Error("Error calling getFeatureDecision", e)
363-
return decisionContext, featureDecision, e
362+
logger.Warning(fmt.Sprintf(`Could not get feature for key "%s": %s`, featureKey, e))
363+
return decisionContext, featureDecision, nil
364364
}
365365

366366
decisionContext = decision.FeatureDecisionContext{
@@ -370,11 +370,11 @@ func (o *OptimizelyClient) getFeatureDecision(featureKey string, userContext ent
370370

371371
featureDecision, err = o.DecisionService.GetFeatureDecision(decisionContext, userContext)
372372
if err != nil {
373-
logger.Warning("error making a decision")
374-
return decisionContext, featureDecision, err
373+
logger.Warning(fmt.Sprintf(`Received error while making a decision for feature "%s": %s`, featureKey, err))
374+
return decisionContext, featureDecision, nil
375375
}
376376

377-
return decisionContext, featureDecision, err
377+
return decisionContext, featureDecision, nil
378378
}
379379

380380
func (o *OptimizelyClient) getExperimentDecision(experimentKey string, userContext entities.UserContext) (decisionContext decision.ExperimentDecisionContext, experimentDecision decision.ExperimentDecision, err error) {
@@ -390,8 +390,8 @@ func (o *OptimizelyClient) getExperimentDecision(experimentKey string, userConte
390390

391391
experiment, e := projectConfig.GetExperimentByKey(experimentKey)
392392
if e != nil {
393-
logger.Error("Error calling getExperimentDecision", e)
394-
return decisionContext, experimentDecision, e
393+
logger.Warning(fmt.Sprintf(`Could not get experiment for key "%s": %s`, experimentKey, e))
394+
return decisionContext, experimentDecision, nil
395395
}
396396

397397
decisionContext = decision.ExperimentDecisionContext{
@@ -401,8 +401,8 @@ func (o *OptimizelyClient) getExperimentDecision(experimentKey string, userConte
401401

402402
experimentDecision, err = o.DecisionService.GetExperimentDecision(decisionContext, userContext)
403403
if err != nil {
404-
logger.Warning(fmt.Sprintf(`error making a decision for experiment "%s"`, experimentKey))
405-
return decisionContext, experimentDecision, err
404+
logger.Warning(fmt.Sprintf(`Received error while making a decision for experiment "%s": %s`, experimentKey, err))
405+
return decisionContext, experimentDecision, nil
406406
}
407407

408408
if experimentDecision.Variation != nil {

pkg/client/client_test.go

Lines changed: 59 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ func TestTrack(t *testing.T) {
125125

126126
}
127127

128-
func TestTrackFail(t *testing.T) {
128+
func TestTrackFailEventNotFound(t *testing.T) {
129129
mockProcessor := &MockProcessor{}
130130
mockDecisionService := new(MockDecisionService)
131131

@@ -137,7 +137,7 @@ func TestTrackFail(t *testing.T) {
137137

138138
err := client.Track("bob", entities.UserContext{ID: "1212121", Attributes: map[string]interface{}{}}, map[string]interface{}{})
139139

140-
assert.Error(t, err)
140+
assert.NoError(t, err)
141141
assert.True(t, len(mockProcessor.Events) == 0)
142142

143143
}
@@ -158,24 +158,6 @@ func TestTrackPanics(t *testing.T) {
158158
assert.True(t, len(mockProcessor.Events) == 0)
159159

160160
}
161-
func TestGetEnabledFeaturesErrorCases(t *testing.T) {
162-
testUserContext := entities.UserContext{ID: "test_user_1"}
163-
164-
// Test instance invalid
165-
mockConfigManager := new(MockProjectConfigManager)
166-
mockConfigManager.On("GetConfig").Return(nil, errors.New("no project config available"))
167-
mockDecisionService := new(MockDecisionService)
168-
169-
client := OptimizelyClient{
170-
ConfigManager: mockConfigManager,
171-
DecisionService: mockDecisionService,
172-
}
173-
result, err := client.GetEnabledFeatures(testUserContext)
174-
assert.Error(t, err)
175-
assert.Empty(t, result)
176-
mockConfigManager.AssertNotCalled(t, "GetFeatureByKey")
177-
mockDecisionService.AssertNotCalled(t, "GetFeatureDecision")
178-
}
179161

180162
func TestGetEnabledFeaturesPanic(t *testing.T) {
181163
testUserContext := entities.UserContext{ID: "test_user_1"}
@@ -1367,15 +1349,16 @@ func TestGetFeatureDecisionErrFeatureDecision(t *testing.T) {
13671349

13681350
expectedFeatureDecision := getTestFeatureDecision(testExperiment, testVariation, true)
13691351
mockDecisionService := new(MockDecisionService)
1370-
mockDecisionService.On("GetFeatureDecision", testDecisionContext, testUserContext).Return(expectedFeatureDecision, errors.New("error feaure"))
1352+
mockDecisionService.On("GetFeatureDecision", testDecisionContext, testUserContext).Return(expectedFeatureDecision, errors.New("error feature"))
13711353

13721354
client := OptimizelyClient{
13731355
ConfigManager: mockConfigManager,
13741356
DecisionService: mockDecisionService,
13751357
}
13761358

1377-
_, _, err := client.getFeatureDecision(testFeatureKey, testUserContext)
1378-
assert.Error(t, err)
1359+
_, decision, err := client.getFeatureDecision(testFeatureKey, testUserContext)
1360+
assert.Equal(t, expectedFeatureDecision, decision)
1361+
assert.NoError(t, err)
13791362
}
13801363

13811364
func TestGetAllFeatureVariables(t *testing.T) {
@@ -1466,9 +1449,11 @@ func TestGetAllFeatureVariablesWithError(t *testing.T) {
14661449
}
14671450

14681451
enabled, variationMap, err := client.GetAllFeatureVariables(testFeatureKey, testUserContext)
1469-
assert.False(t, enabled)
1470-
assert.Equal(t, "", variationMap[testVariableKey])
1471-
assert.NotNil(t, err)
1452+
1453+
// if we have a decision, but also a non-fatal error, we should return the decision
1454+
assert.True(t, enabled)
1455+
assert.Equal(t, testVariableValue, variationMap[testVariableKey])
1456+
assert.NoError(t, err)
14721457
}
14731458

14741459
// Helper Methods
@@ -1523,7 +1508,7 @@ func (s *ClientTestSuiteAB) TestActivate() {
15231508
testUserContext := entities.UserContext{ID: "test_user_1"}
15241509
testExperiment := makeTestExperiment("test_exp_1")
15251510
s.mockConfig.On("GetExperimentByKey", "test_exp_1").Return(testExperiment, nil)
1526-
s.mockConfig.On("GetExperimentByKey", "test_exp_2").Return(testExperiment, errors.New(""))
1511+
s.mockConfig.On("GetExperimentByKey", "test_exp_2").Return(testExperiment, errors.New("Experiment not found"))
15271512

15281513
testDecisionContext := decision.ExperimentDecisionContext{
15291514
Experiment: &testExperiment,
@@ -1547,8 +1532,9 @@ func (s *ClientTestSuiteAB) TestActivate() {
15471532
s.NoError(err1)
15481533
s.Equal(expectedVariation.Key, variationKey1)
15491534

1535+
// should not return error for experiment not found.
15501536
variationKey2, err2 := testClient.Activate("test_exp_2", testUserContext)
1551-
s.Error(err2)
1537+
s.NoError(err2)
15521538
s.Equal("", variationKey2)
15531539

15541540
s.mockConfig.AssertExpectations(s.T())
@@ -1569,6 +1555,22 @@ func (s *ClientTestSuiteAB) TestActivatePanics() {
15691555
s.EqualError(err, "I'm panicking")
15701556
}
15711557

1558+
func (s *ClientTestSuiteAB) TestActivateInvalidConfig() {
1559+
testUserContext := entities.UserContext{}
1560+
1561+
mockConfigManager := new(MockProjectConfigManager)
1562+
expectedError := errors.New("no project config available")
1563+
mockConfigManager.On("GetConfig").Return(s.mockConfig, expectedError)
1564+
testClient := OptimizelyClient{
1565+
ConfigManager: mockConfigManager,
1566+
}
1567+
1568+
variationKey, err := testClient.Activate("test_exp_1", testUserContext)
1569+
s.Equal("", variationKey)
1570+
s.Error(err)
1571+
s.Equal(expectedError, err)
1572+
}
1573+
15721574
func (s *ClientTestSuiteAB) TestGetVariation() {
15731575
testUserContext := entities.UserContext{ID: "test_user_1"}
15741576
testExperiment := makeTestExperiment("test_exp_1")
@@ -1598,7 +1600,7 @@ func (s *ClientTestSuiteAB) TestGetVariation() {
15981600
s.mockEventProcessor.AssertNotCalled(s.T(), "ProcessEvent", mock.AnythingOfType("event.UserEvent"))
15991601
}
16001602

1601-
func (s *ClientTestSuiteAB) TestGetVariationWithError() {
1603+
func (s *ClientTestSuiteAB) TestGetVariationWithDecisionError() {
16021604
testUserContext := entities.UserContext{ID: "test_user_1"}
16031605
testExperiment := makeTestExperiment("test_exp_1")
16041606
s.mockConfig.On("GetExperimentByKey", "test_exp_1").Return(testExperiment, nil)
@@ -1620,7 +1622,7 @@ func (s *ClientTestSuiteAB) TestGetVariationWithError() {
16201622
}
16211623

16221624
variationKey, err := testClient.GetVariation("test_exp_1", testUserContext)
1623-
s.Error(err)
1625+
s.NoError(err)
16241626
s.Equal(expectedVariation.Key, variationKey)
16251627
s.mockConfig.AssertExpectations(s.T())
16261628
s.mockDecisionService.AssertExpectations(s.T())
@@ -1714,14 +1716,18 @@ func (s *ClientTestSuiteFM) TestIsFeatureEnabledWithDecisionError() {
17141716
}
17151717

17161718
s.mockDecisionService.On("GetFeatureDecision", testDecisionContext, testUserContext).Return(expectedFeatureDecision, errors.New(""))
1719+
s.mockEventProcessor.On("ProcessEvent", mock.AnythingOfType("event.UserEvent"))
17171720

17181721
client := OptimizelyClient{
17191722
ConfigManager: s.mockConfigManager,
17201723
DecisionService: s.mockDecisionService,
1724+
EventProcessor: s.mockEventProcessor,
17211725
}
1726+
1727+
// should still return the decision because the error is non-fatal
17221728
result, err := client.IsFeatureEnabled(testFeature.Key, testUserContext)
1723-
s.False(result)
1724-
s.NotNil(err)
1729+
s.True(result)
1730+
s.NoError(err)
17251731
s.mockConfig.AssertExpectations(s.T())
17261732
s.mockConfigManager.AssertExpectations(s.T())
17271733
s.mockDecisionService.AssertExpectations(s.T())
@@ -1752,9 +1758,7 @@ func (s *ClientTestSuiteFM) TestIsFeatureEnabledErrorCases() {
17521758
DecisionService: s.mockDecisionService,
17531759
}
17541760
result, err := client.IsFeatureEnabled(testFeatureKey, testUserContext)
1755-
if s.Error(err) {
1756-
s.Equal(expectedError, err)
1757-
}
1761+
s.NoError(err)
17581762
s.False(result)
17591763
s.mockConfigManager.AssertExpectations(s.T())
17601764
s.mockDecisionService.AssertNotCalled(s.T(), "GetDecision")
@@ -1822,6 +1826,26 @@ func (s *ClientTestSuiteFM) TestGetEnabledFeatures() {
18221826
s.mockDecisionService.AssertExpectations(s.T())
18231827
}
18241828

1829+
func (s *ClientTestSuiteFM) TestGetEnabledFeaturesErrorCases() {
1830+
testUserContext := entities.UserContext{ID: "test_user_1"}
1831+
1832+
// Test instance invalid
1833+
expectedError := errors.New("no project config available")
1834+
mockConfigManager := new(MockProjectConfigManager)
1835+
mockConfigManager.On("GetConfig").Return(s.mockConfig, expectedError)
1836+
1837+
client := OptimizelyClient{
1838+
ConfigManager: mockConfigManager,
1839+
DecisionService: s.mockDecisionService,
1840+
}
1841+
result, err := client.GetEnabledFeatures(testUserContext)
1842+
s.Error(err)
1843+
s.Equal(expectedError, err)
1844+
s.Empty(result)
1845+
mockConfigManager.AssertNotCalled(s.T(), "GetFeatureByKey")
1846+
s.mockDecisionService.AssertNotCalled(s.T(), "GetFeatureDecision")
1847+
}
1848+
18251849
func TestClose(t *testing.T) {
18261850
mockProcessor := &MockProcessor{}
18271851
mockDecisionService := new(MockDecisionService)

0 commit comments

Comments
 (0)