コード例 #1
0
    def test_standard_recorder(self, mocker):
        impressions = [
            Impression('k1', 'f1', 'on', 'l1', 123, None, None),
            Impression('k1', 'f2', 'on', 'l1', 123, None, None)
        ]
        impmanager = mocker.Mock(spec=ImpressionsManager)
        impmanager.process_impressions.return_value = impressions
        event = mocker.Mock(spec=EventStorage)
        impression = mocker.Mock(spec=ImpressionStorage)
        recorder = StandardRecorder(impmanager, event, impression)
        recorder.record_treatment_stats(impressions, 1, 'some')

        assert recorder._impression_storage.put.mock_calls[0][1][0] == impressions
コード例 #2
0
    def setup_method(self):
        """Prepare storages with test data."""
        split_storage = InMemorySplitStorage()
        segment_storage = InMemorySegmentStorage()

        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']:
            split_storage.put(splits.from_raw(split))

        segment_fn = os.path.join(os.path.dirname(__file__), 'files', 'segmentEmployeesChanges.json')
        with open(segment_fn, 'r') as flo:
            data = json.loads(flo.read())
        segment_storage.put(segments.from_raw(data))

        segment_fn = os.path.join(os.path.dirname(__file__), 'files', 'segmentHumanBeignsChanges.json')
        with open(segment_fn, 'r') as flo:
            data = json.loads(flo.read())
        segment_storage.put(segments.from_raw(data))

        storages = {
            'splits': split_storage,
            'segments': segment_storage,
            'impressions': InMemoryImpressionStorage(5000),
            'events': InMemoryEventStorage(5000),
            'telemetry': InMemoryTelemetryStorage()
        }
        impmanager = ImpressionsManager(ImpressionsMode.OPTIMIZED, True)
        recorder = StandardRecorder(impmanager, storages['telemetry'], storages['events'],
                                    storages['impressions'])
        self.factory = SplitFactory('some_api_key', storages, True, recorder)  # pylint:disable=attribute-defined-outside-init
コード例 #3
0
    def test_track(self, mocker):
        """Test that destroy/destroyed calls are forwarded to the factory."""
        split_storage = mocker.Mock(spec=SplitStorage)
        segment_storage = mocker.Mock(spec=SegmentStorage)
        impression_storage = mocker.Mock(spec=ImpressionStorage)
        event_storage = mocker.Mock(spec=EventStorage)
        event_storage.put.return_value = True

        def _get_storage_mock(name):
            return {
                'splits': split_storage,
                'segments': segment_storage,
                'impressions': impression_storage,
                'events': event_storage,
            }[name]
        factory = mocker.Mock(spec=SplitFactory)
        factory._get_storage = _get_storage_mock
        destroyed_mock = mocker.PropertyMock()
        destroyed_mock.return_value = False
        factory._waiting_fork.return_value = False
        type(factory).destroyed = destroyed_mock
        factory._apikey = 'test'
        mocker.patch('splitio.client.client.utctime_ms', new=lambda: 1000)

        impmanager = mocker.Mock(spec=ImpressionManager)
        recorder = StandardRecorder(impmanager, event_storage, impression_storage)
        client = Client(factory, recorder, True)
        assert client.track('key', 'user', 'purchase', 12) is True
        assert mocker.call([
            EventWrapper(
                event=Event('key', 'user', 'purchase', 12, 1000, None),
                size=1024
            )
        ]) in event_storage.put.mock_calls
コード例 #4
0
    def test_destroy(self, mocker):
        """Test that destroy/destroyed calls are forwarded to the factory."""
        split_storage = mocker.Mock(spec=SplitStorage)
        segment_storage = mocker.Mock(spec=SegmentStorage)
        impression_storage = mocker.Mock(spec=ImpressionStorage)
        event_storage = mocker.Mock(spec=EventStorage)

        def _get_storage_mock(name):
            return {
                'splits': split_storage,
                'segments': segment_storage,
                'impressions': impression_storage,
                'events': event_storage,
            }[name]
        factory = mocker.Mock(spec=SplitFactory)
        destroyed_mock = mocker.PropertyMock()
        type(factory).destroyed = destroyed_mock

        impmanager = mocker.Mock(spec=ImpressionManager)
        recorder = StandardRecorder(impmanager, event_storage, impression_storage)
        client = Client(factory, recorder, True)
        client.destroy()
        assert factory.destroy.mock_calls == [mocker.call()]
        assert client.destroyed is not None
        assert destroyed_mock.mock_calls == [mocker.call()]
