Skip to content
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
env: GIMME_GO_VERSION=1.12.x FSC_PATH="/tmp/fsc-repo"
before_script:
- mkdir -p $FSC_PATH
- pushd $FSC_PATH && git init && git fetch --depth=1 https://$CI_USER_TOKEN@github.com/optimizely/fullstack-sdk-compatibility-suite ${FSC_BRANCH:-master} && git checkout FETCH_HEAD && popd
- pushd $FSC_PATH && git init && git fetch --depth=1 https://$CI_USER_TOKEN@github.com/optimizely/fullstack-sdk-compatibility-suite "yasir/go-ignore-fv-cases" && git checkout FETCH_HEAD && popd
install:
- eval "$(gimme)"
script:
Expand Down
2 changes: 2 additions & 0 deletions pkg/config/datafileprojectconfig/mappers/experiment.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ func mapExperiment(rawExperiment datafileEntities.Experiment) entities.Experimen
LayerID: rawExperiment.LayerID,
Key: rawExperiment.Key,
Variations: make(map[string]entities.Variation),
VariationKeyToIDMap: make(map[string]string),
TrafficAllocation: make([]entities.Range, len(rawExperiment.TrafficAllocation)),
AudienceConditionTree: audienceConditionTree,
Whitelist: rawExperiment.ForcedVariations,
Expand All @@ -93,6 +94,7 @@ func mapExperiment(rawExperiment datafileEntities.Experiment) entities.Experimen

for _, variation := range rawExperiment.Variations {
experiment.Variations[variation.ID] = mapVariation(variation)
experiment.VariationKeyToIDMap[variation.Key] = variation.ID
}

