예제 #1
0
def test_buggy_request_handler_is_logged():
    with MyRPCProcessor() as rpc:
        rpc.add_requests([
            RPCRequest('raise', [], 0),
            RPCRequest('raise', [], None),
        ])

        rpc.process_all()
        rpc.expect_internal_error("something", 0)
        assert rpc.error_message_count == 2
        assert rpc.all_done()

        rpc.add_request(RPCRequest('raise_async', [], 0))
        rpc.error_messages.clear()
        rpc.error_message_count = 0
        rpc.process_all()
        rpc.expect_internal_error("anything", 0)
        assert rpc.error_message_count == 1
        assert rpc.all_done()

        rpc.add_request(RPCRequest('raise_async', [], None))
        rpc.error_messages.clear()
        rpc.error_message_count = 0
        rpc.process_all()
        assert not rpc.responses
        assert rpc.error_message_count == 1
        rpc.expect_error_message("anything")
        assert rpc.all_done()
예제 #2
0
def test_unknown_method():
    with MyRPCProcessor() as rpc:
        # Test unknown method, for both notification and request
        rpc.add_request(RPCRequest('unk1', ["a"], 5))
        rpc.expect_error(rpc.protocol.METHOD_NOT_FOUND, "unk1", 5)
        assert rpc.errors == 1
        rpc.add_request(RPCRequest('unk2', ["a"], None))
        rpc.expect_nothing()
        assert rpc.errors == 2
        assert rpc.recv_count == 2
        assert rpc.all_done()
예제 #3
0
def test_bad_handler_lookup():
    with MyRPCProcessor() as rpc:
        rpc.add_requests([
            RPCRequest('bad_request', [], 0),
            RPCRequest('bad_notification', [], None),
        ])

        rpc.expect_internal_error("made_bad_request", 0)
        rpc.expect_error_message("made_bad_notification")
        assert rpc.internal_errors == 2
        assert rpc.errors == 0
        assert rpc.all_done()
예제 #4
0
def test_max_response_size_for_batch():
    with MyRPCProcessor() as rpc:
        rpc.add_batch(
            RPCBatch([
                RPCRequest('echo', ["foo"], 0),
                RPCRequest('echo', ['a' * rpc.max_response_size], 1),
                RPCRequest('sleep', None, 2),
            ]))
        tasks = list(rpc.task_set.tasks)
        rpc.expect_error(rpc.protocol.INVALID_REQUEST, "response too large",
                         None)
        rpc.yield_to_loop()  # Allow the sleep to cancel
        assert sum(task.cancelled() for task in tasks) == 1
예제 #5
0
def test_max_response_size():
    # Test max_response_size is observed
    with MyRPCProcessor() as rpc:
        rpc.add_request(RPCRequest('echo', ['a' * rpc.max_response_size], 0))
        rpc.expect_error(rpc.protocol.INVALID_REQUEST, "response too large", 0)
        assert rpc.errors == 1

    # Test 0 means no limit
    with MyRPCProcessor() as rpc:
        rpc.max_response_size = 0
        rpc.add_request(RPCRequest('echo', ['a' * 5000000], 0))
        rpc.consume_one_response()
        assert rpc.errors == 0
예제 #6
0
def test_named_args_good():
    with MyRPCProcessor() as rpc:
        # Test 2, 1 and no default args
        rpc.add_requests([
            RPCRequest('add', {"x": 1}, 0),
            RPCRequest('add', {
                "x": 1,
                "y": 2
            }, 0),
            RPCRequest('add', {
                "x": 1,
                "y": 2,
                "z": 3
            }, 0),
            RPCRequest('add', {
                "x": 1,
                "z": 8
            }, 0),
            RPCRequest('add', {"x": 1}, None),
            RPCRequest('add_async', {"x": 1}, 0),
            RPCRequest('add_async', {
                "x": 1,
                "y": 2
            }, 0),
            RPCRequest('add_async', {
                "x": 1,
                "y": 2,
                "z": 3
            }, "a"),
            RPCRequest('add_async', {
                "x": 1,
                "z": 8
            }, 0),
            RPCRequest('add_async', {"x": 1}, None),
        ])
        rpc.yield_to_loop()
        assert rpc.recv_count == 10

        rpc.expect_response(7, 0)
        rpc.expect_response(5, 0)
        rpc.expect_response(6, 0)
        rpc.expect_response(13, 0)

        # Order may not be reliable here...
        rpc.expect_response(7, 0)
        rpc.expect_response(5, 0)
        rpc.expect_response(6, "a")
        rpc.expect_response(13, 0)
        assert rpc.all_done()
