Skip to content

Commit 8d50b81

Browse files
committed
added tests for client, factory and input validator
1 parent 1579eb0 commit 8d50b81

File tree

6 files changed

+2737
-765
lines changed

6 files changed

+2737
-765
lines changed

splitio/client/client.py

Lines changed: 111 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class ClientBase(object): # pylint: disable=too-many-instance-attributes
1818

1919
_FAILED_EVAL_RESULT = {
2020
'treatment': CONTROL,
21-
'config': None,
21+
'configurations': None,
2222
'impression': {
2323
'label': Label.EXCEPTION,
2424
'change_number': None,
@@ -86,8 +86,6 @@ def _validate_treatment_input(key, feature, attributes, method):
8686
matching_key, bucketing_key = input_validator.validate_key(key, 'get_' + method.value)
8787
if not matching_key:
8888
raise _InvalidInputError()
89-
# if bucketing_key is None:
90-
# bucketing_key = matching_key
9189

9290
feature = input_validator.validate_feature_flag_name(feature, 'get_' + method.value)
9391
if not feature:
@@ -104,8 +102,6 @@ def _validate_treatments_input(key, features, attributes, method):
104102
matching_key, bucketing_key = input_validator.validate_key(key, 'get_' + method.value)
105103
if not matching_key:
106104
raise _InvalidInputError()
107-
# if bucketing_key is None:
108-
# bucketing_key = matching_key
109105

110106
features = input_validator.validate_feature_flags_get_treatments('get_' + method.value, features)
111107
if not features:
@@ -426,9 +422,9 @@ def _get_treatments_by_flag_sets(self, key, flag_sets, method, attributes=None):
426422
:return: Dictionary with the result of all the feature flags provided
427423
:rtype: dict
428424
"""
429-
feature_flags_names = self._get_feature_flag_names_by_flag_sets(flag_sets, method.value)
425+
feature_flags_names = self._get_feature_flag_names_by_flag_sets(flag_sets, 'get_' + method.value)
430426
if feature_flags_names == []:
431-
_LOGGER.warning("%s: No valid Flag set or no feature flags found for evaluating treatments" % (method.value))
427+
_LOGGER.warning("%s: No valid Flag set or no feature flags found for evaluating treatments", 'get_' + method.value)
432428
return {}
433429

434430
if 'config' in method.value:
@@ -447,7 +443,7 @@ def _get_feature_flag_names_by_flag_sets(self, flag_sets, method_name):
447443
:rtype: list
448444
"""
449445
sanitized_flag_sets = input_validator.validate_flag_sets(flag_sets, method_name)
450-
feature_flags_by_set = self._split_storage.get_feature_flags_by_sets(sanitized_flag_sets)
446+
feature_flags_by_set = self._feature_flag_storage.get_feature_flags_by_sets(sanitized_flag_sets)
451447
if feature_flags_by_set is None:
452448
_LOGGER.warning("Fetching feature flags for flag set %s encountered an error, skipping this flag set." % (flag_sets))
453449
return []
@@ -733,6 +729,113 @@ async def get_treatments_with_config(self, key, feature_flag_names, attributes=N
733729
_LOGGER.error("AA", exc_info=True)
734730
return {feature: (CONTROL, None) for feature in feature_flag_names}
735731

732+
async def get_treatments_by_flag_set(self, key, flag_set, attributes=None):
733+
"""
734+
Get treatments for feature flags that contain given flag set.
735+
This method never raises an exception. If there's a problem, the appropriate log message
736+
will be generated and the method will return the CONTROL treatment.
737+
:param key: The key for which to get the treatment
738+
:type key: str
739+
:param flag_set: flag set
740+
:type flag_sets: str
741+
:param attributes: An optional dictionary of attributes
742+
:type attributes: dict
743+
:return: Dictionary with the result of all the feature flags provided
744+
:rtype: dict
745+
"""
746+
return await self._get_treatments_by_flag_sets( key, [flag_set], MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SET, attributes)
747+
748+
async def get_treatments_by_flag_sets(self, key, flag_sets, attributes=None):
749+
"""
750+
Get treatments for feature flags that contain given flag sets.
751+
This method never raises an exception. If there's a problem, the appropriate log message
752+
will be generated and the method will return the CONTROL treatment.
753+
:param key: The key for which to get the treatment
754+
:type key: str
755+
:param flag_sets: list of flag sets
756+
:type flag_sets: list
757+
:param attributes: An optional dictionary of attributes
758+
:type attributes: dict
759+
:return: Dictionary with the result of all the feature flags provided
760+
:rtype: dict
761+
"""
762+
return await self._get_treatments_by_flag_sets( key, flag_sets, MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SETS, attributes)
763+
764+
async def get_treatments_with_config_by_flag_set(self, key, flag_set, attributes=None):
765+
"""
766+
Get treatments for feature flags that contain given flag set.
767+
This method never raises an exception. If there's a problem, the appropriate log message
768+
will be generated and the method will return the CONTROL treatment.
769+
:param key: The key for which to get the treatment
770+
:type key: str
771+
:param flag_set: flag set
772+
:type flag_sets: str
773+
:param attributes: An optional dictionary of attributes
774+
:type attributes: dict
775+
:return: Dictionary with the result of all the feature flags provided
776+
:rtype: dict
777+
"""
778+
return await self._get_treatments_by_flag_sets( key, [flag_set], MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SET, attributes)
779+
780+
async def get_treatments_with_config_by_flag_sets(self, key, flag_sets, attributes=None):
781+
"""
782+
Get treatments for feature flags that contain given flag set.
783+
This method never raises an exception. If there's a problem, the appropriate log message
784+
will be generated and the method will return the CONTROL treatment.
785+
:param key: The key for which to get the treatment
786+
:type key: str
787+
:param flag_set: flag set
788+
:type flag_sets: str
789+
:param attributes: An optional dictionary of attributes
790+
:type attributes: dict
791+
:return: Dictionary with the result of all the feature flags provided
792+
:rtype: dict
793+
"""
794+
return await self._get_treatments_by_flag_sets( key, flag_sets, MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS, attributes)
795+
796+
async def _get_treatments_by_flag_sets(self, key, flag_sets, method, attributes=None):
797+
"""
798+
Get treatments for feature flags that contain given flag sets.
799+
This method never raises an exception. If there's a problem, the appropriate log message
800+
will be generated and the method will return the CONTROL treatment.
801+
:param key: The key for which to get the treatment
802+
:type key: str
803+
:param flag_sets: list of flag sets
804+
:type flag_sets: list
805+
:param method: Treatment by flag set method flavor
806+
:type method: splitio.models.telemetry.MethodExceptionsAndLatencies
807+
:param attributes: An optional dictionary of attributes
808+
:type attributes: dict
809+
:return: Dictionary with the result of all the feature flags provided
810+
:rtype: dict
811+
"""
812+
feature_flags_names = await self._get_feature_flag_names_by_flag_sets(flag_sets, 'get_' + method.value)
813+
if feature_flags_names == []:
814+
_LOGGER.warning("%s: No valid Flag set or no feature flags found for evaluating treatments", 'get_' + method.value)
815+
return {}
816+
817+
if 'config' in method.value:
818+
return await self._get_treatments(key, feature_flags_names, method, attributes)
819+
820+
with_config = await self._get_treatments(key, feature_flags_names, method, attributes)
821+
return {feature_flag: result[0] for (feature_flag, result) in with_config.items()}
822+
823+
824+
async def _get_feature_flag_names_by_flag_sets(self, flag_sets, method_name):
825+
"""
826+
Sanitize given flag sets and return list of feature flag names associated with them
827+
:param flag_sets: list of flag sets
828+
:type flag_sets: list
829+
:return: list of feature flag names
830+
:rtype: list
831+
"""
832+
sanitized_flag_sets = input_validator.validate_flag_sets(flag_sets, method_name)
833+
feature_flags_by_set = await self._feature_flag_storage.get_feature_flags_by_sets(sanitized_flag_sets)
834+
if feature_flags_by_set is None:
835+
_LOGGER.warning("Fetching feature flags for flag set %s encountered an error, skipping this flag set." % (flag_sets))
836+
return []
837+
return feature_flags_by_set
838+
736839
async def _get_treatments(self, key, features, method, attributes=None):
737840
"""
738841
Validate key, feature flag names and objects, and get the treatments and configs with an optional dictionary of attributes, for async calls

splitio/client/factory.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,7 +1240,10 @@ def get_factory(api_key, **kwargs):
12401240
_INSTANTIATED_FACTORIES.update([api_key])
12411241
_INSTANTIATED_FACTORIES_LOCK.release()
12421242

1243-
config = sanitize_config(api_key, kwargs.get('config', {}))
1243+
config_raw = kwargs.get('config', {})
1244+
total_flag_sets, invalid_flag_sets = _get_total_and_invalid_flag_sets(config_raw)
1245+
1246+
config = sanitize_config(api_key, config_raw)
12441247

12451248
if config['operationMode'] == 'localhost':
12461249
split_factory = _build_localhost_factory(config)
@@ -1256,7 +1259,9 @@ def get_factory(api_key, **kwargs):
12561259
kwargs.get('events_api_base_url'),
12571260
kwargs.get('auth_api_base_url'),
12581261
kwargs.get('streaming_api_base_url'),
1259-
kwargs.get('telemetry_api_base_url'))
1262+
kwargs.get('telemetry_api_base_url'),
1263+
total_flag_sets,
1264+
invalid_flag_sets)
12601265

12611266
return split_factory
12621267

@@ -1285,11 +1290,7 @@ async def get_factory_async(api_key, **kwargs):
12851290
_INSTANTIATED_FACTORIES_LOCK.release()
12861291

12871292
config_raw = kwargs.get('config', {})
1288-
total_flag_sets = 0
1289-
invalid_flag_sets = 0
1290-
if config_raw.get('flagSetsFilter') is not None and isinstance(config_raw.get('flagSetsFilter'), list):
1291-
total_flag_sets = len(config_raw.get('flagSetsFilter'))
1292-
invalid_flag_sets = total_flag_sets - len(input_validator.validate_flag_sets(config_raw.get('flagSetsFilter'), 'Telemetry Init'))
1293+
total_flag_sets, invalid_flag_sets = _get_total_and_invalid_flag_sets(config_raw)
12931294

12941295
config = sanitize_config(api_key, config_raw)
12951296
if config['operationMode'] == 'localhost':
@@ -1319,4 +1320,13 @@ def _get_active_and_redundant_count():
13191320
redundant_factory_count += _INSTANTIATED_FACTORIES[item] - 1
13201321
active_factory_count += _INSTANTIATED_FACTORIES[item]
13211322
_INSTANTIATED_FACTORIES_LOCK.release()
1322-
return redundant_factory_count, active_factory_count
1323+
return redundant_factory_count, active_factory_count
1324+
1325+
def _get_total_and_invalid_flag_sets(config_raw):
1326+
total_flag_sets = 0
1327+
invalid_flag_sets = 0
1328+
if config_raw.get('flagSetsFilter') is not None and isinstance(config_raw.get('flagSetsFilter'), list):
1329+
total_flag_sets = len(config_raw.get('flagSetsFilter'))
1330+
invalid_flag_sets = total_flag_sets - len(input_validator.validate_flag_sets(config_raw.get('flagSetsFilter'), 'Telemetry Init'))
1331+
1332+
return total_flag_sets, invalid_flag_sets

splitio/client/input_validator.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,12 @@ def _check_string_matches(value, operation, pattern, name, length):
9595
"""
9696
if re.search(pattern, value) is None or re.search(pattern, value).group() != value:
9797
_LOGGER.error(
98-
'%s: you passed %s, event_type must ' +
98+
'%s: you passed %s, %s must ' +
9999
'adhere to the regular expression %s. ' +
100100
'This means %s must be alphanumeric, cannot be more ' +
101101
'than %s characters long, and can only include a dash, underscore, ' +
102102
'period, or colon as separators of alphanumeric characters.',
103-
operation, value, pattern, name, length
103+
operation, value, name, pattern, name, length
104104
)
105105
return False
106106
return True
@@ -166,7 +166,7 @@ def _check_valid_object_key(key, name, operation):
166166
:return: The result of validation
167167
:rtype: str|None
168168
"""
169-
if not _check_not_null(key, 'key', operation):
169+
if not _check_not_null(key, name, operation):
170170
return None
171171
if isinstance(key, str):
172172
if not _check_string_not_empty(key, name, operation):
@@ -196,7 +196,7 @@ def _remove_empty_spaces(value, name, operation):
196196
def _convert_str_to_lower(value, name, operation):
197197
lower_value = value.lower()
198198
if value != lower_value:
199-
_LOGGER.warning("%s: %s '%s' should be all lowercase - converting string to lowercase" % (operation, name, value))
199+
_LOGGER.warning("%s: %s '%s' should be all lowercase - converting string to lowercase", operation, name, value)
200200
return lower_value
201201

202202
def validate_key(key, method_name):
@@ -647,7 +647,7 @@ def validate_flag_sets(flag_sets, method_name):
647647
:rtype: list[str]
648648
"""
649649
if not isinstance(flag_sets, list):
650-
_LOGGER.warning("%s: flag sets parameter type should be list object, parameter is discarded" % (method_name))
650+
_LOGGER.warning("%s: flag sets parameter type should be list object, parameter is discarded", method_name)
651651
return []
652652

653653
sanitized_flag_sets = set()

0 commit comments

Comments
 (0)