def test_headers_passthrough(self): """Test that we correctly send RFC-defined headers and merge them with user defined ones""" @asyncio.coroutine def handler(request): return aiohttp.web.Response( text='{"jsonrpc": "2.0", "result": true, "id": 1}', content_type='application/json') self.handler = handler def callback(method, path, *args, **kwargs): expected_headers = { 'Content-Type': 'application/json', 'Accept': 'application/json-rpc', 'X-TestCustomHeader': '1' } self.assertTrue( set(expected_headers.items()).issubset( set(kwargs['headers'].items()))) self.client.request_callback = callback s = Server('/xmlrpc', session=self.client, headers={'X-TestCustomHeader': '1'}) yield from s.foo()
async def test_send_message(test_client): # catch non-json responses async def handler1(request): return aiohttp.web.Response(text='not json', content_type='application/json') def create_app(loop): app = aiohttp.web.Application(loop=loop) app.router.add_route('POST', '/', handler1) return app client = await test_client(create_app) server = Server('/', client) with pytest.raises(TransportError) as transport_error: await server.send_message(Request('my_method', params=None, msg_id=1)) assert transport_error.value.args[0] == ( "Error calling method 'my_method': Cannot deserialize response body") assert isinstance(transport_error.value.args[1], ValueError) # catch non-200 responses async def handler2(request): return aiohttp.web.Response(text='{}', content_type='application/json', status=404) def create_app(loop): app = aiohttp.web.Application(loop=loop) app.router.add_route('POST', '/', handler2) return app client = await test_client(create_app) server = Server('/', client) with pytest.raises(TransportError) as transport_error: await server.send_message(Request('my_method', params=None, msg_id=1)) assert transport_error.value.args[0] == ( "Error calling method 'my_method': HTTP 404 Not Found") # catch aiohttp own exception async def callback(*args, **kwargs): raise aiohttp.ClientOSError('aiohttp exception') def create_app(loop): app = aiohttp.web.Application(loop=loop) return app client = await test_client(create_app) client.post = callback server = Server('/', client) with pytest.raises(TransportError) as transport_error: await server.send_message(Request('my_method', params=None, msg_id=1)) assert transport_error.value.args[0] == ( "Error calling method 'my_method': Transport Error")
async def test_calls(aiohttp_client): """Test RPC call with positional parameters.""" async def handler1(request): request_message = await request.json() assert request_message["params"] == [42, 23] return aiohttp.web.Response( text='{"jsonrpc": "2.0", "result": 19, "id": 1}', content_type='application/json') def create_app(): app = aiohttp.web.Application() app.router.add_route('POST', '/', handler1) return app client = await aiohttp_client(create_app()) server = Server('/', client) assert await server.subtract(42, 23) == 19 async def handler2(request): request_message = await request.json() assert request_message["params"] == {'y': 23, 'x': 42} return aiohttp.web.Response( text='{"jsonrpc": "2.0", "result": 19, "id": 1}', content_type='application/json') def create_app(): app = aiohttp.web.Application() app.router.add_route('POST', '/', handler2) return app client = await aiohttp_client(create_app()) server = Server('/', client) assert await server.subtract(x=42, y=23) == 19 async def handler3(request): request_message = await request.json() assert request_message["params"] == [{'foo': 'bar'}] return aiohttp.web.Response( text='{"jsonrpc": "2.0", "result": null}', content_type='application/json') def create_app(): app = aiohttp.web.Application() app.router.add_route('POST', '/', handler3) return app client = await aiohttp_client(create_app()) server = Server('/', client) await server.foobar({'foo': 'bar'})
def setUp(self): self.loop = setup_test_loop() self.app = self.get_app(self.loop) @asyncio.coroutine def create_client(app, loop): return JsonTestClient(app, loop=loop) self.client = self.loop.run_until_complete( create_client(self.app, self.loop)) self.loop.run_until_complete(self.client.start_server()) random.randint = Mock(return_value=1) self.server = Server('/xmlrpc', session=self.client, timeout=0.2)
async def adjust_imbalanced_hashpower(args): """ does not loop, just try once set to 10% clusters has 90% hash power hash power ratio of individual cluster would be 81:1 (do the math yourself) """ ip_lookup = json.loads(args.ip_lookup) try: clusters = monitoring.find_all_clusters(args.ip, args.p2p_port, args.jrpc_port, ip_lookup) clusters.sort() num_nodes = len(clusters) if num_nodes < 10: raise Exception("no point with < 10 clusters") num_rich = int(num_nodes / 10) print("***********************************************") print("num_nodes = ", num_nodes) print("num_rich = ", num_rich) clusters_rich = clusters[:num_rich] clusters_poor = clusters[num_rich:] servers_rich = [(idx, Server("http://{}".format(cluster))) for idx, cluster in enumerate(clusters_rich)] servers_poor = [(idx, Server("http://{}".format(cluster))) for idx, cluster in enumerate(clusters_poor)] rich_root = int(num_nodes * args.base_root / 9) rich_minor = int(num_nodes * args.base_minor / 9) poor_root = num_nodes * args.base_root * 9 poor_minor = num_nodes * args.base_minor * 9 await asyncio.gather(*[ async_adjust(idx, server, rich_root, rich_minor, not args.do_not_mine) for (idx, server) in servers_rich ]) print("Successfully set {} nodes to root={},minor={} @{}".format( len(servers_rich), rich_root, rich_minor, datetime.now())) print("rich clusters: ", clusters_rich) await asyncio.gather(*[ async_adjust(idx, server, poor_root, poor_minor, not args.do_not_mine) for (idx, server) in servers_poor ]) print("Successfully set {} nodes to root={},minor={} @{}".format( len(servers_poor), poor_root, poor_minor, datetime.now())) print("poor clusters: ", clusters_poor) except Exception as ex: print("Got Exception: {}".format(ex, args.interval)) pass
async def test_batch_requests(test_client): async def batch_handler(request): request_message = await request.json() id1 = request_message[0]['id'] id2 = request_message[1]['id'] return aiohttp.web.Response(text='[{"jsonrpc": "2.0", "result": 11, ' '"id": %d},{"jsonrpc": "2.0", "result' '": 22, "id": %d}]' % (id1, id2), content_type='application/json') def create_app(loop): app = aiohttp.web.Application(loop=loop) app.router.add_route('POST', '/', batch_handler) return app client = await test_client(create_app) server = Server('/', client) x = await server.batch_message(one=server.uno.raw(), two=server.dos.raw()) assert x['one'] == 11 assert x['two'] == 22 y = await server.send_message( Batch(one=server.uno.raw(), two=server.dos.raw())) assert y['one'] == 11 assert y['two'] == 22
async def test_headers_passthrough(aiohttp_client): """Test that we correctly send RFC headers and merge them with users.""" async def handler(request): return aiohttp.web.Response( text='{"jsonrpc": "2.0", "result": true, "id": 1}', content_type='application/json') def create_app(): app = aiohttp.web.Application() app.router.add_route('POST', '/', handler) return app client = await aiohttp_client(create_app()) original_post = client.post async def callback(*args, **kwargs): expected_headers = { 'Content-Type': 'application/json', 'Accept': 'application/json-rpc', 'X-TestCustomHeader': '1' } assert set(expected_headers.items()).issubset( set(kwargs['headers'].items())) return await original_post(*args, **kwargs) client.post = callback server = Server('/', client, headers={'X-TestCustomHeader': '1'}) await server.foo()
async def async_watch(clusters): servers = [ (idx, Server("http://{}".format(cluster))) for idx, cluster in enumerate(clusters) ] while True: await asyncio.gather(*[async_stats(idx, server) for (idx, server) in servers]) print("... as of {}".format(datetime.now())) await asyncio.sleep(CONST_INTERVAL)
async def fetch_peers_async(node): """ :param node: tuple(ip, p2p_port, jrpc_port) :return: list of tuple(ip, p2p_port, jrpc_port) """ json_rpc_url = "http://{}:{}".format(node[0], node[2]) server = Server(json_rpc_url) try: peers = await asyncio.wait_for(server.get_peers(), 5) except Exception: print("Failed to get peers from {}".format(json_rpc_url)) peers = {"peers": []} await server.session.close() return [ ( str(ipaddress.ip_address(int(p["ip"], 16))), int(p["port"], 16), int(p["port"], 16) + node[2] - node[1], ) for p in peers["peers"] ]
async def test_method_call(aiohttp_client): """Mixing *args and **kwargs is forbidden by the spec.""" def create_app(): app = aiohttp.web.Application() return app client = await aiohttp_client(create_app()) server = Server('/', client) with pytest.raises(ProtocolError) as error: await server.testmethod(1, 2, a=1, b=2) assert error.value.args[0] == ( "JSON-RPC spec forbids mixing arguments and keyword arguments")
async def test_batch_non_200_responses(test_client): # catch non-200 responses async def handler2(request): return aiohttp.web.Response(text='{}', content_type='application/json', status=404) def create_app(loop): app = aiohttp.web.Application(loop=loop) app.router.add_route('POST', '/', handler2) return app client = await test_client(create_app) server = Server('/', client) with pytest.raises(TransportError) as transport_error: await server.batch_message(one=server.uno.raw()) assert transport_error.value.args[0] == ( "Error calling with batch-request: HTTP 404 Not Found") # catch aiohttp own exception async def callback(*args, **kwargs): raise aiohttp.ClientOSError('aiohttp exception') def create_app(loop): app = aiohttp.web.Application(loop=loop) return app client = await test_client(create_app) client.post = callback server = Server('/', client) with pytest.raises(TransportError) as transport_error: await server.batch_message(one=server.uno.raw()) assert transport_error.value.args[0] == ( "Error calling with batch-request: Transport Error")
async def test_no_json_header(test_client): async def handler(request): return aiohttp.web.Response( text='{"jsonrpc": "2.0", "result": "31", "id": 1}') def create_app(loop): app = aiohttp.web.Application(loop=loop) app.router.add_route('POST', '/', handler) return app client = await test_client(create_app) server = Server('/', client) result = await server.send_message( Request('net_version', params=[], msg_id=1)) assert result == '31'
async def test_forbid_private_methods(aiohttp_client): """Test that we can't call private methods (those starting with '_').""" def create_app(): app = aiohttp.web.Application() return app client = await aiohttp_client(create_app()) server = Server('/', client) with pytest.raises(AttributeError): await server._foo() # nested private method call with pytest.raises(AttributeError): await server.foo.bar._baz()
async def test_notification(aiohttp_client): """Verify that we ignore the server response.""" async def handler(request): return aiohttp.web.Response( text='{"jsonrpc": "2.0", "result": 19, "id": 1}', content_type='application/json') def create_app(): app = aiohttp.web.Application() app.router.add_route('POST', '/', handler) return app client = await aiohttp_client(create_app()) server = Server('/', client) assert await server.subtract(42, 23, _notification=True) is None
async def test_exception_passthrough(aiohttp_client): async def callback(*args, **kwargs): raise aiohttp.ClientOSError('aiohttp exception') def create_app(): app = aiohttp.web.Application() return app client = await aiohttp_client(create_app()) client.post = callback server = Server('/', client) with pytest.raises(TransportError) as transport_error: await server.foo() assert transport_error.value.args[0] == ( "Error calling method 'foo': Transport Error") assert isinstance(transport_error.value.args[1], aiohttp.ClientOSError)
async def test_batch_cant_deserialize(test_client): # catch non-json responses async def handler1(request): return aiohttp.web.Response(text='not json', content_type='application/json') def create_app(loop): app = aiohttp.web.Application(loop=loop) app.router.add_route('POST', '/', handler1) return app client = await test_client(create_app) server = Server('/', client) with pytest.raises(TransportError) as transport_error: await server.batch_message(one=server.uno.raw(), two=server.dos.raw()) assert transport_error.value.args[0] == ( "Error calling with batch-request: Cannot deserialize response body") assert isinstance(transport_error.value.args[1], ValueError)
async def test_custom_loads(aiohttp_client): """Test RPC call with custom load.""" loads_mock = mock.Mock(wraps=json.loads) async def handler(request): request_message = await request.json() assert request_message["params"] == [42, 23] return aiohttp.web.Response( text='{"jsonrpc": "2.0", "result": 19, "id": 1}', content_type='application/json') def create_app(): app = aiohttp.web.Application() app.router.add_route('POST', '/', handler) return app client = await aiohttp_client(create_app()) server = Server('/', client, loads=loads_mock) assert await server.subtract(42, 23) == 19 assert loads_mock.call_count == 1
async def test_batch_timeout(test_client): # catch timeout responses async def handler(request): try: await asyncio.sleep(10) except asyncio.CancelledError: # Event loop will be terminated before sleep finishes pass return aiohttp.web.Response(text='{}', content_type='application/json') def create_app(loop): app = aiohttp.web.Application(loop=loop) app.router.add_route('POST', '/', handler) return app client = await test_client(create_app) server = Server('/', client, timeout=0.2) with pytest.raises(TransportError) as transport_error: await server.batch_message(one=server.uno.raw(), two=server.dos.raw()) assert isinstance(transport_error.value.args[1], asyncio.TimeoutError)
async def test_send_message_timeout(aiohttp_client): """Test the catching of the timeout responses.""" async def handler(request): try: await asyncio.sleep(10) except asyncio.CancelledError: # Event loop will be terminated before sleep finishes pass return aiohttp.web.Response(text='{}', content_type='application/json') def create_app(): app = aiohttp.web.Application() app.router.add_route('POST', '/', handler) return app client = await aiohttp_client(create_app()) server = Server('/', client, timeout=0.2) with pytest.raises(TransportError) as transport_error: await server.send_message(jsonrpc_base.Request( 'my_method', params=None, msg_id=1)) assert isinstance(transport_error.value.args[1], asyncio.TimeoutError)
async def async_adjust_difficulty(args): """ loops forever """ ip_lookup = json.loads(args.ip_lookup) count = 0 while True: try: clusters = monitoring.find_all_clusters(args.ip, args.p2p_port, args.jrpc_port, ip_lookup) num_nodes = len(clusters) if count == num_nodes: raise Exception("no change") servers = [(idx, Server("http://{}".format(cluster))) for idx, cluster in enumerate(clusters)] await asyncio.gather(*[ async_adjust( idx, server, num_nodes * args.base_root, num_nodes * args.base_minor, not args.do_not_mine, ) for (idx, server) in servers ]) print("Successfully set {} nodes to root={},minor={} @{}".format( num_nodes, num_nodes * args.base_root, num_nodes * args.base_minor, datetime.now(), )) count = num_nodes except Exception as ex: print("Got Exception: {}, continuing in {}".format( ex, args.interval)) pass await asyncio.sleep(args.interval)
async def test_method_nesting(aiohttp_client): """Test that we correctly nest namespaces.""" async def handler(request): request_message = await request.json() if (request_message["params"][0] == request_message["method"]): return aiohttp.web.Response( text='{"jsonrpc": "2.0", "result": true, "id": 1}', content_type='application/json') else: return aiohttp.web.Response( text='{"jsonrpc": "2.0", "result": false, "id": 1}', content_type='application/json') def create_app(): app = aiohttp.web.Application() app.router.add_route('POST', '/', handler) return app client = await aiohttp_client(create_app()) server = Server('/', client) assert await server.nest.testmethod("nest.testmethod") is True assert await server.nest.testmethod.some.other.method( "nest.testmethod.some.other.method") is True
def get_server(self): self.loads_mock = mock.Mock(wraps=json.loads) return Server('/xmlrpc', session=self.client, loads=self.loads_mock, timeout=0.2)
class TestJSONRPCClient(TestCase): def setUp(self): self.loop = setup_test_loop() self.app = self.get_app(self.loop) @asyncio.coroutine def create_client(app, loop): return JsonTestClient(app, loop=loop) self.client = self.loop.run_until_complete( create_client(self.app, self.loop)) self.loop.run_until_complete(self.client.start_server()) random.randint = Mock(return_value=1) self.server = Server('/xmlrpc', session=self.client, timeout=0.2) def tearDown(self): self.loop.run_until_complete(self.client.close()) teardown_test_loop(self.loop) def get_app(self, loop): @asyncio.coroutine def response_func(request): return (yield from self.handler(request)) app = aiohttp.web.Application(loop=loop) app.router.add_post('/xmlrpc', response_func) return app def test_pep8_conformance(self): """Test that we conform to PEP8.""" source_files = [] project_dir = os.path.dirname(os.path.abspath(__file__)) package_dir = os.path.join(project_dir, 'jsonrpc_async') for root, directories, filenames in os.walk(package_dir): source_files.extend([ os.path.join(root, f) for f in filenames if f.endswith('.py') ]) pep8style = pep8.StyleGuide(quiet=False, max_line_length=120) result = pep8style.check_files(source_files) self.assertEqual(result.total_errors, 0, "Found code style errors (and warnings).") @unittest_run_loop @asyncio.coroutine def test_send_message_timeout(self): # catch timeout responses with self.assertRaises(TransportError) as transport_error: @asyncio.coroutine def handler(request): try: yield from asyncio.sleep(10, loop=self.loop) except asyncio.CancelledError: # Event loop will be terminated before sleep finishes pass return aiohttp.web.Response(text='{}', content_type='application/json') self.handler = handler yield from self.server.send_message( jsonrpc_base.Request('my_method', params=None, msg_id=1)) self.assertIsInstance(transport_error.exception.args[1], asyncio.TimeoutError) @unittest_run_loop @asyncio.coroutine def test_send_message(self): # catch non-json responses with self.assertRaises(TransportError) as transport_error: @asyncio.coroutine def handler(request): return aiohttp.web.Response(text='not json', content_type='application/json') self.handler = handler yield from self.server.send_message( jsonrpc_base.Request('my_method', params=None, msg_id=1)) self.assertEqual( transport_error.exception.args[0], "Error calling method 'my_method': Cannot deserialize response body" ) self.assertIsInstance(transport_error.exception.args[1], ValueError) # catch non-200 responses with self.assertRaisesRegex(TransportError, '404'): @asyncio.coroutine def handler(request): return aiohttp.web.Response(text='{}', content_type='application/json', status=404) self.handler = handler yield from self.server.send_message( jsonrpc_base.Request('my_method', params=None, msg_id=1)) # a notification @asyncio.coroutine def handler(request): return aiohttp.web.Response(text='we dont care about this', content_type='application/json') self.handler = handler yield from self.server.send_message( jsonrpc_base.Request('my_notification', params=None)) # catch aiohttp own exception with self.assertRaisesRegex(TransportError, 'aiohttp exception'): def callback(method, path, *args, **kwargs): raise aiohttp.ClientResponseError(message='aiohttp exception') self.client.request_callback = callback yield from self.server.send_message( jsonrpc_base.Request('my_method', params=None, msg_id=1)) @unittest_run_loop @asyncio.coroutine def test_exception_passthrough(self): with self.assertRaises(TransportError) as transport_error: def callback(method, path, *args, **kwargs): raise aiohttp.ClientOSError('aiohttp exception') self.client.request_callback = callback yield from self.server.foo() self.assertEqual(transport_error.exception.args[0], "Error calling method 'foo': Transport Error") self.assertIsInstance(transport_error.exception.args[1], aiohttp.ClientOSError) @unittest_run_loop @asyncio.coroutine def test_forbid_private_methods(self): """Test that we can't call private class methods (those starting with '_')""" with self.assertRaises(AttributeError): yield from self.server._foo() # nested private method call with self.assertRaises(AttributeError): yield from self.server.foo.bar._baz() @unittest_run_loop @asyncio.coroutine def test_headers_passthrough(self): """Test that we correctly send RFC-defined headers and merge them with user defined ones""" @asyncio.coroutine def handler(request): return aiohttp.web.Response( text='{"jsonrpc": "2.0", "result": true, "id": 1}', content_type='application/json') self.handler = handler def callback(method, path, *args, **kwargs): expected_headers = { 'Content-Type': 'application/json', 'Accept': 'application/json-rpc', 'X-TestCustomHeader': '1' } self.assertTrue( set(expected_headers.items()).issubset( set(kwargs['headers'].items()))) self.client.request_callback = callback s = Server('/xmlrpc', session=self.client, headers={'X-TestCustomHeader': '1'}) yield from s.foo() @unittest_run_loop @asyncio.coroutine def test_method_call(self): """mixing *args and **kwargs is forbidden by the spec""" with self.assertRaisesRegex( ProtocolError, 'JSON-RPC spec forbids mixing arguments and keyword arguments' ): yield from self.server.testmethod(1, 2, a=1, b=2) @unittest_run_loop @asyncio.coroutine def test_method_nesting(self): """Test that we correctly nest namespaces""" @asyncio.coroutine def handler(request): request_message = yield from request.json() if (request_message["params"][0] == request_message["method"]): return aiohttp.web.Response( text='{"jsonrpc": "2.0", "result": true, "id": 1}', content_type='application/json') else: return aiohttp.web.Response( text='{"jsonrpc": "2.0", "result": false, "id": 1}', content_type='application/json') self.handler = handler self.assertEqual( (yield from self.server.nest.testmethod("nest.testmethod")), True) self.assertEqual( (yield from self.server.nest.testmethod.some.other.method( "nest.testmethod.some.other.method")), True) @unittest_run_loop @asyncio.coroutine def test_calls(self): # rpc call with positional parameters: @asyncio.coroutine def handler1(request): request_message = yield from request.json() self.assertEqual(request_message["params"], [42, 23]) return aiohttp.web.Response( text='{"jsonrpc": "2.0", "result": 19, "id": 1}', content_type='application/json') self.handler = handler1 self.assertEqual((yield from self.server.subtract(42, 23)), 19) @asyncio.coroutine def handler2(request): request_message = yield from request.json() self.assertEqual(request_message["params"], {'y': 23, 'x': 42}) return aiohttp.web.Response( text='{"jsonrpc": "2.0", "result": 19, "id": 1}', content_type='application/json') self.handler = handler2 self.assertEqual((yield from self.server.subtract(x=42, y=23)), 19) @asyncio.coroutine def handler3(request): request_message = yield from request.json() self.assertEqual(request_message["params"], {'foo': 'bar'}) return aiohttp.web.Response( text='{"jsonrpc": "2.0", "result": null}', content_type='application/json') self.handler = handler3 yield from self.server.foobar({'foo': 'bar'}) @unittest_run_loop @asyncio.coroutine def test_notification(self): # Verify that we ignore the server response @asyncio.coroutine def handler(request): return aiohttp.web.Response( text='{"jsonrpc": "2.0", "result": 19, "id": 1}', content_type='application/json') self.handler = handler self.assertIsNone((yield from self.server.subtract(42, 23, _notification=True)))
def get_server(self): return Server('/xmlrpc', session=self.client, timeout=0.2)