Skip to content

Commit 74455c7

Browse files
authored
Merge pull request #57 from dapper91/dev
Dev
2 parents 0b0d9c7 + b4a4d97 commit 74455c7

File tree

7 files changed

+140
-27
lines changed

7 files changed

+140
-27
lines changed

CHANGELOG.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
Changelog
22
=========
33

4+
1.4.1 (2022-03-06)
5+
------------------
6+
7+
- pytest integration fixed to make asynchronous methods pass-through possible.
8+
9+
410
1.4.0 (2021-11-30)
511
------------------
612

Pipfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@ name = "pypi"
66
[packages]
77
aiohttp = "~=3.0"
88
aio-pika = "~=6.0"
9-
flask = "~=1.0"
9+
flask = "~=2.0"
1010
jsonschema = "~=3.0"
1111
pydantic = "~=1.0"
1212
requests = "~=2.0"
1313
kombu = "~=5.0"
1414
httpx = "~=0.0"
1515
docstring-parser = "~=0.0"
1616
openapi-ui-bundles = "~=0.0"
17+
markupsafe = "==2.0.1"
18+
werkzeug = "~=2.0"
1719

1820
[dev-packages]
1921
aioresponses = "~=0.0"

examples/aiohttp_server_pytest.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import pytest
2+
from aiohttp import web
3+
from unittest import mock
4+
5+
import pjrpc.server
6+
from pjrpc.client.backend import aiohttp as async_client
7+
from pjrpc.server.integration import aiohttp as integration
8+
from pjrpc.client.integrations.pytest import PjRpcAiohttpMocker
9+
10+
methods = pjrpc.server.MethodRegistry()
11+
12+
13+
@methods.add
14+
async def div(a, b):
15+
cli = async_client.Client('http://127.0.0.2:8000/api/v1')
16+
return await cli.proxy.div(a, b)
17+
18+
19+
@pytest.fixture
20+
def http_app():
21+
return web.Application()
22+
23+
24+
@pytest.fixture
25+
def jsonrpc_app(http_app):
26+
jsonrpc_app = integration.Application('/api/v1', app=http_app)
27+
jsonrpc_app.dispatcher.add_methods(methods)
28+
29+
return jsonrpc_app
30+
31+
32+
async def test_pjrpc_server(aiohttp_client, http_app, jsonrpc_app):
33+
jsonrpc_cli = async_client.Client('/api/v1', session=await aiohttp_client(http_app))
34+
35+
with PjRpcAiohttpMocker(passthrough=True) as mocker:
36+
mocker.add('http://127.0.0.2:8000/api/v1', 'div', result=2)
37+
result = await jsonrpc_cli.proxy.div(4, 2)
38+
assert result == 2
39+
40+
localhost_calls = mocker.calls['http://127.0.0.2:8000/api/v1']
41+
assert localhost_calls[('2.0', 'div')].mock_calls == [mock.call(4, 2)]

examples/flask_server_pytest.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import pytest
2+
import flask.testing
3+
from unittest import mock
4+
5+
import pjrpc.server
6+
import werkzeug.test
7+
from pjrpc.client.backend import requests as client
8+
from pjrpc.server.integration import flask as integration
9+
from pjrpc.client.integrations.pytest import PjRpcRequestsMocker
10+
11+
methods = pjrpc.server.MethodRegistry()
12+
13+
14+
@methods.add
15+
def div(a, b):
16+
cli = client.Client('http://127.0.0.2:8000/api/v1')
17+
return cli.proxy.div(a, b)
18+
19+
20+
@pytest.fixture()
21+
def http_app():
22+
return flask.Flask(__name__)
23+
24+
25+
@pytest.fixture
26+
def jsonrpc_app(http_app):
27+
json_rpc = integration.JsonRPC('/api/v1')
28+
json_rpc.dispatcher.add_methods(methods)
29+
30+
json_rpc.init_app(http_app)
31+
32+
return jsonrpc_app
33+
34+
35+
class Response(werkzeug.test.Response):
36+
def raise_for_status(self):
37+
if self.status_code >= 400:
38+
raise Exception('client response error')
39+
40+
@property
41+
def text(self):
42+
return self.data.decode()
43+
44+
45+
@pytest.fixture()
46+
def app_client(http_app):
47+
return flask.testing.FlaskClient(http_app, Response)
48+
49+
50+
def test_pjrpc_server(aiohttp_client, http_app, jsonrpc_app, app_client):
51+
with PjRpcRequestsMocker(passthrough=True) as mocker:
52+
jsonrpc_cli = client.Client('/api/v1', session=app_client)
53+
54+
mocker.add('http://127.0.0.2:8000/api/v1', 'div', result=2)
55+
result = jsonrpc_cli.proxy.div(4, 2)
56+
assert result == 2
57+
58+
localhost_calls = mocker.calls['http://127.0.0.2:8000/api/v1']
59+
assert localhost_calls[('2.0', 'div')].mock_calls == [mock.call(4, 2)]

