# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2016, Anaconda, Inc. All rights reserved. # # Licensed under the terms of the BSD 3-Clause License. # The full license is in the file LICENSE.txt, distributed with this software. # ----------------------------------------------------------------------------- from __future__ import absolute_import, print_function import codecs import os import platform import sys from anaconda_project.test.project_utils import project_no_dedicated_env from anaconda_project.internal.test.tmpfile_utils import (with_directory_contents, with_directory_contents_completing_project_file) from anaconda_project.test.environ_utils import minimal_environ, strip_environ from anaconda_project.local_state_file import DEFAULT_LOCAL_STATE_FILENAME from anaconda_project.local_state_file import LocalStateFile from anaconda_project.requirements_registry.registry import RequirementsRegistry from anaconda_project.requirements_registry.requirement import UserConfigOverrides from anaconda_project.requirements_registry.providers.redis import RedisProvider from anaconda_project.requirements_registry.requirements.redis import RedisRequirement from anaconda_project.prepare import prepare_without_interaction, unprepare from anaconda_project import provide from anaconda_project.project_file import DEFAULT_PROJECT_FILENAME # This is kind of an awkward way to do it for historical reasons, # we print out the logs/errors captured by FakeFrontend, instead # of rewriting the tests in here to have a frontend that prints. def _prepare_printing_errors(project, environ=None, mode=provide.PROVIDE_MODE_DEVELOPMENT): result = prepare_without_interaction(project, environ=environ, mode=mode) for message in project.frontend.logs: print(message) for error in project.frontend.errors: print(error, file=sys.stderr) if not result: assert result.errors == project.frontend.errors return result def _redis_requirement(): return RedisRequirement(registry=RequirementsRegistry(), env_var="REDIS_URL") def test_reading_default_config(): def read_config(dirname): local_state = LocalStateFile.load_for_directory(dirname) requirement = _redis_requirement() provider = RedisProvider() config = provider.read_config(requirement, dict(), local_state, 'default', UserConfigOverrides()) assert 6380 == config['lower_port'] assert 6449 == config['upper_port'] with_directory_contents(dict(), read_config) def test_reading_valid_config(): def read_config(dirname): local_state = LocalStateFile.load_for_directory(dirname) requirement = _redis_requirement() provider = RedisProvider() config = provider.read_config(requirement, dict(), local_state, 'default', UserConfigOverrides()) assert 7389 == config['lower_port'] assert 7421 == config['upper_port'] assert 'find_all' == config['source'] with_directory_contents( { DEFAULT_LOCAL_STATE_FILENAME: """ service_options: REDIS_URL: port_range: 7389-7421 autostart: false """ }, read_config) def _read_invalid_port_range(capsys, port_range): def read_config(dirname): local_state = LocalStateFile.load_for_directory(dirname) requirement = _redis_requirement() provider = RedisProvider() config = provider.read_config(requirement, dict(), local_state, 'default', UserConfigOverrides()) # revert to defaults assert 6380 == config['lower_port'] assert 6449 == config['upper_port'] # should have printed an error out, err = capsys.readouterr() assert ("Invalid port_range '%s', should be like '6380-6449'\n" % (port_range)) == err with_directory_contents( {DEFAULT_LOCAL_STATE_FILENAME: """ service_options: REDIS_URL: port_range: %s """ % port_range}, read_config) def test_garbage_port_range(capsys): _read_invalid_port_range(capsys, "abcdefg") def test_backward_port_range(capsys): _read_invalid_port_range(capsys, "100-99") def test_non_integer_port_range(capsys): _read_invalid_port_range(capsys, "A-Z") def test_zero_lower_port(capsys): _read_invalid_port_range(capsys, "0-1") def test_zero_upper_port(capsys): _read_invalid_port_range(capsys, "1-0") def test_set_config_values_as_strings(): def set_config(dirname): local_state = LocalStateFile.load_for_directory(dirname) requirement = _redis_requirement() provider = RedisProvider() provider.set_config_values_as_strings(requirement, dict(), local_state, 'default', UserConfigOverrides(), dict(lower_port="6001")) config = provider.read_config(requirement, dict(), local_state, 'default', UserConfigOverrides()) assert config['lower_port'] == 6001 assert config['upper_port'] == 6449 provider.set_config_values_as_strings(requirement, dict(), local_state, 'default', UserConfigOverrides(), dict(upper_port="6700")) config2 = provider.read_config(requirement, dict(), local_state, 'default', UserConfigOverrides()) assert config2['lower_port'] == 6001 assert config2['upper_port'] == 6700 provider.set_config_values_as_strings(requirement, dict(), local_state, 'default', UserConfigOverrides(), dict(lower_port="5500", upper_port="6800")) config2 = provider.read_config(requirement, dict(), local_state, 'default', UserConfigOverrides()) assert config2['lower_port'] == 5500 assert config2['upper_port'] == 6800 with_directory_contents(dict(), set_config) def _monkeypatch_can_connect_to_socket_to_succeed(monkeypatch): can_connect_args = dict() def mock_can_connect_to_socket(host, port, timeout_seconds=0.5): can_connect_args['host'] = host can_connect_args['port'] = port can_connect_args['timeout_seconds'] = timeout_seconds return True monkeypatch.setattr("anaconda_project.requirements_registry.network_util.can_connect_to_socket", mock_can_connect_to_socket) return can_connect_args def test_prepare_redis_url_with_dict_in_variables_section(monkeypatch): can_connect_args = _monkeypatch_can_connect_to_socket_to_succeed(monkeypatch) def prepare_redis_url(dirname): project = project_no_dedicated_env(dirname) result = _prepare_printing_errors(project, environ=minimal_environ()) assert result assert dict(REDIS_URL="redis://localhost:6379", PROJECT_DIR=project.directory_path) == strip_environ(result.environ) assert dict(host='localhost', port=6379, timeout_seconds=0.5) == can_connect_args with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: """ services: REDIS_URL: redis """}, prepare_redis_url) def _monkeypatch_can_connect_to_socket_on_nonstandard_port_only(monkeypatch, real_can_connect_to_socket): can_connect_args_list = [] def mock_can_connect_to_socket(host, port, timeout_seconds=0.5): can_connect_args = dict() can_connect_args['host'] = host can_connect_args['port'] = port can_connect_args['timeout_seconds'] = timeout_seconds can_connect_args_list.append(can_connect_args) if port == 6379: return False else: return real_can_connect_to_socket(host, port, timeout_seconds) monkeypatch.setattr("anaconda_project.requirements_registry.network_util.can_connect_to_socket", mock_can_connect_to_socket) return can_connect_args_list def test_prepare_and_unprepare_local_redis_server(monkeypatch): # this test will fail if you don't have Redis installed, since # it actually starts it. if platform.system() == 'Windows': print("Cannot start redis-server on Windows") return from anaconda_project.requirements_registry.network_util import can_connect_to_socket as real_can_connect_to_socket can_connect_args_list = _monkeypatch_can_connect_to_socket_on_nonstandard_port_only( monkeypatch, real_can_connect_to_socket) def start_local_redis(dirname): project = project_no_dedicated_env(dirname) result = _prepare_printing_errors(project, environ=minimal_environ()) assert result local_state_file = LocalStateFile.load_for_directory(dirname) state = local_state_file.get_service_run_state('REDIS_URL') assert 'port' in state port = state['port'] assert dict(REDIS_URL=("redis://localhost:" + str(port)), PROJECT_DIR=project.directory_path) == strip_environ(result.environ) assert len(can_connect_args_list) >= 2 servicedir = os.path.join(dirname, "services") redisdir = os.path.join(servicedir, "REDIS_URL") pidfile = os.path.join(redisdir, "redis.pid") logfile = os.path.join(redisdir, "redis.log") assert os.path.exists(pidfile) assert os.path.exists(logfile) assert real_can_connect_to_socket(host='localhost', port=port) # now clean it up status = unprepare(project, result) assert status assert not os.path.exists(pidfile) assert not os.path.exists(logfile) assert not os.path.exists(redisdir) assert not os.path.exists(servicedir) assert not real_can_connect_to_socket(host='localhost', port=port) local_state_file.load() assert dict() == local_state_file.get_service_run_state("REDIS_URL") with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: """ services: REDIS_URL: redis """}, start_local_redis) def test_prepare_and_unprepare_local_redis_server_with_failed_unprovide(monkeypatch): # this test will fail if you don't have Redis installed, since # it actually starts it. if platform.system() == 'Windows': print("Cannot start redis-server on Windows") return from anaconda_project.requirements_registry.network_util import can_connect_to_socket as real_can_connect_to_socket _monkeypatch_can_connect_to_socket_on_nonstandard_port_only(monkeypatch, real_can_connect_to_socket) def start_local_redis(dirname): project = project_no_dedicated_env(dirname) result = _prepare_printing_errors(project, environ=minimal_environ()) assert result # now clean it up, but arrange for that to fail local_state_file = LocalStateFile.load_for_directory(dirname) local_state_file.set_service_run_state('REDIS_URL', {'shutdown_commands': [['false']]}) local_state_file.save() status = unprepare(project, result) assert not status assert status.status_description == 'Shutdown commands failed for REDIS_URL.' with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: """ services: REDIS_URL: redis """}, start_local_redis) def test_prepare_and_unprepare_two_local_redis_servers_with_failed_unprovide(monkeypatch): # this test will fail if you don't have Redis installed, since # it actually starts it. if platform.system() == 'Windows': print("Cannot start redis-server on Windows") return from anaconda_project.requirements_registry.network_util import can_connect_to_socket as real_can_connect_to_socket _monkeypatch_can_connect_to_socket_on_nonstandard_port_only(monkeypatch, real_can_connect_to_socket) def start_local_redis(dirname): project = project_no_dedicated_env(dirname) result = _prepare_printing_errors(project, environ=minimal_environ()) assert result # now clean it up, but arrange for that to double-fail local_state_file = LocalStateFile.load_for_directory(dirname) local_state_file.set_service_run_state('REDIS_URL', {'shutdown_commands': [['false']]}) local_state_file.set_service_run_state('REDIS_URL_2', {'shutdown_commands': [['false']]}) local_state_file.save() status = unprepare(project, result) assert not status assert status.status_description == 'Failed to clean up REDIS_URL, REDIS_URL_2.' with_directory_contents_completing_project_file( {DEFAULT_PROJECT_FILENAME: """ services: REDIS_URL: redis REDIS_URL_2: redis """}, start_local_redis) def test_prepare_local_redis_server_twice_reuses(monkeypatch): # this test will fail if you don't have Redis installed, since # it actually starts it. if platform.system() == 'Windows': print("Cannot start redis-server on Windows") return from anaconda_project.requirements_registry.network_util import can_connect_to_socket as real_can_connect_to_socket can_connect_args_list = _monkeypatch_can_connect_to_socket_on_nonstandard_port_only( monkeypatch, real_can_connect_to_socket) def start_local_redis(dirname): project = project_no_dedicated_env(dirname) result = _prepare_printing_errors(project, environ=minimal_environ()) assert result assert 'REDIS_URL' in result.environ local_state_file = LocalStateFile.load_for_directory(dirname) state = local_state_file.get_service_run_state("REDIS_URL") assert 'port' in state port = state['port'] assert dict(REDIS_URL=("redis://localhost:" + str(port)), PROJECT_DIR=project.directory_path) == strip_environ(result.environ) assert len(can_connect_args_list) >= 2 pidfile = os.path.join(dirname, "services/REDIS_URL/redis.pid") logfile = os.path.join(dirname, "services/REDIS_URL/redis.log") assert os.path.exists(pidfile) assert os.path.exists(logfile) assert real_can_connect_to_socket(host='localhost', port=port) # be sure we generate the config html that would use the old one requirement = _redis_requirement() status = requirement.check_status(result.environ, local_state_file, 'default', UserConfigOverrides()) # now try again, and we should re-use the exact same server pidfile_mtime = os.path.getmtime(pidfile) with codecs.open(pidfile, 'r', 'utf-8') as file: pidfile_content = file.read() result2 = _prepare_printing_errors(project, environ=minimal_environ()) assert result2 # port should be the same, and set in the environment assert dict(REDIS_URL=("redis://localhost:" + str(port)), PROJECT_DIR=project.directory_path) == strip_environ(result2.environ) # no new pid file assert pidfile_mtime == os.path.getmtime(pidfile) with codecs.open(pidfile, 'r', 'utf-8') as file: pidfile_content2 = file.read() assert pidfile_content == pidfile_content2 # now clean it up status = unprepare(project, result2) assert status assert not os.path.exists(pidfile) assert not real_can_connect_to_socket(host='localhost', port=port) local_state_file.load() assert dict() == local_state_file.get_service_run_state("REDIS_URL") with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: """ services: REDIS_URL: redis """}, start_local_redis) def test_prepare_local_redis_server_times_out(monkeypatch, capsys): # this test will fail if you don't have Redis installed, since # it actually starts it. if platform.system() == 'Windows': print("Cannot start redis-server on Windows") return from anaconda_project.requirements_registry.network_util import can_connect_to_socket as real_can_connect_to_socket _monkeypatch_can_connect_to_socket_on_nonstandard_port_only(monkeypatch, real_can_connect_to_socket) def start_local_redis_and_time_out(dirname): project = project_no_dedicated_env(dirname) from time import sleep as real_sleep killed = {} def mock_sleep_kills_redis(seconds): # first time the Redis provider sleeps to wait for the # server to appear, we kill the server; after that # we make sleep into a no-op so we rapidly time out. if 'done' in killed: return pidfile = os.path.join(dirname, "services", "REDIS_URL", "redis.pid") count = 0 while count < 15: if os.path.exists(pidfile): break real_sleep(0.1) count = count + 1 assert os.path.exists(pidfile) with codecs.open(pidfile, 'r', 'utf-8') as f: for line in f.readlines(): try: import signal os.kill(int(line.strip()), signal.SIGKILL) except Exception: pass # be sure it's gone real_sleep(0.1) killed['done'] = True monkeypatch.setattr('time.sleep', mock_sleep_kills_redis) result = _prepare_printing_errors(project, environ=minimal_environ()) assert not result out, err = capsys.readouterr() assert "redis-server started successfully, but we timed out trying to connect to it on port" in out assert "redis-server process failed or timed out, exited with code 0" in err with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: """ services: REDIS_URL: redis """}, start_local_redis_and_time_out) def _monkeypatch_can_connect_to_socket_always_succeeds_on_nonstandard(monkeypatch): can_connect_args_list = [] def mock_can_connect_to_socket(host, port, timeout_seconds=0.5): can_connect_args = dict() can_connect_args['host'] = host can_connect_args['port'] = port can_connect_args['timeout_seconds'] = timeout_seconds can_connect_args_list.append(can_connect_args) return port != 6379 monkeypatch.setattr("anaconda_project.requirements_registry.network_util.can_connect_to_socket", mock_can_connect_to_socket) return can_connect_args_list def test_fail_to_prepare_local_redis_server_no_port_available(monkeypatch, capsys): can_connect_args_list = _monkeypatch_can_connect_to_socket_always_succeeds_on_nonstandard(monkeypatch) def start_local_redis(dirname): project = project_no_dedicated_env(dirname) result = _prepare_printing_errors(project, environ=minimal_environ()) assert not result assert 73 == len(can_connect_args_list) with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: """ services: REDIS_URL: redis """}, start_local_redis) out, err = capsys.readouterr() assert "All ports from 6380 to 6449 were in use, could not start redis-server on one of them." in err assert "REDIS_URL" in err assert "missing requirement" in err assert "" == out def test_do_not_start_local_redis_server_in_prod_mode(monkeypatch, capsys): can_connect_args_list = _monkeypatch_can_connect_to_socket_always_succeeds_on_nonstandard(monkeypatch) def no_start_local_redis(dirname): project = project_no_dedicated_env(dirname) result = _prepare_printing_errors(project, environ=minimal_environ(), mode=provide.PROVIDE_MODE_PRODUCTION) assert not result assert 3 == len(can_connect_args_list) with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: """ services: REDIS_URL: redis """}, no_start_local_redis) out, err = capsys.readouterr() assert "Could not connect to system default Redis." in err assert "REDIS_URL" in err assert "missing requirement" in err assert "" == out def test_do_not_start_local_redis_server_in_check_mode(monkeypatch, capsys): can_connect_args_list = _monkeypatch_can_connect_to_socket_always_succeeds_on_nonstandard(monkeypatch) def no_start_local_redis(dirname): project = project_no_dedicated_env(dirname) result = _prepare_printing_errors(project, environ=minimal_environ(), mode=provide.PROVIDE_MODE_CHECK) assert not result assert 3 == len(can_connect_args_list) with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: """ services: REDIS_URL: redis """}, no_start_local_redis) out, err = capsys.readouterr() assert "Could not connect to system default Redis." in err assert "REDIS_URL" in err assert "missing requirement" in err assert "" == out def _monkeypatch_can_connect_to_socket_always_fails(monkeypatch): def mock_can_connect_to_socket(host, port, timeout_seconds=0.5): return False monkeypatch.setattr("anaconda_project.requirements_registry.network_util.can_connect_to_socket", mock_can_connect_to_socket) def test_fail_to_prepare_local_redis_server_scope_system(monkeypatch, capsys): _monkeypatch_can_connect_to_socket_always_fails(monkeypatch) def check_no_autostart(dirname): project = project_no_dedicated_env(dirname) result = _prepare_printing_errors(project, environ=minimal_environ()) assert not result with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: """ services: REDIS_URL: redis """, DEFAULT_LOCAL_STATE_FILENAME: """ service_options: REDIS_URL: scope: system """ }, check_no_autostart) out, err = capsys.readouterr() assert out == "" assert err == ( "Could not connect to system default Redis.\n" + "missing requirement to run this project: A running Redis server, located by a redis: URL set as REDIS_URL.\n" + " Environment variable REDIS_URL is not set.\n") def test_redis_server_configure_custom_port_range(monkeypatch, capsys): can_connect_args_list = _monkeypatch_can_connect_to_socket_always_succeeds_on_nonstandard(monkeypatch) def start_local_redis(dirname): project = project_no_dedicated_env(dirname) result = _prepare_printing_errors(project, environ=minimal_environ()) assert not result assert 36 == len(can_connect_args_list) with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: """ services: REDIS_URL: redis """, DEFAULT_LOCAL_STATE_FILENAME: """ service_options: REDIS_URL: port_range: 7389-7421 """ }, start_local_redis) out, err = capsys.readouterr() assert "All ports from 7389 to 7421 were in use, could not start redis-server on one of them." in err assert "REDIS_URL" in err assert "missing requirement" in err assert "" == out def _fail_to_prepare_local_redis_server_exec_fails(monkeypatch, capsys, logfile_fail_mode): # this test will fail if you don't have Redis installed, since # it actually starts it. from anaconda_project.requirements_registry.network_util import can_connect_to_socket as real_can_connect_to_socket _monkeypatch_can_connect_to_socket_on_nonstandard_port_only(monkeypatch, real_can_connect_to_socket) def start_local_redis(dirname): logfile = os.path.join(dirname, "services/REDIS_URL/redis.log") from subprocess import Popen as real_Popen failscript = os.path.join(dirname, "fail.py") with codecs.open(failscript, 'w', 'utf-8') as file: file.write(""" from __future__ import print_function import codecs import sys import os print('It did not work stdout') print('It did not work stderr', file=sys.stderr) logfile = sys.argv[1] fail_mode = sys.argv[2] if fail_mode == 'no_logfile': pass elif fail_mode == 'is_dir': os.makedirs(logfile) else: with codecs.open(logfile, 'w', 'utf-8') as f: f.write('This is in the logfile') sys.exit(1) """) def mock_Popen(*args, **kwargs): if 'args' not in kwargs: # `pip list` goes through this codepath while redis launch # happens to specify args= as a kwarg assert 'pip' in args[0][0] return real_Popen(*args, **kwargs) kwargs['args'] = ['python', failscript, logfile, logfile_fail_mode] return real_Popen(*args, **kwargs) monkeypatch.setattr("subprocess.Popen", mock_Popen) project = project_no_dedicated_env(dirname) result = _prepare_printing_errors(project, environ=minimal_environ()) assert not result with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: """ services: REDIS_URL: redis """}, start_local_redis) # this doesn't capture "It did not work stdout" because # of some pytest detail I don't understand. out, err = capsys.readouterr() assert "REDIS_URL" in err assert "missing requirement" in err # the failed process writes this to stderr, but prepare() moves it to stdout assert "Starting " in out assert "It did not work stderr" in out if logfile_fail_mode == 'logfile_ok': assert "This is in the logfile" in out else: assert "This is in the logfile" not in out if logfile_fail_mode == 'is_dir': assert "Failed to read" in out else: assert "Failed to read" not in out def test_fail_to_prepare_local_redis_server_exec_fails(monkeypatch, capsys): _fail_to_prepare_local_redis_server_exec_fails(monkeypatch, capsys, logfile_fail_mode='logfile_ok') def test_fail_to_prepare_local_redis_server_exec_fails_no_logfile(monkeypatch, capsys): _fail_to_prepare_local_redis_server_exec_fails(monkeypatch, capsys, logfile_fail_mode='no_logfile') def test_fail_to_prepare_local_redis_server_exec_fails_logfile_is_dir(monkeypatch, capsys): _fail_to_prepare_local_redis_server_exec_fails(monkeypatch, capsys, logfile_fail_mode='is_dir') def test_fail_to_prepare_local_redis_server_not_on_path(monkeypatch, capsys): from anaconda_project.requirements_registry.network_util import can_connect_to_socket as real_can_connect_to_socket _monkeypatch_can_connect_to_socket_on_nonstandard_port_only(monkeypatch, real_can_connect_to_socket) def start_local_redis(dirname): from subprocess import Popen as real_Popen def mock_Popen(*args, **kwargs): if 'args' not in kwargs: # `pip list` goes through this codepath while redis launch # happens to specify args= as a kwarg assert 'pip' in args[0][0] return real_Popen(*args, **kwargs) kwargs['args'] = ['this-is-not-on-the-path'] return real_Popen(*args, **kwargs) monkeypatch.setattr("subprocess.Popen", mock_Popen) project = project_no_dedicated_env(dirname) result = _prepare_printing_errors(project, environ=minimal_environ()) assert not result with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: """ services: REDIS_URL: redis """}, start_local_redis) # this doesn't capture "It did not work stdout" because # of some pytest detail I don't understand. out, err = capsys.readouterr() assert "REDIS_URL" in err assert "missing requirement" in err assert "Error executing redis-server: " in err def test_set_scope_in_local_state(monkeypatch): can_connect_args = _monkeypatch_can_connect_to_socket_to_succeed(monkeypatch) def prepare_after_setting_scope(dirname): local_state = LocalStateFile.load_for_directory(dirname) requirement = _redis_requirement() provider = RedisProvider() environ = minimal_environ() config = provider.read_config(requirement, environ, local_state, 'default', UserConfigOverrides()) assert config['source'] == 'find_all' provider.set_config_values_as_strings(requirement, environ, local_state, 'default', UserConfigOverrides(), dict(source='find_project')) config = provider.read_config(requirement, environ, local_state, 'default', UserConfigOverrides()) assert config['source'] == 'find_project' provider.set_config_values_as_strings(requirement, environ, local_state, 'default', UserConfigOverrides(), dict(source='find_all')) config = provider.read_config(requirement, environ, local_state, 'default', UserConfigOverrides()) assert config['source'] == 'find_all' provider.set_config_values_as_strings(requirement, environ, local_state, 'default', UserConfigOverrides(), dict(source='environ')) config = provider.read_config(requirement, environ, local_state, 'default', UserConfigOverrides()) assert config['source'] == 'find_all' # default if no env var set provider.set_config_values_as_strings(requirement, environ, local_state, 'default', UserConfigOverrides(), dict(source='environ')) environ_with_redis_url = environ.copy() environ_with_redis_url['REDIS_URL'] = 'blah' config = provider.read_config(requirement, environ_with_redis_url, local_state, 'default', UserConfigOverrides()) assert config['source'] == 'environ' # default when the env var IS set # use local variable when env var not set provider.set_config_values_as_strings(requirement, environ, local_state, 'default', UserConfigOverrides(), dict(source='variables', value='foo')) config = provider.read_config(requirement, environ, local_state, 'default', UserConfigOverrides()) assert config['source'] == 'variables' assert config['value'] == 'foo' # use local variable when env var _is_ set provider.set_config_values_as_strings(requirement, environ_with_redis_url, local_state, 'default', UserConfigOverrides(), dict(source='variables', value='foo')) config = provider.read_config(requirement, environ, local_state, 'default', UserConfigOverrides()) assert config['source'] == 'variables' assert config['value'] == 'foo' # set to use system, which should override using the local state provider.set_config_values_as_strings(requirement, environ, local_state, 'default', UserConfigOverrides(), dict(source='find_system')) config = provider.read_config(requirement, environ, local_state, 'default', UserConfigOverrides()) assert config['source'] == 'find_system' project = project_no_dedicated_env(dirname) result = _prepare_printing_errors(project, environ=minimal_environ()) assert result assert dict(REDIS_URL="redis://localhost:6379", PROJECT_DIR=project.directory_path) == strip_environ(result.environ) assert dict(host='localhost', port=6379, timeout_seconds=0.5) == can_connect_args with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: """ services: REDIS_URL: redis """}, prepare_after_setting_scope)