userver: /data/code/service_template/third_party/userver/testsuite/pytest_plugins/pytest_userver/plugins/service_client.py Source File
Loading...
Searching...
No Matches
service_client.py
1"""
2Service main and monitor clients.
3"""
4
5import logging
6
7import pytest
8import websockets
9
10from testsuite.daemons import service_client as base_service_client
11from testsuite.utils import compat
12
13from pytest_userver import client
14
15
16logger = logging.getLogger(__name__)
17
18
19@pytest.fixture(name='extra_client_deps')
20def _extra_client_deps() -> None:
21 """
22 Service client dependencies hook. Feel free to override, e.g.:
23
24 @code
25 @pytest.fixture
26 def extra_client_deps(some_fixtures_to_wait_before_service_start):
27 pass
28 @endcode
29 @ingroup userver_testsuite_fixtures
30 """
31
32
33@pytest.fixture(name='auto_client_deps')
34def _auto_client_deps(request) -> None:
35 """
36 Service client dependencies hook that knows about pgsql, mongodb,
37 clickhouse, rabbitmq, redis_store, ydb, and mysql dependencies.
38 To add some other dependencies prefer overriding the
39 extra_client_deps() fixture.
40
41 @ingroup userver_testsuite_fixtures
42 """
43 known_deps = {
44 'pgsql',
45 'mongodb',
46 'clickhouse',
47 'rabbitmq',
48 'redis_store',
49 'mysql',
50 'ydb',
51 }
52
53 try:
54 fixture_lookup_error = pytest.FixtureLookupError
55 except AttributeError:
56 # support for an older version of the pytest
57 import _pytest.fixtures
58 fixture_lookup_error = _pytest.fixtures.FixtureLookupError
59
60 resolved_deps = []
61 for dep in known_deps:
62 try:
63 request.getfixturevalue(dep)
64 resolved_deps.append(dep)
65 except fixture_lookup_error:
66 pass
67
68 logger.debug(
69 'userver fixture "auto_client_deps" resolved dependencies %s',
70 resolved_deps,
71 )
72
73
74@pytest.fixture(name='service_client')
76 ensure_daemon_started,
77 service_daemon,
78 dynamic_config,
79 mock_configs_service,
80 cleanup_userver_dumps,
81 extra_client_deps,
82 auto_client_deps,
83 _config_service_defaults_updated,
84 _testsuite_client_config: client.TestsuiteClientConfig,
85 _service_client_base,
86 _service_client_testsuite,
87) -> client.Client:
88 """
89 Main fixture that provides access to userver based service.
90
91 @snippet samples/testsuite-support/tests/test_ping.py service_client
92 @anchor service_client
93 @ingroup userver_testsuite_fixtures
94 """
95 # The service is lazily started here (not at the 'session' scope)
96 # to allow '*_client_deps' to be active during service start
97 await ensure_daemon_started(service_daemon)
98
99 if not _testsuite_client_config.testsuite_action_path:
100 return _service_client_base
101
102 await _config_service_defaults_updated.update(
103 _service_client_testsuite, dynamic_config,
104 )
105 return _service_client_testsuite
106
107
108@pytest.fixture
109async def websocket_client(service_client, service_port):
110 """
111 Fixture that provides access to userver based websocket service.
112
113 @anchor websocket_client
114 @ingroup userver_testsuite_fixtures
115 """
116
117 class Client:
118 @compat.asynccontextmanager
119 async def get(self, path):
120 update_server_state = getattr(
121 service_client, 'update_server_state', None,
122 )
123 if update_server_state:
124 await update_server_state()
125 ws_context = websockets.connect(
126 f'ws://localhost:{service_port}/{path}',
127 )
128 async with ws_context as socket:
129 yield socket
130
131 return Client()
132
133
134@pytest.fixture
136 service_client,
137 service_client_options,
138 mockserver,
139 monitor_baseurl: str,
140 _testsuite_client_config: client.TestsuiteClientConfig,
142 """
143 Main fixture that provides access to userver monitor listener.
144
145 @snippet samples/testsuite-support/tests/test_metrics.py metrics labels
146 @ingroup userver_testsuite_fixtures
147 """
148 aiohttp_client = client.AiohttpClientMonitor(
149 monitor_baseurl,
150 config=_testsuite_client_config,
151 headers={'x-yatraceid': mockserver.trace_id},
152 **service_client_options,
153 )
154 return client.ClientMonitor(aiohttp_client)
155
156
157@pytest.fixture(name='_service_client_base')
158async def _service_client_base_fixture(
159 service_baseurl, service_client_options,
160):
161 class _ClientDiagnose(base_service_client.Client):
162 def __getattr__(self, name: str) -> None:
163 raise AttributeError(
164 f'"Client" object has no attribute "{name}". '
165 'Note that "service_client" fixture returned the basic '
166 '"testsuite.daemons.service_client.Client" client rather than '
167 'a "pytest_userver.client.Client" client with userver '
168 'extensions. That happened because the service '
169 'static configuration file contains no "tests-control" '
170 'component with "action" field.',
171 )
172
173 return _ClientDiagnose(service_baseurl, **service_client_options)
174
175
176@pytest.fixture(name='_service_client_testsuite')
177def _service_client_testsuite_fixture(
178 service_baseurl,
179 service_client_options,
180 mocked_time,
181 userver_log_capture,
182 testpoint,
183 testpoint_control,
184 cache_invalidation_state,
185 _testsuite_client_config: client.TestsuiteClientConfig,
186) -> client.Client:
187 aiohttp_client = client.AiohttpClient(
188 service_baseurl,
189 config=_testsuite_client_config,
190 testpoint=testpoint,
191 testpoint_control=testpoint_control,
192 log_capture_fixture=userver_log_capture,
193 mocked_time=mocked_time,
194 cache_invalidation_state=cache_invalidation_state,
195 **service_client_options,
196 )
197 return client.Client(aiohttp_client)
198
199
200@pytest.fixture(name='service_baseurl', scope='session')
201def _service_baseurl(service_port) -> str:
202 """
203 Returns the main listener URL of the service.
204
205 Override this fixture to change the main listener URL that the testsuite
206 uses for tests.
207
208 @ingroup userver_testsuite_fixtures
209 """
210 return f'http://localhost:{service_port}/'
211
212
213@pytest.fixture(name='monitor_baseurl', scope='session')
214def _monitor_baseurl(monitor_port) -> str:
215 """
216 Returns the main monitor URL of the service.
217
218 Override this fixture to change the main monitor URL that the testsuite
219 uses for tests.
220
221 @ingroup userver_testsuite_fixtures
222 """
223 return f'http://localhost:{monitor_port}/'
224
225
226@pytest.fixture(scope='session')
227def _testsuite_client_config(
228 pytestconfig, service_config_yaml,
229) -> client.TestsuiteClientConfig:
230 components = service_config_yaml['components_manager']['components']
231
232 def get_component_path(name, argname=None):
233 if name in components:
234 path = components[name]['path']
235 path = path.rstrip('*')
236
237 if argname and f'{{{argname}}}' not in path:
238 raise RuntimeError(
239 f'Component {name} must provide path argument {argname}',
240 )
241 return path
242 return None
243
245 server_monitor_path=get_component_path('handler-server-monitor'),
246 testsuite_action_path=get_component_path('tests-control', 'action'),
247 )