예제 #7
0
def test_incoming_batch_request_cancellation():
    '''Test cancelling incoming batch cancellation, including when
    partially complete.'''
    with MyRPCProcessor() as rpc:
        rpc.add_batch(
            RPCBatch([
                RPCRequest('add_async', [1], 0),
                RPCRequest('sleep', None, 1),
            ]))

        rpc.yield_to_loop()
        assert len(rpc.task_set.tasks) == 1
        rpc.task_set.cancel_all()
        rpc.yield_to_loop()
        assert not rpc.responses
        assert rpc.all_done()
예제 #8
0
def test_partial_async():
    with MyRPCProcessor() as rpc:
        rpc.add_requests([
            RPCRequest('partial_add_async', [10, 15], 3),
        ])

        rpc.expect_response(125, 3)
        assert rpc.all_done()
예제 #9
0
def test_JSONRPCv2_and_JSONRPCLoosemessages():
    tests = [
        (RPCRequest('foo', [], 2),
         {"jsonrpc": "2.0", "method": "foo", "id": 2}),
        (RPCRequest('foo', (), 2),
         {"jsonrpc": "2.0", "method": "foo", "id": 2}),
        (RPCRequest('foo', [], None),
         {"jsonrpc": "2.0", "method": "foo"}),
        (RPCRequest('foo', {}, 2),
         {"jsonrpc": "2.0", "params": {}, "method": "foo", "id": 2}),
        (RPCRequest('foo', (1, 2), 2),
         {"jsonrpc": "2.0", "method": "foo", "params": [1, 2], "id": 2}),
        (RPCRequest('foo', [1, 2], 2),
         {"jsonrpc": "2.0", "method": "foo", "params": [1, 2], "id": 2}),
        (RPCRequest('foo', {"bar": 3, "baz": "bat"}, "it"),
         {"jsonrpc": "2.0", "method": "foo",
          "params": {"bar": 3, "baz": "bat"}, "id": "it"}),
        (RPCResponse('foo', "it"),
         {"jsonrpc": "2.0", "result": "foo", "id": "it"}),
        (RPCResponse(2, "it"),
         {"jsonrpc": "2.0", "result": 2, "id": "it"}),
        (RPCResponse(None, -2),
         {"jsonrpc": "2.0", "result": None, "id": -2}),
        (RPCResponse([1, 2], -1),
         {"jsonrpc": "2.0", "result": [1, 2], "id": -1}),
        (RPCResponse({"kind": 1}, 0),
         {"jsonrpc": "2.0", "result": {"kind": 1}, "id": 0}),
        (RPCResponse(RPCError(3, "j"), 1),
         {"jsonrpc": "2.0", "error": {"code": 3, "message": "j"}, "id": 1}),
        (RPCBatch([
            RPCRequest('foo', [], 2),
            RPCRequest('bar', [2], None)
        ]), [
            {"jsonrpc": "2.0", "method": "foo", "id": 2},
            {"jsonrpc": "2.0", "method": "bar", "params": [2]}
        ]),
    ]

    for rpc in [JSONRPCv2, JSONRPCLoose]:
        for item, payload in tests:
            if isinstance(item, RPCRequest):
                binary = rpc.request_message(item)
            elif isinstance(item, RPCResponse):
                binary = rpc.response_message(item)
            else:
                binary = rpc.batch_message(item)
            test_payload = json.loads(binary.decode())
            assert test_payload == payload
예제 #10
0
def test_incoming_request_cancellation():
    '''Tests cancelling async requests.'''
    with MyRPCProcessor() as rpc:
        # Simple request.
        rpc.add_request(RPCRequest('sleep', None, 0))
        rpc.yield_to_loop()
        rpc.task_set.cancel_all()
        rpc.yield_to_loop()
        assert not rpc.responses
        assert rpc.all_done()
