示例#1
0
    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
示例#2
0
 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')]
示例#3
0
    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
示例#4
0
    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)
示例#6
0
 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']
示例#7
0
    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
示例#8
0
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,
    )
示例#9
0
    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
示例#10
0
    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
示例#11
0
    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)
示例#12
0
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))
示例#13
0
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))
    )