def test_can_use_https_proxy_via_config(): with start_server() as server: config = Config(sdk_key='sdk-key', events_uri='https://not-real', http_proxy=server.uri, diagnostic_opt_out=True) _verify_https_proxy_is_used(server, config)
def test_reconnects_if_stream_is_broken(): store = InMemoryFeatureStore() ready = Event() flagv1 = {'key': 'flagkey', 'version': 1} flagv2 = {'key': 'flagkey', 'version': 2} with start_server() as server: with stream_content(make_put_event([flagv1])) as stream1: with stream_content(make_put_event([flagv2])) as stream2: config = Config(sdk_key='sdk-key', stream_uri=server.uri, initial_reconnect_delay=brief_delay) server.for_path('/all', SequentialHandler(stream1, stream2)) with StreamingUpdateProcessor(config, store, ready, None) as sp: sp.start() server.await_request ready.wait(start_wait) assert sp.initialized() expect_item(store, FEATURES, flagv1) stream1.close() server.await_request expect_update(store, FEATURES, flagv2)
def test_receives_patch_events(): store = InMemoryFeatureStore() ready = Event() flagv1 = {'key': 'flagkey', 'version': 1} flagv2 = {'key': 'flagkey', 'version': 2} segmentv1 = {'key': 'segkey', 'version': 1} segmentv2 = {'key': 'segkey', 'version': 1} with start_server() as server: with stream_content(make_put_event([flagv1], [segmentv1])) as stream: config = Config(sdk_key='sdk-key', stream_uri=server.uri) server.for_path('/all', stream) with StreamingUpdateProcessor(config, store, ready, None) as sp: sp.start() ready.wait(start_wait) assert sp.initialized() expect_item(store, FEATURES, flagv1) expect_item(store, SEGMENTS, segmentv1) stream.push(make_patch_event(FEATURES, flagv2)) expect_update(store, FEATURES, flagv2) stream.push(make_patch_event(SEGMENTS, segmentv2)) expect_update(store, SEGMENTS, segmentv2)
def test_get_one_flag_throws_on_error(): with start_server() as server: config = Config(sdk_key='sdk-key', base_uri=server.uri) fr = FeatureRequesterImpl(config) with pytest.raises(UnsuccessfulResponseException) as e: fr.get_one(FEATURES, 'didnt-set-up-a-response-for-this-flag') assert e.value.status == 404
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_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_client_sends_diagnostics(): with start_server() as poll_server: with start_server() as events_server: poll_server.for_path('/sdk/latest-all', poll_content([always_true_flag])) events_server.for_path('/diagnostic', BasicResponse(202)) config = Config(sdk_key=sdk_key, base_uri=poll_server.uri, events_uri=events_server.uri, stream=False) with LDClient(config=config) as client: assert client.is_initialized() r = events_server.await_request() assert r.headers['Authorization'] == sdk_key data = json.loads(r.body) assert data['kind'] == 'diagnostic-init'
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_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'
def test_client_sends_event_without_diagnostics(): with start_server() as poll_server: with start_server() as events_server: poll_server.for_path('/sdk/latest-all', poll_content([always_true_flag])) events_server.for_path('/bulk', BasicResponse(202)) config = Config(sdk_key=sdk_key, base_uri=poll_server.uri, events_uri=events_server.uri, stream=False, diagnostic_opt_out=True) with LDClient(config=config) as client: assert client.is_initialized() client.identify(user) client.flush() r = events_server.await_request() assert r.headers['Authorization'] == sdk_key data = json.loads(r.body) assert len(data) == 1 assert data[0]['kind'] == 'identify'
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_set_sdk_key_before_init(): _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, stream_uri = stream_server.uri, send_events = 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_sends_headers(): store = InMemoryFeatureStore() ready = Event() with start_server() as server: config = Config(sdk_key='sdk-key', stream_uri=server.uri) server.setup_response('/all', 200, fake_event, response_headers) with StreamingUpdateProcessor(config, None, store, ready, None) as sp: sp.start() req = server.await_request() assert req.headers.get('Authorization') == 'sdk-key' assert req.headers.get('User-Agent') == 'PythonClient/' + VERSION assert req.headers.get('X-LaunchDarkly-Wrapper') is None
def test_uses_stream_uri(): store = InMemoryFeatureStore() ready = Event() with start_server() as server: config = Config(sdk_key='sdk-key', stream_uri=server.uri) server.setup_response('/all', 200, fake_event, response_headers) with StreamingUpdateProcessor(config, None, store, ready, None) as sp: sp.start() req = server.await_request() assert req.method == 'GET' ready.wait(1) assert sp.initialized()
def test_sends_wrapper_header_without_version(): store = InMemoryFeatureStore() ready = Event() with start_server() as server: config = Config(sdk_key='sdk-key', stream_uri=server.uri, wrapper_name='Flask') server.setup_response('/all', 200, fake_event, response_headers) with StreamingUpdateProcessor(config, None, store, ready, None) as sp: sp.start() req = server.await_request() assert req.headers.get('X-LaunchDarkly-Wrapper') == 'Flask'
def do_proxy_tests(action, action_method, monkeypatch): # We'll test each permutation of use_env_vars, secure, and use_auth, except that if secure is # true then we'll only test with use_auth=false because we don't have a way to test proxy # authorization over HTTPS (even though we believe it works). for (use_env_vars, secure, use_auth) in [(False, False, False), (False, False, True), (False, True, False), (True, False, False), (True, False, True), (True, True, False)]: test_desc = "%s, %s, %s" % ( "using env vars" if use_env_vars else "using Config", "secure" if secure else "insecure", "with auth" if use_auth else "no auth") with start_server() as server: proxy_uri = server.uri.replace( 'http://', 'http://*****:*****@') if use_auth else server.uri target_uri = 'https://not-real' if secure else 'http://not-real' if use_env_vars: monkeypatch.setenv('https_proxy' if secure else 'http_proxy', proxy_uri) config = Config(sdk_key='sdk_key', base_uri=target_uri, events_uri=target_uri, stream_uri=target_uri, http=HTTPConfig(http_proxy=proxy_uri), diagnostic_opt_out=True) try: action(server, config, secure) except: print("test action failed (%s)" % test_desc) raise # For an insecure proxy request, our stub server behaves enough like the real thing to satisfy the # HTTP client, so we should be able to see the request go through. Note that the URI path will # actually be an absolute URI for a proxy request. try: req = server.require_request() except: print("server did not receive a request (%s)" % test_desc) raise expected_method = 'CONNECT' if secure else action_method assert req.method == expected_method, "method should be %s, was %s (%s)" % ( expected_method, req.method, test_desc) if use_auth: expected_auth = 'Basic dXNlcjpwYXNz' actual_auth = req.headers.get('Proxy-Authorization') assert actual_auth == expected_auth, "auth header should be %s, was %s (%s)" % ( expected_auth, actual_auth, test_desc) print("do_proxy_tests succeeded for: %s" % test_desc)
def test_client_starts_in_streaming_mode(): with start_server() as stream_server: with stream_content(make_put_event([always_true_flag ])) as stream_handler: stream_server.for_path('/all', stream_handler) config = Config(sdk_key=sdk_key, stream_uri=stream_server.uri, send_events=False) with LDClient(config=config) as client: assert client.is_initialized() assert client.variation(always_true_flag['key'], user, False) == True r = stream_server.await_request() assert r.headers['Authorization'] == sdk_key
def test_client_starts_in_polling_mode(): with start_server() as poll_server: poll_server.for_path('/sdk/latest-all', poll_content([always_true_flag])) config = Config(sdk_key=sdk_key, base_uri=poll_server.uri, stream=False, send_events=False) with LDClient(config=config) as client: assert client.is_initialized() assert client.variation(always_true_flag['key'], user, False) == True r = poll_server.await_request() assert r.headers['Authorization'] == sdk_key
def test_unrecoverable_http_error(status): error_handler = BasicResponse(status) store = InMemoryFeatureStore() ready = Event() with start_server() as server: with stream_content(make_put_event()) as stream: error_then_success = SequentialHandler(error_handler, stream) config = Config(sdk_key='sdk-key', stream_uri=server.uri, initial_reconnect_delay=brief_delay) server.for_path('/all', error_then_success) with StreamingUpdateProcessor(config, store, ready, None) as sp: sp.start() ready.wait(5) assert not sp.initialized() server.should_have_requests(1)
def test_request_properties(): store = InMemoryFeatureStore() ready = Event() with start_server() as server: with stream_content(make_put_event()) as stream: config = Config(sdk_key='sdk-key', stream_uri=server.uri) server.for_path('/all', stream) with StreamingUpdateProcessor(config, store, ready, None) as sp: sp.start() req = server.await_request() assert req.method == 'GET' assert req.headers.get('Authorization') == 'sdk-key' assert req.headers.get( 'User-Agent') == 'PythonClient/' + VERSION assert req.headers.get('X-LaunchDarkly-Wrapper') is None
def test_sends_wrapper_header(): store = InMemoryFeatureStore() ready = Event() with start_server() as server: with stream_content(make_put_event()) as stream: config = Config(sdk_key='sdk-key', stream_uri=server.uri, wrapper_name='Flask', wrapper_version='0.1.0') server.for_path('/all', stream) with StreamingUpdateProcessor(config, store, ready, None) as sp: sp.start() req = server.await_request() assert req.headers.get( 'X-LaunchDarkly-Wrapper') == 'Flask/0.1.0'
def test_records_diagnostic_on_stream_init_failure(): store = InMemoryFeatureStore() ready = Event() with start_server() as server: config = Config(sdk_key='sdk-key', stream_uri=server.uri) server.setup_response('/all', 200, 'event:put\ndata: {\n\n', response_headers) diag_accum = _DiagnosticAccumulator(1) with StreamingUpdateProcessor(config, None, store, ready, diag_accum) as sp: sp.start() server.await_request() server.await_request() recorded_inits = diag_accum.create_event_and_reset( 0, 0)['streamInits'] assert recorded_inits[0]['failed'] is True
def test_records_diagnostic_on_stream_init_success(): store = InMemoryFeatureStore() ready = Event() with start_server() as server: with stream_content(make_put_event()) as stream: config = Config(sdk_key='sdk-key', stream_uri=server.uri) server.for_path('/all', stream) diag_accum = _DiagnosticAccumulator(1) with StreamingUpdateProcessor(config, store, ready, diag_accum) as sp: sp.start() ready.wait(start_wait) recorded_inits = diag_accum.create_event_and_reset( 0, 0)['streamInits'] assert len(recorded_inits) == 1 assert recorded_inits[0]['failed'] is False
def test_client_retries_connection_in_streaming_mode_with_non_fatal_error(): with start_server() as stream_server: with stream_content(make_put_event([always_true_flag ])) as stream_handler: error_then_success = SequentialHandler(BasicResponse(503), stream_handler) stream_server.for_path('/all', error_then_success) config = Config(sdk_key=sdk_key, stream_uri=stream_server.uri, initial_reconnect_delay=0.001, send_events=False) with LDClient(config=config) as client: assert client.is_initialized() assert client.variation(always_true_flag['key'], user, False) == True r = stream_server.await_request() assert r.headers['Authorization'] == sdk_key
def test_retries_on_network_error(): error_handler = CauseNetworkError() store = InMemoryFeatureStore() ready = Event() with start_server() as server: with stream_content(make_put_event()) as stream: two_errors_then_success = SequentialHandler( error_handler, error_handler, stream) config = Config(sdk_key='sdk-key', stream_uri=server.uri, initial_reconnect_delay=brief_delay) server.for_path('/all', two_errors_then_success) with StreamingUpdateProcessor(config, store, ready, None) as sp: sp.start() ready.wait(start_wait) assert sp.initialized() server.await_request server.await_request
def test_get_one_flag_does_not_use_etags(): with start_server() as server: config = Config(sdk_key='sdk-key', base_uri=server.uri) fr = FeatureRequesterImpl(config) etag = 'my-etag' key = 'flag1' flag_data = {'key': key} req_path = '/sdk/latest-flags/' + key server.setup_json_response(req_path, flag_data, {'Etag': etag}) result = fr.get_one(FEATURES, key) assert result == flag_data req = server.require_request() assert 'If-None-Match' not in req.headers.keys() result = fr.get_one(FEATURES, key) assert result == flag_data req = server.require_request() assert 'If-None-Match' not in req.headers.keys( ) # did not send etag from previous request
def test_get_all_data_can_use_cached_data(): with start_server() as server: config = Config(sdk_key='sdk-key', base_uri=server.uri) fr = FeatureRequesterImpl(config) etag1 = 'my-etag-1' etag2 = 'my-etag-2' resp_data1 = {'flags': {}, 'segments': {}} resp_data2 = {'flags': {'flag1': {'key': 'flag1'}}, 'segments': {}} expected_data1 = {FEATURES: {}, SEGMENTS: {}} expected_data2 = {FEATURES: {'flag1': {'key': 'flag1'}}, SEGMENTS: {}} req_path = '/sdk/latest-all' server.for_path(req_path, JsonResponse(resp_data1, {'Etag': etag1})) result = fr.get_all_data() assert result == expected_data1 req = server.require_request() assert 'If-None-Match' not in req.headers.keys() server.for_path(req_path, BasicResponse(304, None, {'Etag': etag1})) result = fr.get_all_data() assert result == expected_data1 req = server.require_request() assert req.headers['If-None-Match'] == etag1 server.for_path(req_path, JsonResponse(resp_data2, {'Etag': etag2})) result = fr.get_all_data() assert result == expected_data2 req = server.require_request() assert req.headers['If-None-Match'] == etag1 server.for_path(req_path, BasicResponse(304, None, {'Etag': etag2})) result = fr.get_all_data() assert result == expected_data2 req = server.require_request() assert req.headers['If-None-Match'] == etag2