2Work with the configuration files of the service in testsuite. 
   29USERVER_CONFIG_HOOKS = [
 
   30    'userver_config_http_server',
 
   31    'userver_config_http_client',
 
   32    'userver_config_logging',
 
   33    'userver_config_testsuite',
 
   34    'userver_config_testsuite_support',
 
   35    'userver_config_secdist',
 
   42logger = logging.getLogger(__name__)
 
   45class _UserverConfigPlugin:
 
   47        self._config_hooks = []
 
   50    def userver_config_hooks(self):
 
   51        return self._config_hooks
 
   53    def pytest_plugin_registered(self, plugin, manager):
 
   54        if not isinstance(plugin, types.ModuleType):
 
   56        uhooks = getattr(plugin, 
'USERVER_CONFIG_HOOKS', 
None)
 
   57        if uhooks 
is not None:
 
   58            self._config_hooks.extend(uhooks)
 
   61class _UserverConfig(typing.NamedTuple):
 
   66def pytest_configure(config):
 
   67    config.pluginmanager.register(_UserverConfigPlugin(), 
'userver_config')
 
   68    config.addinivalue_line(
 
   69        'markers', 
'config: per-test dynamic config values',
 
   73def pytest_addoption(parser) -> None:
 
   74    group = parser.getgroup(
'userver-config')
 
   76        '--service-log-level',
 
   79        choices=[
'trace', 
'debug', 
'info', 
'warning', 
'error', 
'critical'],
 
   84        help=
'Path to service.yaml file.',
 
   87        '--service-config-vars',
 
   89        help=
'Path to config_vars.yaml file.',
 
   94        help=
'Path to secure_data.json file.',
 
   99        help=
'Path to dynamic config fallback file.',
 
  106@pytest.fixture(scope='session') 
  109    Returns the path to service.yaml file set by command line 
  110    `--service-config` option. 
  112    Override this fixture to change the way path to service.yaml is provided. 
  114    @ingroup userver_testsuite_fixtures 
  116    return pytestconfig.option.service_config
 
  119@pytest.fixture(scope='session') 
 
  122    Returns the path to config_vars.yaml file set by command line 
  123    `--service-config-vars` option. 
  125    Override this fixture to change the way path to config_vars.yaml is 
  128    @ingroup userver_testsuite_fixtures 
  130    return pytestconfig.option.service_config_vars
 
  133@pytest.fixture(scope='session') 
 
  136    Returns the path to secure_data.json file set by command line 
  137    `--service-secdist` option. 
  139    Override this fixture to change the way path to secure_data.json is 
  142    @ingroup userver_testsuite_fixtures 
  144    return pytestconfig.option.service_secdist
 
  147@pytest.fixture(scope='session') 
 
  150    Returns the path to dynamic config fallback file set by command line 
  151    `--config-fallback` option. 
  153    Override this fixture to change the way path to dynamic config fallback is 
  156    @ingroup userver_testsuite_fixtures 
  158    return pytestconfig.option.config_fallback
 
  161@pytest.fixture(scope='session') 
 
  164    Returns the path for temporary files. The path is the same for the whole 
  165    session and files are not removed (at least by this fixture) between 
  168    @ingroup userver_testsuite_fixtures 
  170    return tmp_path_factory.mktemp(pathlib.Path(service_binary).name)
 
  173@pytest.fixture(scope='session') 
 
  175        service_tmpdir, service_config_yaml,
 
  178    Dumps the contents of the service_config_yaml into a static config for 
  179    testsuite and returns the path to the config file. 
  181    @ingroup userver_testsuite_fixtures 
  183    dst_path = service_tmpdir / 
'config.yaml' 
  185    config_text = yaml.dump(service_config_yaml)
 
  187        'userver fixture "service_config_path_temp" writes the patched static ' 
  188        'config to "%s":\n%s',
 
  192    dst_path.write_text(config_text)
 
  197@pytest.fixture(scope='session') 
 
  200    Returns the static config values after the USERVER_CONFIG_HOOKS were 
  203    @ingroup userver_testsuite_fixtures 
  205    return _service_config.config_yaml
 
  208@pytest.fixture(scope='session') 
 
  211    Returns the static config variables (config_vars.yaml) values after the 
  212    USERVER_CONFIG_HOOKS were applied (if any). 
  214    @ingroup userver_testsuite_fixtures 
  216    return _service_config.config_vars
 
  219@pytest.fixture(scope='session') 
 
  225        service_config_vars_path,
 
  230    with open(service_config_path, mode=
'rt') 
as fp:
 
  231        config_yaml = yaml.safe_load(fp)
 
  233    if service_config_vars_path:
 
  234        with open(service_config_vars_path, mode=
'rt') 
as fp:
 
  235            config_vars = yaml.safe_load(fp)
 
  239    plugin = pytestconfig.pluginmanager.get_plugin(
'userver_config')
 
  240    for hook 
in plugin.userver_config_hooks:
 
  241        if not callable(hook):
 
  242            hook_func = request.getfixturevalue(hook)
 
  245        hook_func(config_yaml, config_vars)
 
  248        config_yaml.pop(
'config_vars', 
None)
 
  250        config_vars_path = service_tmpdir / 
'config_vars.yaml' 
  251        config_vars_text = yaml.dump(config_vars)
 
  253            'userver fixture "service_config" writes the patched static ' 
  254            'config vars to "%s":\n%s',
 
  258        config_vars_path.write_text(config_vars_text)
 
  259        config_yaml[
'config_vars'] = str(config_vars_path)
 
  261    return _UserverConfig(config_yaml=config_yaml, config_vars=config_vars)
 
  264@pytest.fixture(scope='session') 
  267    Returns a function that adjusts the static configuration file for testsuite. 
  268    Sets the `server.listener.port` to listen on 
  269    @ref pytest_userver.plugins.base.service_port "service_port" fixture value; 
  270    sets the `server.listener-monitor.port` to listen on 
  271    @ref pytest_userver.plugins.base.monitor_port "monitor_port" 
  274    @ingroup userver_testsuite_fixtures 
  277    def _patch_config(config_yaml, config_vars):
 
  278        components = config_yaml[
'components_manager'][
'components']
 
  279        if 'server' in components:
 
  280            server = components[
'server']
 
  281            if 'listener' in server:
 
  282                server[
'listener'][
'port'] = service_port
 
  284            if 'listener-monitor' in server:
 
  285                server[
'listener-monitor'][
'port'] = monitor_port
 
  290@pytest.fixture(scope='session') 
 
  293    By default, userver HTTP client is only allowed to talk to mockserver 
  294    when running in testsuite. This makes tests repeatable and encapsulated. 
  296    Override this fixture to whitelist some additional URLs. 
  297    It is still strongly advised to only talk to localhost in tests. 
  299    @ingroup userver_testsuite_fixtures 
  304@pytest.fixture(scope='session') 
 
  306        mockserver_info, mockserver_ssl_info, allowed_url_prefixes_extra,
 
  309    Returns a function that adjusts the static configuration file for testsuite. 
  310    Sets increased timeout and limits allowed URLs for `http-client` component. 
  312    @ingroup userver_testsuite_fixtures 
  315    def patch_config(config, config_vars):
 
  316        components: dict = config[
'components_manager'][
'components']
 
  317        if not {
'http-client', 
'testsuite-support'}.issubset(
 
  321        http_client = components[
'http-client'] 
or {}
 
  322        http_client[
'testsuite-enabled'] = 
True 
  323        http_client[
'testsuite-timeout'] = 
'10s' 
  325        allowed_urls = [mockserver_info.base_url]
 
  326        if mockserver_ssl_info:
 
  327            allowed_urls.append(mockserver_ssl_info.base_url)
 
  328        allowed_urls += allowed_url_prefixes_extra
 
  329        http_client[
'testsuite-allowed-url-prefixes'] = allowed_urls
 
  334@pytest.fixture(scope='session') 
 
  337    Returns a function that adjusts the static configuration file for testsuite. 
  338    Sets the `logging.loggers.default` to log to `@stderr` with level set 
  339    from `--service-log-level` pytest configuration option. 
  341    @ingroup userver_testsuite_fixtures 
  343    log_level = pytestconfig.option.service_log_level
 
  345    def _patch_config(config_yaml, config_vars):
 
  346        components = config_yaml[
'components_manager'][
'components']
 
  347        if 'logging' in components:
 
  348            components[
'logging'][
'loggers'] = {
 
  350                    'file_path': 
'@stderr',
 
  352                    'overflow_behavior': 
'discard',
 
  355        config_vars[
'logger_level'] = log_level
 
  360@pytest.fixture(scope='session') 
 
  363    Returns a function that adjusts the static configuration file for testsuite. 
  364    Sets the `testsuite-enabled` in config_vars.yaml to `True`; sets the 
  365    `tests-control.testpoint-url` to mockserver URL. 
  367    @ingroup userver_testsuite_fixtures 
  370    def _patch_config(config_yaml, config_vars):
 
  371        config_vars[
'testsuite-enabled'] = 
True 
  372        components = config_yaml[
'components_manager'][
'components']
 
  373        if 'tests-control' in components:
 
  374            components[
'tests-control'][
'testpoint-url'] = mockserver_info.url(
 
  381@pytest.fixture(scope='session') 
 
  384    Returns a function that adjusts the static configuration file for testsuite. 
  385    Sets up `testsuite-support` component, which: 
  387    - increases timeouts for userver drivers 
  388    - disables periodic cache updates 
  389    - enables testsuite tasks 
  391    @ingroup userver_testsuite_fixtures 
  394    def _set_postgresql_options(testsuite_support: dict) -> 
None:
 
  395        testsuite_support[
'testsuite-pg-execute-timeout'] = 
'35s' 
  396        testsuite_support[
'testsuite-pg-statement-timeout'] = 
'30s' 
  397        testsuite_support[
'testsuite-pg-readonly-master-expected'] = 
True 
  399    def _set_redis_timeout(testsuite_support: dict) -> 
None:
 
  400        testsuite_support[
'testsuite-redis-timeout-connect'] = 
'40s' 
  401        testsuite_support[
'testsuite-redis-timeout-single'] = 
'30s' 
  402        testsuite_support[
'testsuite-redis-timeout-all'] = 
'30s' 
  404    def _disable_cache_periodic_update(testsuite_support: dict) -> 
None:
 
  405        testsuite_support[
'testsuite-periodic-update-enabled'] = 
False 
  407    def patch_config(config, config_vars) -> None:
 
  408        components: dict = config[
'components_manager'][
'components']
 
  409        if 'testsuite-support' not in components:
 
  411        testsuite_support = components[
'testsuite-support'] 
or {}
 
  412        _set_postgresql_options(testsuite_support)
 
  413        _set_redis_timeout(testsuite_support)
 
  414        service_runner = pytestconfig.getoption(
'--service-runner-mode', 
False)
 
  415        if not service_runner:
 
  416            _disable_cache_periodic_update(testsuite_support)
 
  417        testsuite_support[
'testsuite-tasks-enabled'] = 
not service_runner
 
  418        components[
'testsuite-support'] = testsuite_support
 
  423@pytest.fixture(scope='session') 
 
  426    Returns a function that adjusts the static configuration file for testsuite. 
  427    Sets the `default-secdist-provider.config` to the value of 
  428    @ref pytest_userver.plugins.config.service_secdist_path "service_secdist_path" 
  431    @ingroup userver_testsuite_fixtures 
  434    def _patch_config(config_yaml, config_vars):
 
  435        if not service_secdist_path:
 
  438        components = config_yaml[
'components_manager'][
'components']
 
  439        if 'default-secdist-provider' not in components:
 
  442        if not service_secdist_path.is_file():
 
  444                f
'"{service_secdist_path}" is not a file. Provide a ' 
  445                f
'"--service-secdist" pytest option or override the ' 
  446                f
'"service_secdist_path" fixture.',
 
  448        components[
'default-secdist-provider'][
'config'] = str(
 
  449            service_secdist_path,