userver: /home/user/userver/testsuite/pytest_plugins/pytest_userver/plugins/grpc/mockserver.py Source File
Loading...
Searching...
No Matches
mockserver.py
1"""
2Mocks for the gRPC servers.
3
4@sa @ref scripts/docs/en/userver/tutorial/grpc_service.md
5"""
6
7from collections.abc import AsyncIterator
8from collections.abc import Iterator
9
10import grpc
11import pytest
12
14
15# @cond
16
17
18DEFAULT_PORT = 8091
19
20USERVER_CONFIG_HOOKS = ['userver_config_grpc_mockserver']
21
22
23# @endcond
24
25
26@pytest.fixture(scope='session')
27def grpc_mockserver_endpoint(pytestconfig, get_free_port) -> str:
28 """
29 Returns the gRPC endpoint to start the mocking server that is set by
30 command line `--grpc-mockserver-host` and `--grpc-mockserver-port` options.
31
32 For port 0, picks some free port.
33
34 Override this fixture to customize the endpoint used by gRPC mockserver.
35
36 @snippet grpc/functional_tests/basic_chaos/tests-grpcclient/conftest.py grpc_mockserver_endpoint example
37 @ingroup userver_testsuite_fixtures
38 """
39 port = pytestconfig.option.grpc_mockserver_port
40 if pytestconfig.option.service_wait or pytestconfig.option.service_disable:
41 port = port or DEFAULT_PORT
42 if port == 0:
43 port = get_free_port()
44 return f'{pytestconfig.option.grpc_mockserver_host}:{port}'
45
46
47@pytest.fixture(scope='session')
48async def grpc_mockserver_session(grpc_mockserver_endpoint) -> AsyncIterator[pytest_userver.grpc.MockserverSession]:
49 """
50 Returns the gRPC mocking server.
51
52 @warning This is a sharp knife, use with caution! For most use-cases, prefer
53 @ref pytest_userver.plugins.grpc.mockserver.grpc_mockserver "grpc_mockserver" instead.
54
55 @ingroup userver_testsuite_fixtures
56 """
57 server = grpc.aio.server()
58 server.add_insecure_port(grpc_mockserver_endpoint)
59
60 async with pytest_userver.grpc.MockserverSession(server=server, experimental=True) as mockserver:
61 yield mockserver
62
63
64@pytest.fixture
65def grpc_mockserver(grpc_mockserver_session, asyncexc_append) -> Iterator[pytest_userver.grpc.Mockserver]:
66 """
67 Returns the gRPC mocking server.
68 In order for gRPC clients in your service to work, mock handlers need to be installed for them using this fixture.
69
70 Example:
71
72 @snippet samples/grpc_service/testsuite/test_grpc.py Prepare modules
73 @snippet samples/grpc_service/testsuite/test_grpc.py grpc client test
74
75 Alternatively, you can create a shorthand for mocking frequently-used services:
76
77 @snippet grpc/functional_tests/metrics/tests/conftest.py Prepare modules
78 @snippet grpc/functional_tests/metrics/tests/conftest.py Prepare server mock
79 @snippet grpc/functional_tests/metrics/tests/test_metrics.py grpc client test
80
81 Mocks are only active within tests after their respective handler functions are created, not between tests.
82 If the service needs the mock during startup, add the fixture that defines your mock to
83 @ref pytest_userver.plugins.service.extra_client_deps "extra_client_deps".
84
85 To return an error status instead of response, use `context` (see
86 [ServicerContext](https://grpc.github.io/grpc/python/grpc_asyncio.html#grpc.aio.ServicerContext)
87 docs):
88
89 @snippet grpc/functional_tests/middleware_client/tests/test_error_status.py Mocked error status
90
91 To trigger special exceptions in the service's gRPC client, raise these mocked errors from the mock handler:
92
93 * @ref pytest_userver.grpc._mocked_errors.TimeoutError "pytest_userver.grpc.TimeoutError"
94 * @ref pytest_userver.grpc._mocked_errors.NetworkError "pytest_userver.grpc.NetworkError"
95
96 @ingroup userver_testsuite_fixtures
97 """
98 with grpc_mockserver_session.asyncexc_append_scope(asyncexc_append):
100 mockserver_session=grpc_mockserver_session,
101 experimental=True,
102 ) as mockserver:
103 yield mockserver
104
105
106@pytest.fixture(scope='session')
108 """
109 Returns a function that adjusts the static config for testsuite.
110 Finds `grpc-client-middleware-pipeline` in config_yaml and
111 enables `grpc-client-middleware-testsuite`.
112
113 @ingroup userver_testsuite_fixtures
114 """
115
116 def get_dict_field(parent: dict, field_name: str) -> dict:
117 if parent.setdefault(field_name, {}) is None:
118 parent[field_name] = {}
119
120 return parent[field_name]
121
122 def patch_config(config_yaml, _config_vars):
123 components = config_yaml['components_manager']['components']
124 if components.get('grpc-client-common', None) is not None:
125 client_middlewares_pipeline = get_dict_field(components, 'grpc-client-middlewares-pipeline')
126 middlewares = get_dict_field(client_middlewares_pipeline, 'middlewares')
127 testsuite_middleware = get_dict_field(middlewares, 'grpc-client-middleware-testsuite')
128 testsuite_middleware['enabled'] = True
129
130 return patch_config
131
132
133# @cond
134
135
136def pytest_addoption(parser):
137 group = parser.getgroup('grpc-mockserver')
138 group.addoption(
139 '--grpc-mockserver-host',
140 default='[::]',
141 help='gRPC mockserver hostname, default is [::]',
142 )
143 group.addoption(
144 '--grpc-mockserver-port',
145 type=int,
146 default=0,
147 help='gRPC mockserver port, by default random port is used',
148 )
149
150
151# @endcond