You've already forked bthlabs-jsonrpc
v1.1.0b1
This commit is contained in:
121
packages/bthlabs-jsonrpc-core/tests/codecs/test_JSONCodec.py
Normal file
121
packages/bthlabs-jsonrpc-core/tests/codecs/test_JSONCodec.py
Normal file
@@ -0,0 +1,121 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# type: ignore
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from unittest import mock
|
||||
|
||||
from bthlabs_jsonrpc_core import codecs
|
||||
|
||||
|
||||
def test_decode():
|
||||
# Given
|
||||
json_codec = codecs.JSONCodec()
|
||||
|
||||
# When
|
||||
result = json_codec.decode('{"spam": true, "eggs": false}')
|
||||
|
||||
# Then
|
||||
expected_result = {
|
||||
'spam': True,
|
||||
'eggs': False,
|
||||
}
|
||||
assert result == expected_result
|
||||
|
||||
|
||||
@mock.patch.object(codecs.json, 'loads')
|
||||
def test_decode_decoder_kwargs(mock_json_loads: mock.Mock):
|
||||
# Given
|
||||
mock_json_loads.return_value = 'spam'
|
||||
|
||||
json_codec = codecs.JSONCodec()
|
||||
|
||||
payload = '{"spam": true, "eggs": false}'
|
||||
fake_json_decoder = mock.Mock(spec=json.JSONDecoder)
|
||||
fake_object_hook = mock.Mock()
|
||||
fake_parse_float = mock.Mock()
|
||||
fake_parse_int = mock.Mock()
|
||||
fake_parse_constant = mock.Mock()
|
||||
fake_object_pairs_hook = mock.Mock()
|
||||
|
||||
# When
|
||||
_ = json_codec.decode(
|
||||
payload,
|
||||
cls=fake_json_decoder,
|
||||
object_hook=fake_object_hook,
|
||||
parse_float=fake_parse_float,
|
||||
parse_int=fake_parse_int,
|
||||
parse_constant=fake_parse_constant,
|
||||
object_pairs_hook=fake_object_pairs_hook,
|
||||
)
|
||||
|
||||
# Then
|
||||
mock_json_loads.assert_called_once_with(
|
||||
payload,
|
||||
cls=fake_json_decoder,
|
||||
object_hook=fake_object_hook,
|
||||
parse_float=fake_parse_float,
|
||||
parse_int=fake_parse_int,
|
||||
parse_constant=fake_parse_constant,
|
||||
object_pairs_hook=fake_object_pairs_hook,
|
||||
)
|
||||
|
||||
|
||||
def test_encode():
|
||||
# Given
|
||||
json_codec = codecs.JSONCodec()
|
||||
|
||||
# When
|
||||
result = json_codec.encode({'spam': True, 'eggs': False})
|
||||
|
||||
# Then
|
||||
expected_result = '{"spam": true, "eggs": false}'
|
||||
assert result == expected_result
|
||||
|
||||
|
||||
@mock.patch.object(codecs.json, 'dumps')
|
||||
def test_encode_encoder_kwargs(mock_json_dumps: mock.Mock):
|
||||
# Given
|
||||
mock_json_dumps.return_value = 'spam'
|
||||
|
||||
json_codec = codecs.JSONCodec()
|
||||
|
||||
payload = {"spam": True, "eggs": False}
|
||||
fake_json_encoder = mock.Mock(spec=json.JSONEncoder)
|
||||
|
||||
# When
|
||||
_ = json_codec.encode(
|
||||
payload,
|
||||
skipkeys=False,
|
||||
ensure_ascii=True,
|
||||
check_circular=True,
|
||||
allow_nan=True,
|
||||
cls=fake_json_encoder,
|
||||
indent=2, separators=(':', ','),
|
||||
default='DEFAULT',
|
||||
sort_keys=False,
|
||||
)
|
||||
|
||||
# Then
|
||||
mock_json_dumps.assert_called_once_with(
|
||||
payload,
|
||||
skipkeys=False,
|
||||
ensure_ascii=True,
|
||||
check_circular=True,
|
||||
allow_nan=True,
|
||||
cls=fake_json_encoder,
|
||||
indent=2, separators=(':', ','),
|
||||
default='DEFAULT',
|
||||
sort_keys=False,
|
||||
)
|
||||
|
||||
|
||||
def test_get_content_type():
|
||||
# Given
|
||||
json_codec = codecs.JSONCodec()
|
||||
|
||||
# When
|
||||
result = json_codec.get_content_type()
|
||||
|
||||
# Then
|
||||
assert result == 'application/json'
|
||||
@@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# type: ignore
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
@@ -8,15 +9,15 @@ from bthlabs_jsonrpc_core.serializer import JSONRPCSerializer
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fake_method_registry():
|
||||
def fake_method_registry() -> mock.Mock:
|
||||
return mock.Mock(spec=MethodRegistry)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fake_handler():
|
||||
def fake_handler() -> mock.Mock:
|
||||
return mock.Mock()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fake_rpc_serializer():
|
||||
def fake_rpc_serializer() -> mock.Mock:
|
||||
return mock.Mock(spec=JSONRPCSerializer)
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# type: ignore
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest import mock
|
||||
|
||||
from bthlabs_jsonrpc_core import decorators
|
||||
@@ -6,9 +9,9 @@ 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):
|
||||
def test_default_namespace(mock_shared_registry: mock.Mock,
|
||||
fake_method_registry: mock.Mock,
|
||||
fake_handler: mock.Mock):
|
||||
# Given
|
||||
mock_shared_registry.return_value = fake_method_registry
|
||||
|
||||
@@ -30,9 +33,9 @@ def test_default_namespace(mock_shared_registry,
|
||||
|
||||
|
||||
@mock.patch.object(decorators.MethodRegistry, 'shared_registry')
|
||||
def test_custom_namespace(mock_shared_registry,
|
||||
fake_method_registry,
|
||||
fake_handler):
|
||||
def test_custom_namespace(mock_shared_registry: mock.Mock,
|
||||
fake_method_registry: mock.Mock,
|
||||
fake_handler: mock.Mock):
|
||||
# Given
|
||||
mock_shared_registry.return_value = fake_method_registry
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# type: ignore
|
||||
from __future__ import annotations
|
||||
|
||||
from bthlabs_jsonrpc_core import exceptions
|
||||
|
||||
|
||||
|
||||
@@ -1,46 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# type: ignore
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
from bthlabs_jsonrpc_core import exceptions, executor
|
||||
from bthlabs_jsonrpc_core.codecs import JSONCodec
|
||||
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():
|
||||
def jsonrpc_error() -> exceptions.BaseJSONRPCError:
|
||||
return exceptions.BaseJSONRPCError('I HAZ FIAL')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def execute_context():
|
||||
def execute_context() -> executor.Executor.ExecuteContext:
|
||||
return executor.Executor.ExecuteContext([])
|
||||
|
||||
|
||||
@@ -55,7 +34,7 @@ def test_CallContext_invalid_context():
|
||||
assert result.kwargs is None
|
||||
|
||||
|
||||
def test_CallContext_is_valid_method_none(fake_handler):
|
||||
def test_CallContext_is_valid_method_none(fake_handler: mock.Mock):
|
||||
# When
|
||||
call_context = executor.Executor.CallContext(None, fake_handler, [], {})
|
||||
|
||||
@@ -71,7 +50,7 @@ def test_CallContext_is_valid_handler_none():
|
||||
assert call_context.is_valid is False
|
||||
|
||||
|
||||
def test_CallContext_is_valid_args_none(fake_handler):
|
||||
def test_CallContext_is_valid_args_none(fake_handler: mock.Mock):
|
||||
# When
|
||||
call_context = executor.Executor.CallContext(
|
||||
'test', fake_handler, None, {},
|
||||
@@ -81,7 +60,7 @@ def test_CallContext_is_valid_args_none(fake_handler):
|
||||
assert call_context.is_valid is False
|
||||
|
||||
|
||||
def test_CallContext_is_valid_kwargs_none(fake_handler):
|
||||
def test_CallContext_is_valid_kwargs_none(fake_handler: mock.Mock):
|
||||
# When
|
||||
call_context = executor.Executor.CallContext(
|
||||
'test', fake_handler, [], None,
|
||||
@@ -91,7 +70,7 @@ def test_CallContext_is_valid_kwargs_none(fake_handler):
|
||||
assert call_context.is_valid is False
|
||||
|
||||
|
||||
def test_CallContext_is_valid(fake_handler):
|
||||
def test_CallContext_is_valid(fake_handler: mock.Mock):
|
||||
# When
|
||||
call_context = executor.Executor.CallContext('test', fake_handler, [], {})
|
||||
|
||||
@@ -99,12 +78,13 @@ def test_CallContext_is_valid(fake_handler):
|
||||
assert call_context.is_valid is True
|
||||
|
||||
|
||||
def test_init_default_namespace():
|
||||
def test_init():
|
||||
# When
|
||||
result = executor.Executor()
|
||||
|
||||
# Then
|
||||
assert result.namespace == MethodRegistry.DEFAULT_NAMESPACE
|
||||
assert isinstance(result.codec, JSONCodec) is True
|
||||
|
||||
|
||||
def test_init_custom_namespace():
|
||||
@@ -115,8 +95,17 @@ def test_init_custom_namespace():
|
||||
assert result.namespace == 'testing'
|
||||
|
||||
|
||||
def test_init_custom_codec(fake_custom_codec: mock.Mock):
|
||||
# When
|
||||
result = executor.Executor(codec=fake_custom_codec)
|
||||
|
||||
# Then
|
||||
assert result.codec == fake_custom_codec
|
||||
|
||||
|
||||
@mock.patch.object(executor.MethodRegistry, 'shared_registry')
|
||||
def test_list_methods(mock_shared_registry, fake_method_registry):
|
||||
def test_list_methods(mock_shared_registry: mock.Mock,
|
||||
fake_method_registry: mock.Mock):
|
||||
# Given
|
||||
fake_method_registry.get_methods.return_value = ['test']
|
||||
mock_shared_registry.return_value = fake_method_registry
|
||||
@@ -170,6 +159,21 @@ def test_deserialize_data():
|
||||
assert result == 'spam'
|
||||
|
||||
|
||||
def test_deserialize_data_custom_codec(fake_custom_codec: mock.Mock):
|
||||
# Given
|
||||
fake_custom_codec.decode.return_value = 'spam'
|
||||
|
||||
the_executor = executor.Executor(codec=fake_custom_codec)
|
||||
|
||||
# When
|
||||
result = the_executor.deserialize_data('"spam"')
|
||||
|
||||
# Then
|
||||
assert result == 'spam'
|
||||
|
||||
fake_custom_codec.decode.assert_called_once_with('"spam"')
|
||||
|
||||
|
||||
def test_deserialize_data_error():
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
@@ -184,7 +188,25 @@ def test_deserialize_data_error():
|
||||
assert False, 'No exception raised?'
|
||||
|
||||
|
||||
def test_get_calls_batch(batch_calls):
|
||||
def test_deserialize_data_custom_codec_error(fake_custom_codec: mock.Mock):
|
||||
# Given
|
||||
error = RuntimeError('I HAZ FAIL')
|
||||
fake_custom_codec.decode.side_effect = error
|
||||
|
||||
the_executor = executor.Executor(codec=fake_custom_codec)
|
||||
|
||||
# When
|
||||
try:
|
||||
_ = the_executor.deserialize_data(None)
|
||||
except Exception as exception:
|
||||
# Then
|
||||
assert isinstance(exception, exceptions.JSONRPCParseError)
|
||||
assert exception.__cause__ == error
|
||||
else:
|
||||
assert False, 'No exception raised?'
|
||||
|
||||
|
||||
def test_get_calls_batch(batch_calls: list):
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
@@ -195,7 +217,7 @@ def test_get_calls_batch(batch_calls):
|
||||
assert result == batch_calls
|
||||
|
||||
|
||||
def test_get_calls_single(single_call):
|
||||
def test_get_calls_single(single_call: dict):
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
@@ -234,7 +256,7 @@ def test_get_call_spec_not_dict():
|
||||
assert False, 'No exception raised?'
|
||||
|
||||
|
||||
def test_get_call_spec_wihtout_jsonrpc(single_call):
|
||||
def test_get_call_spec_wihtout_jsonrpc(single_call: dict):
|
||||
# Given
|
||||
single_call.pop('jsonrpc')
|
||||
|
||||
@@ -250,7 +272,7 @@ def test_get_call_spec_wihtout_jsonrpc(single_call):
|
||||
assert False, 'No exception raised?'
|
||||
|
||||
|
||||
def test_get_call_spec_invalid_jsonrpc(single_call):
|
||||
def test_get_call_spec_invalid_jsonrpc(single_call: dict):
|
||||
# Given
|
||||
single_call['jsonrpc'] = 'test'
|
||||
|
||||
@@ -266,7 +288,7 @@ def test_get_call_spec_invalid_jsonrpc(single_call):
|
||||
assert False, 'No exception raised?'
|
||||
|
||||
|
||||
def test_get_call_spec_wihtout_method(single_call):
|
||||
def test_get_call_spec_wihtout_method(single_call: dict):
|
||||
# Given
|
||||
single_call.pop('method')
|
||||
|
||||
@@ -283,9 +305,9 @@ def test_get_call_spec_wihtout_method(single_call):
|
||||
|
||||
|
||||
@mock.patch.object(executor.MethodRegistry, 'shared_registry')
|
||||
def test_get_call_spec_internal_method(mock_shared_registry,
|
||||
single_call,
|
||||
fake_handler):
|
||||
def test_get_call_spec_internal_method(mock_shared_registry: mock.Mock,
|
||||
single_call: dict,
|
||||
fake_handler: mock.Mock):
|
||||
# Given
|
||||
single_call['method'] = 'system.list_methods'
|
||||
|
||||
@@ -307,11 +329,13 @@ def test_get_call_spec_internal_method(mock_shared_registry,
|
||||
|
||||
|
||||
@mock.patch.object(executor.MethodRegistry, 'shared_registry')
|
||||
def test_get_call_spec_registry_method(mock_shared_registry,
|
||||
single_call,
|
||||
fake_method_registry,
|
||||
fake_handler):
|
||||
def test_get_call_spec_registry_method(mock_shared_registry: mock.Mock,
|
||||
single_call: dict,
|
||||
fake_method_registry: mock.Mock,
|
||||
fake_handler: mock.Mock):
|
||||
# Given
|
||||
single_call['method'] = 'test'
|
||||
|
||||
fake_method_registry.get_handler.return_value = fake_handler
|
||||
mock_shared_registry.return_value = fake_method_registry
|
||||
|
||||
@@ -333,10 +357,12 @@ def test_get_call_spec_registry_method(mock_shared_registry,
|
||||
|
||||
|
||||
@mock.patch.object(executor.MethodRegistry, 'shared_registry')
|
||||
def test_get_call_spec_method_not_found(mock_shared_registry,
|
||||
single_call,
|
||||
fake_method_registry):
|
||||
def test_get_call_spec_method_not_found(mock_shared_registry: mock.Mock,
|
||||
single_call: dict,
|
||||
fake_method_registry: mock.Mock):
|
||||
# Given
|
||||
single_call['method'] = 'test'
|
||||
|
||||
fake_method_registry.get_handler.return_value = None
|
||||
mock_shared_registry.return_value = fake_method_registry
|
||||
|
||||
@@ -353,10 +379,10 @@ def test_get_call_spec_method_not_found(mock_shared_registry,
|
||||
|
||||
|
||||
@mock.patch.object(executor.MethodRegistry, 'shared_registry')
|
||||
def test_get_call_spec_invalid_params(mock_shared_registry,
|
||||
single_call,
|
||||
fake_method_registry,
|
||||
fake_handler):
|
||||
def test_get_call_spec_invalid_params(mock_shared_registry: mock.Mock,
|
||||
single_call: dict,
|
||||
fake_method_registry: mock.Mock,
|
||||
fake_handler: mock.Mock):
|
||||
# Given
|
||||
single_call['params'] = 'spam'
|
||||
|
||||
@@ -376,10 +402,10 @@ def test_get_call_spec_invalid_params(mock_shared_registry,
|
||||
|
||||
|
||||
@mock.patch.object(executor.MethodRegistry, 'shared_registry')
|
||||
def test_get_call_spec_with_args(mock_shared_registry,
|
||||
single_call,
|
||||
fake_method_registry,
|
||||
fake_handler):
|
||||
def test_get_call_spec_with_args(mock_shared_registry: mock.Mock,
|
||||
single_call: dict,
|
||||
fake_method_registry: mock.Mock,
|
||||
fake_handler: mock.Mock):
|
||||
# Given
|
||||
single_call['params'] = ['spam']
|
||||
|
||||
@@ -402,10 +428,10 @@ def test_get_call_spec_with_args(mock_shared_registry,
|
||||
|
||||
|
||||
@mock.patch.object(executor.MethodRegistry, 'shared_registry')
|
||||
def test_get_call_spec_with_kwargs(mock_shared_registry,
|
||||
single_call,
|
||||
fake_method_registry,
|
||||
fake_handler):
|
||||
def test_get_call_spec_with_kwargs(mock_shared_registry: mock.Mock,
|
||||
single_call: dict,
|
||||
fake_method_registry: mock.Mock,
|
||||
fake_handler: mock.Mock):
|
||||
# Given
|
||||
single_call['params'] = {'spam': True}
|
||||
|
||||
@@ -427,7 +453,9 @@ def test_get_call_spec_with_kwargs(mock_shared_registry,
|
||||
mock_enrich_kwargs.assert_called_with({'spam': True})
|
||||
|
||||
|
||||
def test_process_results(batch_calls, single_call, jsonrpc_error):
|
||||
def test_process_results(batch_calls: list,
|
||||
single_call: dict,
|
||||
jsonrpc_error: exceptions.BaseJSONRPCError):
|
||||
# Given
|
||||
call_without_id = {**single_call}
|
||||
call_without_id.pop('id')
|
||||
@@ -465,7 +493,7 @@ def test_process_results(batch_calls, single_call, jsonrpc_error):
|
||||
assert second_response == expected_second_response
|
||||
|
||||
|
||||
def test_process_results_single_call(single_call):
|
||||
def test_process_results_single_call(single_call: dict):
|
||||
# Given
|
||||
call_results = [
|
||||
(single_call, 'OK'),
|
||||
@@ -485,7 +513,7 @@ def test_process_results_single_call(single_call):
|
||||
assert result == expected_result
|
||||
|
||||
|
||||
def test_process_results_top_level_error(jsonrpc_error):
|
||||
def test_process_results_top_level_error(jsonrpc_error: exceptions.BaseJSONRPCError):
|
||||
# Given
|
||||
call_results = [
|
||||
(None, jsonrpc_error),
|
||||
@@ -505,7 +533,7 @@ def test_process_results_top_level_error(jsonrpc_error):
|
||||
assert result == expected_result
|
||||
|
||||
|
||||
def test_process_results_empty(single_call):
|
||||
def test_process_results_empty(single_call: dict):
|
||||
# Given
|
||||
single_call.pop('id')
|
||||
|
||||
@@ -522,9 +550,9 @@ def test_process_results_empty(single_call):
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_call_context_invalid_context(jsonrpc_error,
|
||||
execute_context,
|
||||
single_call):
|
||||
def test_call_context_invalid_context(jsonrpc_error: exceptions.BaseJSONRPCError,
|
||||
execute_context: executor.Executor.ExecuteContext,
|
||||
single_call: dict):
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
@@ -539,10 +567,10 @@ def test_call_context_invalid_context(jsonrpc_error,
|
||||
assert result.is_valid is False
|
||||
|
||||
|
||||
def test_call_context_handle_jsonrpc_error(fake_handler,
|
||||
jsonrpc_error,
|
||||
execute_context,
|
||||
single_call):
|
||||
def test_call_context_handle_jsonrpc_error(fake_handler: mock.Mock,
|
||||
jsonrpc_error: exceptions.BaseJSONRPCError,
|
||||
execute_context: executor.Executor.ExecuteContext,
|
||||
single_call: dict):
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
@@ -562,9 +590,9 @@ def test_call_context_handle_jsonrpc_error(fake_handler,
|
||||
assert call_result[1] == jsonrpc_error
|
||||
|
||||
|
||||
def test_call_context_handle_exception(fake_handler,
|
||||
execute_context,
|
||||
single_call):
|
||||
def test_call_context_handle_exception(fake_handler: mock.Mock,
|
||||
execute_context: executor.Executor.ExecuteContext,
|
||||
single_call: dict):
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
@@ -585,7 +613,9 @@ def test_call_context_handle_exception(fake_handler,
|
||||
assert call_result[1].data == 'I HAZ FIAL'
|
||||
|
||||
|
||||
def test_call_context(fake_handler, execute_context, single_call):
|
||||
def test_call_context(fake_handler: mock.Mock,
|
||||
execute_context: executor.Executor.ExecuteContext,
|
||||
single_call: dict):
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
@@ -610,7 +640,7 @@ def test_call_context(fake_handler, execute_context, single_call):
|
||||
assert execute_context.results[0] == expected_call_result
|
||||
|
||||
|
||||
def test_execute_context_handle_jsonrpc_error(jsonrpc_error):
|
||||
def test_execute_context_handle_jsonrpc_error(jsonrpc_error: exceptions.BaseJSONRPCError):
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
@@ -638,7 +668,7 @@ def test_execute_context_handle_exception():
|
||||
assert result.serializer is None
|
||||
|
||||
|
||||
def test_execute_context_handle_empty_results(single_call):
|
||||
def test_execute_context_handle_empty_results(single_call: dict):
|
||||
# Given
|
||||
the_executor = executor.Executor()
|
||||
|
||||
@@ -656,7 +686,7 @@ def test_execute_context_handle_empty_results(single_call):
|
||||
assert mock_serializer.called is False
|
||||
|
||||
|
||||
def test_execute_context(fake_rpc_serializer, single_call):
|
||||
def test_execute_context(fake_rpc_serializer: mock.Mock, single_call: dict):
|
||||
# Given
|
||||
fake_responses = {
|
||||
'jsonrpc': '2.0',
|
||||
@@ -712,7 +742,7 @@ def test_before_call():
|
||||
|
||||
|
||||
@mock.patch.object(executor.MethodRegistry, 'shared_registry')
|
||||
def test_execute(mock_shared_registry, fake_method_registry):
|
||||
def test_execute(mock_shared_registry: mock.Mock, fake_method_registry: mock.Mock):
|
||||
# Given
|
||||
fake_method_registry.get_handler.return_value = None
|
||||
fake_method_registry.get_methods.return_value = []
|
||||
|
||||
0
packages/bthlabs-jsonrpc-core/tests/ext/__init__.py
Normal file
0
packages/bthlabs-jsonrpc-core/tests/ext/__init__.py
Normal file
22
packages/bthlabs-jsonrpc-core/tests/ext/jwt/conftest.py
Normal file
22
packages/bthlabs-jsonrpc-core/tests/ext/jwt/conftest.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# type: ignore
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def iat() -> datetime.datetime:
|
||||
return datetime.datetime.now()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def nbf() -> datetime.datetime:
|
||||
return datetime.datetime.now()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def exp() -> datetime.datetime:
|
||||
return datetime.datetime.now()
|
||||
255
packages/bthlabs-jsonrpc-core/tests/ext/jwt/test_JWTCodec.py
Normal file
255
packages/bthlabs-jsonrpc-core/tests/ext/jwt/test_JWTCodec.py
Normal file
@@ -0,0 +1,255 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# type: ignore
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import typing
|
||||
from unittest import mock
|
||||
|
||||
import freezegun
|
||||
import jose
|
||||
import pytest
|
||||
|
||||
from bthlabs_jsonrpc_core.ext import jwt
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def key_pair() -> jwt.KeyPair:
|
||||
return jwt.KeyPair(
|
||||
encode_key=jose.jwk.HMACKey(
|
||||
'thisisntsecure', algorithm=jose.constants.ALGORITHMS.HS256,
|
||||
),
|
||||
decode_key=jose.jwk.HMACKey(
|
||||
'thisisntsecure', algorithm=jose.constants.ALGORITHMS.HS256,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def response() -> dict:
|
||||
return {
|
||||
'jsonrpc': '2.0',
|
||||
'id': 'test',
|
||||
'result': ['system.list_methods'],
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def encoded_payload(single_call: dict,
|
||||
iat: datetime.datetime,
|
||||
key_pair: jwt.KeyPair) -> str:
|
||||
claims = {
|
||||
'iss': 'bthlabs_jsonrpc_core_tests',
|
||||
'jsonrpc': single_call,
|
||||
'iat': iat,
|
||||
}
|
||||
|
||||
return jose.jwt.encode(claims, key_pair.encode_key)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def time_claims(iat: datetime.datetime) -> jwt.TimeClaims:
|
||||
return jwt.TimeClaims(iat=iat, nbf=None, exp=None)
|
||||
|
||||
|
||||
def test_init(key_pair: jwt.KeyPair):
|
||||
# When
|
||||
result = jwt.JWTCodec(key_pair)
|
||||
|
||||
# Then
|
||||
assert result.key_pair == key_pair
|
||||
assert result.algorithm == jose.constants.ALGORITHMS.HS256
|
||||
assert result.issuer is None
|
||||
assert result.ttl is None
|
||||
assert result.include_nbf is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'kwarg,value',
|
||||
[
|
||||
('algorithm', jose.constants.ALGORITHMS.RS256),
|
||||
('issuer', 'bthlabs_jsonrpc_core_tests'),
|
||||
('ttl', datetime.timedelta(seconds=10)),
|
||||
('include_nbf', False),
|
||||
],
|
||||
)
|
||||
def test_init_with_kwargs(kwarg: str,
|
||||
value: typing.Any,
|
||||
key_pair: jwt.KeyPair):
|
||||
# Given
|
||||
init_kwargs = {kwarg: value}
|
||||
|
||||
# When
|
||||
result = jwt.JWTCodec(key_pair, **init_kwargs)
|
||||
|
||||
# Then
|
||||
assert getattr(result, kwarg) == value
|
||||
|
||||
|
||||
def test_decode(key_pair: jwt.KeyPair,
|
||||
encoded_payload: str,
|
||||
single_call: dict):
|
||||
# Given
|
||||
codec = jwt.JWTCodec(key_pair)
|
||||
|
||||
# When
|
||||
result = codec.decode(encoded_payload)
|
||||
|
||||
# Then
|
||||
assert result == single_call
|
||||
|
||||
|
||||
def test_decode_jwt_decode_single_call(key_pair: jwt.KeyPair,
|
||||
encoded_payload: str):
|
||||
# Given
|
||||
codec = jwt.JWTCodec(key_pair)
|
||||
|
||||
with mock.patch.object(jwt.jwt, 'decode') as mock_jwt_decode:
|
||||
# When
|
||||
_ = codec.decode(encoded_payload)
|
||||
|
||||
# Then
|
||||
mock_jwt_decode.assert_called_once_with(
|
||||
encoded_payload,
|
||||
key_pair.decode_key,
|
||||
algorithms=[codec.algorithm],
|
||||
)
|
||||
|
||||
|
||||
def test_decode_with_decoder_kwargs(key_pair: jwt.KeyPair,
|
||||
encoded_payload: str):
|
||||
# Given
|
||||
codec = jwt.JWTCodec(key_pair)
|
||||
|
||||
with mock.patch.object(jwt.jwt, 'decode') as mock_jwt_decode:
|
||||
# When
|
||||
_ = codec.decode(encoded_payload, issuer='bthlabs_jsonrpc_core_tests')
|
||||
|
||||
# Then
|
||||
mock_jwt_decode.assert_called_once_with(
|
||||
encoded_payload,
|
||||
key_pair.decode_key,
|
||||
algorithms=[codec.algorithm],
|
||||
issuer='bthlabs_jsonrpc_core_tests',
|
||||
)
|
||||
|
||||
|
||||
@freezegun.freeze_time('2024-01-11 07:09:43')
|
||||
def test_encode(key_pair: jwt.KeyPair, response: dict):
|
||||
# Given
|
||||
codec = jwt.JWTCodec(key_pair)
|
||||
|
||||
# When
|
||||
result = codec.encode(response)
|
||||
|
||||
# Then
|
||||
assert isinstance(result, str) is True
|
||||
|
||||
decoded_result = jose.jwt.decode(result, key_pair.decode_key)
|
||||
expected_decoded_result = {
|
||||
'iat': 1704953383,
|
||||
'nbf': 1704953383,
|
||||
'jsonrpc': response,
|
||||
}
|
||||
assert decoded_result == expected_decoded_result
|
||||
|
||||
|
||||
def test_encode_with_issuer(key_pair: jwt.KeyPair, response: dict):
|
||||
# Given
|
||||
codec = jwt.JWTCodec(key_pair, issuer='bthlabs_jsonrpc_core_tests')
|
||||
|
||||
# When
|
||||
result = codec.encode(response)
|
||||
|
||||
# Then
|
||||
assert isinstance(result, str) is True
|
||||
|
||||
decoded_result = jose.jwt.decode(result, key_pair.decode_key)
|
||||
assert decoded_result['iss'] == 'bthlabs_jsonrpc_core_tests'
|
||||
|
||||
|
||||
def test_encode_with_ttl(key_pair: jwt.KeyPair, response: dict):
|
||||
# Given
|
||||
codec = jwt.JWTCodec(key_pair, ttl=datetime.timedelta(seconds=60))
|
||||
|
||||
# When
|
||||
result = codec.encode(response)
|
||||
|
||||
# Then
|
||||
assert isinstance(result, str) is True
|
||||
|
||||
decoded_result = jose.jwt.decode(result, key_pair.decode_key)
|
||||
assert 'exp' in decoded_result
|
||||
assert decoded_result['exp'] - decoded_result['iat'] == 60
|
||||
|
||||
|
||||
def test_encode_without_nbf(key_pair: jwt.KeyPair, response: dict):
|
||||
# Given
|
||||
codec = jwt.JWTCodec(key_pair, include_nbf=False)
|
||||
|
||||
# When
|
||||
result = codec.encode(response)
|
||||
|
||||
# Then
|
||||
assert isinstance(result, str) is True
|
||||
|
||||
decoded_result = jose.jwt.decode(result, key_pair.decode_key)
|
||||
assert 'nbf' not in decoded_result
|
||||
|
||||
|
||||
def test_encode_jwt_encode_single_call(key_pair: jwt.KeyPair,
|
||||
time_claims: jwt.TimeClaims,
|
||||
response: dict):
|
||||
# Given
|
||||
codec = jwt.JWTCodec(key_pair)
|
||||
|
||||
with mock.patch.object(codec, 'get_time_claims') as mock_get_time_claims:
|
||||
with mock.patch.object(jwt.jwt, 'encode') as mock_jwt_encode:
|
||||
mock_get_time_claims.return_value = time_claims
|
||||
|
||||
# When
|
||||
_ = codec.encode(response)
|
||||
|
||||
mock_jwt_encode.assert_called_once_with(
|
||||
{
|
||||
'iat': time_claims.iat,
|
||||
'jsonrpc': response,
|
||||
},
|
||||
key_pair.encode_key,
|
||||
algorithm=codec.algorithm,
|
||||
)
|
||||
|
||||
|
||||
def test_encode_with_encoder_kwargs(key_pair: jwt.KeyPair,
|
||||
time_claims: jwt.TimeClaims,
|
||||
response: dict):
|
||||
# Given
|
||||
codec = jwt.JWTCodec(key_pair)
|
||||
|
||||
with mock.patch.object(codec, 'get_time_claims') as mock_get_time_claims:
|
||||
with mock.patch.object(jwt.jwt, 'encode') as mock_jwt_encode:
|
||||
mock_get_time_claims.return_value = time_claims
|
||||
|
||||
# When
|
||||
_ = codec.encode(response, headers={'cty': 'JWT'})
|
||||
|
||||
mock_jwt_encode.assert_called_once_with(
|
||||
{
|
||||
'iat': time_claims.iat,
|
||||
'jsonrpc': response,
|
||||
},
|
||||
key_pair.encode_key,
|
||||
algorithm=codec.algorithm,
|
||||
headers={'cty': 'JWT'},
|
||||
)
|
||||
|
||||
|
||||
def test_get_content_type():
|
||||
# Given
|
||||
codec = jwt.JWTCodec(key_pair)
|
||||
|
||||
# When
|
||||
result = codec.get_content_type()
|
||||
|
||||
# Then
|
||||
assert result == 'application/jwt'
|
||||
@@ -0,0 +1,50 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
|
||||
from bthlabs_jsonrpc_core.ext import jwt
|
||||
|
||||
|
||||
def test_as_claims(iat: datetime.datetime):
|
||||
# Given
|
||||
time_claims = jwt.TimeClaims(iat=iat, nbf=None, exp=None)
|
||||
|
||||
# When
|
||||
result = time_claims.as_claims()
|
||||
|
||||
# Then
|
||||
expected_claims = {
|
||||
'iat': iat,
|
||||
}
|
||||
assert result == expected_claims
|
||||
|
||||
|
||||
def test_as_claims_with_nbf(iat: datetime.datetime, nbf: datetime.datetime):
|
||||
# Given
|
||||
time_claims = jwt.TimeClaims(iat=iat, nbf=nbf, exp=None)
|
||||
|
||||
# When
|
||||
result = time_claims.as_claims()
|
||||
|
||||
# Then
|
||||
expected_claims = {
|
||||
'iat': iat,
|
||||
'nbf': nbf,
|
||||
}
|
||||
assert result == expected_claims
|
||||
|
||||
|
||||
def test_as_claims_with_exp(iat: datetime.datetime, exp: datetime.datetime):
|
||||
# Given
|
||||
time_claims = jwt.TimeClaims(iat=iat, nbf=None, exp=exp)
|
||||
|
||||
# When
|
||||
result = time_claims.as_claims()
|
||||
|
||||
# Then
|
||||
expected_claims = {
|
||||
'iat': iat,
|
||||
'exp': exp,
|
||||
}
|
||||
assert result == expected_claims
|
||||
@@ -13,7 +13,7 @@ def test_init():
|
||||
|
||||
|
||||
@mock.patch.object(registry.MethodRegistry, '__init__')
|
||||
def test_shared_registry(mock_init):
|
||||
def test_shared_registry(mock_init: mock.Mock):
|
||||
# Given
|
||||
mock_init.return_value = None
|
||||
|
||||
@@ -32,7 +32,8 @@ def test_shared_registry(mock_init):
|
||||
|
||||
|
||||
@mock.patch.object(registry.MethodRegistry, '__init__')
|
||||
def test_shared_registry_with_instance(mock_init, fake_method_registry):
|
||||
def test_shared_registry_with_instance(mock_init: mock.Mock,
|
||||
fake_method_registry: mock.Mock):
|
||||
# Given
|
||||
mock_init.return_value = None
|
||||
|
||||
@@ -52,7 +53,7 @@ def test_shared_registry_with_instance(mock_init, fake_method_registry):
|
||||
registry.MethodRegistry.INSTANCE = None
|
||||
|
||||
|
||||
def test_register_method(fake_handler):
|
||||
def test_register_method(fake_handler: mock.Mock):
|
||||
# Given
|
||||
the_registry = registry.MethodRegistry()
|
||||
|
||||
@@ -64,7 +65,7 @@ def test_register_method(fake_handler):
|
||||
assert the_registry.registry['testing'] == expected_namespace
|
||||
|
||||
|
||||
def test_register_method_existing_namespace(fake_handler):
|
||||
def test_register_method_existing_namespace(fake_handler: mock.Mock):
|
||||
# Given
|
||||
spam_handler = mock.Mock()
|
||||
|
||||
@@ -93,7 +94,7 @@ def test_get_methods():
|
||||
assert set(result) == expected_methods
|
||||
|
||||
|
||||
def test_get_handler(fake_handler):
|
||||
def test_get_handler(fake_handler: mock.Mock):
|
||||
# Given
|
||||
spam_handler = mock.Mock()
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
import decimal
|
||||
import typing
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
@@ -20,7 +21,7 @@ def test_init():
|
||||
'value,expected',
|
||||
[(None, True), (False, True), (0, True), ('spam', True), ([], False)],
|
||||
)
|
||||
def test_is_simple_value(value, expected):
|
||||
def test_is_simple_value(value: typing.Any, expected: bool):
|
||||
# Given
|
||||
the_serializer = serializer.JSONRPCSerializer('spam')
|
||||
|
||||
@@ -38,7 +39,7 @@ def test_is_simple_value(value, expected):
|
||||
(tuple(), True), ({}, False),
|
||||
],
|
||||
)
|
||||
def test_is_sequence_value(value, expected):
|
||||
def test_is_sequence_value(value: typing.Any, expected: bool):
|
||||
# Given
|
||||
the_serializer = serializer.JSONRPCSerializer('spam')
|
||||
|
||||
@@ -53,7 +54,7 @@ def test_is_sequence_value(value, expected):
|
||||
'value,expected',
|
||||
[({}, True), ([], False)],
|
||||
)
|
||||
def test_is_dict_value(value, expected):
|
||||
def test_is_dict_value(value: typing.Any, expected: bool):
|
||||
# Given
|
||||
the_serializer = serializer.JSONRPCSerializer('spam')
|
||||
|
||||
@@ -68,7 +69,7 @@ def test_is_dict_value(value, expected):
|
||||
'value,expected',
|
||||
[(uuid.uuid4(), True), (decimal.Decimal('42'), True), ([], False)],
|
||||
)
|
||||
def test_is_string_coercible_value(value, expected):
|
||||
def test_is_string_coercible_value(value: typing.Any, expected: bool):
|
||||
# Given
|
||||
the_serializer = serializer.JSONRPCSerializer('spam')
|
||||
|
||||
@@ -92,7 +93,7 @@ def test_is_string_coercible_value(value, expected):
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_serialize_datetime(value, expected):
|
||||
def test_serialize_datetime(value: typing.Any, expected: bool):
|
||||
# Given
|
||||
the_serializer = serializer.JSONRPCSerializer('spam')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user