Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
4 changes: 1 addition & 3 deletions optimizely/optimizely_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import copy
from typing import Any, Optional

from . import logger
from .helpers.condition import ConditionOperatorTypes
from .helpers.types import VariationDict, ExperimentDict, RolloutDict, AttributeDict, EventDict
from .project_config import ProjectConfig
Expand Down Expand Up @@ -50,7 +49,6 @@ def __init__(
self.attributes = attributes or []
self.events = events or []
self.audiences = audiences or []
self.logger = logger

def get_datafile(self) -> Optional[str]:
""" Get the datafile associated with OptimizelyConfig.
Expand Down Expand Up @@ -248,7 +246,7 @@ def stringify_conditions(self, conditions: str | list[Any], audiences_map: dict[
operand = conditions[i].upper()
else:
# Check if element is a list or not
if type(conditions[i]) == list:
if isinstance(conditions[i], list):
# Check if at the end or not to determine where to add the operand
# Recursive call to call stringify on embedded list
if i + 1 < length:
Expand Down
108 changes: 56 additions & 52 deletions tests/test_optimizely_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# limitations under the License.

import json
from unittest.mock import patch

from optimizely import optimizely, project_config
from optimizely import optimizely_config
Expand All @@ -23,7 +24,7 @@ def setUp(self):
base.BaseTest.setUp(self)
opt_instance = optimizely.Optimizely(json.dumps(self.config_dict_with_features))
self.project_config = opt_instance.config_manager.get_config()
self.opt_config_service = optimizely_config.OptimizelyConfigService(self.project_config, logger.SimpleLogger()) # todo - added logger
self.opt_config_service = optimizely_config.OptimizelyConfigService(self.project_config, logger.SimpleLogger())

self.expected_config = {
'sdk_key': 'features-test',
Expand Down Expand Up @@ -1235,7 +1236,7 @@ def setUp(self):
}

self.actual_config = self.opt_config_service.get_config()
self.actual_config_dict = self.to_dict(self.actual_config) # TODO - fails here after I add logger, actual_config not a dict?
self.actual_config_dict = self.to_dict(self.actual_config)

self.typed_audiences_config = {
'version': '2',
Expand Down Expand Up @@ -1452,7 +1453,7 @@ def test__get_config(self):
def test__get_config__invalid_project_config(self):
""" Test that get_config returns None when invalid project config supplied. """

opt_service = optimizely_config.OptimizelyConfigService({"key": "invalid"})
opt_service = optimizely_config.OptimizelyConfigService({"key": "invalid"}, None)
self.assertIsNone(opt_service.get_config())

def test__get_experiments_maps(self):
Expand All @@ -1473,69 +1474,72 @@ def test__get_experiments_maps(self):

self.assertEqual(expected_id_map, self.to_dict(actual_id_map))

# TODO - I ADDED
def test__duplicate_experiment_keys(self):
""" Test that multiple features don't have the same experiment key. """

# update the test datafile with an additional feature flag with the same experiment rule key
new_experiment = {
'key': 'test_experiment', # added duplicate "test_experiment"
'status': 'Running',
'layerId': '8',
"audienceConditions": [
"or",
"11160"
],
'audienceIds': ['11160'],
'id': '111137',
'forcedVariations': {},
'trafficAllocation': [
{'entityId': '222242', 'endOfRange': 8000},
{'entityId': '', 'endOfRange': 10000}
],
'variations': [
{
'id': '222242',
'key': 'control',
'variables': [],
}
],
'key': 'test_experiment', # added duplicate "test_experiment"
'status': 'Running',
'layerId': '8',
"audienceConditions": [
"or",
"11160"
],
'audienceIds': ['11160'],
'id': '111137',
'forcedVariations': {},
'trafficAllocation': [
{'entityId': '222242', 'endOfRange': 8000},
{'entityId': '', 'endOfRange': 10000}
],
'variations': [
{
'id': '222242',
'key': 'control',
'variables': [],
}
],
}

new_feature = {
'id': '91117',
'key': 'new_feature',
'experimentIds': ['111137'],
'rolloutId': '',
'variables': [
{'id': '127', 'key': 'is_working', 'defaultValue': 'true', 'type': 'boolean'},
{'id': '128', 'key': 'environment', 'defaultValue': 'devel', 'type': 'string'},
{'id': '129', 'key': 'cost', 'defaultValue': '10.99', 'type': 'double'},
{'id': '130', 'key': 'count', 'defaultValue': '999', 'type': 'integer'},
{'id': '131', 'key': 'variable_without_usage', 'defaultValue': '45', 'type': 'integer'},
{'id': '132', 'key': 'object', 'defaultValue': '{"test": 12}', 'type': 'string',
'subType': 'json'},
{'id': '133', 'key': 'true_object', 'defaultValue': '{"true_test": 23.54}', 'type': 'json'},
],
}
'id': '91117',
'key': 'new_feature',
'experimentIds': ['111137'],
'rolloutId': '',
'variables': [
{'id': '127', 'key': 'is_working', 'defaultValue': 'true', 'type': 'boolean'},
{'id': '128', 'key': 'environment', 'defaultValue': 'devel', 'type': 'string'},
{'id': '129', 'key': 'cost', 'defaultValue': '10.99', 'type': 'double'},
{'id': '130', 'key': 'count', 'defaultValue': '999', 'type': 'integer'},
{'id': '131', 'key': 'variable_without_usage', 'defaultValue': '45', 'type': 'integer'},
{'id': '132', 'key': 'object', 'defaultValue': '{"test": 12}', 'type': 'string',
'subType': 'json'},
{'id': '133', 'key': 'true_object', 'defaultValue': '{"true_test": 23.54}', 'type': 'json'},
],
}

