Skip to content

Commit

Permalink
Merge pull request #57 from dapper91/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
dapper91 committed Mar 6, 2022
2 parents 0b0d9c7 + b4a4d97 commit 74455c7
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 27 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
Changelog
=========

1.4.1 (2022-03-06)
------------------

- pytest integration fixed to make asynchronous methods pass-through possible.


1.4.0 (2021-11-30)
------------------

Expand Down
4 changes: 3 additions & 1 deletion Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ name = "pypi"
[packages]
aiohttp = "~=3.0"
aio-pika = "~=6.0"
flask = "~=1.0"
flask = "~=2.0"
jsonschema = "~=3.0"
pydantic = "~=1.0"
requests = "~=2.0"
kombu = "~=5.0"
httpx = "~=0.0"
docstring-parser = "~=0.0"
openapi-ui-bundles = "~=0.0"
markupsafe = "==2.0.1"
werkzeug = "~=2.0"

[dev-packages]
aioresponses = "~=0.0"
Expand Down
41 changes: 41 additions & 0 deletions examples/aiohttp_server_pytest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import pytest
from aiohttp import web
from unittest import mock

import pjrpc.server
from pjrpc.client.backend import aiohttp as async_client
from pjrpc.server.integration import aiohttp as integration
from pjrpc.client.integrations.pytest import PjRpcAiohttpMocker

methods = pjrpc.server.MethodRegistry()


@methods.add
async def div(a, b):
cli = async_client.Client('http://127.0.0.2:8000/api/v1')
return await cli.proxy.div(a, b)


@pytest.fixture
def http_app():
return web.Application()


@pytest.fixture
def jsonrpc_app(http_app):
jsonrpc_app = integration.Application('/api/v1', app=http_app)
jsonrpc_app.dispatcher.add_methods(methods)

return jsonrpc_app


async def test_pjrpc_server(aiohttp_client, http_app, jsonrpc_app):
jsonrpc_cli = async_client.Client('/api/v1', session=await aiohttp_client(http_app))

with PjRpcAiohttpMocker(passthrough=True) as mocker:
mocker.add('http://127.0.0.2:8000/api/v1', 'div', result=2)
result = await jsonrpc_cli.proxy.div(4, 2)
assert result == 2

localhost_calls = mocker.calls['http://127.0.0.2:8000/api/v1']
assert localhost_calls[('2.0', 'div')].mock_calls == [mock.call(4, 2)]
59 changes: 59 additions & 0 deletions examples/flask_server_pytest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import pytest
import flask.testing
from unittest import mock

import pjrpc.server
import werkzeug.test
from pjrpc.client.backend import requests as client
from pjrpc.server.integration import flask as integration
from pjrpc.client.integrations.pytest import PjRpcRequestsMocker

methods = pjrpc.server.MethodRegistry()


@methods.add
def div(a, b):
cli = client.Client('http://127.0.0.2:8000/api/v1')
return cli.proxy.div(a, b)


@pytest.fixture()
def http_app():
return flask.Flask(__name__)


@pytest.fixture
def jsonrpc_app(http_app):
json_rpc = integration.JsonRPC('/api/v1')
json_rpc.dispatcher.add_methods(methods)

json_rpc.init_app(http_app)

return jsonrpc_app


class Response(werkzeug.test.Response):
def raise_for_status(self):
if self.status_code >= 400:
raise Exception('client response error')

@property
def text(self):
return self.data.decode()


@pytest.fixture()
def app_client(http_app):
return flask.testing.FlaskClient(http_app, Response)


def test_pjrpc_server(aiohttp_client, http_app, jsonrpc_app, app_client):
with PjRpcRequestsMocker(passthrough=True) as mocker:
jsonrpc_cli = client.Client('/api/v1', session=app_client)

mocker.add('http://127.0.0.2:8000/api/v1', 'div', result=2)
result = jsonrpc_cli.proxy.div(4, 2)
assert result == 2

