def test_copy_config(): old_sdk_key = "OLD_SDK_KEY" new_sdk_key = "NEW_SDK_KEY" old_config = Config(sdk_key=old_sdk_key, stream=False) assert old_config.sdk_key is old_sdk_key assert old_config.stream is False new_config = old_config.copy_with_new_sdk_key(new_sdk_key) assert new_config.sdk_key is new_sdk_key assert new_config.stream is False
def test_can_use_https_proxy_via_environment_var(monkeypatch): with start_server() as server: monkeypatch.setenv('https_proxy', server.uri) config = Config(sdk_key='sdk-key', events_uri='https://not-real', diagnostic_opt_out=True) _verify_https_proxy_is_used(server, config)
def test_does_not_block_on_full_inbox(): config = Config( events_max_pending=1 ) # this sets the size of both the inbox and the outbox to 1 ep_inbox_holder = [None] ep_inbox = None def dispatcher_factory(inbox, config, http, diag): ep_inbox_holder[ 0] = inbox # it's an array because otherwise it's hard for a closure to modify a variable return None # the dispatcher object itself doesn't matter, we only manipulate the inbox def event_consumer(): while True: message = ep_inbox.get(block=True) if message.type == 'stop': message.param.set() return def start_consuming_events(): Thread(target=event_consumer).start() with DefaultEventProcessor(config, mock_http, dispatcher_factory) as ep: ep_inbox = ep_inbox_holder[0] event1 = {'kind': 'custom', 'key': 'event1', 'user': user} event2 = {'kind': 'custom', 'key': 'event2', 'user': user} ep.send_event(event1) ep.send_event(event2) # this event should be dropped - inbox is full message1 = ep_inbox.get(block=False) had_no_more = ep_inbox.empty() start_consuming_events() assert message1.param == event1 assert had_no_more
def __init__(self, **kwargs): if not 'diagnostic_opt_out' in kwargs: kwargs['diagnostic_opt_out'] = True if not 'sdk_key' in kwargs: kwargs['sdk_key'] = 'SDK_KEY' config = Config(**kwargs) diagnostic_accumulator = _DiagnosticAccumulator(create_diagnostic_id(config)) DefaultEventProcessor.__init__(self, config, mock_http, diagnostic_accumulator = diagnostic_accumulator)
def test_sdk_key_is_sent(): setup_processor(Config(sdk_key = 'SDK_KEY')) ep.send_event({ 'kind': 'identify', 'user': user }) ep.flush() ep._wait_until_inactive() assert mock_http.request_headers.get('Authorization') is 'SDK_KEY'
def test_general_connection_error_does_not_cause_immediate_failure( ignore_mock): mock_requester.exception = Exception("bad") start_time = time.time() setup_processor(Config("SDK_KEY")) ready.wait(0.3) assert not pp.initialized() assert mock_requester.request_count >= 2
def get_ld_client(): #if this was an app in prod, keey would go in secret file #hard-coding as a shortcut for now! if "ld_client" not in g: ldclient.set_config(Config("sdk-6d5668a8-bca4-43e8-a4ed-aae8b501be6c")) g.ld_client = ldclient.get() return g.ld_client
def test_create_diagnostic_id(): test_config = Config(sdk_key="SDK_KEY", http=HTTPConfig()) diag_id = create_diagnostic_id(test_config) assert len(diag_id) == 2 uid = diag_id['diagnosticId'] # Will throw if invalid UUID4 uuid.UUID('urn:uuid:' + uid) assert diag_id['sdkKeySuffix'] == 'DK_KEY'
def test_get_one_flag_returns_data(): with start_server() as server: config = Config(sdk_key='sdk-key', base_uri=server.uri) fr = FeatureRequesterImpl(config) key = 'flag1' flag_data = {'key': key} server.setup_json_response('/sdk/latest-flags/' + key, flag_data) result = fr.get_one(FEATURES, key) assert result == flag_data
def test_user_is_filtered_in_custom_event(): setup_processor(Config(inline_users_in_events = True, all_attributes_private = True)) e = { 'kind': 'custom', 'key': 'eventkey', 'user': user, 'data': { 'thing': 'stuff '} } ep.send_event(e) output = flush_and_get_events() assert len(output) == 1 check_custom_event(output[0], e, filtered_user)
def test_cannot_connect_with_selfsigned_cert_by_default(): with start_secure_server() as server: server.for_path('/sdk/latest-all', poll_content()) config = Config(sdk_key='sdk_key', base_uri=server.uri, stream=False, send_events=False) with LDClient(config=config, start_wait=1.5) as client: assert not client.is_initialized()
def test_set_config(): _reset_client() with start_server() as stream_server: with stream_content(make_put_event()) as stream_handler: try: stream_server.for_path('/all', stream_handler) ldclient.set_config(Config(sdk_key, offline=True)) assert ldclient.get().is_offline() is True ldclient.set_config(Config(sdk_key, stream_uri = stream_server.uri, send_events = False)) assert ldclient.get().is_offline() is False wait_until(ldclient.get().is_initialized, timeout=10) r = stream_server.await_request() assert r.headers['Authorization'] == sdk_key finally: _reset_client()
def test_custom_event_can_contain_inline_user(): setup_processor(Config(inline_users_in_events = True)) e = { 'kind': 'custom', 'key': 'eventkey', 'user': user, 'data': { 'thing': 'stuff '} } ep.send_event(e) output = flush_and_get_events() assert len(output) == 1 check_custom_event(output[0], e, user)
def test_can_connect_with_selfsigned_cert_if_ssl_verify_is_false(): with start_secure_server() as server: server.for_path('/sdk/latest-all', poll_content()) config = Config(sdk_key='sdk_key', base_uri=server.uri, stream=False, send_events=False, verify_ssl=False) with LDClient(config=config) as client: assert client.is_initialized()
def test_custom_event_is_queued_with_user(): setup_processor(Config()) e = { 'kind': 'custom', 'key': 'eventkey', 'user': user, 'data': { 'thing': 'stuff '} } ep.send_event(e) output = flush_and_get_events() assert len(output) == 2 check_index_event(output[0], e, user) check_custom_event(output[1], e, None)
def test_can_connect_with_selfsigned_cert_by_setting_ca_certs(): with start_secure_server() as server: server.for_path('/sdk/latest-all', poll_content()) config = Config(sdk_key='sdk_key', base_uri=server.uri, stream=False, send_events=False, http=HTTPConfig(ca_certs='./testing/selfsigned.pem')) with LDClient(config=config) as client: assert client.is_initialized()
def test_event_payload_id_is_sent(): with DefaultEventProcessor(Config(sdk_key='SDK_KEY'), mock_http) as ep: ep.send_event({'kind': 'identify', 'user': user}) ep.flush() ep._wait_until_inactive() headerVal = mock_http.request_headers.get('X-LaunchDarkly-Payload-ID') assert headerVal is not None # Throws on invalid UUID uuid.UUID(headerVal)
def test_nontracked_events_are_summarized(): setup_processor(Config()) e1 = { 'kind': 'feature', 'key': 'flagkey1', 'version': 11, 'user': user, 'variation': 1, 'value': 'value1', 'default': 'default1', 'trackEvents': False } e2 = { 'kind': 'feature', 'key': 'flagkey2', 'version': 22, 'user': user, 'variation': 2, 'value': 'value2', 'default': 'default2', 'trackEvents': False } ep.send_event(e1) ep.send_event(e2) output = flush_and_get_events() assert len(output) == 2 check_index_event(output[0], e1, user) se = output[1] assert se['kind'] == 'summary' assert se['startDate'] == e1['creationDate'] assert se['endDate'] == e2['creationDate'] assert se['features'] == { 'flagkey1': { 'default': 'default1', 'counters': [{ 'version': 11, 'variation': 1, 'value': 'value1', 'count': 1 }] }, 'flagkey2': { 'default': 'default2', 'counters': [{ 'version': 22, 'variation': 2, 'value': 'value2', 'count': 1 }] } }
def init_app(self, app): """Initialize the Feature Flag environment.""" self.app = app self.sdk_key = app.config.get('LD_SDK_KEY') if app.env == 'production': config = Config(sdk_key=self.sdk_key, connect_timeout=5) else: factory = FileDataSource.factory(paths=['flags.json'], auto_update=True) config = Config(sdk_key=self.sdk_key, update_processor_class=factory, send_events=False) ldclient_set_config(config) client = ldclient_get() app.extensions['featureflags'] = client app.teardown_appcontext(self.teardown)
def test_can_connect_with_selfsigned_cert_if_disable_ssl_verification_is_true( ): with start_secure_server() as server: server.for_path('/sdk/latest-all', poll_content()) config = Config(sdk_key='sdk_key', base_uri=server.uri, stream=False, send_events=False, http=HTTPConfig(disable_ssl_verification=True)) with LDClient(config=config) as client: assert client.is_initialized()
def test_client_fails_to_start_in_streaming_mode_with_401_error(): with start_server() as stream_server: stream_server.for_path('/all', BasicResponse(401)) config = Config(sdk_key=sdk_key, stream_uri=stream_server.uri, send_events=False) with LDClient(config=config) as client: assert not client.is_initialized() assert client.variation(always_true_flag['key'], user, False) == False
def test_get_one_flag_sends_headers(): with start_server() as server: config = Config(sdk_key='sdk-key', base_uri=server.uri) fr = FeatureRequesterImpl(config) key = 'flag1' flag_data = {'key': key} server.setup_json_response('/sdk/latest-flags/' + key, flag_data) fr.get_one(FEATURES, key) req = server.require_request() assert req.headers['Authorization'] == 'sdk-key' assert req.headers['User-Agent'] == 'PythonClient/' + VERSION assert req.headers.get('X-LaunchDarkly-Wrapper') is None
def test_get_one_flag_sends_wrapper_header_without_version(): with start_server() as server: config = Config(sdk_key='sdk-key', base_uri=server.uri, wrapper_name='Flask') fr = FeatureRequesterImpl(config) key = 'flag1' flag_data = {'key': key} server.setup_json_response('/sdk/latest-flags/' + key, flag_data) fr.get_one(FEATURES, key) req = server.require_request() assert req.headers.get('X-LaunchDarkly-Wrapper') == 'Flask'
def test_evaluates_simplified_flag_with_client_as_expected(): path = make_temp_file(all_properties_json) try: factory = Files.new_data_source(paths=path) client = LDClient( config=Config(update_processor_class=factory, send_events=False)) value = client.variation('flag2', {'key': 'user'}, '') assert value == 'value2' finally: os.remove(path) if client is not None: client.close()
def test_diagnostic_accumulator(): test_config = Config(sdk_key="SDK_KEY") diag_id = create_diagnostic_id(test_config) diag_accum = _DiagnosticAccumulator(diag_id) # Test default periodic event def_diag_event = diag_accum.create_event_and_reset(0, 0) assert len(def_diag_event) == 8 assert def_diag_event['kind'] == 'diagnostic' assert def_diag_event['id'] == diag_id assert def_diag_event['creationDate'] == diag_accum.data_since_date assert def_diag_event['dataSinceDate'] assert def_diag_event['droppedEvents'] == 0 assert def_diag_event['deduplicatedUsers'] == 0 assert def_diag_event['eventsInLastBatch'] == 0 assert def_diag_event['streamInits'] == [] # Verify converts to json without failure json.dumps(def_diag_event) # Test periodic event after recording values diag_accum.record_stream_init(100, 100, False) diag_accum.record_stream_init(300, 200, True) diag_accum.record_events_in_batch(10) diag_accum.record_events_in_batch(50) diag_event = diag_accum.create_event_and_reset(10, 15) assert len(diag_event) == 8 assert diag_event['kind'] == 'diagnostic' assert diag_event['id'] == diag_id assert diag_event['creationDate'] == diag_accum.data_since_date assert diag_event['dataSinceDate'] == def_diag_event['creationDate'] assert diag_event['droppedEvents'] == 10 assert diag_event['deduplicatedUsers'] == 15 assert diag_event['eventsInLastBatch'] == 50 assert diag_event['streamInits'] == [{ 'timestamp': 100, 'durationMillis': 100, 'failed': False }, { 'timestamp': 300, 'durationMillis': 200, 'failed': True }] json.dumps(diag_event) reset_diag_event = diag_accum.create_event_and_reset(0, 0) assert reset_diag_event['creationDate'] == diag_accum.data_since_date assert reset_diag_event['dataSinceDate'] == diag_event['creationDate'] del reset_diag_event['creationDate'] del def_diag_event['creationDate'] del reset_diag_event['dataSinceDate'] del def_diag_event['dataSinceDate'] assert reset_diag_event == def_diag_event
def test_get_all_data_sends_headers(): with start_server() as server: config = Config(sdk_key='sdk-key', base_uri=server.uri) fr = FeatureRequesterImpl(config) resp_data = {'flags': {}, 'segments': {}} server.for_path('/sdk/latest-all', JsonResponse(resp_data)) fr.get_all_data() req = server.require_request() assert req.headers['Authorization'] == 'sdk-key' assert req.headers['User-Agent'] == 'PythonClient/' + VERSION assert req.headers.get('X-LaunchDarkly-Wrapper') is None
def test_index_event_is_still_generated_if_inline_users_is_true_but_feature_event_is_not_tracked(): setup_processor(Config(inline_users_in_events = True)) e = { 'kind': 'feature', 'key': 'flagkey', 'version': 11, 'user': user, 'variation': 1, 'value': 'value', 'default': 'default', 'trackEvents': False } ep.send_event(e) output = flush_and_get_events() assert len(output) == 2 check_index_event(output[0], e, user) check_summary_event(output[1])
def verify_recoverable_http_error(status): setup_processor(Config(sdk_key = 'SDK_KEY')) mock_http.set_response_status(status) ep.send_event({ 'kind': 'identify', 'user': user }) ep.flush() ep._wait_until_inactive() mock_http.reset() ep.send_event({ 'kind': 'identify', 'user': user }) ep.flush() ep._wait_until_inactive() assert mock_http.request_data is not None
def test_feature_event_can_contain_inline_user(): setup_processor(Config(inline_users_in_events = True)) e = { 'kind': 'feature', 'key': 'flagkey', 'version': 11, 'user': user, 'variation': 1, 'value': 'value', 'default': 'default', 'trackEvents': True } ep.send_event(e) output = flush_and_get_events() assert len(output) == 2 check_feature_event(output[0], e, False, user) check_summary_event(output[1])
def test_get_all_data_returns_data(): with start_server() as server: config = Config(sdk_key='sdk-key', base_uri=server.uri) fr = FeatureRequesterImpl(config) flags = {'flag1': {'key': 'flag1'}} segments = {'segment1': {'key': 'segment1'}} resp_data = {'flags': flags, 'segments': segments} expected_data = {FEATURES: flags, SEGMENTS: segments} server.for_path('/sdk/latest-all', JsonResponse(resp_data)) result = fr.get_all_data() assert result == expected_data
def test_get_all_data_sends_wrapper_header_without_version(): with start_server() as server: config = Config(sdk_key='sdk-key', base_uri=server.uri, wrapper_name='Flask') fr = FeatureRequesterImpl(config) resp_data = {'flags': {}, 'segments': {}} server.for_path('/sdk/latest-all', JsonResponse(resp_data)) fr.get_all_data() req = server.require_request() assert req.headers.get('X-LaunchDarkly-Wrapper') == 'Flask'