예제 #11
0
def test_RPCBatch():
    with pytest.raises(AssertionError):
        RPCBatch([])
    with pytest.raises(AssertionError):
        RPCBatch(x for x in (1, 2))
    with pytest.raises(AssertionError):
        RPCBatch([RPCRequest('method', [], 0), RPCResponse(6, 0)])
    with pytest.raises(AssertionError):
        RPCBatch([RPCError(0, 'message', 0), RPCResponse(6, 0)])

    requests = [
        RPCRequest('method', [], 0),
        RPCRequest('foo', [], None),
        RPCRequest('t', [1], 1)
    ]
    batch = RPCBatch(requests)
    assert batch.is_request_batch()
    assert len(batch) == len(requests)
    assert list(batch.requests()) == [requests[0], requests[2]]
    # test iter()
    assert requests == list(batch)
    assert batch.request_ids() == {1, 0}
    assert isinstance(batch.request_ids(), frozenset)
    assert repr(batch) == ("RPCBatch([RPCRequest('method', [], 0), "
                           "RPCRequest('foo', [], None), "
                           "RPCRequest('t', [1], 1)])")

    responses = [RPCResponse(6, 2)]
    batch = RPCBatch(responses)
    assert not batch.is_request_batch()
    assert len(batch) == len(responses)
    assert list(batch.requests()) == responses
    # test iter()
    assert responses == list(batch)
    assert batch.request_ids() == {2}
    assert repr(batch) == "RPCBatch([RPCResponse(6, 2)])"
예제 #12
0
def test_too_many_or_few_array_args():
    with MyRPCProcessor() as rpc:
        # Test too many args, both notification and request
        rpc.add_requests([
            RPCRequest('add', [], 0),
            RPCRequest('add', [], None),
            RPCRequest('add', [1, 2, 3, 4], 0),
            RPCRequest('add', [1, 2, 3, 4], None),
            RPCRequest('add_async', [], 0),
            RPCRequest('add_async', [], None),
            RPCRequest('add_async', [1, 2, 3, 4], 0),
            RPCRequest('add_async', [1, 2, 3, 4], None),
        ])
        rpc.expect_error(rpc.protocol.INVALID_ARGS, "0 arguments", 0)
        rpc.expect_error(rpc.protocol.INVALID_ARGS, "4 arguments", 0)
        rpc.expect_error(rpc.protocol.INVALID_ARGS, "0 arguments", 0)
        rpc.expect_error(rpc.protocol.INVALID_ARGS, "4 arguments", 0)
        assert rpc.all_done()
예제 #13
0
def test_named_args_bad():
    with MyRPCProcessor() as rpc:
        # Test 2, 1 and no default args
        for method in ('add', 'add_async'):
            rpc.add_requests([
                # Bad names
                RPCRequest(method, {
                    "x": 1,
                    "t": 1,
                    "u": 3
                }, 0),
                RPCRequest(method, {
                    "x": 1,
                    "t": 2
                }, 0),
                RPCRequest(method, {
                    "x": 1,
                    "t": 2
                }, None),
                # x is required
                RPCRequest(method, {}, 0),
                RPCRequest(method, {"y": 3}, 0),
                RPCRequest(method, {
                    "y": 3,
                    "z": 4
                }, 0),
                RPCRequest(method, {"y": 3}, None),
            ])

        for method in ('add', 'add_async'):
            rpc.expect_error(rpc.protocol.INVALID_ARGS, 'parameters "t", "u"',
                             0)
            rpc.expect_error(rpc.protocol.INVALID_ARGS, 'parameter "t"', 0)
            rpc.expect_error(rpc.protocol.INVALID_ARGS, 'parameter "x"', 0)
            rpc.expect_error(rpc.protocol.INVALID_ARGS, 'parameter "x"', 0)
            rpc.expect_error(rpc.protocol.INVALID_ARGS, 'parameter "x"', 0)
        assert rpc.all_done()

        # Test plural
        rpc.add_request(RPCRequest('notify', {}, 0))
        rpc.expect_error(rpc.protocol.INVALID_ARGS, 'parameters "x", "y"', 0)
        assert rpc.all_done()
예제 #14
0
def test_good_args():
    with MyRPCProcessor() as rpc:
        # Test 2, 1 and no default args
        rpc.add_requests([
            RPCRequest('add', [1], 0),
            RPCRequest('add', [1, 2], 0),
            RPCRequest('add', [1, 2, 3], 0),
            RPCRequest('add', [1], None),
            RPCRequest('add_async', [1], 0),
            RPCRequest('add_async', [1, 2], 0),
            RPCRequest('add_async', [1, 2, 3], 0),
            RPCRequest('add_async', [1], None),
        ])
        rpc.expect_response(7, 0)
        rpc.expect_response(5, 0)
        rpc.expect_response(6, 0)

        # Order may not be reliable here...
        rpc.expect_response(7, 0)
        rpc.expect_response(5, 0)
        rpc.expect_response(6, 0)
        assert rpc.all_done()