localhost_calls = mocker.calls['http://127.0.0.2:8000/api/v1']
assert localhost_calls[('2.0', 'div')].mock_calls == [mock.call(4, 2)]
2 changes: 1 addition & 1 deletion pjrpc/__about__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
__description__ = 'Extensible JSON-RPC library'
__url__ = 'https://github.com/dapper91/pjrpc'

__version__ = '1.4.0'
__version__ = '1.4.1'

__author__ = 'Dmitry Pershin'
__email__ = '[email protected]'
Expand Down
25 changes: 15 additions & 10 deletions pjrpc/client/integrations/pytest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import collections
import functools as ft
import json
import sys
import unittest.mock
from typing import Any, Callable, Dict, Optional, Union

Expand All @@ -17,8 +16,6 @@
from pjrpc import Response
from pjrpc.common import UNSET, UnsetType

IS_GE_PY38 = sys.version_info >= (3, 8)


class Match:
"""
Expand Down Expand Up @@ -162,13 +159,21 @@ def start(self):
Activates a patcher.
"""

self._patcher = self._mocker.patch(self._target, side_effect=self._on_request, autospec=True)
patcher = self._mocker.patch(self._target)
with patcher:
if asyncio.iscoroutinefunction(patcher.temp_original):
self._async_resp = True

if self._async_resp:
async def side_effect(*args, **kwargs):
return await self._on_request(*args, **kwargs)
else:
def side_effect(*args, **kwargs):
return self._on_request(*args, **kwargs)

result = self._patcher.start()
if asyncio.iscoroutinefunction(self._patcher.temp_original):
self._async_resp = True
self._patcher = self._mocker.patch(self._target, side_effect=side_effect, autospec=True)

return result
return self._patcher.start()

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

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

if not IS_GE_PY38 and self._async_resp:
if self._async_resp:
async def wrapper():
return json.dumps(response.to_json())
return wrapper()
Expand Down
30 changes: 15 additions & 15 deletions tests/client/test_pytest_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from pjrpc.client.integrations.pytest import PjRpcMocker


class TestClient:
class SyncClient:
def __init__(self, endpoint):
self._endpoint = endpoint

Expand All @@ -20,11 +20,11 @@ def endpoint():

@pytest.fixture
def cli(endpoint):
return TestClient(endpoint)
return SyncClient(endpoint)


def test_context_manager(cli, endpoint):
with PjRpcMocker('test_pytest_plugin.TestClient._request') as mocker:
with PjRpcMocker('test_pytest_plugin.SyncClient._request') as mocker:
mocker.add(endpoint, 'method', result='result')
cli._request(json.dumps(pjrpc.Request(method='method').to_json()))

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


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


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


def test_pjrpc_mocker_round_robin(cli, endpoint):
with PjRpcMocker('test_pytest_plugin.TestClient._request') as mocker:
with PjRpcMocker('test_pytest_plugin.SyncClient._request') as mocker:
mocker.add(endpoint, 'method', result='result1')
mocker.add(endpoint, 'method', result='result2')

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


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


def test_pjrpc_mocker_calls(endpoint):
cli1 = TestClient('endpoint1')
cli2 = TestClient('endpoint2')
cli1 = SyncClient('endpoint1')
cli2 = SyncClient('endpoint2')

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


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


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

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


class TestAsyncClient:
class AsyncClient:
def __init__(self, endpoint):
self._endpoint = endpoint

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


async def test_pjrpc_mocker_async(endpoint):
cli = TestAsyncClient(endpoint)
cli = AsyncClient(endpoint)

with PjRpcMocker('test_pytest_plugin.TestAsyncClient._request') as mocker:
with PjRpcMocker('test_pytest_plugin.AsyncClient._request') as mocker:
mocker.add(endpoint, 'method1', result='result1')
mocker.add(endpoint, 'method2', result='result2')

Expand Down

0 comments on commit 74455c7

Please sign in to comment.