From 7e473880403ce444dbd7d8c20a2c9eeaa11dd191 Mon Sep 17 00:00:00 2001 From: Yasir Ali Date: Wed, 27 Nov 2019 17:37:42 +0500 Subject: [PATCH 01/16] Implementations. --- pkg/config/polling_manager.go | 24 +++- tests/integration/main_test.go | 4 +- tests/integration/models/api_options.go | 2 + .../models/datafilemanager_configuration.go | 27 +++++ .../optlyplugins/test_config_manager.go | 114 ++++++++++++++++++ tests/integration/optlyplugins/utils.go | 85 +++++++++++++ tests/integration/support/client_wrapper.go | 85 +++++++------ tests/integration/support/steps.go | 28 ++++- 8 files changed, 321 insertions(+), 48 deletions(-) create mode 100644 tests/integration/models/datafilemanager_configuration.go create mode 100644 tests/integration/optlyplugins/test_config_manager.go create mode 100644 tests/integration/optlyplugins/utils.go diff --git a/pkg/config/polling_manager.go b/pkg/config/polling_manager.go index 968a7303a..81697b2ba 100644 --- a/pkg/config/polling_manager.go +++ b/pkg/config/polling_manager.go @@ -47,12 +47,13 @@ var cmLogger = logging.GetLogger("PollingConfigManager") // PollingProjectConfigManager maintains a dynamic copy of the project config type PollingProjectConfigManager struct { - requester utils.Requester - pollingInterval time.Duration - notificationCenter notification.Center - initDatafile []byte - lastModified string - datafileURLTemplate string + requester utils.Requester + pollingInterval time.Duration + notificationCenter notification.Center + initDatafile []byte + lastModified string + datafileURLTemplate string + projectConfigUpdateHandlers []func(notification.ProjectConfigUpdateNotification) configLock sync.RWMutex err error @@ -83,6 +84,13 @@ func PollingInterval(interval time.Duration) OptionFunc { } } +// ProjectConfigUpdateNotificationHandlers is an optional function, sets passed notification handlers +func ProjectConfigUpdateNotificationHandlers(handlers ...func(notification.ProjectConfigUpdateNotification)) OptionFunc { + return func(p *PollingProjectConfigManager) { + p.projectConfigUpdateHandlers = handlers + } +} + // InitialDatafile is an optional function, sets a passed datafile func InitialDatafile(datafile []byte) OptionFunc { return func(p *PollingProjectConfigManager) { @@ -196,6 +204,10 @@ func NewPollingProjectConfigManager(sdkKey string, pollingMangerOptions ...Optio opt(&pollingProjectConfigManager) } + for _, handler := range pollingProjectConfigManager.projectConfigUpdateHandlers { + pollingProjectConfigManager.OnProjectConfigUpdate(handler) + } + initDatafile := pollingProjectConfigManager.initDatafile pollingProjectConfigManager.SyncConfig(sdkKey, initDatafile) // initial poll return &pollingProjectConfigManager diff --git a/tests/integration/main_test.go b/tests/integration/main_test.go index cb8d7d03e..d293b7d96 100644 --- a/tests/integration/main_test.go +++ b/tests/integration/main_test.go @@ -26,7 +26,7 @@ import ( "github.com/optimizely/go-sdk/tests/integration/support" ) -var opt = godog.Options{Output: colors.Colored(os.Stdout)} +var opt = godog.Options{Output: colors.Colored(os.Stdout), Tags: "@DATAFILE_MANAGER"} func init() { godog.BindFlags("godog.", flag.CommandLine, &opt) @@ -57,6 +57,8 @@ func FeatureContext(s *godog.Suite) { s.Step(`^(\d+) "([^"]*)" listener is added$`, context.ListenerIsAdded) s.Step(`^the User Profile Service is "([^"]*)"$`, context.TheUserProfileServiceIs) s.Step(`^user "([^"]*)" has mapping "([^"]*)": "([^"]*)" in User Profile Service$`, context.UserHasMappingInUserProfileService) + s.Step(`^a datafile manager with the configuration$`, context.DatafileManagerConfigurationIs) + s.Step(`^in the response, the "([^"]*)" listener was called (\d+) times$`, context.TheListenerWasCalledNTimes) s.Step(`^([^\\\"]*) is called with arguments$`, context.IsCalledWithArguments) s.Step(`^the result should be (?:string )?"([^"]*)"$`, context.TheResultShouldBeString) s.Step(`^the result should be (?:integer )?(\d+)$`, context.TheResultShouldBeInteger) diff --git a/tests/integration/models/api_options.go b/tests/integration/models/api_options.go index bd3c0c0eb..435816aa4 100644 --- a/tests/integration/models/api_options.go +++ b/tests/integration/models/api_options.go @@ -21,7 +21,9 @@ type APIOptions struct { DatafileName string APIName string Arguments string + DFMConfiguration DataFileManagerConfiguration Listeners map[string]int UserProfileServiceType string UPSMapping map[string]map[string]string + ScenarioID string } diff --git a/tests/integration/models/datafilemanager_configuration.go b/tests/integration/models/datafilemanager_configuration.go new file mode 100644 index 000000000..95058fd9b --- /dev/null +++ b/tests/integration/models/datafilemanager_configuration.go @@ -0,0 +1,27 @@ +/**************************************************************************** + * 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 + +// DataFileManagerConfiguration represents a datafile manager configuration +type DataFileManagerConfiguration struct { + SDKKey string `yaml:"sdk_key"` + Mode string `yaml:"mode,omitempty"` + Revision *int `yaml:"revision,omitempty"` + DatafileCondition string `yaml:"datafile_condition,omitempty"` + UpdateInterval *int `yaml:"update_interval,omitempty"` + Timeout *int `yaml:"timeout,omitempty"` +} diff --git a/tests/integration/optlyplugins/test_config_manager.go b/tests/integration/optlyplugins/test_config_manager.go new file mode 100644 index 000000000..9b23951f0 --- /dev/null +++ b/tests/integration/optlyplugins/test_config_manager.go @@ -0,0 +1,114 @@ +/**************************************************************************** + * 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 optlyplugins + +import ( + "time" + + "github.com/optimizely/go-sdk/pkg" + "github.com/optimizely/go-sdk/pkg/notification" + "github.com/optimizely/go-sdk/tests/integration/models" +) + +// DefaultInitializationTimeout defines default timeout for datafile sync +const DefaultInitializationTimeout = time.Duration(3000) * time.Millisecond + +// TestConfigManager represents a ProjectConfigManager with custom implementations +type TestConfigManager struct { + pkg.ProjectConfigManager + listenersCalled []notification.ProjectConfigUpdateNotification +} + +// CreateListenerCallbacks - Creates Notification Listeners +func (c *TestConfigManager) CreateListenerCallbacks(apiOptions models.APIOptions) (listeners []func(notification notification.ProjectConfigUpdateNotification)) { + + projectConfigUpdateCallback := func(notification notification.ProjectConfigUpdateNotification) { + c.listenersCalled = append(c.listenersCalled, notification) + } + + for listenerType, count := range apiOptions.Listeners { + for i := 1; i <= count; i++ { + switch listenerType { + case "Config-update": + listeners = append(listeners, projectConfigUpdateCallback) + break + default: + break + } + } + } + return listeners +} + +// Verify - Verifies configuration tests +func (c *TestConfigManager) Verify(apiOptions models.APIOptions) { + timeout := DefaultInitializationTimeout + if apiOptions.DFMConfiguration.Timeout != nil { + timeout = time.Duration(*(apiOptions.DFMConfiguration.Timeout)) * time.Millisecond + } + + start := time.Now() + switch apiOptions.DFMConfiguration.Mode { + case "wait_for_on_ready": + for { + t := time.Now() + elapsed := t.Sub(start) + if elapsed >= timeout { + break + } + // Check if projectconfig is ready + _, err := c.GetConfig() + if err == nil { + break + } + } + break + case "wait_for_config_update": + revision := 0 + if apiOptions.DFMConfiguration.Revision != nil { + revision = *(apiOptions.DFMConfiguration.Revision) + } + for { + t := time.Now() + elapsed := t.Sub(start) + if elapsed >= timeout { + break + } + // this means we want the manager to poll until we get to a specific revision + if revision > 0 { + if revision == len(c.listenersCalled) { + break + } + } else if len(c.listenersCalled) == 1 { + break + } + } + break + default: + break + } +} + +// GetListenersCalled - Returns listeners called +func (c *TestConfigManager) GetListenersCalled() []notification.ProjectConfigUpdateNotification { + listenerCalled := c.listenersCalled + // Since for every scenario, a new sdk instance is created, emptying listenersCalled is required for scenario's + // where multiple requests are executed but no session is to be maintained among them. + // @TODO: Make it optional once event-batching(sessioned) tests are implemented. + c.listenersCalled = nil + return listenerCalled +} diff --git a/tests/integration/optlyplugins/utils.go b/tests/integration/optlyplugins/utils.go new file mode 100644 index 000000000..734963c52 --- /dev/null +++ b/tests/integration/optlyplugins/utils.go @@ -0,0 +1,85 @@ +/**************************************************************************** + * 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 optlyplugins + +import ( + "io/ioutil" + "log" + "os" + "path" + "path/filepath" + "time" + + "github.com/optimizely/go-sdk/pkg/config" + "github.com/optimizely/go-sdk/pkg/utils" + "github.com/optimizely/go-sdk/tests/integration/models" +) + +const localDatafileURLTemplate = "http://localhost:3001/datafiles/%s.json?request_id=" +const defaultPollingInterval = time.Duration(1000) * time.Millisecond + +// CreatePollingConfigManager creates a pollingConfigManager with given configuration +func CreatePollingConfigManager(options models.APIOptions) *TestConfigManager { + var pollingConfigManagerOptions []config.OptionFunc + + // Setting up initial datafile + if options.DatafileName != "" { + datafile, err := GetDatafile(options.DatafileName) + if err != nil { + log.Fatal(err) + } + pollingConfigManagerOptions = append(pollingConfigManagerOptions, config.InitialDatafile(datafile)) + } + // Setting up polling interval + pollingTimeInterval := defaultPollingInterval + if options.DFMConfiguration.UpdateInterval != nil { + pollingTimeInterval = time.Duration((*options.DFMConfiguration.UpdateInterval)) * time.Millisecond + } + pollingConfigManagerOptions = append(pollingConfigManagerOptions, config.PollingInterval(pollingTimeInterval)) + sdkKey := GetSDKKey(options.DFMConfiguration) + urlString := localDatafileURLTemplate + options.ScenarioID + pollingConfigManagerOptions = append(pollingConfigManagerOptions, config.DatafileTemplate(urlString)) + + testConfigManagerInstance := &TestConfigManager{} + pollingConfigManagerOptions = append(pollingConfigManagerOptions, config.ProjectConfigUpdateNotificationHandlers(testConfigManagerInstance.CreateListenerCallbacks(options)...)) + + configManager := config.NewPollingProjectConfigManager( + sdkKey, + pollingConfigManagerOptions..., + ) + testConfigManagerInstance.ProjectConfigManager = configManager + exeCtx := utils.NewCancelableExecutionCtx() + configManager.Start(sdkKey, exeCtx) + testConfigManagerInstance.Verify(options) + + return testConfigManagerInstance +} + +// GetDatafile returns datafile,error for the provided datafileName +func GetDatafile(datafileName string) ([]byte, error) { + datafileDir := os.Getenv("DATAFILES_DIR") + return ioutil.ReadFile(filepath.Clean(path.Join(datafileDir, datafileName))) +} + +// GetSDKKey returns SDKKey for configuration +func GetSDKKey(configuration models.DataFileManagerConfiguration) (sdkKey string) { + sdkKey = configuration.SDKKey + if configuration.DatafileCondition != "" { + sdkKey += "_" + configuration.DatafileCondition + } + return sdkKey +} diff --git a/tests/integration/support/client_wrapper.go b/tests/integration/support/client_wrapper.go index 10f83f5e7..723547e09 100644 --- a/tests/integration/support/client_wrapper.go +++ b/tests/integration/support/client_wrapper.go @@ -17,11 +17,9 @@ package support import ( - "io/ioutil" "log" - "os" - "path" - "path/filepath" + + "github.com/optimizely/go-sdk/pkg" "github.com/optimizely/go-sdk/pkg/client" "github.com/optimizely/go-sdk/pkg/config" @@ -39,10 +37,11 @@ var clientInstance *ClientWrapper // 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 + Client *client.OptimizelyClient + DecisionService decision.Service + EventDispatcher event.Dispatcher + UserProfileService decision.UserProfileService + PollingConfigManager pkg.ProjectConfigManager } // DeleteInstance deletes cached instance of optly wrapper @@ -56,56 +55,62 @@ func GetInstance(apiOptions models.APIOptions) *ClientWrapper { if clientInstance != nil { return clientInstance } + optimizelyFactory := &client.OptimizelyFactory{} + factoryClientoptions := []client.OptionFunc{} + compositeExperimentServiceoptions := []decision.CESOptionFunc{} - datafileDir := os.Getenv("DATAFILES_DIR") - datafile, err := ioutil.ReadFile(filepath.Clean(path.Join(datafileDir, apiOptions.DatafileName))) - if err != nil { - log.Fatal(err) - } - configManager, err := config.NewStaticProjectConfigManagerFromPayload(datafile) - if err != nil { - log.Fatal(err) + clientInstance = &ClientWrapper{} + + // Check if DFM configuration was provided + if apiOptions.DFMConfiguration.SDKKey != "" { + clientInstance.PollingConfigManager = optlyplugins.CreatePollingConfigManager(apiOptions) + } else { + datafile, err := optlyplugins.GetDatafile(apiOptions.DatafileName) + if err != nil { + log.Fatal(err) + } + configManager, err := config.NewStaticProjectConfigManagerFromPayload(datafile) + if err != nil { + log.Fatal(err) + } + config, err := configManager.GetConfig() + if err != nil { + log.Fatal(err) + } + optimizelyFactory.Datafile = datafile + clientInstance.PollingConfigManager = &optlyplugins.TestConfigManager{ProjectConfigManager: configManager} + + userProfileService := userprofileservice.CreateUserProfileService(config, apiOptions) + compositeExperimentServiceoptions = append(compositeExperimentServiceoptions, decision.WithUserProfileService(userProfileService)) + clientInstance.UserProfileService = userProfileService } + factoryClientoptions = append(factoryClientoptions, client.WithConfigManager(clientInstance.PollingConfigManager)) eventProcessor := event.NewBatchEventProcessor( event.WithBatchSize(models.EventProcessorDefaultBatchSize), event.WithQueueSize(models.EventProcessorDefaultQueueSize), event.WithFlushInterval(models.EventProcessorDefaultFlushInterval), ) - optimizelyFactory := &client.OptimizelyFactory{ - Datafile: datafile, - } - eventProcessor.EventDispatcher = &optlyplugins.ProxyEventDispatcher{} - - config, err := configManager.GetConfig() - if err != nil { - log.Fatal(err) - } - - userProfileService := userprofileservice.CreateUserProfileService(config, apiOptions) compositeExperimentService := decision.NewCompositeExperimentService( - decision.WithUserProfileService(userProfileService), + compositeExperimentServiceoptions..., ) - // @TODO: Add sdkKey dynamically once event-batching support is implemented - compositeService := *decision.NewCompositeService("", decision.WithCompositeExperimentService(compositeExperimentService)) + + compositeService := *decision.NewCompositeService(optlyplugins.GetSDKKey(apiOptions.DFMConfiguration), decision.WithCompositeExperimentService(compositeExperimentService)) decisionService := &optlyplugins.TestCompositeService{CompositeService: compositeService} - client, err := optimizelyFactory.Client( - client.WithConfigManager(configManager), - client.WithDecisionService(decisionService), - client.WithEventProcessor(eventProcessor)) + factoryClientoptions = append(factoryClientoptions, client.WithDecisionService(decisionService)) + factoryClientoptions = append(factoryClientoptions, client.WithEventProcessor(eventProcessor)) + + client, err := optimizelyFactory.Client(factoryClientoptions...) if err != nil { log.Fatal(err) } - clientInstance = &ClientWrapper{ - Client: client, - DecisionService: decisionService, - EventDispatcher: eventProcessor.EventDispatcher, - UserProfileService: userProfileService, - } + clientInstance.Client = client + clientInstance.DecisionService = decisionService + clientInstance.EventDispatcher = eventProcessor.EventDispatcher return clientInstance } diff --git a/tests/integration/support/steps.go b/tests/integration/support/steps.go index db0e0017a..20bee4243 100644 --- a/tests/integration/support/steps.go +++ b/tests/integration/support/steps.go @@ -23,6 +23,7 @@ import ( "strings" "github.com/optimizely/go-sdk/pkg/decision" + "github.com/optimizely/go-sdk/pkg/notification" "github.com/DATA-DOG/godog/gherkin" "github.com/google/uuid" @@ -78,6 +79,18 @@ func (c *ScenarioCtx) UserHasMappingInUserProfileService(userID, experimentKey, return nil } +// DatafileManagerConfigurationIs provides dfm configuration +func (c *ScenarioCtx) DatafileManagerConfigurationIs(options *gherkin.DocString) error { + + var datafileManagerConfiguration models.DataFileManagerConfiguration + + if err := yaml.Unmarshal([]byte(options.Content), &datafileManagerConfiguration); err != nil { + return fmt.Errorf("invalid dfm configuration") + } + c.apiOptions.DFMConfiguration = datafileManagerConfiguration + return nil +} + // IsCalledWithArguments calls an SDK API with arguments. func (c *ScenarioCtx) IsCalledWithArguments(apiName string, arguments *gherkin.DocString) error { c.apiOptions.APIName = apiName @@ -86,6 +99,7 @@ func (c *ScenarioCtx) IsCalledWithArguments(apiName string, arguments *gherkin.D // Clearing old state of response, eventdispatcher and decision service c.apiResponse = models.APIResponse{} c.clientWrapper = GetInstance(c.apiOptions) + response, err := c.clientWrapper.InvokeAPI(c.apiOptions) c.apiResponse = response //Reset listeners so that same listener is not added twice for a scenario @@ -403,6 +417,18 @@ func (c *ScenarioCtx) ThereIsNoUserProfileState() error { return getErrorWithDiff([]decision.UserProfile{}, actualProfiles, "User profile state not empty") } +// TheListenerWasCalledNTimes checks if listener was called a given amount of time +func (c *ScenarioCtx) TheListenerWasCalledNTimes(listenerType string, count int) error { + var listenersCalled []notification.ProjectConfigUpdateNotification + if listenerType == "Config-update" { + listenersCalled = c.clientWrapper.PollingConfigManager.(*optlyplugins.TestConfigManager).GetListenersCalled() + if len(listenersCalled) == count { + return nil + } + } + return fmt.Errorf("Number of listeners called should be %d but received %d", count, len(listenersCalled)) +} + // Reset clears all data before each scenario, assigns new scenarioID and sets session as false func (c *ScenarioCtx) Reset() { // Delete cached optly wrapper instance @@ -411,5 +437,5 @@ func (c *ScenarioCtx) Reset() { c.apiOptions = models.APIOptions{} c.apiResponse = models.APIResponse{} c.clientWrapper = nil - c.scenarioID = uuid.New().String() + c.apiOptions.ScenarioID = uuid.New().String() } From 50018bd4d865014ed0eea7ec991b51c18592bf5b Mon Sep 17 00:00:00 2001 From: Yasir Ali Date: Wed, 27 Nov 2019 18:04:38 +0500 Subject: [PATCH 02/16] fixes. --- tests/integration/main_test.go | 2 +- tests/integration/optlyplugins/test_config_manager.go | 3 ++- tests/integration/optlyplugins/utils.go | 6 ++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/integration/main_test.go b/tests/integration/main_test.go index d293b7d96..4b7fda7a2 100644 --- a/tests/integration/main_test.go +++ b/tests/integration/main_test.go @@ -26,7 +26,7 @@ import ( "github.com/optimizely/go-sdk/tests/integration/support" ) -var opt = godog.Options{Output: colors.Colored(os.Stdout), Tags: "@DATAFILE_MANAGER"} +var opt = godog.Options{Output: colors.Colored(os.Stdout)} func init() { godog.BindFlags("godog.", flag.CommandLine, &opt) diff --git a/tests/integration/optlyplugins/test_config_manager.go b/tests/integration/optlyplugins/test_config_manager.go index 9b23951f0..cebc8cdc5 100644 --- a/tests/integration/optlyplugins/test_config_manager.go +++ b/tests/integration/optlyplugins/test_config_manager.go @@ -88,12 +88,13 @@ func (c *TestConfigManager) Verify(apiOptions models.APIOptions) { if elapsed >= timeout { break } - // this means we want the manager to poll until we get to a specific revision if revision > 0 { + // This means we want the manager to poll until we get to a specific revision if revision == len(c.listenersCalled) { break } } else if len(c.listenersCalled) == 1 { + // For cases where we are just waiting for config listener break } } diff --git a/tests/integration/optlyplugins/utils.go b/tests/integration/optlyplugins/utils.go index 734963c52..e5a2455ea 100644 --- a/tests/integration/optlyplugins/utils.go +++ b/tests/integration/optlyplugins/utils.go @@ -30,6 +30,10 @@ import ( ) const localDatafileURLTemplate = "http://localhost:3001/datafiles/%s.json?request_id=" + +// SyncConfig doesn't request for new datafile if we provide a valid datafile +// this requires us to keep defaultPollingInterval low so that the request +// initiated from Start method is executed quickly const defaultPollingInterval = time.Duration(1000) * time.Millisecond // CreatePollingConfigManager creates a pollingConfigManager with given configuration @@ -62,6 +66,8 @@ func CreatePollingConfigManager(options models.APIOptions) *TestConfigManager { pollingConfigManagerOptions..., ) testConfigManagerInstance.ProjectConfigManager = configManager + // Since we are using TestConfigManager over ProjectConfigManager, factory will + // not call the start method for ProjectConfigManager, so we have to do it manually exeCtx := utils.NewCancelableExecutionCtx() configManager.Start(sdkKey, exeCtx) testConfigManagerInstance.Verify(options) From 2615617b766932b5ebd49e046d36b9a8e27c3d96 Mon Sep 17 00:00:00 2001 From: Yasir Ali Date: Wed, 27 Nov 2019 18:27:53 +0500 Subject: [PATCH 03/16] linter fixes. --- pkg/config/polling_manager.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/config/polling_manager.go b/pkg/config/polling_manager.go index 81697b2ba..e995ddf94 100644 --- a/pkg/config/polling_manager.go +++ b/pkg/config/polling_manager.go @@ -204,8 +204,11 @@ func NewPollingProjectConfigManager(sdkKey string, pollingMangerOptions ...Optio opt(&pollingProjectConfigManager) } + // To send notification for initial poll aswell for _, handler := range pollingProjectConfigManager.projectConfigUpdateHandlers { - pollingProjectConfigManager.OnProjectConfigUpdate(handler) + if _, err := pollingProjectConfigManager.OnProjectConfigUpdate(handler); err != nil { + break + } } initDatafile := pollingProjectConfigManager.initDatafile From b7d84dac2623615f7885cbb3569e37ee5224754e Mon Sep 17 00:00:00 2001 From: Yasir Ali Date: Wed, 27 Nov 2019 22:29:13 +0500 Subject: [PATCH 04/16] fixes. --- pkg/config/polling_manager.go | 5 ++--- .../optlyplugins/test_config_manager.go | 16 ++++++++-------- tests/integration/optlyplugins/utils.go | 5 +++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pkg/config/polling_manager.go b/pkg/config/polling_manager.go index e995ddf94..42ac5b714 100644 --- a/pkg/config/polling_manager.go +++ b/pkg/config/polling_manager.go @@ -84,8 +84,8 @@ func PollingInterval(interval time.Duration) OptionFunc { } } -// ProjectConfigUpdateNotificationHandlers is an optional function, sets passed notification handlers -func ProjectConfigUpdateNotificationHandlers(handlers ...func(notification.ProjectConfigUpdateNotification)) OptionFunc { +// NotificationHandlers is an optional function, sets passed notification handlers +func NotificationHandlers(handlers ...func(notification.ProjectConfigUpdateNotification)) OptionFunc { return func(p *PollingProjectConfigManager) { p.projectConfigUpdateHandlers = handlers } @@ -204,7 +204,6 @@ func NewPollingProjectConfigManager(sdkKey string, pollingMangerOptions ...Optio opt(&pollingProjectConfigManager) } - // To send notification for initial poll aswell for _, handler := range pollingProjectConfigManager.projectConfigUpdateHandlers { if _, err := pollingProjectConfigManager.OnProjectConfigUpdate(handler); err != nil { break diff --git a/tests/integration/optlyplugins/test_config_manager.go b/tests/integration/optlyplugins/test_config_manager.go index cebc8cdc5..1f32525e4 100644 --- a/tests/integration/optlyplugins/test_config_manager.go +++ b/tests/integration/optlyplugins/test_config_manager.go @@ -33,8 +33,8 @@ type TestConfigManager struct { listenersCalled []notification.ProjectConfigUpdateNotification } -// CreateListenerCallbacks - Creates Notification Listeners -func (c *TestConfigManager) CreateListenerCallbacks(apiOptions models.APIOptions) (listeners []func(notification notification.ProjectConfigUpdateNotification)) { +// GetListenerCallbacks - Creates and returns listener callback array +func (c *TestConfigManager) GetListenerCallbacks(apiOptions models.APIOptions) (listeners []func(notification notification.ProjectConfigUpdateNotification)) { projectConfigUpdateCallback := func(notification notification.ProjectConfigUpdateNotification) { c.listenersCalled = append(c.listenersCalled, notification) @@ -55,14 +55,14 @@ func (c *TestConfigManager) CreateListenerCallbacks(apiOptions models.APIOptions } // Verify - Verifies configuration tests -func (c *TestConfigManager) Verify(apiOptions models.APIOptions) { +func (c *TestConfigManager) Verify(configuration models.DataFileManagerConfiguration) { timeout := DefaultInitializationTimeout - if apiOptions.DFMConfiguration.Timeout != nil { - timeout = time.Duration(*(apiOptions.DFMConfiguration.Timeout)) * time.Millisecond + if configuration.Timeout != nil { + timeout = time.Duration(*(configuration.Timeout)) * time.Millisecond } start := time.Now() - switch apiOptions.DFMConfiguration.Mode { + switch configuration.Mode { case "wait_for_on_ready": for { t := time.Now() @@ -79,8 +79,8 @@ func (c *TestConfigManager) Verify(apiOptions models.APIOptions) { break case "wait_for_config_update": revision := 0 - if apiOptions.DFMConfiguration.Revision != nil { - revision = *(apiOptions.DFMConfiguration.Revision) + if configuration.Revision != nil { + revision = *(configuration.Revision) } for { t := time.Now() diff --git a/tests/integration/optlyplugins/utils.go b/tests/integration/optlyplugins/utils.go index e5a2455ea..9a96c2a55 100644 --- a/tests/integration/optlyplugins/utils.go +++ b/tests/integration/optlyplugins/utils.go @@ -59,7 +59,7 @@ func CreatePollingConfigManager(options models.APIOptions) *TestConfigManager { pollingConfigManagerOptions = append(pollingConfigManagerOptions, config.DatafileTemplate(urlString)) testConfigManagerInstance := &TestConfigManager{} - pollingConfigManagerOptions = append(pollingConfigManagerOptions, config.ProjectConfigUpdateNotificationHandlers(testConfigManagerInstance.CreateListenerCallbacks(options)...)) + pollingConfigManagerOptions = append(pollingConfigManagerOptions, config.NotificationHandlers(testConfigManagerInstance.GetListenerCallbacks(options)...)) configManager := config.NewPollingProjectConfigManager( sdkKey, @@ -70,7 +70,8 @@ func CreatePollingConfigManager(options models.APIOptions) *TestConfigManager { // not call the start method for ProjectConfigManager, so we have to do it manually exeCtx := utils.NewCancelableExecutionCtx() configManager.Start(sdkKey, exeCtx) - testConfigManagerInstance.Verify(options) + // Verify datafile configuration tests + testConfigManagerInstance.Verify(options.DFMConfiguration) return testConfigManagerInstance } From b8db4a7a75eeefc0f316d2fd1416fd9e447ff4be Mon Sep 17 00:00:00 2001 From: Yasir Ali Date: Mon, 2 Dec 2019 11:41:54 +0500 Subject: [PATCH 05/16] Added tags and dfm server implementation. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 043e6552f..907e467a8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,10 +55,11 @@ jobs: 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 && ln -s features/support/datafiles/ public && pushd services/datafile && npm install && popd && node services/datafile && popd install: - eval "$(gimme)" script: - - ./scripts/run-fsc-tests.sh -f "$FSC_PATH/features/" -d "$FSC_PATH/features/support/datafiles/" -t "$TAGS" + - ./scripts/run-fsc-tests.sh -f "$FSC_PATH/features/" -d "$FSC_PATH/features/support/datafiles/" -t "~@FORCED_VARIATION&&~@INPUT_VALIDATION&&~@EVENT_BATCHING&&~@NO_EASY_EVENT_TRACKING&&~@OASIS-3654&&~@TARGETED_ROLLOUT&&~@EXPERIMENT_STATUS&&@~UNSUPPORTED_DATAFILE_VERSION&&@~TRACK_LISTENER&&@~ACTIVATE_LISTENER&&~@OPTIMIZELY_CONFIG" - stage: 'Source clear' env: GIMME_GO_VERSION=master GIMME_OS=linux GIMME_ARCH=amd64 From cf8778bdb2cebd5f65d625117f5ac8f6b83a21f5 Mon Sep 17 00:00:00 2001 From: Yasir Ali Date: Mon, 2 Dec 2019 11:57:32 +0500 Subject: [PATCH 06/16] Testing. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 907e467a8..7dad7b2a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,10 @@ git: install: - eval "$(gimme)" stages: + - 'Integration tests' - 'Lint' - 'Unit test' - 'Benchmark test' - - 'Integration tests' - 'Source clear' jobs: include: @@ -52,6 +52,7 @@ jobs: - stage: 'Integration tests' env: GIMME_GO_VERSION=1.12.x FSC_PATH="/tmp/fsc-repo" + language: node_js 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 From ead290190008941cca2147f16d54ff1343696f8f Mon Sep 17 00:00:00 2001 From: Yasir Ali Date: Mon, 2 Dec 2019 12:02:14 +0500 Subject: [PATCH 07/16] Testing. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7dad7b2a9..4be685ba0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,7 +56,7 @@ jobs: 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 && ln -s features/support/datafiles/ public && pushd services/datafile && npm install && popd && node services/datafile && popd + - pushd $FSC_PATH && ln -s features/support/datafiles/ public && pushd services/datafile && nvm install && nvm use && npm install && popd && node services/datafile && popd install: - eval "$(gimme)" script: From 79d348118fc0160411e561f5a823b04af4539598 Mon Sep 17 00:00:00 2001 From: Yasir Ali Date: Mon, 2 Dec 2019 12:05:17 +0500 Subject: [PATCH 08/16] Testing. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4be685ba0..4c24e63b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,7 +56,7 @@ jobs: 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 && ln -s features/support/datafiles/ public && pushd services/datafile && nvm install && nvm use && npm install && popd && node services/datafile && popd + - pushd $FSC_PATH && ln -s features/support/datafiles/ public && pushd services/datafile && nvm install && nvm use && npm install && popd && node services/datafile & && popd install: - eval "$(gimme)" script: From e64d58f78bc6d520ba891216b7f3442f1e48aa77 Mon Sep 17 00:00:00 2001 From: Yasir Ali Date: Mon, 2 Dec 2019 12:12:44 +0500 Subject: [PATCH 09/16] Testing. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4c24e63b3..24225d692 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,7 +56,8 @@ jobs: 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 && ln -s features/support/datafiles/ public && pushd services/datafile && nvm install && nvm use && npm install && popd && node services/datafile & && popd + - pushd $FSC_PATH && ln -s features/support/datafiles/ public && pushd services/datafile && nvm install && nvm use && npm install && popd && node services/datafile & + - popd install: - eval "$(gimme)" script: From 7bf5a6a2650d8f3868402413f75e1ff56e171903 Mon Sep 17 00:00:00 2001 From: Yasir Ali Date: Mon, 2 Dec 2019 12:20:46 +0500 Subject: [PATCH 10/16] Testing. --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 24225d692..83e8a31b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,8 +55,10 @@ jobs: language: node_js 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 && ln -s features/support/datafiles/ public && pushd services/datafile && nvm install && nvm use && npm install && popd && node services/datafile & + - 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 + - ln -s features/support/datafiles/ public + - pushd services/datafile && nvm install && nvm use && npm install && popd + - node services/datafile/ & - popd install: - eval "$(gimme)" From 3e0ed58cdb40831c146fdb16e453b894b8b18d6c Mon Sep 17 00:00:00 2001 From: Yasir Ali Date: Mon, 2 Dec 2019 12:24:03 +0500 Subject: [PATCH 11/16] Testing. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 83e8a31b2..f52f949c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,7 +58,7 @@ jobs: - 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 - ln -s features/support/datafiles/ public - pushd services/datafile && nvm install && nvm use && npm install && popd - - node services/datafile/ & + - node services/datafile/ &> /dev/null & - popd install: - eval "$(gimme)" From 0c8aaa3a0fc5619338abde25ac0bfc448f065656 Mon Sep 17 00:00:00 2001 From: Yasir Ali Date: Mon, 2 Dec 2019 12:30:29 +0500 Subject: [PATCH 12/16] fixes. --- .travis.yml | 7 +------ scripts/setup-integration-tests.sh | 8 ++++++++ 2 files changed, 9 insertions(+), 6 deletions(-) create mode 100755 scripts/setup-integration-tests.sh diff --git a/.travis.yml b/.travis.yml index f52f949c8..3cf1ef540 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,12 +54,7 @@ jobs: env: GIMME_GO_VERSION=1.12.x FSC_PATH="/tmp/fsc-repo" language: node_js 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 - - ln -s features/support/datafiles/ public - - pushd services/datafile && nvm install && nvm use && npm install && popd - - node services/datafile/ &> /dev/null & - - popd + - ./scripts/setup-integration-tests.sh install: - eval "$(gimme)" script: diff --git a/scripts/setup-integration-tests.sh b/scripts/setup-integration-tests.sh new file mode 100755 index 000000000..f57d9b737 --- /dev/null +++ b/scripts/setup-integration-tests.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +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 +ln -s features/support/datafiles/ public +pushd services/datafile && nvm install && nvm use && npm install && popd +node services/datafile/ &> /dev/null & +popd \ No newline at end of file From 60a83379384953d5b223e3f69acc93ad4fb481e3 Mon Sep 17 00:00:00 2001 From: Yasir Ali Date: Mon, 2 Dec 2019 12:34:34 +0500 Subject: [PATCH 13/16] fixes. --- scripts/setup-integration-tests.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/setup-integration-tests.sh b/scripts/setup-integration-tests.sh index f57d9b737..1ead76d1b 100755 --- a/scripts/setup-integration-tests.sh +++ b/scripts/setup-integration-tests.sh @@ -1,5 +1,6 @@ #!/bin/bash +. ~/.nvm/nvm.sh 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 ln -s features/support/datafiles/ public From 1536493503110b397e430acffc31ff286737c40f Mon Sep 17 00:00:00 2001 From: Yasir Ali Date: Mon, 2 Dec 2019 13:03:26 +0500 Subject: [PATCH 14/16] fixes. --- .travis.yml | 2 +- .../optlyplugins/test_config_manager.go | 87 ++++++++++--------- tests/integration/optlyplugins/utils.go | 14 +-- tests/integration/support/client_wrapper.go | 2 +- tests/integration/support/steps.go | 2 +- 5 files changed, 58 insertions(+), 49 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3cf1ef540..aa9b38f41 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,9 +5,9 @@ git: install: - eval "$(gimme)" stages: - - 'Integration tests' - 'Lint' - 'Unit test' + - 'Integration tests' - 'Benchmark test' - 'Source clear' jobs: diff --git a/tests/integration/optlyplugins/test_config_manager.go b/tests/integration/optlyplugins/test_config_manager.go index 1f32525e4..e7ccc8db0 100644 --- a/tests/integration/optlyplugins/test_config_manager.go +++ b/tests/integration/optlyplugins/test_config_manager.go @@ -17,6 +17,7 @@ package optlyplugins import ( + "sync" "time" "github.com/optimizely/go-sdk/pkg" @@ -27,14 +28,14 @@ import ( // DefaultInitializationTimeout defines default timeout for datafile sync const DefaultInitializationTimeout = time.Duration(3000) * time.Millisecond -// TestConfigManager represents a ProjectConfigManager with custom implementations -type TestConfigManager struct { +// TestProjectConfigManager represents a ProjectConfigManager with custom implementations +type TestProjectConfigManager struct { pkg.ProjectConfigManager listenersCalled []notification.ProjectConfigUpdateNotification } // GetListenerCallbacks - Creates and returns listener callback array -func (c *TestConfigManager) GetListenerCallbacks(apiOptions models.APIOptions) (listeners []func(notification notification.ProjectConfigUpdateNotification)) { +func (c *TestProjectConfigManager) GetListenerCallbacks(apiOptions models.APIOptions) (listeners []func(notification notification.ProjectConfigUpdateNotification)) { projectConfigUpdateCallback := func(notification notification.ProjectConfigUpdateNotification) { c.listenersCalled = append(c.listenersCalled, notification) @@ -55,57 +56,65 @@ func (c *TestConfigManager) GetListenerCallbacks(apiOptions models.APIOptions) ( } // Verify - Verifies configuration tests -func (c *TestConfigManager) Verify(configuration models.DataFileManagerConfiguration) { +func (c *TestProjectConfigManager) Verify(configuration models.DataFileManagerConfiguration) { timeout := DefaultInitializationTimeout if configuration.Timeout != nil { timeout = time.Duration(*(configuration.Timeout)) * time.Millisecond } - start := time.Now() - switch configuration.Mode { - case "wait_for_on_ready": - for { - t := time.Now() - elapsed := t.Sub(start) - if elapsed >= timeout { - break - } - // Check if projectconfig is ready - _, err := c.GetConfig() - if err == nil { - break + verify := func(wg *sync.WaitGroup) { + start := time.Now() + switch configuration.Mode { + case "wait_for_on_ready": + for { + t := time.Now() + elapsed := t.Sub(start) + if elapsed >= timeout { + break + } + // Check if projectconfig is ready + _, err := c.GetConfig() + if err == nil { + break + } } - } - break - case "wait_for_config_update": - revision := 0 - if configuration.Revision != nil { - revision = *(configuration.Revision) - } - for { - t := time.Now() - elapsed := t.Sub(start) - if elapsed >= timeout { - break + break + case "wait_for_config_update": + revision := 0 + if configuration.Revision != nil { + revision = *(configuration.Revision) } - if revision > 0 { - // This means we want the manager to poll until we get to a specific revision - if revision == len(c.listenersCalled) { + for { + t := time.Now() + elapsed := t.Sub(start) + if elapsed >= timeout { + break + } + if revision > 0 { + // This means we want the manager to poll until we get to a specific revision + if revision == len(c.listenersCalled) { + break + } + } else if len(c.listenersCalled) == 1 { + // For cases where we are just waiting for config listener break } - } else if len(c.listenersCalled) == 1 { - // For cases where we are just waiting for config listener - break } + break + default: + break } - break - default: - break + wg.Done() } + + var wg sync.WaitGroup + wg.Add(1) + go verify(&wg) + wg.Wait() } // GetListenersCalled - Returns listeners called -func (c *TestConfigManager) GetListenersCalled() []notification.ProjectConfigUpdateNotification { +func (c *TestProjectConfigManager) GetListenersCalled() []notification.ProjectConfigUpdateNotification { listenerCalled := c.listenersCalled // Since for every scenario, a new sdk instance is created, emptying listenersCalled is required for scenario's // where multiple requests are executed but no session is to be maintained among them. diff --git a/tests/integration/optlyplugins/utils.go b/tests/integration/optlyplugins/utils.go index 6687713da..da62a01f3 100644 --- a/tests/integration/optlyplugins/utils.go +++ b/tests/integration/optlyplugins/utils.go @@ -37,7 +37,7 @@ const localDatafileURLTemplate = "http://localhost:3001/datafiles/%s.json?reques const defaultPollingInterval = time.Duration(1000) * time.Millisecond // CreatePollingConfigManager creates a pollingConfigManager with given configuration -func CreatePollingConfigManager(options models.APIOptions) *TestConfigManager { +func CreatePollingConfigManager(options models.APIOptions) *TestProjectConfigManager { var pollingConfigManagerOptions []config.OptionFunc // Setting up initial datafile @@ -58,22 +58,22 @@ func CreatePollingConfigManager(options models.APIOptions) *TestConfigManager { urlString := localDatafileURLTemplate + options.ScenarioID pollingConfigManagerOptions = append(pollingConfigManagerOptions, config.WithDatafileURLTemplate(urlString)) - testConfigManagerInstance := &TestConfigManager{} - pollingConfigManagerOptions = append(pollingConfigManagerOptions, config.WithNotificationHandlers(testConfigManagerInstance.GetListenerCallbacks(options)...)) + testProjectConfigManagerInstance := &TestProjectConfigManager{} + pollingConfigManagerOptions = append(pollingConfigManagerOptions, config.WithNotificationHandlers(testProjectConfigManagerInstance.GetListenerCallbacks(options)...)) configManager := config.NewPollingProjectConfigManager( sdkKey, pollingConfigManagerOptions..., ) - testConfigManagerInstance.ProjectConfigManager = configManager - // Since we are using TestConfigManager over ProjectConfigManager, factory will + testProjectConfigManagerInstance.ProjectConfigManager = configManager + // Since we are using TestProjectConfigManager over ProjectConfigManager, factory will // not call the start method for ProjectConfigManager, so we have to do it manually exeCtx := utils.NewCancelableExecutionCtx() configManager.Start(sdkKey, exeCtx) // Verify datafile configuration tests - testConfigManagerInstance.Verify(options.DFMConfiguration) + testProjectConfigManagerInstance.Verify(options.DFMConfiguration) - return testConfigManagerInstance + return testProjectConfigManagerInstance } // GetDatafile returns datafile,error for the provided datafileName diff --git a/tests/integration/support/client_wrapper.go b/tests/integration/support/client_wrapper.go index 723547e09..91a50d6a3 100644 --- a/tests/integration/support/client_wrapper.go +++ b/tests/integration/support/client_wrapper.go @@ -78,7 +78,7 @@ func GetInstance(apiOptions models.APIOptions) *ClientWrapper { log.Fatal(err) } optimizelyFactory.Datafile = datafile - clientInstance.PollingConfigManager = &optlyplugins.TestConfigManager{ProjectConfigManager: configManager} + clientInstance.PollingConfigManager = &optlyplugins.TestProjectConfigManager{ProjectConfigManager: configManager} userProfileService := userprofileservice.CreateUserProfileService(config, apiOptions) compositeExperimentServiceoptions = append(compositeExperimentServiceoptions, decision.WithUserProfileService(userProfileService)) diff --git a/tests/integration/support/steps.go b/tests/integration/support/steps.go index 20bee4243..6547683cc 100644 --- a/tests/integration/support/steps.go +++ b/tests/integration/support/steps.go @@ -421,7 +421,7 @@ func (c *ScenarioCtx) ThereIsNoUserProfileState() error { func (c *ScenarioCtx) TheListenerWasCalledNTimes(listenerType string, count int) error { var listenersCalled []notification.ProjectConfigUpdateNotification if listenerType == "Config-update" { - listenersCalled = c.clientWrapper.PollingConfigManager.(*optlyplugins.TestConfigManager).GetListenersCalled() + listenersCalled = c.clientWrapper.PollingConfigManager.(*optlyplugins.TestProjectConfigManager).GetListenersCalled() if len(listenersCalled) == count { return nil } From e4b8e2e75e3b7e33a1be56c2d951ebeb6e05a301 Mon Sep 17 00:00:00 2001 From: Yasir Ali Date: Mon, 2 Dec 2019 13:07:12 +0500 Subject: [PATCH 15/16] Reverting temporary changes. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index aa9b38f41..c57095029 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,7 +58,7 @@ jobs: install: - eval "$(gimme)" script: - - ./scripts/run-fsc-tests.sh -f "$FSC_PATH/features/" -d "$FSC_PATH/features/support/datafiles/" -t "~@FORCED_VARIATION&&~@INPUT_VALIDATION&&~@EVENT_BATCHING&&~@NO_EASY_EVENT_TRACKING&&~@OASIS-3654&&~@TARGETED_ROLLOUT&&~@EXPERIMENT_STATUS&&@~UNSUPPORTED_DATAFILE_VERSION&&@~TRACK_LISTENER&&@~ACTIVATE_LISTENER&&~@OPTIMIZELY_CONFIG" + - ./scripts/run-fsc-tests.sh -f "$FSC_PATH/features/" -d "$FSC_PATH/features/support/datafiles/" -t "$TAGS" - stage: 'Source clear' env: GIMME_GO_VERSION=master GIMME_OS=linux GIMME_ARCH=amd64 From 58a9b7cbc70161eb524bc0e3108ddc1290ad6e67 Mon Sep 17 00:00:00 2001 From: Yasir Ali Date: Mon, 2 Dec 2019 13:34:31 +0500 Subject: [PATCH 16/16] Improving code coverage. --- pkg/config/polling_manager.go | 6 ++++-- pkg/config/polling_manager_test.go | 13 +++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/pkg/config/polling_manager.go b/pkg/config/polling_manager.go index 775b17dd6..014f7e84c 100644 --- a/pkg/config/polling_manager.go +++ b/pkg/config/polling_manager.go @@ -206,9 +206,11 @@ func NewPollingProjectConfigManager(sdkKey string, pollingMangerOptions ...Optio } for _, handler := range pollingProjectConfigManager.projectConfigUpdateHandlers { - if _, err := pollingProjectConfigManager.OnProjectConfigUpdate(handler); err != nil { - break + if _, err := pollingProjectConfigManager.OnProjectConfigUpdate(handler); err == nil { + // To bypass linter warnings for ignoring error + continue } + break } initDatafile := pollingProjectConfigManager.initDatafile diff --git a/pkg/config/polling_manager_test.go b/pkg/config/polling_manager_test.go index 8da24ea29..170e7b7f1 100644 --- a/pkg/config/polling_manager_test.go +++ b/pkg/config/polling_manager_test.go @@ -263,3 +263,16 @@ func TestDatafileTemplate(t *testing.T) { assert.Equal(t, datafileTemplate, configManager.datafileURLTemplate) } + +func TestNotificationHandlers(t *testing.T) { + + projectConfigUpdateCallback := func(notification notification.ProjectConfigUpdateNotification) { + } + + sdkKey := "test_sdk_key" + exeCtx := utils.NewCancelableExecutionCtx() + configManager := NewPollingProjectConfigManager(sdkKey, WithNotificationHandlers(projectConfigUpdateCallback)) + configManager.Start(sdkKey, exeCtx) + + assert.Equal(t, len(configManager.projectConfigUpdateHandlers), 1) +}