def test_get_split(self, mocker): """Test retrieving a split works.""" adapter = mocker.Mock(spec=RedisAdapter) adapter.get.return_value = '{"name": "some_split"}' from_raw = mocker.Mock() mocker.patch('splitio.storage.redis.splits.from_raw', new=from_raw) storage = RedisSplitStorage(adapter) storage.get('some_split') assert adapter.get.mock_calls == [ mocker.call('SPLITIO.split.some_split') ] assert from_raw.mock_calls == [mocker.call({"name": "some_split"})] # Test that a missing split returns None and doesn't call from_raw adapter.reset_mock() from_raw.reset_mock() adapter.get.return_value = None result = storage.get('some_split') assert result is None assert adapter.get.mock_calls == [ mocker.call('SPLITIO.split.some_split') ] assert not from_raw.mock_calls
def test_get_changenumber(self, mocker): """Test fetching changenumber.""" adapter = mocker.Mock(spec=RedisAdapter) storage = RedisSplitStorage(adapter) adapter.get.return_value = '-1' assert storage.get_change_number() == -1 assert adapter.get.mock_calls == [mocker.call('SPLITIO.splits.till')]
def test_get_all_splits(self, mocker): """Test fetching all splits.""" adapter = mocker.Mock(spec=RedisAdapter) storage = RedisSplitStorage(adapter) from_raw = mocker.Mock() mocker.patch('splitio.storage.redis.splits.from_raw', new=from_raw) adapter.keys.return_value = [ 'SPLITIO.split.split1', 'SPLITIO.split.split2', 'SPLITIO.split.split3' ] def _mget_mock(*_): return [ '{"name": "split1"}', '{"name": "split2"}', '{"name": "split3"}' ] adapter.mget.side_effect = _mget_mock storage.get_all_splits() assert adapter.keys.mock_calls == [mocker.call('SPLITIO.split.*')] assert adapter.mget.mock_calls == [ mocker.call([ 'SPLITIO.split.split1', 'SPLITIO.split.split2', 'SPLITIO.split.split3' ]) ] assert len(from_raw.mock_calls) == 3 assert mocker.call({'name': 'split1'}) in from_raw.mock_calls assert mocker.call({'name': 'split2'}) in from_raw.mock_calls assert mocker.call({'name': 'split3'}) in from_raw.mock_calls
def test_get_split_with_cache(self, mocker): """Test retrieving a split works.""" adapter = mocker.Mock(spec=RedisAdapter) adapter.get.return_value = '{"name": "some_split"}' from_raw = mocker.Mock() mocker.patch('splitio.models.splits.from_raw', new=from_raw) storage = RedisSplitStorage(adapter, True, 1) storage.get('some_split') assert adapter.get.mock_calls == [mocker.call('SPLITIO.split.some_split')] assert from_raw.mock_calls == [mocker.call({"name": "some_split"})] # hit the cache: storage.get('some_split') storage.get('some_split') storage.get('some_split') assert adapter.get.mock_calls == [mocker.call('SPLITIO.split.some_split')] assert from_raw.mock_calls == [mocker.call({"name": "some_split"})] # Test that a missing split returns None and doesn't call from_raw adapter.reset_mock() from_raw.reset_mock() adapter.get.return_value = None result = storage.get('some_split') assert result is not None time.sleep(1) # wait for expiration result = storage.get('some_split') assert result is None assert adapter.get.mock_calls == [mocker.call('SPLITIO.split.some_split')] assert not from_raw.mock_calls
def test_put_fetch(self): """Test storing and retrieving splits in redis.""" adapter = _build_default_client({}) try: storage = RedisSplitStorage(adapter) with open( os.path.join(os.path.dirname(__file__), 'files', 'split_changes.json'), 'r') as flo: split_changes = json.load(flo) split_objects = [ splits.from_raw(raw) for raw in split_changes['splits'] ] for split_object in split_objects: raw = split_object.to_json() adapter.set( RedisSplitStorage._SPLIT_KEY.format( split_name=split_object.name), json.dumps(raw)) original_splits = {split.name: split for split in split_objects} fetched_splits = { name: storage.get(name) for name in original_splits.keys() } assert set(original_splits.keys()) == set(fetched_splits.keys()) for original_split in original_splits.values(): fetched_split = fetched_splits[original_split.name] assert original_split.traffic_type_name == fetched_split.traffic_type_name assert original_split.seed == fetched_split.seed assert original_split.algo == fetched_split.algo assert original_split.status == fetched_split.status assert original_split.change_number == fetched_split.change_number assert original_split.killed == fetched_split.killed assert original_split.default_treatment == fetched_split.default_treatment for index, original_condition in enumerate( original_split.conditions): fetched_condition = fetched_split.conditions[index] assert original_condition.label == fetched_condition.label assert original_condition.condition_type == fetched_condition.condition_type assert len(original_condition.matchers) == len( fetched_condition.matchers) assert len(original_condition.partitions) == len( fetched_condition.partitions) adapter.set(RedisSplitStorage._SPLIT_TILL_KEY, split_changes['till']) assert storage.get_change_number() == split_changes['till'] finally: to_delete = [ "SPLITIO.split.sample_feature", "SPLITIO.splits.till", "SPLITIO.split.all_feature", "SPLITIO.split.killed_feature", "SPLITIO.split.Risk_Max_Deductible", "SPLITIO.split.whitelist_feature", "SPLITIO.split.regex_test", "SPLITIO.split.boolean_test", "SPLITIO.split.dependency_test" ] for item in to_delete: adapter.delete(item)
def test_get_split_names(self, mocker): """Test getching split names.""" adapter = mocker.Mock(spec=RedisAdapter) storage = RedisSplitStorage(adapter) adapter.keys.return_value = [ 'SPLITIO.split.split1', 'SPLITIO.split.split2', 'SPLITIO.split.split3' ] assert storage.get_split_names() == ['split1', 'split2', 'split3']
def test_is_valid_traffic_type(self, mocker): """Test that traffic type validation works.""" adapter = mocker.Mock(spec=RedisAdapter) storage = RedisSplitStorage(adapter) adapter.get.return_value = '1' assert storage.is_valid_traffic_type('any') is True adapter.get.return_value = '0' assert storage.is_valid_traffic_type('any') is False adapter.get.return_value = None assert storage.is_valid_traffic_type('any') is False
def _build_redis_factory(api_key, cfg): """Build and return a split factory with redis-based storage.""" sdk_metadata = util.get_metadata(cfg) redis_adapter = redis.build(cfg) cache_enabled = cfg.get('redisLocalCacheEnabled', False) cache_ttl = cfg.get('redisLocalCacheTTL', 5) storages = { 'splits': RedisSplitStorage(redis_adapter, cache_enabled, cache_ttl), 'segments': RedisSegmentStorage(redis_adapter), 'impressions': RedisImpressionsStorage(redis_adapter, sdk_metadata), 'events': RedisEventsStorage(redis_adapter, sdk_metadata), } data_sampling = cfg.get('dataSampling', DEFAULT_DATA_SAMPLING) if data_sampling < _MIN_DEFAULT_DATA_SAMPLING_ALLOWED: _LOGGER.warning("dataSampling cannot be less than %.2f, defaulting to minimum", _MIN_DEFAULT_DATA_SAMPLING_ALLOWED) data_sampling = _MIN_DEFAULT_DATA_SAMPLING_ALLOWED recorder = PipelinedRecorder( redis_adapter.pipeline, ImpressionsManager(cfg['impressionsMode'], False, _wrap_impression_listener(cfg['impressionListener'], sdk_metadata)), storages['events'], storages['impressions'], data_sampling, ) return SplitFactory( api_key, storages, cfg['labelsEnabled'], recorder, )
def setup_method(self): """Prepare storages with test data.""" metadata = SdkMetadata('python-1.2.3', 'some_ip', 'some_name') redis_client = build(DEFAULT_CONFIG.copy()) split_storage = RedisSplitStorage(redis_client, True) segment_storage = RedisSegmentStorage(redis_client) split_fn = os.path.join(os.path.dirname(__file__), 'files', 'splitChanges.json') with open(split_fn, 'r') as flo: data = json.loads(flo.read()) for split in data['splits']: redis_client.set(split_storage._get_key(split['name']), json.dumps(split)) redis_client.set(split_storage._SPLIT_TILL_KEY, data['till']) segment_fn = os.path.join(os.path.dirname(__file__), 'files', 'segmentEmployeesChanges.json') with open(segment_fn, 'r') as flo: data = json.loads(flo.read()) redis_client.sadd(segment_storage._get_key(data['name']), *data['added']) redis_client.set(segment_storage._get_till_key(data['name']), data['till']) segment_fn = os.path.join(os.path.dirname(__file__), 'files', 'segmentHumanBeignsChanges.json') with open(segment_fn, 'r') as flo: data = json.loads(flo.read()) redis_client.sadd(segment_storage._get_key(data['name']), *data['added']) redis_client.set(segment_storage._get_till_key(data['name']), data['till']) storages = { 'splits': split_storage, 'segments': segment_storage, 'impressions': RedisImpressionsStorage(redis_client, metadata), 'events': RedisEventsStorage(redis_client, metadata), } impmanager = ImpressionsManager(storages['impressions'].put, ImpressionsMode.DEBUG) recorder = PipelinedRecorder(redis_client.pipeline, impmanager, storages['events'], storages['impressions']) self.factory = SplitFactory('some_api_key', storages, True, recorder) # pylint:disable=attribute-defined-outside-init
def test_get_splits_with_cache(self, mocker): """Test retrieving a list of passed splits.""" adapter = mocker.Mock(spec=RedisAdapter) storage = RedisSplitStorage(adapter) from_raw = mocker.Mock() mocker.patch('splitio.models.splits.from_raw', new=from_raw) adapter.mget.return_value = ['{"name": "split1"}', '{"name": "split2"}', None] result = storage.fetch_many(['split1', 'split2', 'split3']) assert len(result) == 3 assert mocker.call({'name': 'split1'}) in from_raw.mock_calls assert mocker.call({'name': 'split2'}) in from_raw.mock_calls assert result['split1'] is not None assert result['split2'] is not None assert 'split3' in result
def setup_method(self): """Prepare storages with test data.""" metadata = SdkMetadata('python-1.2.3', 'some_ip', 'some_name') redis_client = RedisAdapter(StrictRedis()) split_storage = RedisSplitStorage(redis_client, True) segment_storage = RedisSegmentStorage(redis_client) split_fn = os.path.join(os.path.dirname(__file__), 'files', 'splitChanges.json') with open(split_fn, 'r') as flo: data = json.loads(flo.read()) for split in data['splits']: redis_client.set(split_storage._get_key(split['name']), json.dumps(split)) redis_client.set(split_storage._SPLIT_TILL_KEY, data['till']) segment_fn = os.path.join(os.path.dirname(__file__), 'files', 'segmentEmployeesChanges.json') with open(segment_fn, 'r') as flo: data = json.loads(flo.read()) redis_client.sadd(segment_storage._get_key(data['name']), *data['added']) redis_client.set(segment_storage._get_till_key(data['name']), data['till']) segment_fn = os.path.join(os.path.dirname(__file__), 'files', 'segmentHumanBeignsChanges.json') with open(segment_fn, 'r') as flo: data = json.loads(flo.read()) redis_client.sadd(segment_storage._get_key(data['name']), *data['added']) redis_client.set(segment_storage._get_till_key(data['name']), data['till']) self.factory = SplitFactory( 'some_api_key', { #pylint:disable=attribute-defined-outside-init 'splits': split_storage, 'segments': segment_storage, 'impressions': RedisImpressionsStorage(redis_client, metadata), 'events': RedisEventsStorage(redis_client, metadata), 'telemetry': RedisTelemetryStorage(redis_client, metadata) }, True)
def _build_redis_factory(config): """Build and return a split factory with redis-based storage.""" cfg = DEFAULT_CONFIG.copy() cfg.update(config) sdk_metadata = util.get_metadata(config) redis_adapter = redis.build(config) storages = { 'splits': RedisSplitStorage(redis_adapter), 'segments': RedisSegmentStorage(redis_adapter), 'impressions': RedisImpressionsStorage(redis_adapter, sdk_metadata), 'events': RedisEventsStorage(redis_adapter, sdk_metadata), 'telemetry': RedisTelemetryStorage(redis_adapter, sdk_metadata) } return SplitFactory(storages, cfg['labelsEnabled'], impression_listener=_wrap_impression_listener( cfg['impressionListener'], sdk_metadata))
def _build_redis_factory(api_key, cfg): """Build and return a split factory with redis-based storage.""" sdk_metadata = util.get_metadata(cfg) redis_adapter = redis.build(cfg) cache_enabled = cfg.get('redisLocalCacheEnabled', False) cache_ttl = cfg.get('redisLocalCacheTTL', 5) storages = { 'splits': RedisSplitStorage(redis_adapter, cache_enabled, cache_ttl), 'segments': RedisSegmentStorage(redis_adapter), 'impressions': RedisImpressionsStorage(redis_adapter, sdk_metadata), 'events': RedisEventsStorage(redis_adapter, sdk_metadata), 'telemetry': RedisTelemetryStorage(redis_adapter, sdk_metadata) } return SplitFactory( api_key, storages, cfg['labelsEnabled'], ImpressionsManager(storages['impressions'].put, cfg['impressionsMode'], False, _wrap_impression_listener(cfg['impressionListener'], sdk_metadata)) )