pjrpc/__about__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
__description__ = 'Extensible JSON-RPC library'
33
__url__ = 'https://github.com/dapper91/pjrpc'
44

5-
__version__ = '1.4.0'
5+
__version__ = '1.4.1'
66

77
__author__ = 'Dmitry Pershin'
88
__email__ = '[email protected]'

pjrpc/client/integrations/pytest.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import collections
88
import functools as ft
99
import json
10-
import sys
1110
import unittest.mock
1211
from typing import Any, Callable, Dict, Optional, Union
1312

@@ -17,8 +16,6 @@
1716
from pjrpc import Response
1817
from pjrpc.common import UNSET, UnsetType
1918

20-
IS_GE_PY38 = sys.version_info >= (3, 8)
21-
2219

2320
class Match:
2421
"""
@@ -162,13 +159,21 @@ def start(self):
162159
Activates a patcher.
163160
"""
164161

165-
self._patcher = self._mocker.patch(self._target, side_effect=self._on_request, autospec=True)
162+
patcher = self._mocker.patch(self._target)
163+
with patcher:
164+
if asyncio.iscoroutinefunction(patcher.temp_original):
165+
self._async_resp = True
166+
167+
if self._async_resp:
168+
async def side_effect(*args, **kwargs):
169+
return await self._on_request(*args, **kwargs)
170+
else:
171+
def side_effect(*args, **kwargs):
172+
return self._on_request(*args, **kwargs)
166173

167-
result = self._patcher.start()
168-
if asyncio.iscoroutinefunction(self._patcher.temp_original):
169-
self._async_resp = True
174+
self._patcher = self._mocker.patch(self._target, side_effect=side_effect, autospec=True)
170175

171-
return result
176+
return self._patcher.start()
172177

