def test_put_fetch_contains_ip_address_disabled(self): """Test storing and retrieving splits in redis.""" adapter = _build_default_client({}) cfg = DEFAULT_CONFIG.copy() cfg.update({'IPAddressesEnabled': False}) metadata = get_metadata(cfg) storage = RedisTelemetryStorage(adapter, metadata) try: storage.inc_counter('counter1') storage.inc_counter('counter1') storage.inc_counter('counter2') assert adapter.get(storage._get_counter_key('counter1')) == '2' assert adapter.get(storage._get_counter_key('counter2')) == '1' storage.inc_latency('latency1', 3) storage.inc_latency('latency1', 3) storage.inc_latency('latency2', 6) assert adapter.get(storage._get_latency_key('latency1', 3)) == '2' assert adapter.get(storage._get_latency_key('latency2', 6)) == '1' storage.put_gauge('gauge1', 3) storage.put_gauge('gauge2', 1) assert adapter.get(storage._get_gauge_key('gauge1')) == '3' assert adapter.get(storage._get_gauge_key('gauge2')) == '1' finally: adapter.delete(storage._get_counter_key('counter1'), storage._get_counter_key('counter2'), storage._get_latency_key('latency1', 3), storage._get_latency_key('latency2', 6), storage._get_gauge_key('gauge1'), storage._get_gauge_key('gauge2'))
def test_post_impressions_ip_address_disabled(self, mocker): """Test impressions posting API call.""" httpclient = mocker.Mock(spec=client.HttpClient) httpclient.post.return_value = client.HttpResponse(200, '') cfg = DEFAULT_CONFIG.copy() cfg.update({'IPAddressesEnabled': False}) sdk_metadata = get_metadata(cfg) impressions_api = impressions.ImpressionsAPI(httpclient, 'some_api_key', sdk_metadata) response = impressions_api.flush_impressions(self.impressions) call_made = httpclient.post.mock_calls[0] # validate positional arguments assert call_made[1] == ('events', '/testImpressions/bulk', 'some_api_key') # validate key-value args (headers) assert call_made[2]['extra_headers'] == { 'SplitSDKVersion': 'python-%s' % __version__, } # validate key-value args (body) assert call_made[2]['body'] == self.expectedImpressions
def _get_config(user_config): """ Get sdk configuration using defaults + user overrides. :param user_config: User configuration. :type user_config: dict :return: Calculated configuration. :rtype: dict """ sdk_config = DEFAULT_CONFIG.copy() sdk_config.update(user_config) return sdk_config
def test_put_fetch_contains_ip_address_disabled(self): """Test storing and retrieving splits in redis.""" adapter = _build_default_client({}) try: cfg = DEFAULT_CONFIG.copy() cfg.update({'IPAddressesEnabled': False}) self._put_events(adapter, get_metadata(cfg)) evts = adapter.lrange('SPLITIO.events', 0, 2) assert len(evts) == 3 for rawEvent in evts: event = json.loads(rawEvent) assert event['m']['i'] == 'NA' assert event['m']['n'] == 'NA' finally: adapter.delete('SPLITIO.events')
def _build_uwsgi_factory(config): """Build and return a split factory with redis-based storage.""" cfg = DEFAULT_CONFIG.copy() cfg.update(config) sdk_metadata = util.get_metadata(cfg) uwsgi_adapter = get_uwsgi() storages = { 'splits': UWSGISplitStorage(uwsgi_adapter), 'segments': UWSGISegmentStorage(uwsgi_adapter), 'impressions': UWSGIImpressionStorage(uwsgi_adapter), 'events': UWSGIEventStorage(uwsgi_adapter), 'telemetry': UWSGITelemetryStorage(uwsgi_adapter) } return SplitFactory(storages, cfg['labelsEnabled'], impression_listener=_wrap_impression_listener( cfg['impressionListener'], sdk_metadata))
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 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_post_impressions(self, mocker): """Test impressions posting API call.""" httpclient = mocker.Mock(spec=client.HttpClient) httpclient.post.return_value = client.HttpResponse(200, '') cfg = DEFAULT_CONFIG.copy() cfg.update({ 'IPAddressesEnabled': True, 'machineName': 'some_machine_name', 'machineIp': '123.123.123.123' }) sdk_metadata = get_metadata(cfg) impressions_api = impressions.ImpressionsAPI(httpclient, 'some_api_key', sdk_metadata) response = impressions_api.flush_impressions(self.impressions) call_made = httpclient.post.mock_calls[0] # validate positional arguments assert call_made[1] == ('events', '/testImpressions/bulk', 'some_api_key') # validate key-value args (headers) assert call_made[2]['extra_headers'] == { 'SplitSDKVersion': 'python-%s' % __version__, 'SplitSDKMachineIP': '123.123.123.123', 'SplitSDKMachineName': 'some_machine_name', 'SplitSDKImpressionsMode': 'OPTIMIZED' } # validate key-value args (body) assert call_made[2]['body'] == self.expectedImpressions httpclient.reset_mock() def raise_exception(*args, **kwargs): raise client.HttpClientException('some_message') httpclient.post.side_effect = raise_exception with pytest.raises(APIException) as exc_info: response = impressions_api.flush_impressions(self.impressions) assert exc_info.type == APIException assert exc_info.value.message == 'some_message'
def test_auth(self, mocker): """Test auth API call.""" token = "eyJhbGciOiJIUzI1NiIsImtpZCI6IjVZOU05US45QnJtR0EiLCJ0eXAiOiJKV1QifQ.eyJ4LWFibHktY2FwYWJpbGl0eSI6IntcIk56TTJNREk1TXpjMF9NVGd5TlRnMU1UZ3dOZz09X3NlZ21lbnRzXCI6W1wic3Vic2NyaWJlXCJdLFwiTnpNMk1ESTVNemMwX01UZ3lOVGcxTVRnd05nPT1fc3BsaXRzXCI6W1wic3Vic2NyaWJlXCJdLFwiY29udHJvbF9wcmlcIjpbXCJzdWJzY3JpYmVcIixcImNoYW5uZWwtbWV0YWRhdGE6cHVibGlzaGVyc1wiXSxcImNvbnRyb2xfc2VjXCI6W1wic3Vic2NyaWJlXCIsXCJjaGFubmVsLW1ldGFkYXRhOnB1Ymxpc2hlcnNcIl19IiwieC1hYmx5LWNsaWVudElkIjoiY2xpZW50SWQiLCJleHAiOjE2MDIwODgxMjcsImlhdCI6MTYwMjA4NDUyN30.5_MjWonhs6yoFhw44hNJm3H7_YMjXpSW105DwjjppqE" httpclient = mocker.Mock(spec=client.HttpClient) payload = '{{"pushEnabled": true, "token": "{token}"}}'.format( token=token) cfg = DEFAULT_CONFIG.copy() cfg.update({ 'IPAddressesEnabled': True, 'machineName': 'some_machine_name', 'machineIp': '123.123.123.123' }) sdk_metadata = get_metadata(cfg) httpclient.get.return_value = client.HttpResponse(200, payload) auth_api = auth.AuthAPI(httpclient, 'some_api_key', sdk_metadata) response = auth_api.authenticate() assert response.push_enabled == True assert response.token == token call_made = httpclient.get.mock_calls[0] # validate positional arguments assert call_made[1] == ('auth', '/v2/auth', 'some_api_key') # validate key-value args (headers) assert call_made[2]['extra_headers'] == { 'SplitSDKVersion': 'python-%s' % __version__, 'SplitSDKMachineIP': '123.123.123.123', 'SplitSDKMachineName': 'some_machine_name' } httpclient.reset_mock() def raise_exception(*args, **kwargs): raise client.HttpClientException('some_message') httpclient.get.side_effect = raise_exception with pytest.raises(APIException) as exc_info: response = auth_api.authenticate() assert exc_info.type == APIException assert exc_info.value.message == 'some_message'
def _build_localhost_factory(config): """Build and return a localhost factory for testing/development purposes.""" cfg = DEFAULT_CONFIG.copy() cfg.update(config) storages = { 'splits': InMemorySplitStorage(), 'segments': InMemorySegmentStorage( ), # not used, just to avoid possible future errors. 'impressions': LocalhostImpressionsStorage(), 'events': LocalhostEventsStorage(), 'telemetry': LocalhostTelemetryStorage() } ready_event = threading.Event() tasks = { 'splits': LocalhostSplitSynchronizationTask(cfg['splitFile'], storages['splits'], cfg['featuresRefreshRate'], ready_event) } tasks['splits'].start() return SplitFactory('localhost', storages, False, None, tasks, ready_event)
def _build_in_memory_factory(api_key, config, sdk_url=None, events_url=None): # pylint: disable=too-many-locals """Build and return a split factory tailored to the supplied config.""" if not input_validator.validate_factory_instantiation(api_key): return None cfg = DEFAULT_CONFIG.copy() cfg.update(config) http_client = HttpClient(sdk_url=sdk_url, events_url=events_url, timeout=cfg.get('connectionTimeout')) sdk_metadata = util.get_metadata(cfg) apis = { 'splits': SplitsAPI(http_client, api_key), 'segments': SegmentsAPI(http_client, api_key), 'impressions': ImpressionsAPI(http_client, api_key, sdk_metadata), '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() } # Synchronization flags splits_ready_flag = threading.Event() segments_ready_flag = threading.Event() sdk_ready_flag = threading.Event() tasks = { 'splits': SplitSynchronizationTask(apis['splits'], storages['splits'], cfg['featuresRefreshRate'], splits_ready_flag), 'segments': SegmentSynchronizationTask(apis['segments'], storages['segments'], storages['splits'], cfg['segmentsRefreshRate'], segments_ready_flag), 'impressions': ImpressionsSyncTask(apis['impressions'], storages['impressions'], cfg['impressionsRefreshRate'], cfg['impressionsBulkSize']), 'events': EventsSyncTask( apis['events'], storages['events'], cfg['eventsPushRate'], cfg['eventsBulkSize'], ), 'telemetry': TelemetrySynchronizationTask(apis['telemetry'], storages['telemetry'], cfg['metricsRefreshRate']) } # Start tasks that have no dependencies tasks['splits'].start() tasks['impressions'].start() tasks['events'].start() tasks['telemetry'].start() storages['events'].set_queue_full_hook(tasks['events'].flush) storages['impressions'].set_queue_full_hook(tasks['impressions'].flush) def split_ready_task(): """Wait for splits to be ready and start fetching segments.""" splits_ready_flag.wait() tasks['segments'].start() def segment_ready_task(): """Wait for segments to be ready and set the main ready flag.""" segments_ready_flag.wait() sdk_ready_flag.set() split_completion_thread = threading.Thread(target=split_ready_task) split_completion_thread.setDaemon(True) split_completion_thread.start() segment_completion_thread = threading.Thread(target=segment_ready_task) segment_completion_thread.setDaemon(True) segment_completion_thread.start() return SplitFactory(api_key, storages, cfg['labelsEnabled'], apis, tasks, sdk_ready_flag, impression_listener=_wrap_impression_listener( cfg['impressionListener'], sdk_metadata))