You've already forked bthlabs-jsonrpc
Initial public releases
* `bthlabs-jsonrpc-aiohttp` v1.0.0 * `bthlabs-jsonrpc-core` v1.0.0 * `bthlabs-jsonrpc-django` v1.0.0
This commit is contained in:
0
packages/bthlabs-jsonrpc-core/tests/__init__.py
Normal file
0
packages/bthlabs-jsonrpc-core/tests/__init__.py
Normal file
22
packages/bthlabs-jsonrpc-core/tests/conftest.py
Normal file
22
packages/bthlabs-jsonrpc-core/tests/conftest.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
from bthlabs_jsonrpc_core.registry import MethodRegistry
|
||||
from bthlabs_jsonrpc_core.serializer import JSONRPCSerializer
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fake_method_registry():
|
||||
return mock.Mock(spec=MethodRegistry)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fake_handler():
|
||||
return mock.Mock()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fake_rpc_serializer():
|
||||
return mock.Mock(spec=JSONRPCSerializer)
|
||||
@@ -0,0 +1,53 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from unittest import mock
|
||||
|
||||
from bthlabs_jsonrpc_core import decorators
|
||||
from bthlabs_jsonrpc_core.registry import MethodRegistry
|
||||
|
||||
|
||||
@mock.patch.object(decorators.MethodRegistry, 'shared_registry')
|
||||
def test_default_namespace(mock_shared_registry,
|
||||
fake_method_registry,
|
||||
fake_handler):
|
||||
# Given
|
||||
mock_shared_registry.return_value = fake_method_registry
|
||||
|
||||
decorator = decorators.register_method('test')
|
||||
|
||||
# When
|
||||
result = decorator(fake_handler)
|
||||
|
||||
# Then
|
||||
assert result is fake_handler
|
||||
assert result.jsonrpc_method == 'test'
|
||||
assert result.jsonrpc_namespace == MethodRegistry.DEFAULT_NAMESPACE
|
||||
|
||||
assert mock_shared_registry.called is True
|
||||
|
||||
fake_method_registry.register_method.assert_called_with(
|
||||
MethodRegistry.DEFAULT_NAMESPACE, 'test', fake_handler,
|
||||
)
|
||||
|
||||
|
||||
@mock.patch.object(decorators.MethodRegistry, 'shared_registry')
|
||||
def test_custom_namespace(mock_shared_registry,
|
||||
fake_method_registry,
|
||||
fake_handler):
|
||||
# Given
|
||||
mock_shared_registry.return_value = fake_method_registry
|
||||
|
||||
decorator = decorators.register_method('test', namespace='testing')
|
||||
|
||||
# When
|
||||
result = decorator(fake_handler)
|
||||
|
||||
# Then
|
||||
assert result is fake_handler
|
||||
assert result.jsonrpc_method == 'test'
|
||||
assert result.jsonrpc_namespace == 'testing'
|
||||
|
||||
assert mock_shared_registry.called is True
|
||||
|
||||
fake_method_registry.register_method.assert_called_with(
|
||||
'testing', 'test', fake_handler,
|
||||
)
|
||||
@@ -0,0 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from bthlabs_jsonrpc_core import exceptions
|
||||
|
||||
|
||||
def test_to_rpc():
|
||||
# Given
|
||||
exception = exceptions.BaseJSONRPCError(data='spam')
|
||||
exception.ERROR_CODE = -32604
|
||||
exception.ERROR_MESSAGE = 'Test error'
|
||||
|
||||
# When
|
||||
result = exception.to_rpc()
|
||||
|
||||
# Then
|
||||
assert result['code'] == exception.ERROR_CODE
|
||||
assert result['message'] == exception.ERROR_MESSAGE
|
||||
assert result['data'] == exception.data
|
||||
|
||||
|
||||
def test_to_rpc_without_data():
|
||||
# Given
|
||||
exception = exceptions.BaseJSONRPCError()
|
||||
exception.ERROR_CODE = -32604
|
||||
exception.ERROR_MESSAGE = 'Test error'
|
||||
|
||||
# When
|
||||
result = exception.to_rpc()
|
||||
|
||||
# Then
|
||||
assert result['code'] == exception.ERROR_CODE
|
||||
assert result['message'] == exception.ERROR_MESSAGE
|
||||
assert 'data' not in result
|
||||
776
packages/bthlabs-jsonrpc-core/tests/executor/test_Executor.py
Normal file
776
packages/bthlabs-jsonrpc-core/tests/executor/test_Executor.py
Normal file
@@ -0,0 +1,776 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import json
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
from bthlabs_jsonrpc_core import exceptions, executor
|
||||
from bthlabs_jsonrpc_core.registry import MethodRegistry
|
||||
from bthlabs_jsonrpc_core.serializer import JSONRPCSerializer
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def single_call():
|
||||
return {
|
||||
'jsonrpc': '2.0',
|
||||
'id': 'test',
|
||||
'method': 'test',
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def batch_calls():
|
||||
return [
|
||||
{
|
||||
'jsonrpc': '2.0',
|
||||
'id': 'test',
|
||||
'method': 'test',
|
||||
},
|
||||
{
|
||||
'jsonrpc': '2.0',
|
||||
'id': 'test2',
|
||||
'method': 'test',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def jsonrpc_error():
|
||||
return exceptions.BaseJSONRPCError('I HAZ FIAL')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def execute_context():
|
||||
return executor.Executor.ExecuteContext([])
|
||||
|
||||
|
||||
def test_CallContext_invalid_context():
|
||||
# When
|
||||
result = executor.Executor.CallContext.invalid_context()
|
||||
|
||||
# Then
|
||||
assert result.method is None
|
||||
assert result.handler is None
|
||||
assert result.args is None
|
||||
assert result.kwargs is None
|
||||
|
||||
|
||||
def test_CallContext_is_valid_method_none(fake_handler):
|
||||
# When
|
||||
call_context = executor.Executor.CallContext(None, fake_handler, [], {})
|
||||
|
||||
# Then
|
||||
assert call_context.is_valid is False
|
||||
|
||||
|
||||
def test_CallContext_is_valid_handler_none():
|
||||
# When
|
||||
call_context = executor.Executor.CallContext('test', None, [], {})
|
||||
|
||||
# Then
|
||||
assert call_context.is_valid is False
|
||||
|
||||
|
||||
def test_CallContext_is_valid_args_none(fake_handler):
|
||||
# When
|
||||
call_context = executor.Executor.CallContext(
|
||||
'test', fake_handler, None, {},
|
||||
)
|
||||
|
||||
# Then
|
||||
assert call_context.is_valid is False
|
||||
|
||||
|
||||
def test_CallContext_is_valid_kwargs_none(fake_handler):
|
||||
# When
|
||||
call_context = executor.Executor.CallContext(
|
||||
'test', fake_handler, [], None,
|
||||
)
|
||||
|
||||
# Then
|
||||
assert call_context.is_valid is False
|
||||
|
||||
|
||||
def test_CallContext_is_valid(fake_handler):
|
||||
# When
|
||||
call_context = executor.Executor.CallContext('test', fake_handler, [], {})
|
||||
|
||||
# Then
|
||||
assert call_context.is_valid is True
|
||||
|
||||
|
||||
def test_init_default_namespace():
|
||||
# When
|
||||
result = executor.Executor()
|
||||
|
||||
# Then
|
||||
assert result.namespace == MethodRegistry.DEFAULT_NAMESPACE
|
||||
|
||||
|
||||
def test_init_custom_namespace():
|
||||
# When
|
||||
result = executor.Executor(namespace='testing')
|
||||
|
||||
# Then
|
||||
assert result.namespace == 'testing'
|
||||
|
||||
|
||||
@mock.patch.object(executor.MethodRegistry, 'shared_registry')
|
||||
def test_list_methods(mock_shared_registry, fake_method_registry):
|
||||
# Given
|
||||
fake_method_registry.get_methods.return_value = ['test']
|
||||
mock_shared_registry.return_value = fake_method_registry
|
||||
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
result = the_executor.list_methods()
|
||||
|
||||
# Then
|
||||
assert result == ['system.list_methods', 'test']
|
||||
|
||||
assert mock_shared_registry.called is True
|
||||
|
||||
fake_method_registry.get_methods.assert_called_with(the_executor.namespace)
|
||||
|
||||
|
||||
def test_get_internal_handler_list_methods():
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
result = the_executor.get_internal_handler('system.list_methods')
|
||||
|
||||
# Then
|
||||
assert result == the_executor.list_methods
|
||||
|
||||
|
||||
def test_get_internal_handler_method_not_found():
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
try:
|
||||
_ = the_executor.get_internal_handler('test')
|
||||
except Exception as exception:
|
||||
# Then
|
||||
assert isinstance(exception, exceptions.JSONRPCMethodNotFoundError)
|
||||
else:
|
||||
assert False, 'No exception raised?'
|
||||
|
||||
|
||||
def test_deserialize_data():
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
result = the_executor.deserialize_data('"spam"')
|
||||
|
||||
# Then
|
||||
assert result == 'spam'
|
||||
|
||||
|
||||
def test_deserialize_data_error():
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
try:
|
||||
_ = the_executor.deserialize_data(None)
|
||||
except Exception as exception:
|
||||
# Then
|
||||
assert isinstance(exception, exceptions.JSONRPCParseError)
|
||||
else:
|
||||
assert False, 'No exception raised?'
|
||||
|
||||
|
||||
def test_get_calls_batch(batch_calls):
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
result = the_executor.get_calls(batch_calls)
|
||||
|
||||
# Then
|
||||
assert result == batch_calls
|
||||
|
||||
|
||||
def test_get_calls_single(single_call):
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
result = the_executor.get_calls(single_call)
|
||||
|
||||
# Then
|
||||
assert result == [single_call]
|
||||
|
||||
|
||||
def test_get_calls_empty():
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
try:
|
||||
_ = the_executor.get_calls([])
|
||||
except Exception as exception:
|
||||
# Then
|
||||
assert isinstance(exception, exceptions.JSONRPCInvalidRequestError)
|
||||
else:
|
||||
assert False, 'No exception raised?'
|
||||
|
||||
|
||||
def test_get_call_spec_not_dict():
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
try:
|
||||
_ = the_executor.get_call_spec(None)
|
||||
except Exception as exception:
|
||||
# Then
|
||||
assert isinstance(exception, exceptions.JSONRPCInvalidRequestError)
|
||||
else:
|
||||
assert False, 'No exception raised?'
|
||||
|
||||
|
||||
def test_get_call_spec_wihtout_jsonrpc(single_call):
|
||||
# Given
|
||||
single_call.pop('jsonrpc')
|
||||
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
try:
|
||||
_ = the_executor.get_call_spec(single_call)
|
||||
except Exception as exception:
|
||||
# Then
|
||||
assert isinstance(exception, exceptions.JSONRPCInvalidRequestError)
|
||||
else:
|
||||
assert False, 'No exception raised?'
|
||||
|
||||
|
||||
def test_get_call_spec_invalid_jsonrpc(single_call):
|
||||
# Given
|
||||
single_call['jsonrpc'] = 'test'
|
||||
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
try:
|
||||
_ = the_executor.get_call_spec(single_call)
|
||||
except Exception as exception:
|
||||
# Then
|
||||
assert isinstance(exception, exceptions.JSONRPCInvalidRequestError)
|
||||
else:
|
||||
assert False, 'No exception raised?'
|
||||
|
||||
|
||||
def test_get_call_spec_wihtout_method(single_call):
|
||||
# Given
|
||||
single_call.pop('method')
|
||||
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
try:
|
||||
_ = the_executor.get_call_spec(single_call)
|
||||
except Exception as exception:
|
||||
# Then
|
||||
assert isinstance(exception, exceptions.JSONRPCInvalidRequestError)
|
||||
else:
|
||||
assert False, 'No exception raised?'
|
||||
|
||||
|
||||
@mock.patch.object(executor.MethodRegistry, 'shared_registry')
|
||||
def test_get_call_spec_internal_method(mock_shared_registry,
|
||||
single_call,
|
||||
fake_handler):
|
||||
# Given
|
||||
single_call['method'] = 'system.list_methods'
|
||||
|
||||
the_executor = executor.Executor()
|
||||
|
||||
with mock.patch.object(the_executor, 'get_internal_handler') as mock_get_internal_handler:
|
||||
mock_get_internal_handler.return_value = fake_handler
|
||||
|
||||
# When
|
||||
result = the_executor.get_call_spec(single_call)
|
||||
|
||||
# Then
|
||||
assert result[0] == 'system.list_methods'
|
||||
assert result[1] is fake_handler
|
||||
|
||||
mock_get_internal_handler.assert_called_with('system.list_methods')
|
||||
|
||||
assert mock_shared_registry.called is False
|
||||
|
||||
|
||||
@mock.patch.object(executor.MethodRegistry, 'shared_registry')
|
||||
def test_get_call_spec_registry_method(mock_shared_registry,
|
||||
single_call,
|
||||
fake_method_registry,
|
||||
fake_handler):
|
||||
# Given
|
||||
fake_method_registry.get_handler.return_value = fake_handler
|
||||
mock_shared_registry.return_value = fake_method_registry
|
||||
|
||||
the_executor = executor.Executor()
|
||||
|
||||
with mock.patch.object(the_executor, 'get_internal_handler') as mock_get_internal_handler:
|
||||
# When
|
||||
result = the_executor.get_call_spec(single_call)
|
||||
|
||||
# Then
|
||||
assert result[0] == 'test'
|
||||
assert result[1] is fake_handler
|
||||
|
||||
assert mock_get_internal_handler.called is False
|
||||
|
||||
fake_method_registry.get_handler.assert_called_with(
|
||||
the_executor.namespace, 'test',
|
||||
)
|
||||
|
||||
|
||||
@mock.patch.object(executor.MethodRegistry, 'shared_registry')
|
||||
def test_get_call_spec_method_not_found(mock_shared_registry,
|
||||
single_call,
|
||||
fake_method_registry):
|
||||
# Given
|
||||
fake_method_registry.get_handler.return_value = None
|
||||
mock_shared_registry.return_value = fake_method_registry
|
||||
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
try:
|
||||
_ = the_executor.get_call_spec(single_call)
|
||||
except Exception as exception:
|
||||
# Then
|
||||
assert isinstance(exception, exceptions.JSONRPCMethodNotFoundError)
|
||||
else:
|
||||
assert False, 'No exception raised?'
|
||||
|
||||
|
||||
@mock.patch.object(executor.MethodRegistry, 'shared_registry')
|
||||
def test_get_call_spec_invalid_params(mock_shared_registry,
|
||||
single_call,
|
||||
fake_method_registry,
|
||||
fake_handler):
|
||||
# Given
|
||||
single_call['params'] = 'spam'
|
||||
|
||||
fake_method_registry.get_handler.return_value = fake_handler
|
||||
mock_shared_registry.return_value = fake_method_registry
|
||||
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
try:
|
||||
_ = the_executor.get_call_spec(single_call)
|
||||
except Exception as exception:
|
||||
# Then
|
||||
assert isinstance(exception, exceptions.JSONRPCInvalidParamsError)
|
||||
else:
|
||||
assert False, 'No exception raised?'
|
||||
|
||||
|
||||
@mock.patch.object(executor.MethodRegistry, 'shared_registry')
|
||||
def test_get_call_spec_with_args(mock_shared_registry,
|
||||
single_call,
|
||||
fake_method_registry,
|
||||
fake_handler):
|
||||
# Given
|
||||
single_call['params'] = ['spam']
|
||||
|
||||
fake_method_registry.get_handler.return_value = fake_handler
|
||||
mock_shared_registry.return_value = fake_method_registry
|
||||
|
||||
the_executor = executor.Executor()
|
||||
|
||||
with mock.patch.object(the_executor, 'enrich_args') as mock_enrich_args:
|
||||
mock_enrich_args.return_value = ['spam', 'eggs']
|
||||
|
||||
# When
|
||||
result = the_executor.get_call_spec(single_call)
|
||||
|
||||
# Then
|
||||
assert result[2] == ['spam', 'eggs']
|
||||
assert result[3] == {}
|
||||
|
||||
mock_enrich_args.assert_called_with(['spam'])
|
||||
|
||||
|
||||
@mock.patch.object(executor.MethodRegistry, 'shared_registry')
|
||||
def test_get_call_spec_with_kwargs(mock_shared_registry,
|
||||
single_call,
|
||||
fake_method_registry,
|
||||
fake_handler):
|
||||
# Given
|
||||
single_call['params'] = {'spam': True}
|
||||
|
||||
fake_method_registry.get_handler.return_value = fake_handler
|
||||
mock_shared_registry.return_value = fake_method_registry
|
||||
|
||||
the_executor = executor.Executor()
|
||||
|
||||
with mock.patch.object(the_executor, 'enrich_kwargs') as mock_enrich_kwargs:
|
||||
mock_enrich_kwargs.return_value = {'spam': True, 'eggs': False}
|
||||
|
||||
# When
|
||||
result = the_executor.get_call_spec(single_call)
|
||||
|
||||
# Then
|
||||
assert result[2] == []
|
||||
assert result[3] == {'spam': True, 'eggs': False}
|
||||
|
||||
mock_enrich_kwargs.assert_called_with({'spam': True})
|
||||
|
||||
|
||||
def test_process_results(batch_calls, single_call, jsonrpc_error):
|
||||
# Given
|
||||
call_without_id = {**single_call}
|
||||
call_without_id.pop('id')
|
||||
|
||||
call_results = [
|
||||
(batch_calls[0], 'OK'),
|
||||
(batch_calls[1], jsonrpc_error),
|
||||
(call_without_id, '???'),
|
||||
(call_without_id, jsonrpc_error),
|
||||
]
|
||||
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
result = the_executor.process_results(call_results)
|
||||
|
||||
# Then
|
||||
assert isinstance(result, list)
|
||||
assert len(result) == 2
|
||||
|
||||
first_response, second_response = result
|
||||
|
||||
expected_first_response = {
|
||||
'jsonrpc': '2.0',
|
||||
'id': batch_calls[0]['id'],
|
||||
'result': 'OK',
|
||||
}
|
||||
assert first_response == expected_first_response
|
||||
|
||||
expected_second_response = {
|
||||
'jsonrpc': '2.0',
|
||||
'id': batch_calls[1]['id'],
|
||||
'error': jsonrpc_error,
|
||||
}
|
||||
assert second_response == expected_second_response
|
||||
|
||||
|
||||
def test_process_results_single_call(single_call):
|
||||
# Given
|
||||
call_results = [
|
||||
(single_call, 'OK'),
|
||||
]
|
||||
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
result = the_executor.process_results(call_results)
|
||||
|
||||
# Then
|
||||
expected_result = {
|
||||
'jsonrpc': '2.0',
|
||||
'id': single_call['id'],
|
||||
'result': 'OK',
|
||||
}
|
||||
assert result == expected_result
|
||||
|
||||
|
||||
def test_process_results_top_level_error(jsonrpc_error):
|
||||
# Given
|
||||
call_results = [
|
||||
(None, jsonrpc_error),
|
||||
]
|
||||
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
result = the_executor.process_results(call_results)
|
||||
|
||||
# Then
|
||||
expected_result = {
|
||||
'jsonrpc': '2.0',
|
||||
'id': None,
|
||||
'error': jsonrpc_error,
|
||||
}
|
||||
assert result == expected_result
|
||||
|
||||
|
||||
def test_process_results_empty(single_call):
|
||||
# Given
|
||||
single_call.pop('id')
|
||||
|
||||
call_results = [
|
||||
(single_call, 'OK'),
|
||||
]
|
||||
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
result = the_executor.process_results(call_results)
|
||||
|
||||
# Then
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_call_context_invalid_context(jsonrpc_error,
|
||||
execute_context,
|
||||
single_call):
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
with mock.patch.object(the_executor, 'get_call_spec') as mock_get_call_spec:
|
||||
mock_get_call_spec.side_effect = jsonrpc_error
|
||||
|
||||
# When
|
||||
with the_executor.call_context(execute_context, single_call) as result:
|
||||
pass
|
||||
|
||||
# Then
|
||||
assert result.is_valid is False
|
||||
|
||||
|
||||
def test_call_context_handle_jsonrpc_error(fake_handler,
|
||||
jsonrpc_error,
|
||||
execute_context,
|
||||
single_call):
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
with mock.patch.object(the_executor, 'get_call_spec') as mock_get_call_spec:
|
||||
mock_get_call_spec.return_value = (
|
||||
'test', fake_handler, [], {},
|
||||
)
|
||||
|
||||
# When
|
||||
with the_executor.call_context(execute_context, single_call) as _:
|
||||
raise jsonrpc_error
|
||||
|
||||
# Then
|
||||
assert len(execute_context.results) == 1
|
||||
|
||||
call_result = execute_context.results[0]
|
||||
assert call_result[1] == jsonrpc_error
|
||||
|
||||
|
||||
def test_call_context_handle_exception(fake_handler,
|
||||
execute_context,
|
||||
single_call):
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
with mock.patch.object(the_executor, 'get_call_spec') as mock_get_call_spec:
|
||||
mock_get_call_spec.return_value = (
|
||||
'test', fake_handler, [], {},
|
||||
)
|
||||
|
||||
# When
|
||||
with the_executor.call_context(execute_context, single_call) as _:
|
||||
raise RuntimeError('I HAZ FIAL')
|
||||
|
||||
# Then
|
||||
assert len(execute_context.results) == 1
|
||||
|
||||
call_result = execute_context.results[0]
|
||||
assert isinstance(call_result[1], exceptions.JSONRPCInternalError)
|
||||
assert call_result[1].data == 'I HAZ FIAL'
|
||||
|
||||
|
||||
def test_call_context(fake_handler, execute_context, single_call):
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
with mock.patch.object(the_executor, 'get_call_spec') as mock_get_call_spec:
|
||||
mock_get_call_spec.return_value = (
|
||||
'test', fake_handler, [], {},
|
||||
)
|
||||
|
||||
# When
|
||||
with the_executor.call_context(execute_context, single_call) as result:
|
||||
result.result = 'OK'
|
||||
|
||||
# Then
|
||||
assert result.method == 'test'
|
||||
assert result.handler is fake_handler
|
||||
assert result.args == []
|
||||
assert result.kwargs == {}
|
||||
|
||||
assert len(execute_context.results) == 1
|
||||
|
||||
expected_call_result = (single_call, 'OK')
|
||||
assert execute_context.results[0] == expected_call_result
|
||||
|
||||
|
||||
def test_execute_context_handle_jsonrpc_error(jsonrpc_error):
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
with the_executor.execute_context() as result:
|
||||
raise jsonrpc_error
|
||||
|
||||
# Then
|
||||
assert result.results == [(None, jsonrpc_error)]
|
||||
|
||||
|
||||
def test_execute_context_handle_exception():
|
||||
# Given
|
||||
error = RuntimeError('I HAZ FIAL')
|
||||
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
try:
|
||||
with the_executor.execute_context() as result:
|
||||
raise error
|
||||
except Exception as exception:
|
||||
assert exception is error
|
||||
|
||||
assert result.serializer is None
|
||||
|
||||
|
||||
def test_execute_context_handle_empty_results(single_call):
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
with mock.patch.object(the_executor, 'process_results') as mock_process_results:
|
||||
with mock.patch.object(the_executor, 'serializer') as mock_serializer:
|
||||
mock_process_results.return_value = None
|
||||
|
||||
# When
|
||||
with the_executor.execute_context() as result:
|
||||
result.results.append((single_call, 'OK'))
|
||||
|
||||
# Then
|
||||
assert result.serializer is None
|
||||
|
||||
assert mock_serializer.called is False
|
||||
|
||||
|
||||
def test_execute_context(fake_rpc_serializer, single_call):
|
||||
# Given
|
||||
fake_responses = {
|
||||
'jsonrpc': '2.0',
|
||||
'id': 'test',
|
||||
'result': 'OK',
|
||||
}
|
||||
|
||||
the_executor = executor.Executor()
|
||||
|
||||
with mock.patch.object(the_executor, 'process_results') as mock_process_results:
|
||||
with mock.patch.object(the_executor, 'serializer') as mock_serializer:
|
||||
mock_process_results.return_value = fake_responses
|
||||
|
||||
mock_serializer.return_value = fake_rpc_serializer
|
||||
|
||||
# When
|
||||
with the_executor.execute_context() as result:
|
||||
result.results.append((single_call, 'OK'))
|
||||
|
||||
# Then
|
||||
assert result.serializer is fake_rpc_serializer
|
||||
|
||||
mock_process_results.assert_called_with([(single_call, 'OK')])
|
||||
|
||||
mock_serializer.assert_called_with(fake_responses)
|
||||
|
||||
|
||||
def test_enrich_args():
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
result = the_executor.enrich_args(['spam', 'eggs'])
|
||||
|
||||
# Then
|
||||
assert result == ['spam', 'eggs']
|
||||
|
||||
|
||||
def test_enrich_kwargs():
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
# When
|
||||
result = the_executor.enrich_kwargs({'spam': True, 'eggs': False})
|
||||
|
||||
# Then
|
||||
assert result == {'spam': True, 'eggs': False}
|
||||
|
||||
|
||||
@pytest.mark.skip('NOOP')
|
||||
def test_before_call():
|
||||
pass
|
||||
|
||||
|
||||
@mock.patch.object(executor.MethodRegistry, 'shared_registry')
|
||||
def test_execute(mock_shared_registry, fake_method_registry):
|
||||
# Given
|
||||
fake_method_registry.get_handler.return_value = None
|
||||
fake_method_registry.get_methods.return_value = []
|
||||
mock_shared_registry.return_value = fake_method_registry
|
||||
|
||||
batch = [
|
||||
{
|
||||
'jsonrpc': '2.0',
|
||||
'id': 'call_1',
|
||||
'method': 'system.list_methods',
|
||||
'params': ['spam'],
|
||||
},
|
||||
{
|
||||
'jsonrpc': '2.0',
|
||||
'id': 'call_2',
|
||||
'method': 'idontexist',
|
||||
},
|
||||
{
|
||||
'jsonrpc': '2.0',
|
||||
'method': 'system.list_methods',
|
||||
'params': {'spam': True},
|
||||
},
|
||||
]
|
||||
|
||||
payload = json.dumps(batch)
|
||||
|
||||
the_executor = executor.Executor()
|
||||
|
||||
with mock.patch.object(the_executor, 'before_call') as mock_before_call:
|
||||
# When
|
||||
result = the_executor.execute(payload)
|
||||
|
||||
# Then
|
||||
assert isinstance(result, JSONRPCSerializer)
|
||||
|
||||
expected_result_data = [
|
||||
{
|
||||
'jsonrpc': '2.0',
|
||||
'id': 'call_1',
|
||||
'result': ['system.list_methods'],
|
||||
},
|
||||
{
|
||||
'jsonrpc': '2.0',
|
||||
'id': 'call_2',
|
||||
'error': {
|
||||
'code': exceptions.JSONRPCMethodNotFoundError.ERROR_CODE,
|
||||
'message': exceptions.JSONRPCMethodNotFoundError.ERROR_MESSAGE,
|
||||
},
|
||||
},
|
||||
]
|
||||
assert result.data == expected_result_data
|
||||
|
||||
fake_method_registry.get_handler.assert_called_with(
|
||||
'jsonrpc', 'idontexist',
|
||||
)
|
||||
|
||||
assert mock_before_call.call_count == 2
|
||||
mock_before_call.assert_any_call('system.list_methods', ['spam'], {})
|
||||
mock_before_call.assert_any_call(
|
||||
'system.list_methods', [], {'spam': True},
|
||||
)
|
||||
@@ -0,0 +1,108 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from unittest import mock
|
||||
|
||||
from bthlabs_jsonrpc_core import registry
|
||||
|
||||
|
||||
def test_init():
|
||||
# When
|
||||
result = registry.MethodRegistry()
|
||||
|
||||
# Then
|
||||
assert result.registry == {'jsonrpc': {}}
|
||||
|
||||
|
||||
@mock.patch.object(registry.MethodRegistry, '__init__')
|
||||
def test_shared_registry(mock_init):
|
||||
# Given
|
||||
mock_init.return_value = None
|
||||
|
||||
# When
|
||||
result = registry.MethodRegistry.shared_registry()
|
||||
|
||||
# Then
|
||||
assert isinstance(result, registry.MethodRegistry)
|
||||
|
||||
assert registry.MethodRegistry.INSTANCE is result
|
||||
|
||||
assert mock_init.called is True
|
||||
|
||||
# After
|
||||
registry.MethodRegistry.INSTANCE = None
|
||||
|
||||
|
||||
@mock.patch.object(registry.MethodRegistry, '__init__')
|
||||
def test_shared_registry_with_instance(mock_init, fake_method_registry):
|
||||
# Given
|
||||
mock_init.return_value = None
|
||||
|
||||
registry.MethodRegistry.INSTANCE = fake_method_registry
|
||||
|
||||
# When
|
||||
result = registry.MethodRegistry.shared_registry()
|
||||
|
||||
# Then
|
||||
assert result is fake_method_registry
|
||||
|
||||
assert registry.MethodRegistry.INSTANCE is fake_method_registry
|
||||
|
||||
assert mock_init.called is False
|
||||
|
||||
# After
|
||||
registry.MethodRegistry.INSTANCE = None
|
||||
|
||||
|
||||
def test_register_method(fake_handler):
|
||||
# Given
|
||||
the_registry = registry.MethodRegistry()
|
||||
|
||||
# When'
|
||||
the_registry.register_method('testing', 'test', fake_handler)
|
||||
|
||||
# Then
|
||||
expected_namespace = {'test': fake_handler}
|
||||
assert the_registry.registry['testing'] == expected_namespace
|
||||
|
||||
|
||||
def test_register_method_existing_namespace(fake_handler):
|
||||
# Given
|
||||
spam_handler = mock.Mock()
|
||||
|
||||
the_registry = registry.MethodRegistry()
|
||||
the_registry.registry['jsonrpc']['spam'] = spam_handler
|
||||
|
||||
# When'
|
||||
the_registry.register_method('jsonrpc', 'test', fake_handler)
|
||||
|
||||
# Then
|
||||
expected_namespace = {'spam': spam_handler, 'test': fake_handler}
|
||||
assert the_registry.registry['jsonrpc'] == expected_namespace
|
||||
|
||||
|
||||
def test_get_methods():
|
||||
# Given
|
||||
the_registry = registry.MethodRegistry()
|
||||
the_registry.registry['jsonrpc']['spam'] = mock.Mock()
|
||||
the_registry.registry['jsonrpc']['eggs'] = mock.Mock()
|
||||
|
||||
# When'
|
||||
result = the_registry.get_methods('jsonrpc')
|
||||
|
||||
# Then
|
||||
expected_methods = {'spam', 'eggs'}
|
||||
assert set(result) == expected_methods
|
||||
|
||||
|
||||
def test_get_handler(fake_handler):
|
||||
# Given
|
||||
spam_handler = mock.Mock()
|
||||
|
||||
the_registry = registry.MethodRegistry()
|
||||
the_registry.registry['jsonrpc']['spam'] = spam_handler
|
||||
the_registry.registry['jsonrpc']['eggs'] = fake_handler
|
||||
|
||||
# When'
|
||||
result = the_registry.get_handler('jsonrpc', 'eggs')
|
||||
|
||||
# Then
|
||||
assert result is fake_handler
|
||||
@@ -0,0 +1,193 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
import decimal
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
|
||||
from bthlabs_jsonrpc_core import exceptions, serializer
|
||||
|
||||
|
||||
def test_init():
|
||||
# When
|
||||
result = serializer.JSONRPCSerializer('spam')
|
||||
|
||||
# Then
|
||||
assert result._data == 'spam'
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'value,expected',
|
||||
[(None, True), (False, True), (0, True), ('spam', True), ([], False)],
|
||||
)
|
||||
def test_is_simple_value(value, expected):
|
||||
# Given
|
||||
the_serializer = serializer.JSONRPCSerializer('spam')
|
||||
|
||||
# When
|
||||
result = the_serializer.is_simple_value(value)
|
||||
|
||||
# Then
|
||||
assert result == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'value,expected',
|
||||
[
|
||||
([], True), ((x for x in [0, 1]), True), (set(), True),
|
||||
(tuple(), True), ({}, False),
|
||||
],
|
||||
)
|
||||
def test_is_sequence_value(value, expected):
|
||||
# Given
|
||||
the_serializer = serializer.JSONRPCSerializer('spam')
|
||||
|
||||
# When
|
||||
result = the_serializer.is_sequence_value(value)
|
||||
|
||||
# Then
|
||||
assert result == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'value,expected',
|
||||
[({}, True), ([], False)],
|
||||
)
|
||||
def test_is_dict_value(value, expected):
|
||||
# Given
|
||||
the_serializer = serializer.JSONRPCSerializer('spam')
|
||||
|
||||
# When
|
||||
result = the_serializer.is_dict_value(value)
|
||||
|
||||
# Then
|
||||
assert result == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'value,expected',
|
||||
[(uuid.uuid4(), True), (decimal.Decimal('42'), True), ([], False)],
|
||||
)
|
||||
def test_is_string_coercible_value(value, expected):
|
||||
# Given
|
||||
the_serializer = serializer.JSONRPCSerializer('spam')
|
||||
|
||||
# When
|
||||
result = the_serializer.is_string_coercible_value(value)
|
||||
|
||||
# Then
|
||||
assert result == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'value,expected',
|
||||
[
|
||||
(
|
||||
datetime.datetime(1987, 10, 3, 8, 0, 0, 0, tzinfo=datetime.timezone.utc),
|
||||
'1987-10-03T08:00:00+00:00',
|
||||
),
|
||||
(
|
||||
datetime.date(1987, 10, 3),
|
||||
'1987-10-03',
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_serialize_datetime(value, expected):
|
||||
# Given
|
||||
the_serializer = serializer.JSONRPCSerializer('spam')
|
||||
|
||||
# When
|
||||
result = the_serializer.serialize_datetime(value)
|
||||
|
||||
# Then
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_serialize_sequence():
|
||||
# Given
|
||||
the_serializer = serializer.JSONRPCSerializer('spam')
|
||||
|
||||
# When
|
||||
result = the_serializer.serialize_sequence([(0, 1), 'spam'])
|
||||
|
||||
# Then
|
||||
assert result == [[0, 1], 'spam']
|
||||
|
||||
|
||||
def test_serialize_dict():
|
||||
# Given
|
||||
the_serializer = serializer.JSONRPCSerializer('spam')
|
||||
|
||||
# When
|
||||
result = the_serializer.serialize_dict(
|
||||
{'spam': True, 'eggs': {'key': decimal.Decimal('42')}},
|
||||
)
|
||||
|
||||
# Then
|
||||
assert result == {'spam': True, 'eggs': {'key': '42'}}
|
||||
|
||||
|
||||
def test_serialize_string_coercible():
|
||||
# Given
|
||||
the_serializer = serializer.JSONRPCSerializer('spam')
|
||||
|
||||
# When
|
||||
result = the_serializer.serialize_string_coercible(decimal.Decimal('42'))
|
||||
|
||||
# Then
|
||||
assert result == '42'
|
||||
|
||||
|
||||
def test_serialize_value():
|
||||
# Given
|
||||
value = [
|
||||
serializer.JSONRPCSerializer('spam'),
|
||||
'eggs',
|
||||
datetime.datetime(1987, 10, 3, 8, 0, 0, 0, tzinfo=datetime.timezone.utc),
|
||||
[0, 1],
|
||||
{'spam': True},
|
||||
decimal.Decimal('42'),
|
||||
exceptions.BaseJSONRPCError(),
|
||||
]
|
||||
|
||||
the_serializer = serializer.JSONRPCSerializer('spam')
|
||||
|
||||
# When
|
||||
result = the_serializer.serialize_value(value)
|
||||
|
||||
# Then
|
||||
expected = [
|
||||
'spam',
|
||||
'eggs',
|
||||
'1987-10-03T08:00:00+00:00',
|
||||
[0, 1],
|
||||
{'spam': True},
|
||||
'42',
|
||||
exceptions.BaseJSONRPCError().to_rpc(),
|
||||
]
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_serialize_value_no_to_rpc():
|
||||
# Given
|
||||
the_serializer = serializer.JSONRPCSerializer('spam')
|
||||
|
||||
# When
|
||||
try:
|
||||
_ = the_serializer.serialize_value(object())
|
||||
except Exception as exception:
|
||||
# Then
|
||||
assert isinstance(exception, exceptions.JSONRPCSerializerError)
|
||||
|
||||
|
||||
def test_data():
|
||||
# Given
|
||||
the_serializer = serializer.JSONRPCSerializer(decimal.Decimal('42'))
|
||||
|
||||
# When
|
||||
result = the_serializer.data
|
||||
|
||||
# Then
|
||||
assert result == '42'
|
||||
|
||||
assert the_serializer._serialized_data == '42'
|
||||
Reference in New Issue
Block a user