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,
139@pytest.fixture(scope='session')
142 Returns congestion control fake-mode value.
144 @ingroup userver_testsuite_fixtures
150 original_service_config,
156 config_yaml = original_service_config.config_yaml
157 config_vars = original_service_config.config_vars
158 components = config_yaml[
'components_manager'][
'components']
159 listener = components.get(
'server', {}).get(listener_name, {})
162 port = listener.get(
'port',
None)
163 if isinstance(port, str)
and port.startswith(
'$'):
164 port = config_vars.get(port[1:],
None)
or listener.get(
170 f
'components_manager.components.server.{listener_name}.port '
171 f
'in the static config, or pass {option_name} pytest option, '
172 f
'or override the {port_fixture.__name__} fixture'
178_allocated_ports = set()
181@pytest.fixture(scope='session')
185 _testsuite_socket_cleanup,
186 _testsuite_default_af,
187) -> Callable[[int | None], int]:
189 A function that chooses a free port based on the optional hint given in the parameter.
191 @ingroup userver_testsuite_fixtures
194 family, address = _testsuite_default_af
196 def choose(port_hint: int |
None =
None, /) -> int:
197 should_not_randomize_ports = pytestconfig.option.service_runner_mode
198 if should_not_randomize_ports
and port_hint
is not None and port_hint != 0:
199 if _is_port_free(port_hint, family, address):
200 _allocated_ports.add(port_hint)
202 port = _get_free_port_not_allocated(get_free_port)
203 _allocated_ports.add(port)
209def _get_free_port_not_allocated(get_free_port) -> int:
211 port = get_free_port()
212 if port
not in _allocated_ports:
214 raise testsuite.plugins.network.NoEnabledPorts()
217def _is_port_free(port_num: int, family: int, address: str) -> bool:
219 with socket.socket(family, socket.SOCK_STREAM)
as sock:
220 sock.bind((address, port_num))
227@pytest.fixture(scope='session')
228def userver_base_prepare_service_config(congestion_control_fake_mode):
229 def patch_config(config, config_vars):
230 components = config[
'components_manager'][
'components']
231 if 'congestion-control' in components:
232 if components[
'congestion-control']
is None:
233 components[
'congestion-control'] = {}
235 components[
'congestion-control'][
'fake-mode'] = congestion_control_fake_mode