예제 #15
0
def test_JSONRPCv1_messages():
    tests = [
        (RPCRequest('foo', [], 2),
         {"method": "foo", "params": [], "id": 2}),
        (RPCRequest('foo', [], None),
         {"method": "foo", "params": [], "id": None}),
        (RPCRequest('foo', [1, 2], "s"),
         {"method": "foo", "params": [1, 2], "id": "s"}),
        (RPCRequest('foo', [1, 2], ["x"]),
         {"method": "foo", "params": [1, 2], "id": ["x"]}),
        (RPCResponse('foo', "it"),
         {"result": "foo", "error": None, "id": "it"}),
        (RPCResponse(2, "it"),
         {"result": 2, "error": None, "id": "it"}),
        (RPCResponse(None, -2),
         {"result": None, "error": None, "id": -2}),
        (RPCResponse([1, 2], -1),
         {"result": [1, 2], "error": None, "id": -1}),
        (RPCResponse({"kind": 1}, [1]),
         {"result": {"kind": 1}, "error": None, "id": [1]}),
        (RPCResponse(RPCError(3, "j"), 1),
         {"result": None, "error": {"code": 3, "message": "j"}, "id": 1}),
    ]
    rpc = JSONRPCv1
    for item, payload in tests:
        if isinstance(item, RPCRequest):
            binary = rpc.request_message(item)
        else:
            binary = rpc.response_message(item)
        test_payload = json.loads(binary.decode())
        assert test_payload == payload

    with pytest.raises(TypeError):
        rpc.request_message(RPCRequest('foo', {}, 2))
    with pytest.raises(TypeError):
        rpc.request_message(RPCRequest('foo', {"x": 1}, 2))
예제 #16
0
def test_RPCRequest():
    request = RPCRequestOut('method', [1], None)
    assert request.method == 'method'
    assert request.args == [1]
    assert request.request_id == 0
    assert not request.is_notification()
    assert repr(request) == "RPCRequest('method', [1], 0)"
    request = RPCRequestOut('method', (1, ), None)
    assert request.method == 'method'
    assert request.args == (1, )
    assert request.request_id == 1
    assert not request.is_notification()
    assert repr(request) == "RPCRequest('method', (1,), 1)"
    request = RPCRequestOut('foo', {"bar": 1}, None)
    assert request.method == 'foo'
    assert request.args == {"bar": 1}
    assert request.request_id == 2
    assert not request.is_notification()
    assert repr(request) == "RPCRequest('foo', {'bar': 1}, 2)"
    request = RPCRequest('foo', [], None)
    assert request.method == 'foo'
    assert request.args == []
    assert request.request_id is None
    assert request.is_notification()
    assert repr(request) == "RPCRequest('foo', [], None)"
    request = RPCRequest('foo', (), None)
    assert request.method == 'foo'
    assert request.args == ()
    assert request.request_id is None
    assert request.is_notification()
    assert repr(request) == "RPCRequest('foo', (), None)"
    # Check {} is preserved (different call semantics)
    for request in [RPCRequest('add', {}, 0), RPCRequestOut('add', {}, None)]:
        assert request.method == 'add'
        assert request.args == {}
        request = RPCRequest('add', {}, 0)
    # Check None gives []
    for request in [
            RPCRequest('add', None, 0),
            RPCRequestOut('add', None, None)
    ]:
        request = RPCRequest('add', None, 0)
        assert request.method == 'add'
        assert request.args == []

    loop = asyncio.get_event_loop()
    # Result setting
    valid = False

    def on_done(req):
        nonlocal valid
        valid = req is request

    # Test args good type
    with pytest.raises(ValueError):
        RPCRequestOut('method', 0, on_done)

    request = RPCRequestOut('method', [0], on_done)
    assert not request.done()
    with pytest.raises(asyncio.InvalidStateError):
        request.result()
    assert not request.done()
    request.set_result(42)

    assert not valid  # Not scheduled yet
    loop.run_until_complete(asyncio.sleep(0))
    assert valid

    assert request.result() == 42
    with pytest.raises(asyncio.InvalidStateError):
        request.set_result(35)
    assert request.result() == 42

    # Result setting
    request = RPCRequestOut('method', None, on_done)
    loop.call_later(0.001, request.set_result, 45)
    valid = False
    assert loop.run_until_complete(request) == 45
    assert valid
    assert request.result() == 45

    request = RPCRequestOut('method', None, on_done)
    loop = asyncio.get_event_loop()
    request.set_result(46)
    assert loop.run_until_complete(request) == 46
