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(), 'telemetry': LocalhostTelemetryStorage() } synchronizers = SplitSynchronizers( LocalSplitSynchronizer(cfg['splitFile'], storages['splits']), None, None, None, None, None, ) tasks = SplitTasks( SplitSynchronizationTask( synchronizers.split_sync.synchronize_splits, cfg['featuresRefreshRate'], ), None, None, None, None, None, ) ready_event = threading.Event() synchronizer = LocalhostSynchronizer(synchronizers, tasks) manager = Manager(ready_event, synchronizer, None, False) manager.start() return SplitFactory( 'localhost', storages, False, ImpressionsManager(storages['impressions'].put, cfg['impressionsMode'], True, None), manager, ready_event )
def test_sync_all(self, mocker): split_storage = mocker.Mock(spec=SplitStorage) split_storage.get_change_number.return_value = 123 split_storage.get_segment_names.return_value = ['segmentA'] split_api = mocker.Mock() split_api.fetch_splits.return_value = {'splits': self.splits, 'since': 123, 'till': 123} split_sync = SplitSynchronizer(split_api, split_storage) segment_storage = mocker.Mock(spec=SegmentStorage) segment_storage.get_change_number.return_value = 123 segment_api = mocker.Mock() segment_api.fetch_segment.return_value = {'name': 'segmentA', 'added': ['key1', 'key2', 'key3'], 'removed': [], 'since': 123, 'till': 123} segment_sync = SegmentSynchronizer(segment_api, split_storage, segment_storage) split_synchronizers = SplitSynchronizers(split_sync, segment_sync, mocker.Mock(), mocker.Mock(), mocker.Mock()) synchronizer = Synchronizer(split_synchronizers, mocker.Mock(spec=SplitTasks)) synchronizer.sync_all() inserted_split = split_storage.put.mock_calls[0][1][0] assert isinstance(inserted_split, Split) assert inserted_split.name == 'some_name' inserted_segment = segment_storage.update.mock_calls[0][1] assert inserted_segment[0] == 'segmentA' assert inserted_segment[1] == ['key1', 'key2', 'key3'] assert inserted_segment[2] == []
def test_synchronize_splits(self, mocker): split_storage = InMemorySplitStorage() split_api = mocker.Mock() split_api.fetch_splits.return_value = {'splits': self.splits, 'since': 123, 'till': 123} split_sync = SplitSynchronizer(split_api, split_storage) segment_storage = InMemorySegmentStorage() segment_api = mocker.Mock() segment_api.fetch_segment.return_value = {'name': 'segmentA', 'added': ['key1', 'key2', 'key3'], 'removed': [], 'since': 123, 'till': 123} segment_sync = SegmentSynchronizer(segment_api, split_storage, segment_storage) split_synchronizers = SplitSynchronizers(split_sync, segment_sync, mocker.Mock(), mocker.Mock(), mocker.Mock()) synchronizer = Synchronizer(split_synchronizers, mocker.Mock(spec=SplitTasks)) synchronizer.synchronize_splits(123) inserted_split = split_storage.get('some_name') assert isinstance(inserted_split, Split) assert inserted_split.name == 'some_name' if not segment_sync._worker_pool.wait_for_completion(): inserted_segment = segment_storage.get('segmentA') assert inserted_segment.name == 'segmentA' assert inserted_segment.keys == {'key1', 'key2', 'key3'}
def test_shutdown(self, mocker): def stop_mock(event): event.set() return def stop_mock_2(): return split_task = mocker.Mock(spec=SplitSynchronizationTask) split_task.stop.side_effect = stop_mock_2 segment_task = mocker.Mock(spec=SegmentSynchronizationTask) segment_task.stop.side_effect = stop_mock_2 impression_task = mocker.Mock(spec=ImpressionsSyncTask) impression_task.stop.side_effect = stop_mock impression_count_task = mocker.Mock(spec=ImpressionsCountSyncTask) impression_count_task.stop.side_effect = stop_mock event_task = mocker.Mock(spec=EventsSyncTask) event_task.stop.side_effect = stop_mock segment_sync = mocker.Mock(spec=SegmentSynchronizer) split_synchronizers = SplitSynchronizers(mocker.Mock(), segment_sync, mocker.Mock(), mocker.Mock(), mocker.Mock()) split_tasks = SplitTasks(split_task, segment_task, impression_task, event_task, impression_count_task) synchronizer = Synchronizer(split_synchronizers, split_tasks) synchronizer.shutdown(True) assert len(split_task.stop.mock_calls) == 1 assert len(segment_task.stop.mock_calls) == 1 assert len(segment_sync.shutdown.mock_calls) == 1 assert len(impression_task.stop.mock_calls) == 1 assert len(impression_count_task.stop.mock_calls) == 1 assert len(event_task.stop.mock_calls) == 1
def test_error(self, mocker): split_task = mocker.Mock(spec=SplitSynchronizationTask) split_tasks = SplitTasks(split_task, mocker.Mock(), mocker.Mock(), mocker.Mock(), mocker.Mock(), mocker.Mock()) storage = mocker.Mock(spec=SplitStorage) api = mocker.Mock() def run(x): raise APIException("something broke") api.fetch_splits.side_effect = run storage.get_change_number.return_value = -1 split_sync = SplitSynchronizer(api, storage) synchronizers = SplitSynchronizers(split_sync, mocker.Mock(), mocker.Mock(), mocker.Mock(), mocker.Mock(), mocker.Mock()) synchronizer = Synchronizer(synchronizers, split_tasks) manager = Manager(threading.Event(), synchronizer, mocker.Mock(), False) with pytest.raises(APIException): manager.start()
def test_stop_periodic_fetching(self, mocker): split_task = mocker.Mock(spec=SplitSynchronizationTask) segment_task = mocker.Mock(spec=SegmentSynchronizationTask) segment_sync = mocker.Mock(spec=SegmentSynchronizer) split_synchronizers = SplitSynchronizers(mocker.Mock(), segment_sync, mocker.Mock(), mocker.Mock(), mocker.Mock()) split_tasks = SplitTasks(split_task, segment_task, mocker.Mock(), mocker.Mock(), mocker.Mock()) synchronizer = Synchronizer(split_synchronizers, split_tasks) synchronizer.stop_periodic_fetching() assert len(split_task.stop.mock_calls) == 1 assert len(segment_task.stop.mock_calls) == 1 assert len(segment_sync.shutdown.mock_calls) == 0
def test_sync_all_failed_splits(self, mocker): api = mocker.Mock() storage = mocker.Mock() def run(x, c): raise APIException("something broke") api.fetch_splits.side_effect = run split_sync = SplitSynchronizer(api, storage) split_synchronizers = SplitSynchronizers(split_sync, mocker.Mock(), mocker.Mock(), mocker.Mock(), mocker.Mock()) sychronizer = Synchronizer(split_synchronizers, mocker.Mock(spec=SplitTasks)) sychronizer.synchronize_splits(None) # APIExceptions are handled locally and should not be propagated! sychronizer.sync_all() # sync_all should not throw!
def test_sync_all_failed_segments(self, mocker): api = mocker.Mock() storage = mocker.Mock() split_storage = mocker.Mock(spec=SplitStorage) split_storage.get_segment_names.return_value = ['segmentA'] split_sync = mocker.Mock(spec=SplitSynchronizer) split_sync.synchronize_splits.return_value = None def run(x, y): raise APIException("something broke") api.fetch_segment.side_effect = run segment_sync = SegmentSynchronizer(api, split_storage, storage) split_synchronizers = SplitSynchronizers(split_sync, segment_sync, mocker.Mock(), mocker.Mock(), mocker.Mock()) sychronizer = Synchronizer(split_synchronizers, mocker.Mock(spec=SplitTasks)) sychronizer.sync_all() # SyncAll should not throw! assert not sychronizer._synchronize_segments()
def test_sync_all_failed_splits(self, mocker): api = mocker.Mock() storage = mocker.Mock() def run(x): raise APIException("something broke") api.fetch_splits.side_effect = run split_sync = SplitSynchronizer(api, storage) split_synchronizers = SplitSynchronizers(split_sync, mocker.Mock(), mocker.Mock(), mocker.Mock(), mocker.Mock(), mocker.Mock()) sychronizer = Synchronizer(split_synchronizers, mocker.Mock(spec=SplitTasks)) with pytest.raises(APIException): sychronizer.synchronize_splits(None) with pytest.raises(APIException): sychronizer.sync_all()
def test_synchronize_splits_calling_segment_sync_once(self, mocker): split_storage = InMemorySplitStorage() split_api = mocker.Mock() split_api.fetch_splits.return_value = {'splits': self.splits, 'since': 123, 'till': 123} split_sync = SplitSynchronizer(split_api, split_storage) counts = {'segments': 0} def sync_segments(*_): """Sync Segments.""" counts['segments'] += 1 return True segment_sync = mocker.Mock() segment_sync.synchronize_segments.side_effect = sync_segments segment_sync.segment_exist_in_storage.return_value = False split_synchronizers = SplitSynchronizers(split_sync, segment_sync, mocker.Mock(), mocker.Mock(), mocker.Mock()) synchronizer = Synchronizer(split_synchronizers, mocker.Mock(spec=SplitTasks)) synchronizer.synchronize_splits(123, True) assert counts['segments'] == 1
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), 'segments': SegmentsAPI(http_client, api_key), 'impressions': ImpressionsAPI(http_client, api_key, sdk_metadata, cfg['impressionsMode']), 'events': EventsAPI(http_client, api_key, sdk_metadata), 'telemetry': TelemetryAPI(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']), 'telemetry': InMemoryTelemetryStorage() } imp_manager = ImpressionsManager( storages['impressions'].put, 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']), TelemetrySynchronizer(apis['telemetry'], storages['telemetry']), 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']), TelemetrySynchronizationTask( synchronizers.telemetry_sync.synchronize_telemetry, cfg['metricsRefreshRate'], ), ImpressionsCountSyncTask(synchronizers.impressions_count_sync.synchronize_counters) ) synchronizer = Synchronizer(synchronizers, tasks) sdk_ready_flag = threading.Event() manager = Manager(sdk_ready_flag, synchronizer, apis['auth'], cfg['streamingEnabled'], streaming_api_base_url) initialization_thread = threading.Thread(target=manager.start, name="SDKInitializer") initialization_thread.setDaemon(True) initialization_thread.start() storages['events'].set_queue_full_hook(tasks.events_task.flush) storages['impressions'].set_queue_full_hook(tasks.impressions_task.flush) return SplitFactory(api_key, storages, cfg['labelsEnabled'], imp_manager, manager, sdk_ready_flag)
def test_destroy_with_event(self, mocker): """Test that tasks are shutdown and data is flushed when destroy is called.""" def stop_mock(event): time.sleep(0.1) event.set() return def stop_mock_2(): return split_async_task_mock = mocker.Mock(spec=asynctask.AsyncTask) split_async_task_mock.stop.side_effect = stop_mock_2 def _split_task_init_mock(self, synchronize_splits, period): self._task = split_async_task_mock self._period = period mocker.patch('splitio.client.factory.SplitSynchronizationTask.__init__', new=_split_task_init_mock) segment_async_task_mock = mocker.Mock(spec=asynctask.AsyncTask) segment_async_task_mock.stop.side_effect = stop_mock_2 def _segment_task_init_mock(self, synchronize_segments, period): self._task = segment_async_task_mock self._period = period mocker.patch('splitio.client.factory.SegmentSynchronizationTask.__init__', new=_segment_task_init_mock) imp_async_task_mock = mocker.Mock(spec=asynctask.AsyncTask) imp_async_task_mock.stop.side_effect = stop_mock def _imppression_task_init_mock(self, synchronize_impressions, period): self._period = period self._task = imp_async_task_mock mocker.patch('splitio.client.factory.ImpressionsSyncTask.__init__', new=_imppression_task_init_mock) evt_async_task_mock = mocker.Mock(spec=asynctask.AsyncTask) evt_async_task_mock.stop.side_effect = stop_mock def _event_task_init_mock(self, synchronize_events, period): self._period = period self._task = evt_async_task_mock mocker.patch('splitio.client.factory.EventsSyncTask.__init__', new=_event_task_init_mock) telemetry_async_task_mock = mocker.Mock(spec=asynctask.AsyncTask) telemetry_async_task_mock.stop.side_effect = stop_mock_2 def _telemetry_task_init_mock(self, synchronize_counters, period): self._period = period self._task = telemetry_async_task_mock mocker.patch('splitio.client.factory.ImpressionsCountSyncTask.__init__', new=_telemetry_task_init_mock) imp_count_async_task_mock = mocker.Mock(spec=asynctask.AsyncTask) imp_count_async_task_mock.stop.side_effect = stop_mock def _imppression_count_task_init_mock(self, synchronize_counters): self._task = imp_count_async_task_mock mocker.patch('splitio.client.factory.ImpressionsCountSyncTask.__init__', new=_imppression_count_task_init_mock) split_sync = mocker.Mock(spec=SplitSynchronizer) split_sync.synchronize_splits.return_values = None segment_sync = mocker.Mock(spec=SegmentSynchronizer) segment_sync.synchronize_segments.return_values = None syncs = SplitSynchronizers(split_sync, segment_sync, mocker.Mock(), mocker.Mock(), mocker.Mock(), mocker.Mock()) tasks = SplitTasks(split_async_task_mock, segment_async_task_mock, imp_async_task_mock, evt_async_task_mock, telemetry_async_task_mock, imp_count_async_task_mock) # Setup synchronizer def _split_synchronizer(self, ready_flag, some, auth_api, streaming_enabled, sse_url=None): synchronizer = Synchronizer(syncs, tasks) self._ready_flag = ready_flag self._synchronizer = synchronizer self._streaming_enabled = False mocker.patch('splitio.sync.manager.Manager.__init__', new=_split_synchronizer) # Start factory and make assertions factory = get_factory('some_api_key') factory.block_until_ready() assert factory.ready assert factory.destroyed is False event = threading.Event() factory.destroy(event) assert not event.is_set() time.sleep(1) assert event.is_set() assert len(imp_async_task_mock.stop.mock_calls) == 1 assert len(evt_async_task_mock.stop.mock_calls) == 1 assert len(telemetry_async_task_mock.stop.mock_calls) == 1 assert len(imp_count_async_task_mock.stop.mock_calls) == 1 assert factory.destroyed is True