173178
def stop(self) -> None:
174179
"""
@@ -186,7 +191,7 @@ def _cleanup_matches(self, endpoint: str, version: str = '2.0', method_name: Opt
186191
if not self._matches[endpoint]:
187192
self._matches.pop(endpoint)
188193

189-
def _on_request(self, origin_self: Any, request_text: str, is_notification: bool = False, **kwargs: Any) -> str:
194+
def _on_request(self, origin_self: Any, request_text: str, is_notification: bool = False, **kwargs: Any):
190195
endpoint = origin_self._endpoint
191196
matches = self._matches.get(endpoint)
192197
if matches is None:
@@ -208,7 +213,7 @@ def _on_request(self, origin_self: Any, request_text: str, is_notification: bool
208213
request = pjrpc.Request.from_json(json_data)
209214
response = self._match_request(endpoint, request.version, request.method, request.params, request.id)
210215

211-
if not IS_GE_PY38 and self._async_resp:
216+
if self._async_resp:
212217
async def wrapper():
213218
return json.dumps(response.to_json())
214219
return wrapper()

tests/client/test_pytest_plugin.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from pjrpc.client.integrations.pytest import PjRpcMocker
66

77

8-
class TestClient:
8+
class SyncClient:
99
def __init__(self, endpoint):
1010
self._endpoint = endpoint
1111

@@ -20,11 +20,11 @@ def endpoint():
2020

2121
@pytest.fixture
2222
def cli(endpoint):
23-
return TestClient(endpoint)
23+
return SyncClient(endpoint)
2424

2525

2626
def test_context_manager(cli, endpoint):
27-
with PjRpcMocker('test_pytest_plugin.TestClient._request') as mocker:
27+
with PjRpcMocker('test_pytest_plugin.SyncClient._request') as mocker:
2828
mocker.add(endpoint, 'method', result='result')
2929
cli._request(json.dumps(pjrpc.Request(method='method').to_json()))
3030

@@ -34,7 +34,7 @@ def test_context_manager(cli, endpoint):
3434

3535

3636
def test_pjrpc_mocker_result_error_id(cli, endpoint):
37-
with PjRpcMocker('test_pytest_plugin.TestClient._request') as mocker:
37+
with PjRpcMocker('test_pytest_plugin.SyncClient._request') as mocker:
3838
mocker.add(endpoint, 'method1', result='result')
3939
response = pjrpc.Response.from_json(
4040
json.loads(
@@ -54,7 +54,7 @@ def test_pjrpc_mocker_result_error_id(cli, endpoint):
5454

5555

5656
def test_pjrpc_mocker_once_param(cli, endpoint):
57-
with PjRpcMocker('test_pytest_plugin.TestClient._request', passthrough=True) as mocker:
57+
with PjRpcMocker('test_pytest_plugin.SyncClient._request', passthrough=True) as mocker:
5858
mocker.add(endpoint, 'method', result='result', once=True)
5959
response = pjrpc.Response.from_json(
6060
json.loads(
@@ -72,7 +72,7 @@ def test_pjrpc_mocker_once_param(cli, endpoint):
7272

7373

7474
def test_pjrpc_mocker_round_robin(cli, endpoint):
75-
with PjRpcMocker('test_pytest_plugin.TestClient._request') as mocker:
75+
with PjRpcMocker('test_pytest_plugin.SyncClient._request') as mocker:
7676
mocker.add(endpoint, 'method', result='result1')
7777
mocker.add(endpoint, 'method', result='result2')
7878

@@ -99,7 +99,7 @@ def test_pjrpc_mocker_round_robin(cli, endpoint):
9999

100100

101101
def test_pjrpc_replace_remove(cli, endpoint):
102-
with PjRpcMocker('test_pytest_plugin.TestClient._request', passthrough=True) as mocker:
102+
with PjRpcMocker('test_pytest_plugin.SyncClient._request', passthrough=True) as mocker:
103103
mocker.add(endpoint, 'method', result='result1')
104104
response = pjrpc.Response.from_json(
105105
json.loads(
@@ -126,10 +126,10 @@ def test_pjrpc_replace_remove(cli, endpoint):
126126

127127

128128
def test_pjrpc_mocker_calls(endpoint):
129-
cli1 = TestClient('endpoint1')
130-
cli2 = TestClient('endpoint2')
129+
cli1 = SyncClient('endpoint1')
130+
cli2 = SyncClient('endpoint2')
131131

132-
with PjRpcMocker('test_pytest_plugin.TestClient._request') as mocker:
132+
with PjRpcMocker('test_pytest_plugin.SyncClient._request') as mocker:
133133
mocker.add('endpoint1', 'method1', result='result')
134134
mocker.add('endpoint1', 'method2', result='result')
135135
mocker.add('endpoint2', 'method1', result='result')
@@ -145,7 +145,7 @@ def test_pjrpc_mocker_calls(endpoint):
145145

146146

147147
def test_pjrpc_mocker_callback(cli, endpoint):
148-
with PjRpcMocker('test_pytest_plugin.TestClient._request') as mocker:
148+
with PjRpcMocker('test_pytest_plugin.SyncClient._request') as mocker:
149149
def callback(**kwargs):
150150
assert kwargs == {'a': 1, 'b': '2'}
151151
return 'result'
@@ -162,7 +162,7 @@ def callback(**kwargs):
162162

163163

164164
def test_pjrpc_mocker_passthrough(cli, endpoint):
165-
with PjRpcMocker('test_pytest_plugin.TestClient._request', passthrough=True) as mocker:
165+
with PjRpcMocker('test_pytest_plugin.SyncClient._request', passthrough=True) as mocker:
166166
mocker.add('other_endpoint', 'method', result='result')
167167

168168
response = pjrpc.Response.from_json(
@@ -174,7 +174,7 @@ def test_pjrpc_mocker_passthrough(cli, endpoint):
174174
assert response.result == 'original_result'
175175

176176

177-
class TestAsyncClient:
177+
class AsyncClient:
178178
def __init__(self, endpoint):
179179
self._endpoint = endpoint
180180

@@ -183,9 +183,9 @@ async def _request(self, data, is_notification=False, **kwargs):
183183

184184

185185
async def test_pjrpc_mocker_async(endpoint):
186-
cli = TestAsyncClient(endpoint)
186+
cli = AsyncClient(endpoint)
187187

188-
with PjRpcMocker('test_pytest_plugin.TestAsyncClient._request') as mocker:
188+
with PjRpcMocker('test_pytest_plugin.AsyncClient._request') as mocker:
189189
mocker.add(endpoint, 'method1', result='result1')
190190
mocker.add(endpoint, 'method2', result='result2')
191191

0 commit comments

Comments
 (0)