for i, allocation := range rawExperiment.TrafficAllocation {
Expand Down
17 changes: 11 additions & 6 deletions pkg/config/datafileprojectconfig/mappers/experiment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ func TestMapExperiments(t *testing.T) {
FeatureEnabled: false,
},
},
VariationKeyToIDMap: map[string]string{
"variation_1": "21111",
"variation_2": "21112",
},
TrafficAllocation: []entities.Range{
{
EntityID: "21111",
Expand Down Expand Up @@ -132,12 +136,13 @@ func TestMapExperimentsWithStringAudienceCondition(t *testing.T) {
experiments, experimentKeyMap := MapExperiments(rawExperiments, experimentGroupMap)
expectedExperiments := map[string]entities.Experiment{
"11111": {
AudienceIds: []string{"31111"},
ID: "11111",
GroupID: "15",
Key: "test_experiment_11111",
Variations: map[string]entities.Variation{},
TrafficAllocation: []entities.Range{},
AudienceIds: []string{"31111"},
ID: "11111",
GroupID: "15",
Key: "test_experiment_11111",
Variations: map[string]entities.Variation{},
VariationKeyToIDMap: map[string]string{},
TrafficAllocation: []entities.Range{},
AudienceConditionTree: &entities.TreeNode{
Operator: "or",
Nodes: []*entities.TreeNode{
Expand Down
4 changes: 2 additions & 2 deletions pkg/config/datafileprojectconfig/mappers/rollout_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ func TestMapRollouts(t *testing.T) {
"21111": entities.Rollout{
ID: "21111",
Experiments: []entities.Experiment{
entities.Experiment{ID: "11111", Key: "exp_11111", Variations: map[string]entities.Variation{}, TrafficAllocation: []entities.Range{}},
entities.Experiment{ID: "11112", Key: "exp_11112", Variations: map[string]entities.Variation{}, TrafficAllocation: []entities.Range{}},
entities.Experiment{ID: "11111", Key: "exp_11111", Variations: map[string]entities.Variation{}, VariationKeyToIDMap: map[string]string{}, TrafficAllocation: []entities.Range{}},
entities.Experiment{ID: "11112", Key: "exp_11112", Variations: map[string]entities.Variation{}, VariationKeyToIDMap: map[string]string{}, TrafficAllocation: []entities.Range{}},
},
},
}
Expand Down
6 changes: 2 additions & 4 deletions pkg/decision/experiment_override_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,8 @@ func (s ExperimentOverrideService) GetDecision(decisionContext ExperimentDecisio
return decision, nil
}

// TODO(Matt): Implement and use a way to access variations by key
for _, variation := range decisionContext.Experiment.Variations {
variation := variation
if variation.Key == variationKey {
if variationID, ok := decisionContext.Experiment.VariationKeyToIDMap[variationKey]; ok {
if variation, ok := decisionContext.Experiment.Variations[variationID]; ok {
decision.Variation = &variation
decision.Reason = reasons.OverrideVariationAssignmentFound
eosLogger.Debug(fmt.Sprintf("Override variation %v found for user %v", variationKey, userContext.ID))
Expand Down
6 changes: 2 additions & 4 deletions pkg/decision/experiment_whitelist_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,8 @@ func (s ExperimentWhitelistService) GetDecision(decisionContext ExperimentDecisi
return decision, nil
}

// TODO(Matt): Add a VariationsByKey map to the Experiment struct, and use it to look up Variation by key
for _, variation := range decisionContext.Experiment.Variations {
variation := variation
if variation.Key == variationKey {
if id, ok := decisionContext.Experiment.VariationKeyToIDMap[variationKey]; ok {
if variation, ok := decisionContext.Experiment.Variations[id]; ok {
decision.Reason = reasons.WhitelistVariationAssignmentFound
decision.Variation = &variation
return decision, nil
Expand Down
23 changes: 23 additions & 0 deletions pkg/decision/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ var testExp1111 = entities.Experiment{
Variations: map[string]entities.Variation{
"2222": testExp1111Var2222,
},
VariationKeyToIDMap: map[string]string{
"2222": "2222",
},
TrafficAllocation: []entities.Range{
entities.Range{EntityID: "2222", EndOfRange: 10000},
},
Expand Down Expand Up @@ -140,6 +143,9 @@ var testExp1112 = entities.Experiment{
Variations: map[string]entities.Variation{
"2222": testExp1111Var2222,
},
VariationKeyToIDMap: map[string]string{
"2222": "2222",
},
TrafficAllocation: []entities.Range{
entities.Range{EntityID: "2222", EndOfRange: 10000},
},
Expand Down Expand Up @@ -172,6 +178,10 @@ var testExp1113 = entities.Experiment{
"2223": testExp1113Var2223,
"2224": testExp1113Var2224,
},
VariationKeyToIDMap: map[string]string{
"2223": "2223",
"2224": "2224",
},
TrafficAllocation: []entities.Range{
entities.Range{EntityID: "2223", EndOfRange: 5000},
entities.Range{EntityID: "2224", EndOfRange: 10000},
Expand All @@ -190,6 +200,10 @@ var testExp1114 = entities.Experiment{
"2225": testExp1114Var2225,
"2226": testExp1114Var2226,
},
VariationKeyToIDMap: map[string]string{
"2225": "2225",
"2226": "2226",
},
TrafficAllocation: []entities.Range{
entities.Range{EntityID: "2225", EndOfRange: 5000},
entities.Range{EntityID: "2226", EndOfRange: 10000},
Expand All @@ -214,6 +228,9 @@ var testExp1115 = entities.Experiment{
Variations: map[string]entities.Variation{
"2227": testExp1115Var2227,
},
VariationKeyToIDMap: map[string]string{
"2227": "2227",
},
TrafficAllocation: []entities.Range{
entities.Range{EntityID: "2227", EndOfRange: 5000},
},
Expand All @@ -239,6 +256,9 @@ var testTargetedExp1116 = entities.Experiment{
Variations: map[string]entities.Variation{
"2228": testTargetedExp1116Var2228,
},
VariationKeyToIDMap: map[string]string{
"2228": "2228",
},
TrafficAllocation: []entities.Range{
entities.Range{EntityID: "2228", EndOfRange: 10000},
},
Expand All @@ -254,6 +274,9 @@ var testExpWhitelist = entities.Experiment{
Variations: map[string]entities.Variation{
"2229": testExpWhitelistVar2229,
},
VariationKeyToIDMap: map[string]string{
"var_2229": "2229",
},
TrafficAllocation: []entities.Range{
entities.Range{EntityID: "2229", EndOfRange: 10000},
},
Expand Down
1 change: 1 addition & 0 deletions pkg/entities/experiment.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Experiment struct {
LayerID string
Key string
Variations map[string]Variation // keyed by variation ID
VariationKeyToIDMap map[string]string
TrafficAllocation []Range
GroupID string
AudienceConditionTree *TreeNode
Expand Down
2 changes: 1 addition & 1 deletion scripts/run-fsc-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,5 @@ mkdir -p $GO_FEATUREFILES_PATH
cp -r $FEATURE_FILES_PATH $GO_FEATUREFILES_PATH

export DATAFILES_DIR="$DATAFILES_PATH"
go test -v $(pwd)/tests/integration --godog.tags="$TAG_FILTER_EXPRESSION" --godog.f=progress
go test -v $(pwd)/tests/integration --godog.tags="~@DATAFILE_MANAGER&&~@INPUT_VALIDATION&&~@EVENT_BATCHING&&~@NO_EASY_EVENT_TRACKING&&~@OASIS-3654&&~@TARGETED_ROLLOUT&&~@EXPERIMENT_STATUS&&@~UNSUPPORTED_DATAFILE_VERSION&&@~TRACK_LISTENER&&@~ACTIVATE_LISTENER&&~@OPTIMIZELY_CONFIG&&~@LOG_EVENT_LISTENER" --godog.f=progress
echo "Ready for testing."
4 changes: 4 additions & 0 deletions tests/integration/models/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ const (
Activate SDKAPI = "activate"
// Track - the api type is Track
Track SDKAPI = "track"
// SetForcedVariation - the api type is SetForcedVariation
SetForcedVariation SDKAPI = "set_forced_variation"
// GetForcedVariation - the api type is GetForcedVariation
GetForcedVariation SDKAPI = "get_forced_variation"
)

// KeyListenerCalled - Key for listener called
Expand Down
24 changes: 24 additions & 0 deletions tests/integration/models/forced_variation_params.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/****************************************************************************
* Copyright 2019, 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 models

// ForcedVariationRequestParams represents params required for Get and Set ForcedVariation API
type ForcedVariationRequestParams struct {
ExperimentKey string `yaml:"experiment_key"`
UserID string `yaml:"user_id"`
VariationKey string `yaml:"forced_variation_key,omitempty"`
}
5 changes: 2 additions & 3 deletions tests/integration/optlyplugins/userprofileservice/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,9 @@ func CreateUserProfileService(config pkg.ProjectConfig, apiOptions models.APIOpt
for experimentKey, variationKey := range bucketMap {
if experiment, err := config.GetExperimentByKey(experimentKey); err == nil {
decisionKey := decision.NewUserDecisionKey(experiment.ID)
for _, variation := range experiment.Variations {
if variation.Key == variationKey {
if variationID, ok := experiment.VariationKeyToIDMap[variationKey]; ok {
if variation, ok := experiment.Variations[variationID]; ok {
profile.ExperimentBucketMap[decisionKey] = variation.ID
break
}
}
}
Expand Down
58 changes: 54 additions & 4 deletions tests/integration/support/client_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"os"
"path"
"path/filepath"
"strconv"

"github.com/optimizely/go-sdk/pkg/client"
"github.com/optimizely/go-sdk/pkg/config"
Expand All @@ -34,15 +35,19 @@ import (
"gopkg.in/yaml.v3"
)

// Map to hold clientwrapper instances against scenarioID
// Cached instance of optly wrapper
var clientInstance *ClientWrapper

// Since notificationManager is mapped against sdkKey, we need a unique sdkKey for every scenario
var sdkKey int

// ClientWrapper - wrapper around the optimizely client that keeps track of various custom components used with the client
type ClientWrapper struct {
Client *client.OptimizelyClient
DecisionService decision.Service
EventDispatcher event.Dispatcher
UserProfileService decision.UserProfileService
OverrideStore decision.ExperimentOverrideStore
}

// DeleteInstance deletes cached instance of optly wrapper
Expand All @@ -57,6 +62,7 @@ func GetInstance(apiOptions models.APIOptions) *ClientWrapper {
return clientInstance
}

sdkKey++
datafileDir := os.Getenv("DATAFILES_DIR")
datafile, err := ioutil.ReadFile(filepath.Clean(path.Join(datafileDir, apiOptions.DatafileName)))
if err != nil {
Expand Down Expand Up @@ -84,18 +90,23 @@ func GetInstance(apiOptions models.APIOptions) *ClientWrapper {
log.Fatal(err)
}

overrideStore := decision.NewMapExperimentOverridesStore()
userProfileService := userprofileservice.CreateUserProfileService(config, apiOptions)
compositeExperimentService := decision.NewCompositeExperimentService(
decision.WithUserProfileService(userProfileService),
decision.WithOverrideStore(overrideStore),
)

// @TODO: Add sdkKey dynamically once event-batching support is implemented
compositeService := *decision.NewCompositeService("", decision.WithCompositeExperimentService(compositeExperimentService))

compositeService := *decision.NewCompositeService(strconv.Itoa(sdkKey), decision.WithCompositeExperimentService(compositeExperimentService))
decisionService := &optlyplugins.TestCompositeService{CompositeService: compositeService}

client, err := optimizelyFactory.Client(
client.WithConfigManager(configManager),
client.WithDecisionService(decisionService),
client.WithEventProcessor(eventProcessor))
client.WithEventProcessor(eventProcessor),
)
if err != nil {
log.Fatal(err)
}
Expand All @@ -105,14 +116,16 @@ func GetInstance(apiOptions models.APIOptions) *ClientWrapper {
DecisionService: decisionService,
EventDispatcher: eventProcessor.EventDispatcher,
UserProfileService: userProfileService,
OverrideStore: overrideStore,
}
clientInstance.DecisionService.(*optlyplugins.TestCompositeService).AddListeners(apiOptions.Listeners)

return clientInstance
}

// InvokeAPI processes request with arguments
func (c *ClientWrapper) InvokeAPI(request models.APIOptions) (models.APIResponse, error) {

c.DecisionService.(*optlyplugins.TestCompositeService).AddListeners(request.Listeners)
var response models.APIResponse
var err error

Expand Down Expand Up @@ -147,6 +160,12 @@ func (c *ClientWrapper) InvokeAPI(request models.APIOptions) (models.APIResponse
case models.Track:
response, err = c.track(request)
break
case models.SetForcedVariation:
response, err = c.setForcedVariation(request)
break
case models.GetForcedVariation:
response, err = c.getForcedVariation(request)
break
default:
break
}
Expand Down Expand Up @@ -328,3 +347,34 @@ func (c *ClientWrapper) track(request models.APIOptions) (models.APIResponse, er
response.Result = "NULL"
return response, err
}

func (c *ClientWrapper) setForcedVariation(request models.APIOptions) (models.APIResponse, error) {
var params models.ForcedVariationRequestParams
var response models.APIResponse
err := yaml.Unmarshal([]byte(request.Arguments), &params)
response.Result = "NULL"
if err == nil {
// For removeForcedVariation cases
if params.VariationKey == "" {
c.OverrideStore.(*decision.MapExperimentOverridesStore).RemoveVariation(decision.ExperimentOverrideKey{ExperimentKey: params.ExperimentKey, UserID: params.UserID})
} else {
c.OverrideStore.(*decision.MapExperimentOverridesStore).SetVariation(decision.ExperimentOverrideKey{ExperimentKey: params.ExperimentKey, UserID: params.UserID}, params.VariationKey)
response.Result = params.VariationKey
}
}
return response, err
}

func (c *ClientWrapper) getForcedVariation(request models.APIOptions) (models.APIResponse, error) {
var params models.ForcedVariationRequestParams
var response models.APIResponse
err := yaml.Unmarshal([]byte(request.Arguments), &params)
response.Result = "NULL"
if err == nil {
variation, success := c.OverrideStore.(*decision.MapExperimentOverridesStore).GetVariation(decision.ExperimentOverrideKey{ExperimentKey: params.ExperimentKey, UserID: params.UserID})
if success {
response.Result = variation
}
}
return response, err
}
Loading