Skip to content

[FSSDK-11649] Fix FSC failed tests for CMAB #411

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1199,6 +1199,9 @@ func (o *OptimizelyClient) getAllOptions(options *decide.Options) decide.Options
ExcludeVariables: o.defaultDecideOptions.ExcludeVariables || options.ExcludeVariables,
IgnoreUserProfileService: o.defaultDecideOptions.IgnoreUserProfileService || options.IgnoreUserProfileService,
IncludeReasons: o.defaultDecideOptions.IncludeReasons || options.IncludeReasons,
IgnoreCMABCache: o.defaultDecideOptions.IgnoreCMABCache || options.IgnoreCMABCache,
ResetCMABCache: o.defaultDecideOptions.ResetCMABCache || options.ResetCMABCache,
InvalidateUserCMABCache: o.defaultDecideOptions.InvalidateUserCMABCache || options.InvalidateUserCMABCache,
}
}

Expand Down Expand Up @@ -1252,5 +1255,14 @@ func isNil(v interface{}) bool {
func (o *OptimizelyClient) handleDecisionServiceError(err error, key string, userContext OptimizelyUserContext) OptimizelyDecision {
o.logger.Warning(fmt.Sprintf(`Received error while making a decision for feature %q: %s`, key, err))

return NewErrorDecision(key, userContext, err)
// Return the error decision with the correct format for decision fields
return OptimizelyDecision{
FlagKey: key,
UserContext: userContext,
VariationKey: "",
RuleKey: "",
Enabled: false,
Variables: optimizelyjson.NewOptimizelyJSONfromMap(map[string]interface{}{}),
Reasons: []string{err.Error()},
}
}
30 changes: 30 additions & 0 deletions pkg/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3186,6 +3186,36 @@ func (s *ClientTestSuiteTrackNotification) TestRemoveOnTrackThrowsErrorWhenRemov
mockNotificationCenter.AssertExpectations(s.T())
}

func TestOptimizelyClient_handleDecisionServiceError(t *testing.T) {
// Create the client
client := &OptimizelyClient{
logger: logging.GetLogger("", ""),
}

// Create a CMAB error
cmabErrorMessage := "Failed to fetch CMAB data for experiment exp_1."
cmabError := fmt.Errorf(cmabErrorMessage)

// Create a user context - needs to match the signature expected by handleDecisionServiceError
testUserContext := OptimizelyUserContext{
UserID: "test_user",
Attributes: map[string]interface{}{},
}

// Call the error handler directly
decision := client.handleDecisionServiceError(cmabError, "test_flag", testUserContext)

// Verify the decision is correctly formatted
assert.False(t, decision.Enabled)
assert.Equal(t, "", decision.VariationKey) // Should be empty string, not nil
assert.Equal(t, "", decision.RuleKey) // Should be empty string, not nil
assert.Contains(t, decision.Reasons, cmabErrorMessage)

// Check that reasons contains exactly the expected message
assert.Equal(t, 1, len(decision.Reasons), "Reasons array should have exactly one item")
assert.Equal(t, cmabErrorMessage, decision.Reasons[0], "Error message should be added verbatim")
}

func TestClientTestSuiteAB(t *testing.T) {
suite.Run(t, new(ClientTestSuiteAB))
}
Expand Down
21 changes: 21 additions & 0 deletions pkg/cmab/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/****************************************************************************
* Copyright 2025, Optimizely, Inc. and contributors *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
***************************************************************************/

// Package cmab to define cmab errors//
package cmab

// CmabFetchFailed is the error message format for CMAB fetch failures
const CmabFetchFailed = "failed to fetch CMAB data for experiment %s"
3 changes: 1 addition & 2 deletions pkg/cmab/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,7 @@ func (s *DefaultCmabService) GetDecision(
// Fetch new decision
decision, err := s.fetchDecision(ruleID, userContext.ID, filteredAttributes)
if err != nil {
decision.Reasons = append(reasons, decision.Reasons...)
return decision, fmt.Errorf("CMAB API error: %w", err)
return Decision{Reasons: reasons}, fmt.Errorf("CMAB API error: %w", err)
}

// Cache the decision
Expand Down
5 changes: 2 additions & 3 deletions pkg/decision/experiment_cmab_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,8 @@ func (s *ExperimentCmabService) GetDecision(decisionContext ExperimentDecisionCo
// Get CMAB decision
cmabDecision, err := s.cmabService.GetDecision(projectConfig, userContext, experiment.ID, options)
if err != nil {
message := fmt.Sprintf("Failed to get CMAB decision: %v", err)
decisionReasons.AddInfo(message)
return decision, decisionReasons, fmt.Errorf("failed to get CMAB decision: %w", err)
// Format the error correctly with the experiment key we already have
return decision, decisionReasons, fmt.Errorf(cmab.CmabFetchFailed, experiment.Key)
}

// Find variation by ID
Expand Down
4 changes: 2 additions & 2 deletions pkg/decision/experiment_cmab_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ func (s *ExperimentCmabTestSuite) TestGetDecisionWithCmabServiceError() {

// Mock CMAB service to return error
s.mockCmabService.On("GetDecision", s.mockProjectConfig, s.testUserContext, "cmab_exp_1", s.options).
Return(cmab.Decision{}, errors.New("CMAB service error"))
Return(cmab.Decision{}, errors.New("failed to fetch CMAB data for experiment"))

// Create CMAB service with mocked dependencies (same pattern as TestGetDecisionSuccess)
cmabService := &ExperimentCmabService{
Expand All @@ -320,7 +320,7 @@ func (s *ExperimentCmabTestSuite) TestGetDecisionWithCmabServiceError() {

// Should return the CMAB service error
s.Error(err)
s.Contains(err.Error(), "CMAB service error")
s.Contains(err.Error(), "failed to fetch CMAB data for experiment")
s.Nil(decision.Variation) // No variation when error occurs

s.mockExperimentBucketer.AssertExpectations(s.T())
Expand Down
Loading