예제 #17
0
def test_request_round_trip():
    '''Round trip test - we send binary requests to ourselves, process the
    requests, send binary responses in response also to ourselves, and then
    process the results.

    This tests request and response serialization, and also that request
    handlers are invoked when a response is received.

    The tests cover a range of valid and invalid requests, and problem
    triggers.

    We also insert a fake duplicate response, and a fake response without
    a request ID, to test they are appropriately handled.
    '''
    handled = []

    def handle_add(request):
        assert request.method == 'add'
        if request.args[1] == "a":
            result = request.exception()
            assert isinstance(result, RPCError)
            assert result.code == -1
            assert "numbers" in result.message
            handled.append('add_bad')
        else:
            result = request.result()
            assert request.args == [1, 5, 10]
            assert result == 16
            handled.append(request.method)

    def handle_add_async(request):
        assert request.method == 'add_async'
        if request.args[0] == "b":
            result = request.exception()
            assert isinstance(result, RPCError)
            assert result.code == -1
            assert "numbers" in result.message
            handled.append('add_async_bad')
        else:
            result = request.result()
            assert request.args == [1, 5, 10]
            assert result == 16
            handled.append(request.method)

    def handle_echo(request):
        assert request.method == 'echo'
        assert request.args[0] == request.result()
        handled.append(request.method)

    def handle_bad_echo(request):
        assert request.method == 'echo'
        assert not request.args
        result = request.exception()
        assert isinstance(result, RPCError)
        assert result.code == rpc.protocol.INVALID_ARGS
        handled.append('bad_echo')

    def bad_request_handler(request):
        assert request.method == 'bad_request'
        result = request.exception()
        assert isinstance(result, RPCError)
        assert result.code == rpc.protocol.INTERNAL_ERROR
        handled.append(request.method)

    null_handler_request = RPCRequestOut('echo', [2], None)
    requests = [
        RPCRequestOut('add', [1, 5, 10], handle_add),
        # Bad type
        RPCRequestOut('add', [1, "a", 10], handle_add),
        # Test a notification, and a bad one
        RPCRequestOut('echo', ["ping"], handle_echo),
        RPCRequest('echo', [], None),
        # Test a None response
        RPCRequestOut('echo', [None], handle_echo),
        # Throw in an async request
        RPCRequestOut('add_async', [1, 5, 10], handle_add_async),
        RPCRequestOut('add_async', ["b"], handle_add_async),
        # An invalid request
        RPCRequestOut('echo', [], handle_bad_echo),
        # test a null handler
        null_handler_request,
        # test a remote bad request getter
        RPCRequestOut('bad_request', [], bad_request_handler),
    ]

    with MyRPCProcessor() as rpc:
        # Send each request and feed them back into the RPC object as if
        # it were receiving its own messages.
        for request in requests:
            # Send it and fake receiving it
            rpc.send_request(request)
            rpc.message_received(rpc.responses.pop())
        assert rpc.sent_count == len(requests)

        # Check all_requests
        assert isinstance(rpc.all_requests(), list)
        assert set(rpc.all_requests()) == set(
            req for req in requests if isinstance(req, RPCRequestOut))

        # Now process the queue and the async jobs, generating queued responses
        rpc.process_all()

        # Get the queued responses and send them back to ourselves
        responses = rpc.consume_responses()
        assert rpc.all_done()

        # Did we did get the null handler response - no other way to detect it
        text = f'"id": {null_handler_request.request_id}'.encode()
        assert any(text in response for response in responses)

        for response in responses:
            rpc.message_received(response)
        rpc.yield_to_loop()

        # Nothing left
        assert not rpc.all_requests()

        assert sorted(handled) == [
            'add', 'add_async', 'add_async_bad', 'add_bad', 'bad_echo',
            'bad_request', 'echo', 'echo'
        ]

        # Responses are handled synchronously so no process_all() is needed
        assert rpc.all_done()
예제 #18
0
def test_batch_not_allowed():
    for rpc in no_batch_rpcs:
        item = rpc.message_to_item(b'[]')
        assert_is_error(item, 'dict', rpc.INVALID_REQUEST, None)
        with pytest.raises(RuntimeError):
            rpc.batch_message(RPCBatch([RPCRequest('', [], 0)]))