8from testsuite
import types
12from .
import classes, exceptions, server
14MOCKSERVER_DEFAULT_PORT = 9999
15MOCKSERVER_SSL_DEFAULT_PORT = 9998
17_SSL_KEY_FILE_INI_KEY =
'mockserver-ssl-key-file'
18_SSL_CERT_FILE_INI_KEY =
'mockserver-ssl-cert-file'
20MOCKSERVER_PORT_HELP =
"""
21{proto} mockserver port for default worker.
22Random port is used by default. If testsuite is started with
23--service-wait or --service-disabled default is forced to {default}.
26logger = logging.getLogger(__name__)
29def pytest_addoption(parser):
30 group = parser.getgroup(
'mockserver')
32 '--mockserver-nofail',
34 help=
'Do not fail if no handler is set.',
39 help=
'Default host for http mockserver.',
45 help=MOCKSERVER_PORT_HELP.format(
47 default=MOCKSERVER_DEFAULT_PORT,
51 '--mockserver-ssl-host',
53 help=
'Default host for https mockserver.',
56 '--mockserver-ssl-port',
59 help=MOCKSERVER_PORT_HELP.format(
61 default=MOCKSERVER_SSL_DEFAULT_PORT,
65 '--mockserver-unix-socket',
67 help=
'Bind server to unix socket instead of tcp',
72 help=
'Enable debugging logs.',
75 'mockserver-tracing-enabled',
79 'When request trace-id header not from testsuite:\n'
80 ' True: handle, if handler missing return http status 500\n'
81 ' False: handle, if handler missing raise '
82 'HandlerNotFoundError\n'
83 'When request trace-id header from other test:\n'
84 ' True: do not handle, return http status 500\n'
85 ' False: handle, if handler missing raise HandlerNotFoundError'
89 'mockserver-trace-id-header',
90 default=server.DEFAULT_TRACE_ID_HEADER,
92 'name of tracing http header, value changes from test to test and '
93 'is constant within test'
97 'mockserver-span-id-header',
98 default=server.DEFAULT_SPAN_ID_HEADER,
99 help=
'name of tracing http header, value is unique for each request',
102 'mockserver-ssl-cert-file',
104 help=
'path to ssl certificate file to setup mockserver_ssl',
107 'mockserver-ssl-key-file',
109 help=
'path to ssl key file to setup mockserver_ssl',
112 'mockserver-http-proxy-enabled',
115 help=
'If enabled mockserver acts as http proxy',
119def pytest_configure(config):
120 config.addinivalue_line(
122 'mockserver_assert_lost_calls: assert that all calls to mockservers are checked',
126def pytest_register_object_hooks():
128 '$mockserver': {
'$fixture':
'_mockserver_hook'},
129 '$mockserver_https': {
'$fixture':
'_mockserver_https_hook'},
133@pytest.fixture(name='mockserver_strict_default')
134def fixture_mockserver_strict_default():
138@pytest.fixture(name='mockserver_create_session')
139def fixture_mockserver_create_session(
142 testsuite_traceid_manager: TraceidManager,
143 mockserver_strict_default: bool,
145 assert_lost_calls = request.node.get_closest_marker(
146 'mockserver_assert_lost_calls'
149 @contextlib.contextmanager
150 def create_session(mockserver):
151 with mockserver.new_session(
152 asyncexc_append=asyncexc_append,
153 traceid_manager=testsuite_traceid_manager,
155 yield server.MockserverFixture(
158 strict_default=mockserver_strict_default,
161 calls = session.collect_calls()
162 if assert_lost_calls:
164 raise exceptions.MockServerError(
165 f
'mockserver is expected to have lost calls, but it doesnt'
169 raise exceptions.MockServerError(
170 f
'mockserver handler with strict=True has skipped calls: {calls}'
173 return create_session
176@pytest.fixture(name='_mockserver_create_session')
177def legacy_fixture_mockserver_create_session(
178 mockserver_create_session,
180 def create_session(*args, **kwargs):
182 'Use mockserver_create_session() fixture instead',
185 return mockserver_create_session(*args, **kwargs)
187 return create_session
192 _mockserver: server.Server,
193 mockserver_create_session,
194) -> types.YieldFixture[server.MockserverFixture]:
195 """Returns per-test mockserver client.
197 Use it to register HTTP handlers that the service under test can call.
199 @ingroup userver_testsuite_fixtures
200 Part of the [yandex-taxi-testsuite](https://github.com/yandex/yandex-taxi-testsuite/blob/develop/testsuite/mockserver/pytest_plugin.py#L191)
202 with mockserver_create_session(_mockserver)
as fixture:
208 _mockserver_ssl: server.Server |
None,
209 mockserver_create_session,
210) -> types.AsyncYieldFixture[server.MockserverSslFixture]:
211 if _mockserver_ssl
is None:
212 raise exceptions.MockServerError(
213 f
'mockserver_ssl is not configured. {_SSL_KEY_FILE_INI_KEY} and '
214 f
'{_SSL_CERT_FILE_INI_KEY} must be specified in pytest.ini',
216 with mockserver_create_session(_mockserver_ssl)
as fixture:
220@pytest.fixture(scope='session')
222 _mockserver_socket: classes.MockserverSocket,
223) -> classes.MockserverInfo:
224 """Returns mockserver information object.
226 @ingroup userver_testsuite_fixtures
227 Part of the [yandex-taxi-testsuite](https://github.com/yandex/yandex-taxi-testsuite/blob/develop/testsuite/mockserver/pytest_plugin.py#L218)
229 return _mockserver_socket.info
232@pytest.fixture(scope='session')
233def mockserver_ssl_info(
234 _mockserver_ssl_socket: classes.MockserverSocket |
None,
235) -> classes.MockserverInfo |
None:
236 if _mockserver_ssl_socket
is None:
238 return _mockserver_ssl_socket.info
241@pytest.fixture(scope='session')
242def mockserver_ssl_cert(pytestconfig) -> classes.SslCertInfo | None:
243 def _get_ini_path(name):
244 values = pytestconfig.getini(name)
248 raise exceptions.MockServerError(
249 f
'{name} ini setting has multiple values',
251 return str(values[0])
253 cert_path = _get_ini_path(_SSL_CERT_FILE_INI_KEY)
254 key_path = _get_ini_path(_SSL_KEY_FILE_INI_KEY)
255 if cert_path
and key_path:
256 return classes.SslCertInfo(
258 private_key_path=key_path,
263@pytest.fixture(scope='session')
264async def mockserver_create(
267 @contextlib.asynccontextmanager
273 ssl_cert: classes.SslCertInfo |
None =
None,
274 config: classes.MockserverConfig |
None =
None,
276 socket_info = server._create_mockserver_socket(
279 socket_path=socket_path,
280 https=bool(ssl_cert),
282 async with server._create_server_from_socket(
284 config
or _mockserver_config,
292@pytest.fixture(scope='session')
293async def _mockserver(
295 _mockserver_socket: classes.MockserverSocket,
296 _mockserver_config: classes.MockserverConfig,
298 async with server._create_server_from_socket(
299 _mockserver_socket, _mockserver_config
304@pytest.fixture(scope='session')
305async def _mockserver_ssl(
307 _mockserver_ssl_socket: classes.MockserverSocket,
308 _mockserver_config: classes.MockserverConfig,
311 if mockserver_ssl_cert:
312 async with server._create_server_from_socket(
313 _mockserver_ssl_socket,
315 ssl_cert=mockserver_ssl_cert,
322@pytest.fixture(scope='session')
323def _mockserver_hook(mockserver_info):
324 def wrapper(doc: dict):
325 return _mockserver_info_hook(doc,
'$mockserver', mockserver_info)
330@pytest.fixture(scope='session')
331def _mockserver_https_hook(mockserver_ssl_info):
332 def wrapper(doc: dict):
333 return _mockserver_info_hook(
342@pytest.fixture(scope='session')
343def _mockserver_socket(
344 pytestconfig, _mockserver_config
345) -> classes.MockserverSocket:
346 port = _mockserver_getport(
348 pytestconfig.option.mockserver_port,
349 default_port=MOCKSERVER_DEFAULT_PORT,
351 mockserver_socket = server._create_mockserver_socket(
352 socket_path=pytestconfig.option.mockserver_unix_socket,
353 host=pytestconfig.option.mockserver_host,
356 with net.closing_sockets(mockserver_socket.sockets):
358 for sock
in mockserver_socket.sockets:
359 info.append(sock.getsockname())
360 logger.debug(
'Mockserver bound to %r', info)
361 yield mockserver_socket
364@pytest.fixture(scope='session')
365def _mockserver_ssl_socket(
367) -> classes.MockserverSocket | None:
368 port = _mockserver_getport(
370 pytestconfig.option.mockserver_ssl_port,
371 default_port=MOCKSERVER_SSL_DEFAULT_PORT,
373 mockserver_socket = server._create_mockserver_socket(
374 host=pytestconfig.option.mockserver_ssl_host,
378 with net.closing_sockets(mockserver_socket.sockets):
380 for sock
in mockserver_socket.sockets:
381 info.append(sock.getsockname())
382 logger.debug(
'Mockserver HTTPS bound to %r', info)
383 yield mockserver_socket
386@pytest.fixture(scope='session')
387async def mockserver_set_debug(_mockserver, _mockserver_ssl):
388 def set_debug(enabled: bool):
389 loop = asyncio.get_running_loop()
390 for obj
in (loop, _mockserver, _mockserver_ssl):
392 obj.set_debug(enabled)
397@pytest.fixture(scope='session')
398def _mockserver_config(pytestconfig) -> classes.MockserverConfig:
399 return classes.MockserverConfig(
400 nofail=pytestconfig.option.mockserver_nofail,
401 debug=pytestconfig.option.mockserver_debug,
402 tracing_enabled=pytestconfig.getini(
'mockserver-tracing-enabled'),
403 trace_id_header=pytestconfig.getini(
'mockserver-trace-id-header'),
404 span_id_header=pytestconfig.getini(
'mockserver-span-id-header'),
405 http_proxy_enabled=pytestconfig.getini(
'mockserver-http-proxy-enabled'),
409def _mockserver_info_hook(
410 doc: dict, key=
None, mockserver_info: classes.MockserverInfo |
None =
None
412 if mockserver_info
is None:
413 raise RuntimeError(f
'Missing {key} argument')
414 if not doc.get(
'$schema',
True):
416 elif mockserver_info.https:
420 return '%s%s:%d%s' % (
422 mockserver_info.host,
423 mockserver_info.port,
428def _mockserver_getport(config, option_port, default_port):
431 if config.option.service_wait
or config.option.service_disable: