2Configure the service in testsuite.
5from collections.abc
import Callable
11import testsuite.plugins.network
13USERVER_CONFIG_HOOKS = [
'userver_base_prepare_service_config']
16def pytest_addoption(parser) -> None:
17 group = parser.getgroup(
'userver')
21 help=
'Path to service build directory.',
24 group = parser.getgroup(
'Test service')
28 help=
'Path to service binary.',
32 help=(
'Main HTTP port of the service (default: use the port from the static config)'),
38 help=(
'Monitor HTTP port of the service (default: use the port from the static config)'),
43 '--service-source-dir',
45 help=
'Path to service source directory.',
46 default=pathlib.Path(),
50def pytest_configure(config):
51 config.option.asyncio_mode =
'auto'
54@pytest.fixture(scope='session')
57 Returns the path to the service source directory that is set by command
58 line `--service-source-dir` option.
60 Override this fixture to change the way the path to the service
61 source directory is detected by testsuite.
63 @ingroup userver_testsuite_fixtures
65 return pytestconfig.option.service_source_dir
68@pytest.fixture(scope='session')
71 Returns the build directory set by command line `--build-dir` option.
73 Override this fixture to change the way the build directory is
74 detected by the testsuite.
76 @ingroup userver_testsuite_fixtures
78 return pytestconfig.option.build_dir
81@pytest.fixture(scope='session')
84 Returns the path to service binary set by command line `--service-binary`
87 Override this fixture to change the way the path to service binary is
88 detected by the testsuite.
90 @ingroup userver_testsuite_fixtures
92 return pytestconfig.option.service_binary
95@pytest.fixture(scope='session')
96def service_port(pytestconfig, _original_service_config, choose_free_port) -> int:
98 Returns the main listener port number of the service set by command line
99 `--service-port` option.
100 If no port is specified in the command line option, keeps the original port
101 specified in the static config.
103 Override this fixture to change the way the main listener port number is
104 detected by the testsuite.
106 @ingroup userver_testsuite_fixtures
108 return pytestconfig.option.service_port
or _get_port(
109 _original_service_config,
117@pytest.fixture(scope='session')
118def monitor_port(pytestconfig, _original_service_config, choose_free_port) -> int:
120 Returns the monitor listener port number of the service set by command line
121 `--monitor-port` option.
122 If no port is specified in the command line option, keeps the original port
123 specified in the static config.
125 Override this fixture to change the way the monitor listener port number
126 is detected by testsuite.
128 @ingroup userver_testsuite_fixtures
130 return pytestconfig.option.monitor_port
or _get_port(
131 _original_service_config,
140 original_service_config,
146 config_yaml = original_service_config.config_yaml
147 config_vars = original_service_config.config_vars
148 components = config_yaml[
'components_manager'][
'components']
149 listener = components.get(
'server', {}).get(listener_name, {})
152 port = listener.get(
'port',
None)
153 if isinstance(port, str)
and port.startswith(
'$'):
154 port = config_vars.get(port[1:],
None)
or listener.get(
160 f
'components_manager.components.server.{listener_name}.port '
161 f
'in the static config, or pass {option_name} pytest option, '
162 f
'or override the {port_fixture.__name__} fixture'
168_allocated_ports = set()
171@pytest.fixture(scope='session')
175 _testsuite_socket_cleanup,
176 _testsuite_default_af,
177) -> Callable[[int | None], int]:
179 A function that chooses a free port based on the optional hint given in the parameter.
181 @ingroup userver_testsuite_fixtures
184 family, address = _testsuite_default_af
186 def choose(port_hint: int |
None =
None, /) -> int:
187 should_not_randomize_ports = pytestconfig.option.service_runner_mode
188 if should_not_randomize_ports
and port_hint
is not None and port_hint != 0:
189 if _is_port_free(port_hint, family, address):
190 _allocated_ports.add(port_hint)
192 port = _get_free_port_not_allocated(get_free_port)
193 _allocated_ports.add(port)
199def _get_free_port_not_allocated(get_free_port) -> int:
201 port = get_free_port()
202 if port
not in _allocated_ports:
204 raise testsuite.plugins.network.NoEnabledPorts()
207def _is_port_free(port_num: int, family: int, address: str) -> bool:
209 with socket.socket(family, socket.SOCK_STREAM)
as sock:
210 sock.bind((address, port_num))
217@pytest.fixture(scope='session')
218def userver_base_prepare_service_config():
219 def patch_config(config, config_vars):
220 components = config[
'components_manager'][
'components']
221 if 'congestion-control' in components:
222 if components[
'congestion-control']
is None:
223 components[
'congestion-control'] = {}
225 components[
'congestion-control'][
'fake-mode'] =
True