# add new feature with the same rule key
# add new experiment rule with the same key and a new feature with the same rule key
self.config_dict_with_features['experiments'].append(new_experiment)
self.config_dict_with_features['featureFlags'].append(new_feature)

config_with_duplicate_key = self.config_dict_with_features
opt_instance = optimizely.Optimizely(json.dumps(config_with_duplicate_key))
self.project_config = opt_instance.config_manager.get_config()
self.opt_config_service = optimizely_config.OptimizelyConfigService(self.project_config, logger=logger.SimpleLogger())

actual_key_map, actual_id_map = self.opt_config_service._get_experiments_maps()
with patch('optimizely.logger.SimpleLogger.warning') as mock_logger:
Comment thread
andrewleap-optimizely marked this conversation as resolved.
self.opt_config_service = optimizely_config.OptimizelyConfigService(self.project_config,
logger=logger.SimpleLogger())

self.assertIsInstance(actual_key_map, dict)
for exp in actual_key_map.values():
self.assertIsInstance(exp, optimizely_config.OptimizelyExperiment)
actual_key_map, actual_id_map = self.opt_config_service._get_experiments_maps()

self.assertIsInstance(actual_key_map, dict)
for exp in actual_key_map.values():
self.assertIsInstance(exp, optimizely_config.OptimizelyExperiment)

# assert on the log message
# TODO
# Assert that the warning method of the mock logger was called with the expected message
expected_warning_message = f"Duplicate experiment keys found in datafile: {new_experiment['key']}"
mock_logger.assert_called_with(expected_warning_message)

# assert we get ID of the duplicated experiment
assert actual_key_map.get('test_experiment').id == "111137"
Expand Down Expand Up @@ -1746,7 +1750,7 @@ def test_get_audiences(self):
error_handler=None
)

config_service = optimizely_config.OptimizelyConfigService(proj_conf)
config_service = optimizely_config.OptimizelyConfigService(proj_conf, logger)
Comment thread
andrewleap-optimizely marked this conversation as resolved.
Outdated

for audience in config_service.audiences:
self.assertIsInstance(audience, optimizely_config.OptimizelyAudience)
Expand Down Expand Up @@ -1814,7 +1818,7 @@ def test_stringify_audience_conditions_all_cases(self):
'("us" OR ("female" AND "adult")) AND ("fr" AND ("male" OR "adult"))'
]

config_service = optimizely_config.OptimizelyConfigService(config)
config_service = optimizely_config.OptimizelyConfigService(config, None)

for i in range(len(audiences_input)):
result = config_service.stringify_conditions(audiences_input[i], audiences_map)
Expand All @@ -1832,7 +1836,7 @@ def test_optimizely_audience_conversion(self):
error_handler=None
)

config_service = optimizely_config.OptimizelyConfigService(proj_conf)
config_service = optimizely_config.OptimizelyConfigService(proj_conf, None)

for audience in config_service.audiences:
self.assertIsInstance(audience, optimizely_config.OptimizelyAudience)
Expand All @@ -1848,7 +1852,7 @@ def test_get_variations_from_experiments_map(self):
error_handler=None
)

config_service = optimizely_config.OptimizelyConfigService(proj_conf)
config_service = optimizely_config.OptimizelyConfigService(proj_conf, None)

experiments_key_map, experiments_id_map = config_service._get_experiments_maps()

Expand Down