2020-05-25 19:18:03 +03:00
|
|
|
use json_rpc::prelude::*;
|
2020-01-29 23:30:09 +03:00
|
|
|
|
|
|
|
use futures::FutureExt;
|
|
|
|
use futures::Stream;
|
2020-05-25 19:18:03 +03:00
|
|
|
use futures::task::LocalSpawnExt;
|
2020-01-29 23:30:09 +03:00
|
|
|
use json_rpc::*;
|
|
|
|
use json_rpc::api::RemoteMethodCall;
|
|
|
|
use json_rpc::api::Result;
|
|
|
|
use json_rpc::error::RpcError;
|
|
|
|
use json_rpc::error::HandlingError;
|
|
|
|
use json_rpc::messages::Id;
|
|
|
|
use json_rpc::messages::Message;
|
|
|
|
use json_rpc::messages::Version;
|
|
|
|
use json_rpc::test_util::transport::mock::MockTransport;
|
|
|
|
use serde::Deserialize;
|
|
|
|
use serde::Serialize;
|
|
|
|
use std::future::Future;
|
|
|
|
use std::pin::Pin;
|
2020-05-25 19:18:03 +03:00
|
|
|
use utils::test::traits::*;
|
2020-01-29 23:30:09 +03:00
|
|
|
|
|
|
|
type MockEvent = json_rpc::handler::Event<MockNotification>;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// =====================
|
|
|
|
// === Mock Protocol ===
|
|
|
|
// =====================
|
|
|
|
|
|
|
|
|
|
|
|
// === Remote Method ===
|
|
|
|
|
|
|
|
fn pow_impl(msg:MockRequestMessage) -> MockResponseMessage {
|
|
|
|
let ret = MockResponse { result : msg.i * msg.i };
|
|
|
|
Message::new_success(msg.id,ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// === Protocol Data ===
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
|
|
|
struct MockRequest {i:i64}
|
|
|
|
|
|
|
|
impl RemoteMethodCall for MockRequest {
|
|
|
|
const NAME:&'static str = "pow";
|
|
|
|
type Returned = MockResponse;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
|
|
|
struct MockResponse { result:i64 }
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
|
|
|
#[serde(tag = "method", content="params")]
|
|
|
|
pub enum MockNotification {
|
|
|
|
Meow {text:String},
|
|
|
|
Bark {text:String},
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// === Helper Aliases ===
|
|
|
|
|
|
|
|
type MockRequestMessage = messages::RequestMessage<MockRequest>;
|
|
|
|
|
|
|
|
type MockResponseMessage = messages::ResponseMessage<MockResponse>;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ===================
|
|
|
|
// === Mock Client ===
|
|
|
|
// ===================
|
|
|
|
|
|
|
|
pub struct Client {
|
|
|
|
pub handler : Handler<MockNotification>,
|
|
|
|
pub events_stream : Pin<Box<dyn Stream<Item = MockEvent>>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Client {
|
|
|
|
pub fn new(transport:impl Transport + 'static) -> Client {
|
2020-02-13 05:16:38 +03:00
|
|
|
let handler = Handler::new(transport);
|
|
|
|
let events_stream = Box::pin(handler.handler_event_stream());
|
2020-01-29 23:30:09 +03:00
|
|
|
Client {
|
|
|
|
handler,
|
|
|
|
events_stream,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn pow(&mut self, i:i64) -> impl Future<Output = Result<i64>> {
|
|
|
|
let input = MockRequest { i };
|
|
|
|
self.handler.open_request(input).map(|result| result.map(|r| r.result))
|
|
|
|
}
|
|
|
|
|
2020-02-13 05:16:38 +03:00
|
|
|
pub fn events_processor(&mut self) -> impl Future<Output = ()> {
|
|
|
|
self.handler.runner()
|
2020-01-29 23:30:09 +03:00
|
|
|
}
|
|
|
|
|
2020-05-25 19:18:03 +03:00
|
|
|
pub fn expect_no_notification_yet(&mut self) {
|
|
|
|
self.events_stream.expect_pending()
|
2020-01-29 23:30:09 +03:00
|
|
|
}
|
|
|
|
|
2020-05-25 19:18:03 +03:00
|
|
|
pub fn expect_notification(&mut self) -> MockNotification {
|
|
|
|
let event = self.events_stream.expect_next();
|
|
|
|
if let MockEvent::Notification(notification) = event {
|
|
|
|
notification
|
2020-01-29 23:30:09 +03:00
|
|
|
} else {
|
2020-05-25 19:18:03 +03:00
|
|
|
panic!("Expected a notification, got different kind of event: {:?}",event)
|
2020-01-29 23:30:09 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn expect_handling_error(&mut self) -> HandlingError {
|
2020-05-25 19:18:03 +03:00
|
|
|
let event = self.events_stream.expect_next();
|
2020-01-29 23:30:09 +03:00
|
|
|
if let json_rpc::handler::Event::Error(err) = event {
|
|
|
|
err
|
|
|
|
} else {
|
2020-05-25 19:18:03 +03:00
|
|
|
panic!("Expected an error event, got different kind of event: {:?}", event)
|
2020-01-29 23:30:09 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ============
|
|
|
|
// === Test ===
|
|
|
|
// ============
|
|
|
|
|
2020-02-13 05:16:38 +03:00
|
|
|
struct Fixture {
|
|
|
|
transport : MockTransport,
|
|
|
|
client : Client,
|
|
|
|
pool : futures::executor::LocalPool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Fixture {
|
|
|
|
pub fn new() -> Fixture {
|
|
|
|
let transport = MockTransport::new();
|
|
|
|
let mut client = Client::new(transport.clone());
|
|
|
|
let pool = futures::executor::LocalPool::new();
|
|
|
|
let fut = client.events_processor();
|
|
|
|
pool.spawner().spawn_local(fut).unwrap();
|
|
|
|
Fixture {transport,client,pool}
|
|
|
|
}
|
2020-01-29 23:30:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_success_call() {
|
2020-02-13 05:16:38 +03:00
|
|
|
let mut fixture = Fixture::new();
|
2020-01-29 23:30:09 +03:00
|
|
|
let call_input = 8;
|
2020-02-13 05:16:38 +03:00
|
|
|
let mut fut = Box::pin(fixture.client.pow(8));
|
2020-01-29 23:30:09 +03:00
|
|
|
let expected_first_request_id = Id(0);
|
|
|
|
|
|
|
|
// validate request sent
|
2020-05-13 18:28:39 +03:00
|
|
|
let req_msg = fixture.transport.expect_json_message::<MockRequestMessage>();
|
2020-01-29 23:30:09 +03:00
|
|
|
assert_eq!(req_msg.id, expected_first_request_id);
|
|
|
|
assert_eq!(req_msg.method, MockRequest::NAME);
|
|
|
|
assert_eq!(req_msg.i, call_input);
|
|
|
|
assert_eq!(req_msg.jsonrpc, Version::V2);
|
|
|
|
|
2020-05-25 19:18:03 +03:00
|
|
|
fut.expect_pending(); // no reply
|
2020-01-29 23:30:09 +03:00
|
|
|
|
|
|
|
// let's reply
|
|
|
|
let reply = pow_impl(req_msg);
|
2020-05-13 18:28:39 +03:00
|
|
|
fixture.transport.mock_peer_json_message(reply);
|
2020-01-29 23:30:09 +03:00
|
|
|
|
2020-02-13 05:16:38 +03:00
|
|
|
// before yielding control message should be in buffer and futures should not complete
|
2020-05-25 19:18:03 +03:00
|
|
|
fut.expect_pending(); // not ticked
|
2020-01-29 23:30:09 +03:00
|
|
|
|
2020-02-13 05:16:38 +03:00
|
|
|
// yield control to executor
|
|
|
|
fixture.pool.run_until_stalled();
|
|
|
|
|
2020-05-25 19:18:03 +03:00
|
|
|
let result = fut.expect_ok();
|
2020-01-29 23:30:09 +03:00
|
|
|
assert_eq!(result, 8*8);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_error_call() {
|
2020-02-13 05:16:38 +03:00
|
|
|
let mut fixture = Fixture::new();
|
|
|
|
let mut fut = Box::pin(fixture.client.pow(8));
|
2020-05-25 19:18:03 +03:00
|
|
|
fut.expect_pending(); // no reply
|
2020-01-29 23:30:09 +03:00
|
|
|
|
|
|
|
// reply with error
|
2020-05-13 18:28:39 +03:00
|
|
|
let req_msg = fixture.transport.expect_json_message::<MockRequestMessage>();
|
2020-01-29 23:30:09 +03:00
|
|
|
let error_code = 5;
|
|
|
|
let error_description = "wrong!";
|
|
|
|
let error_data = None;
|
|
|
|
let error_msg: MockResponseMessage = Message::new_error(
|
|
|
|
req_msg.id,
|
|
|
|
error_code,
|
|
|
|
error_description.into(),
|
|
|
|
error_data.clone(),
|
|
|
|
);
|
2020-05-13 18:28:39 +03:00
|
|
|
fixture.transport.mock_peer_json_message(error_msg);
|
2020-01-29 23:30:09 +03:00
|
|
|
|
|
|
|
// receive error
|
2020-02-13 05:16:38 +03:00
|
|
|
fixture.pool.run_until_stalled();
|
|
|
|
|
2020-05-25 19:18:03 +03:00
|
|
|
let result = fut.expect_err();
|
2020-01-29 23:30:09 +03:00
|
|
|
if let RpcError::RemoteError(e) = result {
|
|
|
|
assert_eq!(e.code, error_code);
|
|
|
|
assert_eq!(e.data, error_data);
|
|
|
|
assert_eq!(e.message, error_description);
|
|
|
|
} else {
|
|
|
|
panic!("Expected an error to be RemoteError");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_garbage_reply_error() {
|
2020-02-13 05:16:38 +03:00
|
|
|
let mut fixture = Fixture::new();
|
|
|
|
let mut fut = Box::pin(fixture.client.pow(8));
|
2020-05-25 19:18:03 +03:00
|
|
|
fut.expect_pending(); // no reply
|
2020-05-13 18:28:39 +03:00
|
|
|
fixture.transport.mock_peer_text_message("hello, nice to meet you");
|
2020-02-13 05:16:38 +03:00
|
|
|
|
|
|
|
fixture.pool.run_until_stalled();
|
|
|
|
|
2020-05-25 19:18:03 +03:00
|
|
|
fut.expect_pending(); // no valid reply
|
2020-02-13 05:16:38 +03:00
|
|
|
let internal_error = fixture.client.expect_handling_error();
|
2020-01-29 23:30:09 +03:00
|
|
|
if let HandlingError::InvalidMessage(_) = internal_error {
|
|
|
|
} else {
|
|
|
|
panic!("Expected an error to be InvalidMessage");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_disconnect_error() {
|
2020-02-13 05:16:38 +03:00
|
|
|
let mut fixture = Fixture::new();
|
|
|
|
let mut fut = Box::pin(fixture.client.pow(8));
|
2020-05-25 19:18:03 +03:00
|
|
|
fut.expect_pending(); // no reply nor relevant event
|
2020-02-13 05:16:38 +03:00
|
|
|
fixture.transport.mock_connection_closed();
|
2020-05-25 19:18:03 +03:00
|
|
|
fut.expect_pending(); // closing event not yet processed
|
2020-02-13 05:16:38 +03:00
|
|
|
|
|
|
|
fixture.pool.run_until_stalled();
|
|
|
|
|
2020-05-25 19:18:03 +03:00
|
|
|
let result = fut.expect_err();
|
2020-01-29 23:30:09 +03:00
|
|
|
if let RpcError::LostConnection = result {} else {
|
|
|
|
panic!("Expected an error to be RemoteError");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_sending_while_disconnected() {
|
2020-02-13 05:16:38 +03:00
|
|
|
let mut fixture = Fixture::new();
|
|
|
|
fixture.transport.mock_connection_closed();
|
|
|
|
let mut fut = Box::pin(fixture.client.pow(8));
|
2020-05-25 19:18:03 +03:00
|
|
|
fut.expect_err();
|
2020-01-29 23:30:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn test_notification(mock_notif:MockNotification) {
|
2020-02-13 05:16:38 +03:00
|
|
|
let mut fixture = Fixture::new();
|
|
|
|
let message = Message::new(mock_notif.clone());
|
2020-05-25 19:18:03 +03:00
|
|
|
fixture.client.events_stream.expect_pending();
|
2020-05-25 16:05:28 +03:00
|
|
|
fixture.transport.mock_peer_json_message(message);
|
2020-05-25 19:18:03 +03:00
|
|
|
fixture.client.events_stream.expect_pending();
|
2020-02-13 05:16:38 +03:00
|
|
|
fixture.pool.run_until_stalled();
|
|
|
|
|
2020-05-25 19:18:03 +03:00
|
|
|
let notification = fixture.client.expect_notification();
|
|
|
|
assert_eq!(notification,mock_notif);
|
2020-01-29 23:30:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_recognizing_notifications() {
|
|
|
|
let meow_notification = MockNotification::Meow {text:"meow!".into()};
|
|
|
|
test_notification(meow_notification);
|
|
|
|
|
|
|
|
let bark_notification = MockNotification::Bark {text:"woof!".into()};
|
|
|
|
test_notification(bark_notification);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_handling_invalid_notification() {
|
|
|
|
let other_notification = r#"{
|
|
|
|
"jsonrpc": "2.0",
|
|
|
|
"method": "update",
|
|
|
|
"params": [1,2,3,4,5]
|
|
|
|
}"#;
|
|
|
|
|
2020-02-13 05:16:38 +03:00
|
|
|
let mut fixture = Fixture::new();
|
2020-05-25 19:18:03 +03:00
|
|
|
fixture.client.expect_no_notification_yet();
|
2020-05-13 18:28:39 +03:00
|
|
|
fixture.transport.mock_peer_text_message(other_notification);
|
2020-05-25 19:18:03 +03:00
|
|
|
fixture.client.expect_no_notification_yet();
|
2020-02-13 05:16:38 +03:00
|
|
|
|
|
|
|
fixture.pool.run_until_stalled();
|
|
|
|
|
|
|
|
let internal_error = fixture.client.expect_handling_error();
|
2020-01-29 23:30:09 +03:00
|
|
|
if let HandlingError::InvalidNotification(_) = internal_error {}
|
|
|
|
else {
|
|
|
|
panic!("expected InvalidNotification error");
|
|
|
|
}
|
|
|
|
}
|