コード例 #5
0
def _build_localhost_factory(cfg):
    """Build and return a localhost factory for testing/development purposes."""
    storages = {
        'splits': InMemorySplitStorage(),
        'segments': InMemorySegmentStorage(),  # not used, just to avoid possible future errors.
        'impressions': LocalhostImpressionsStorage(),
        'events': LocalhostEventsStorage(),
    }

    synchronizers = SplitSynchronizers(
        LocalSplitSynchronizer(cfg['splitFile'], storages['splits']),
        None, None, None, None,
    )

    tasks = SplitTasks(
        SplitSynchronizationTask(
            synchronizers.split_sync.synchronize_splits,
            cfg['featuresRefreshRate'],
        ), None, None, None, None,
    )

    sdk_metadata = util.get_metadata(cfg)
    ready_event = threading.Event()
    synchronizer = LocalhostSynchronizer(synchronizers, tasks)
    manager = Manager(ready_event, synchronizer, None, False, sdk_metadata)
    manager.start()
    recorder = StandardRecorder(
        ImpressionsManager(cfg['impressionsMode'], True, None),
        storages['events'],
        storages['impressions'],
    )
    return SplitFactory(
        'localhost',
        storages,
        False,
        recorder,
        manager,
        ready_event
    )
コード例 #6
0
    def test_get_treatment_with_config(self, mocker):
        """Test get_treatment execution paths."""
        split_storage = mocker.Mock(spec=SplitStorage)
        segment_storage = mocker.Mock(spec=SegmentStorage)
        impression_storage = mocker.Mock(spec=ImpressionStorage)
        event_storage = mocker.Mock(spec=EventStorage)

        def _get_storage_mock(name):
            return {
                'splits': split_storage,
                'segments': segment_storage,
                'impressions': impression_storage,
                'events': event_storage,
            }[name]

        destroyed_property = mocker.PropertyMock()
        destroyed_property.return_value = False

        factory = mocker.Mock(spec=SplitFactory)
        factory._get_storage.side_effect = _get_storage_mock
        factory._waiting_fork.return_value = False
        type(factory).destroyed = destroyed_property

        mocker.patch('splitio.client.client.utctime_ms', new=lambda: 1000)
        mocker.patch('splitio.client.client.get_latency_bucket_index', new=lambda x: 5)

        impmanager = mocker.Mock(spec=ImpressionManager)
        recorder = StandardRecorder(impmanager, event_storage, impression_storage)
        client = Client(factory, recorder, True)
        client._evaluator = mocker.Mock(spec=Evaluator)
        client._evaluator.evaluate_feature.return_value = {
            'treatment': 'on',
            'configurations': '{"some_config": True}',
            'impression': {
                'label': 'some_label',
                'change_number': 123
            }
        }
        _logger = mocker.Mock()
        client._send_impression_to_listener = mocker.Mock()

        assert client.get_treatment_with_config(
            'some_key',
            'some_feature'
        ) == ('on', '{"some_config": True}')
        assert mocker.call(
            [(Impression('some_key', 'some_feature', 'on', 'some_label', 123, None, 1000), None)]
        ) in impmanager.process_impressions.mock_calls
        assert _logger.mock_calls == []

        # Test with client not ready
        ready_property = mocker.PropertyMock()
        ready_property.return_value = False
        type(factory).ready = ready_property
        impmanager.process_impressions.reset_mock()
        assert client.get_treatment_with_config('some_key', 'some_feature', {'some_attribute': 1}) == ('control', None)
        assert mocker.call(
            [(Impression('some_key', 'some_feature', 'control', Label.NOT_READY, mocker.ANY, mocker.ANY, mocker.ANY),
              {'some_attribute': 1})]
        ) in impmanager.process_impressions.mock_calls

        # Test with exception:
        ready_property.return_value = True
        split_storage.get_change_number.return_value = -1

        def _raise(*_):
            raise Exception('something')
        client._evaluator.evaluate_feature.side_effect = _raise
        assert client.get_treatment_with_config('some_key', 'some_feature') == ('control', None)
        assert mocker.call(
            [(Impression('some_key', 'some_feature', 'control', 'exception', -1, None, 1000), None)]
        ) in impmanager.process_impressions.mock_calls
