def test_headers_from_metadata(self, mocker): """Test headers from metadata call.""" metadata = headers_from_metadata(SdkMetadata('1.0', 'some', '1.2.3.4')) assert metadata['SplitSDKVersion'] == '1.0' assert metadata['SplitSDKMachineIP'] == '1.2.3.4' assert metadata['SplitSDKMachineName'] == 'some' assert 'SplitSDKClientKey' not in metadata metadata = headers_from_metadata(SdkMetadata('1.0', 'some', '1.2.3.4'), 'abcd') assert metadata['SplitSDKVersion'] == '1.0' assert metadata['SplitSDKMachineIP'] == '1.2.3.4' assert metadata['SplitSDKMachineName'] == 'some' assert metadata['SplitSDKClientKey'] == 'abcd' metadata = headers_from_metadata(SdkMetadata('1.0', 'some', 'NA')) assert metadata['SplitSDKVersion'] == '1.0' assert 'SplitSDKMachineIP' not in metadata assert 'SplitSDKMachineName' not in metadata assert 'SplitSDKClientKey' not in metadata metadata = headers_from_metadata(SdkMetadata('1.0', 'some', 'unknown')) assert metadata['SplitSDKVersion'] == '1.0' assert 'SplitSDKMachineIP' not in metadata assert 'SplitSDKMachineName' not in metadata assert 'SplitSDKClientKey' not in metadata
def test_fetch_split_changes(self, mocker): """Test split changes fetching API call.""" httpclient = mocker.Mock(spec=client.HttpClient) httpclient.get.return_value = client.HttpResponse( 200, '{"prop1": "value1"}') split_api = splits.SplitsAPI(httpclient, 'some_api_key', SdkMetadata('1.0', 'some', '1.2.3.4')) response = split_api.fetch_splits(123) assert response['prop1'] == 'value1' assert httpclient.get.mock_calls == [ mocker.call('sdk', '/splitChanges', 'some_api_key', extra_headers={ 'SplitSDKVersion': '1.0', 'SplitSDKMachineIP': '1.2.3.4', 'SplitSDKMachineName': 'some' }, query={'since': 123}) ] 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 = split_api.fetch_splits(123) assert exc_info.type == APIException assert exc_info.value.message == 'some_message'
def test_split_sse_error(self): """Test correct initialization. Client ends the connection.""" events = [] def handler(event): """Handler.""" events.append(event) request_queue = Queue() server = SSEMockServer(request_queue) server.start() status = { 'on_connect': False, 'on_disconnect': False, } def on_connect(): """On connect handler.""" status['on_connect'] = True def on_disconnect(): """On disconnect handler.""" status['on_disconnect'] = True client = SplitSSEClient(handler, SdkMetadata('1.0', 'some', '1.2.3.4'), on_connect, on_disconnect, "abcd", base_url='http://localhost:' + str(server.port())) token = Token( True, 'some', { 'chan1': ['subscribe'], 'chan2': ['subscribe', 'channel-metadata:publishers'] }, 1, 2) server.publish({'event': 'error'}) # send an error event early to unblock start assert not client.start(token) request = request_queue.get(1) assert request.path == '/event-stream?v=1.1&accessToken=some&channels=chan1,[?occupancy=metrics.publishers]chan2' assert request.headers['accept'] == 'text/event-stream' assert request.headers['SplitSDKVersion'] == '1.0' assert request.headers['SplitSDKMachineIP'] == '1.2.3.4' assert request.headers['SplitSDKMachineName'] == 'some' assert request.headers['SplitSDKClientKey'] == 'abcd' server.publish(SSEMockServer.VIOLENT_REQUEST_END) server.stop() time.sleep(1) assert status['on_connect'] assert status['on_disconnect']
def test_start_streaming_false(self, mocker): splits_ready_event = threading.Event() synchronizer = mocker.Mock(spec=Synchronizer) manager = Manager(splits_ready_event, synchronizer, mocker.Mock(), False, SdkMetadata('1.0', 'some', '1.2.3.4')) manager.start() splits_ready_event.wait(2) assert splits_ready_event.is_set() assert len(synchronizer.sync_all.mock_calls) == 1 assert len(synchronizer.start_periodic_fetching.mock_calls) == 1 assert len(synchronizer.start_periodic_data_recording.mock_calls) == 1
def test_post_latencies(self, mocker): """Test impressions posting API call.""" httpclient = mocker.Mock(spec=client.HttpClient) httpclient.post.return_value = client.HttpResponse(200, '') sdk_metadata = SdkMetadata('python-1.2.3', 'some_machine_name', '123.123.123.123') telemetry_api = telemetry.TelemetryAPI(httpclient, 'some_api_key', sdk_metadata) response = telemetry_api.flush_latencies({ 'l1': [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 ] }) call_made = httpclient.post.mock_calls[0] # validate positional arguments assert call_made[1] == ('events', '/metrics/times', 'some_api_key') # validate key-value args (headers) assert call_made[2]['extra_headers'] == { 'SplitSDKVersion': 'python-1.2.3', 'SplitSDKMachineIP': '123.123.123.123', 'SplitSDKMachineName': 'some_machine_name' } # validate key-value args (body) assert call_made[2]['body'] == [{ 'name': 'l1', 'latencies': [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 ] }] 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 = telemetry_api.flush_latencies({ 'l1': [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 ] }) assert exc_info.type == APIException assert exc_info.value.message == 'some_message'
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 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 test_error(self, mocker): split_task = mocker.Mock(spec=SplitSynchronizationTask) split_tasks = SplitTasks(split_task, 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()) synchronizer = Synchronizer(synchronizers, split_tasks) manager = Manager(threading.Event(), synchronizer, mocker.Mock(), False, SdkMetadata('1.0', 'some', '1.2.3.4')) manager.start() # should not throw!
def test_post_events(self, mocker): """Test impressions posting API call.""" httpclient = mocker.Mock(spec=client.HttpClient) httpclient.post.return_value = client.HttpResponse(200, '') sdk_metadata = SdkMetadata('python-1.2.3', 'some_machine_name', '123.123.123.123') events_api = events.EventsAPI(httpclient, 'some_api_key', sdk_metadata) response = events_api.flush_events([ Event('k1', 'user', 'purchase', 12.50, 123456), Event('k2', 'user', 'purchase', 12.50, 123456), Event('k3', 'user', 'purchase', None, 123456), Event('k4', 'user', 'purchase', None, 123456) ]) call_made = httpclient.post.mock_calls[0] # validate positional arguments assert call_made[1] == ('events', '/events/bulk', 'some_api_key') # validate key-value args (headers) assert call_made[2]['extra_headers'] == { 'SplitSDKVersion': 'python-1.2.3', 'SplitSDKMachineIP': '123.123.123.123', 'SplitSDKMachineName': 'some_machine_name' } # validate key-value args (body) assert call_made[2]['body'] == [{ 'key': 'k1', 'trafficTypeName': 'user', 'eventTypeId': 'purchase', 'value': 12.50, 'timestamp': 123456 }, { 'key': 'k2', 'trafficTypeName': 'user', 'eventTypeId': 'purchase', 'value': 12.50, 'timestamp': 123456 }, { 'key': 'k3', 'trafficTypeName': 'user', 'eventTypeId': 'purchase', 'value': None, 'timestamp': 123456 }, { 'key': 'k4', 'trafficTypeName': 'user', 'eventTypeId': 'purchase', 'value': None, 'timestamp': 123456 }] 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 = events_api.flush_events([ Event('k1', 'user', 'purchase', 12.50, 123456), Event('k2', 'user', 'purchase', 12.50, 123456), Event('k3', 'user', 'purchase', None, 123456), Event('k4', 'user', 'purchase', None, 123456) ]) assert exc_info.type == APIException assert exc_info.value.message == 'some_message'
def test_post_impressions(self, mocker): """Test impressions posting API call.""" httpclient = mocker.Mock(spec=client.HttpClient) httpclient.post.return_value = client.HttpResponse(200, '') sdk_metadata = SdkMetadata('python-1.2.3', 'some_machine_name', '123.123.123.123') impressions_api = impressions.ImpressionsAPI(httpclient, 'some_api_key', sdk_metadata) response = impressions_api.flush_impressions([ Impression('k1', 'f1', 'on', 'l1', 123456, 'b1', 321654), Impression('k2', 'f2', 'off', 'l1', 123456, 'b1', 321654), Impression('k3', 'f1', 'on', 'l1', 123456, 'b1', 321654), ]) 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-1.2.3', 'SplitSDKMachineIP': '123.123.123.123', 'SplitSDKMachineName': 'some_machine_name' } # validate key-value args (body) assert call_made[2]['body'] == [{ 'testName': 'f1', 'keyImpressions': [ { 'keyName': 'k1', 'bucketingKey': 'b1', 'treatment': 'on', 'label': 'l1', 'time': 321654, 'changeNumber': 123456 }, { 'keyName': 'k3', 'bucketingKey': 'b1', 'treatment': 'on', 'label': 'l1', 'time': 321654, 'changeNumber': 123456 }, ], }, { 'testName': 'f2', 'keyImpressions': [ { 'keyName': 'k2', 'bucketingKey': 'b1', 'treatment': 'off', 'label': 'l1', 'time': 321654, 'changeNumber': 123456 }, ] }] 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([ Impression('k1', 'f1', 'on', 'l1', 123456, 'b1', 321654), Impression('k2', 'f2', 'off', 'l1', 123456, 'b1', 321654), Impression('k3', 'f1', 'on', 'l1', 123456, 'b1', 321654), ]) assert exc_info.type == APIException assert exc_info.value.message == 'some_message'