Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions test/functional/interface_rpc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/env python3
# Copyright (c) 2018-2019 The Bitcoin Core developers
# Copyright (c) 2017-2018 The Raven Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.

"""Tests some generic aspects of the RPC interface."""

import os
from test_framework.authproxy import JSONRPCException
from test_framework.test_framework import RavenTestFramework
from test_framework.util import assert_equal, assert_greater_than_or_equal


def expect_http_status(expected_http_status, expected_rpc_code, fcn, *args):
try:
fcn(*args)
raise AssertionError("Expected RPC error %d, got none" % expected_rpc_code)
except JSONRPCException as exc:
assert_equal(exc.error["code"], expected_rpc_code)
assert_equal(exc.http_status, expected_http_status)


class RPCInterfaceTest(RavenTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True


def test_getrpcinfo(self):
self.log.info("Testing getrpcinfo...")

info = self.nodes[0].getrpcinfo()
assert_equal(len(info['active_commands']), 1)

command = info['active_commands'][0]
assert_equal(command['method'], 'getrpcinfo')
assert_greater_than_or_equal(command['duration'], 0)


def test_batch_request(self):
self.log.info("Testing basic JSON-RPC batch request...")

results = self.nodes[0].batch([
# A basic request that will work fine.
{"method": "getblockcount", "id": 1},
# Request that will fail. The whole batch request should still
# work fine.
{"method": "invalidmethod", "id": 2},
# Another call that should succeed.
{"method": "getbestblockhash", "id": 3},
])

result_by_id = {}
for res in results:
result_by_id[res["id"]] = res

assert_equal(result_by_id[1]['error'], None)
assert_equal(result_by_id[1]['result'], 0)

assert_equal(result_by_id[2]['error']['code'], -32601)
assert_equal(result_by_id[2]['result'], None)

assert_equal(result_by_id[3]['error'], None)
assert result_by_id[3]['result'] is not None

def test_http_status_codes(self):
self.log.info("Testing HTTP status codes for JSON-RPC requests...")

expect_http_status(404, -32601, self.nodes[0].invalidmethod)
expect_http_status(500, -8, self.nodes[0].getblockhash, 42)

def run_test(self):
self.test_getrpcinfo()
self.test_batch_request()
self.test_http_status_codes()


if __name__ == '__main__':
RPCInterfaceTest().main()
31 changes: 20 additions & 11 deletions test/functional/test_framework/authproxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

import base64
import decimal
from http import HTTPStatus
import http.client
import json
import logging
Expand All @@ -51,13 +52,14 @@


class JSONRPCException(Exception):
def __init__(self, rpc_error):
def __init__(self, rpc_error, http_status=None):
try:
errmsg = '%(message)s (%(code)i)' % rpc_error
except (KeyError, TypeError):
errmsg = ''
super().__init__(errmsg)
self.error = rpc_error
self.http_status = http_status


def encode_decimal(o):
Expand Down Expand Up @@ -147,23 +149,30 @@ def get_request(self, *args, **argsn):

def __call__(self, *args, **argsn):
post_data = json.dumps(self.get_request(*args, **argsn), default=encode_decimal, ensure_ascii=self.ensure_ascii)
response = self._request('POST', self.__url.path, post_data.encode('utf-8'))
response, status = self._request('POST', self.__url.path, post_data.encode('utf-8'))
if response['error'] is not None:
log.debug("----<authproxy>----")
log.debug("Call failed. postdata:")
log.debug("---------------------------<authproxy>---------------------------")
log.debug("Call failed! postdata:")
log.debug(post_data)
log.debug("----</authproxy>---")
raise JSONRPCException(response['error'])
log.debug("---------------------------</authproxy>--------------------------")
raise JSONRPCException(response['error'], status)
elif 'result' not in response:
raise JSONRPCException({
'code': -343, 'message': 'missing JSON-RPC result'})
'code': -343, 'message': 'missing JSON-RPC result'}, status)
elif status != HTTPStatus.OK:
raise JSONRPCException({
'code': -342, 'message': 'non-200 HTTP status code but no JSON-RPC error'}, status)
else:
return response['result']

def batch(self, rpc_call_list):
postdata = json.dumps(list(rpc_call_list), default=encode_decimal, ensure_ascii=self.ensure_ascii)
log.debug("--> " + postdata)
return self._request('POST', self.__url.path, postdata.encode('utf-8'))
response, status = self._request('POST', self.__url.path, postdata.encode('utf-8'))
if status != HTTPStatus.OK:
raise JSONRPCException({
'code': -342, 'message': 'non-200 HTTP status code but no JSON-RPC error'}, status)
return response

def _get_response(self):
req_start_time = time.time()
Expand All @@ -188,8 +197,8 @@ def _get_response(self):

content_type = http_response.getheader('Content-Type')
if content_type != 'application/json':
raise JSONRPCException({
'code': -342, 'message': 'non-JSON HTTP response with \'%i %s\' from server' % (http_response.status, http_response.reason)})
raise JSONRPCException({'code': -342, 'message': 'non-JSON HTTP response with \'%i %s\' from server' % (http_response.status, http_response.reason)},
http_response.status)

response_data = http_response.read().decode('utf8')
response = json.loads(response_data, parse_float=decimal.Decimal)
Expand All @@ -198,7 +207,7 @@ def _get_response(self):
log.debug("<-%s- [%.6f] %s" % (response["id"], elapsed, json.dumps(response["result"], default=encode_decimal, ensure_ascii=self.ensure_ascii)))
else:
log.debug("<-- [%.6f] %s" % (elapsed, response_data))
return response
return response, http_response.status

def __truediv__(self, relative_uri):
return AuthServiceProxy("{}/{}".format(self.__service_url, relative_uri), self._service_name, connection=self.__conn)
1 change: 1 addition & 0 deletions test/functional/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@
'rpc_signrawtransaction.py',
'wallet_resendtransactions.py',
'wallet_txn_clone.py --mineblock',
'interface_rpc.py',
'rpc_signmessage.py',
'rpc_deprecated.py',
'wallet_coinbase_category.py',
Expand Down