コード例 #7
0
ファイル: test_client.py プロジェクト: cfogg/python-client
    def test_get_treatments_with_config(self, mocker):
        """Test get_treatment execution paths."""
        split_storage = mocker.Mock(spec=SplitStorage)
        segment_storage = mocker.Mock(spec=SegmentStorage)
        impression_storage = mocker.Mock(spec=ImpressionStorage)
        event_storage = mocker.Mock(spec=EventStorage)
        telemetry_storage = mocker.Mock(spec=TelemetryStorage)

        def _get_storage_mock(name):
            return {
                'splits': split_storage,
                'segments': segment_storage,
                'impressions': impression_storage,
                'events': event_storage,
                'telemetry': telemetry_storage
            }[name]

        destroyed_property = mocker.PropertyMock()
        destroyed_property.return_value = False

        factory = mocker.Mock(spec=SplitFactory)
        factory._get_storage.side_effect = _get_storage_mock
        factory._waiting_fork.return_value = False
        type(factory).destroyed = destroyed_property

        mocker.patch('splitio.client.client.utctime_ms', new=lambda: 1000)
        mocker.patch('splitio.client.client.get_latency_bucket_index',
                     new=lambda x: 5)

        impmanager = mocker.Mock(spec=ImpressionManager)
        recorder = StandardRecorder(impmanager, telemetry_storage,
                                    event_storage, impression_storage)
        client = Client(factory, recorder, True)
        client._evaluator = mocker.Mock(spec=Evaluator)
        evaluation = {
            'treatment': 'on',
            'configurations': '{"color": "red"}',
            'impression': {
                'label': 'some_label',
                'change_number': 123
            }
        }
        client._evaluator.evaluate_features.return_value = {
            'f1': evaluation,
            'f2': evaluation
        }
        _logger = mocker.Mock()
        assert client.get_treatments_with_config('key', ['f1', 'f2']) == {
            'f1': ('on', '{"color": "red"}'),
            'f2': ('on', '{"color": "red"}')
        }

        impressions_called = impmanager.process_impressions.mock_calls[0][1][0]
        assert (Impression('key', 'f1', 'on', 'some_label', 123, None,
                           1000), None) in impressions_called
        assert (Impression('key', 'f2', 'on', 'some_label', 123, None,
                           1000), None) in impressions_called
        assert mocker.call('sdk.getTreatmentsWithConfig',
                           5) in telemetry_storage.inc_latency.mock_calls
        assert _logger.mock_calls == []

        # Test with client not ready
        ready_property = mocker.PropertyMock()
        ready_property.return_value = False
        type(factory).ready = ready_property
        impmanager.process_impressions.reset_mock()
        assert client.get_treatments_with_config('some_key', ['some_feature'],
                                                 {'some_attribute': 1}) == {
                                                     'some_feature':
                                                     ('control', None)
                                                 }
        assert mocker.call([
            (Impression('some_key', 'some_feature', 'control', Label.NOT_READY,
                        mocker.ANY, mocker.ANY, mocker.ANY), {
                            'some_attribute': 1
                        })
        ]) in impmanager.process_impressions.mock_calls

        # Test with exception:
        ready_property.return_value = True
        split_storage.get_change_number.return_value = -1

        def _raise(*_):
            raise Exception('something')

        client._evaluator.evaluate_features.side_effect = _raise
        assert client.get_treatments_with_config('key', ['f1', 'f2']) == {
            'f1': ('control', None),
            'f2': ('control', None)
        }
        assert len(telemetry_storage.inc_latency.mock_calls) == 2
コード例 #8
0
def _build_in_memory_factory(api_key, cfg, sdk_url=None, events_url=None,  # pylint:disable=too-many-arguments,too-many-locals
                             auth_api_base_url=None, streaming_api_base_url=None):
    """Build and return a split factory tailored to the supplied config."""
    if not input_validator.validate_factory_instantiation(api_key):
        return None

    http_client = HttpClient(
        sdk_url=sdk_url,
        events_url=events_url,
        auth_url=auth_api_base_url,
        timeout=cfg.get('connectionTimeout')
    )

    sdk_metadata = util.get_metadata(cfg)
    apis = {
        'auth': AuthAPI(http_client, api_key, sdk_metadata),
        'splits': SplitsAPI(http_client, api_key, sdk_metadata),
        'segments': SegmentsAPI(http_client, api_key, sdk_metadata),
        'impressions': ImpressionsAPI(http_client, api_key, sdk_metadata, cfg['impressionsMode']),
        'events': EventsAPI(http_client, api_key, sdk_metadata),
    }

    if not input_validator.validate_apikey_type(apis['segments']):
        return None

    storages = {
        'splits': InMemorySplitStorage(),
        'segments': InMemorySegmentStorage(),
        'impressions': InMemoryImpressionStorage(cfg['impressionsQueueSize']),
        'events': InMemoryEventStorage(cfg['eventsQueueSize']),
    }

    imp_manager = ImpressionsManager(
        cfg['impressionsMode'],
        True,
        _wrap_impression_listener(cfg['impressionListener'], sdk_metadata))

    synchronizers = SplitSynchronizers(
        SplitSynchronizer(apis['splits'], storages['splits']),
        SegmentSynchronizer(apis['segments'], storages['splits'], storages['segments']),
        ImpressionSynchronizer(apis['impressions'], storages['impressions'],
                               cfg['impressionsBulkSize']),
        EventSynchronizer(apis['events'], storages['events'], cfg['eventsBulkSize']),
        ImpressionsCountSynchronizer(apis['impressions'], imp_manager),
    )

    tasks = SplitTasks(
        SplitSynchronizationTask(
            synchronizers.split_sync.synchronize_splits,
            cfg['featuresRefreshRate'],
        ),
        SegmentSynchronizationTask(
            synchronizers.segment_sync.synchronize_segments,
            cfg['segmentsRefreshRate'],
        ),
        ImpressionsSyncTask(
            synchronizers.impressions_sync.synchronize_impressions,
            cfg['impressionsRefreshRate'],
        ),
        EventsSyncTask(synchronizers.events_sync.synchronize_events, cfg['eventsPushRate']),
        ImpressionsCountSyncTask(synchronizers.impressions_count_sync.synchronize_counters)
    )

    synchronizer = Synchronizer(synchronizers, tasks)

    preforked_initialization = cfg.get('preforkedInitialization', False)

    sdk_ready_flag = threading.Event() if not preforked_initialization else None
    manager = Manager(sdk_ready_flag, synchronizer, apis['auth'], cfg['streamingEnabled'],
                      sdk_metadata, streaming_api_base_url, api_key[-4:])

    storages['events'].set_queue_full_hook(tasks.events_task.flush)
    storages['impressions'].set_queue_full_hook(tasks.impressions_task.flush)

    recorder = StandardRecorder(
        imp_manager,
        storages['events'],
        storages['impressions'],
    )

    if preforked_initialization:
        synchronizer.sync_all()
        synchronizer._split_synchronizers._segment_sync.shutdown()
        return SplitFactory(api_key, storages, cfg['labelsEnabled'],
                            recorder, manager, preforked_initialization=preforked_initialization)

    initialization_thread = threading.Thread(target=manager.start, name="SDKInitializer")
    initialization_thread.setDaemon(True)
    initialization_thread.start()

    return SplitFactory(api_key, storages, cfg['labelsEnabled'],
                        recorder, manager, sdk_ready_flag)