2020-01-29 23:30:09 +03:00
|
|
|
use prelude::*;
|
|
|
|
|
|
|
|
use futures::FutureExt;
|
|
|
|
use futures::Stream;
|
|
|
|
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;
|
|
|
|
use utils::test::poll_future_output;
|
|
|
|
use utils::test::poll_stream_output;
|
2020-02-13 05:16:38 +03:00
|
|
|
use futures::task::LocalSpawnExt;
|
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
|
|
|
}
|
|
|
|
|
|
|
|
pub fn try_get_event(&mut self) -> Option<MockEvent> {
|
|
|
|
poll_stream_output(&mut self.events_stream)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn try_get_notification(&mut self) -> Option<MockNotification> {
|
|
|
|
let event = self.try_get_event()?;
|
|
|
|
if let MockEvent::Notification(n) = event {
|
|
|
|
Some(n)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn expect_notification(&mut self) -> MockNotification {
|
|
|
|
self.try_get_notification().expect("expected notification event")
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn expect_handling_error(&mut self) -> HandlingError {
|
|
|
|
let event = self.try_get_event().expect("no events, while expected error event");
|
|
|
|
if let json_rpc::handler::Event::Error(err) = event {
|
|
|
|
err
|
|
|
|
} else {
|
|
|
|
panic!("expected error event, encountered: {:?}", event)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ============
|
|
|
|
// === 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-02-13 05:16:38 +03:00
|
|
|
let req_msg = fixture.transport.expect_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);
|
|
|
|
|
|
|
|
assert!(poll_future_output(&mut fut).is_none()); // no reply
|
|
|
|
|
|
|
|
// let's reply
|
|
|
|
let reply = pow_impl(req_msg);
|
2020-02-13 05:16:38 +03:00
|
|
|
fixture.transport.mock_peer_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-01-29 23:30:09 +03:00
|
|
|
assert!(poll_future_output(&mut fut).is_none()); // not ticked
|
|
|
|
|
2020-02-13 05:16:38 +03:00
|
|
|
// yield control to executor
|
|
|
|
fixture.pool.run_until_stalled();
|
|
|
|
|
2020-01-29 23:30:09 +03:00
|
|
|
let result = poll_future_output(&mut fut);
|
|
|
|
let result = result.expect("result should be present");
|
|
|
|
let result = result.expect("result should be a success");
|
|
|
|
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-01-29 23:30:09 +03:00
|
|
|
assert!(poll_future_output(&mut fut).is_none()); // no reply
|
|
|
|
|
|
|
|
// reply with error
|
2020-02-13 05:16:38 +03:00
|
|
|
let req_msg = fixture.transport.expect_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-02-13 05:16:38 +03:00
|
|
|
fixture.transport.mock_peer_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-01-29 23:30:09 +03:00
|
|
|
let result = poll_future_output(&mut fut);
|
|
|
|
let result = result.expect("result should be present");
|
|
|
|
let result = result.expect_err("result should be a failure");
|
|
|
|
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-01-29 23:30:09 +03:00
|
|
|
assert!(poll_future_output(&mut fut).is_none()); // no reply
|
2020-02-13 05:16:38 +03:00
|
|
|
fixture.transport.mock_peer_message_text("hello, nice to meet you");
|
|
|
|
|
|
|
|
fixture.pool.run_until_stalled();
|
|
|
|
|
2020-01-29 23:30:09 +03:00
|
|
|
assert!(poll_future_output(&mut fut).is_none()); // 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));
|
|
|
|
assert!(poll_future_output(&mut fut).is_none()); // no reply nor relevant event
|
|
|
|
fixture.transport.mock_connection_closed();
|
|
|
|
assert!(poll_future_output(&mut fut).is_none()); // closing event not yet processed
|
|
|
|
|
|
|
|
fixture.pool.run_until_stalled();
|
|
|
|
|
2020-01-29 23:30:09 +03:00
|
|
|
let result = poll_future_output(&mut fut);
|
|
|
|
let result = result.expect("result should be present");
|
|
|
|
let result = result.expect_err("result should be a failure");
|
|
|
|
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-01-29 23:30:09 +03:00
|
|
|
let result = poll_future_output(&mut fut).unwrap();
|
|
|
|
assert!(result.is_err())
|
|
|
|
}
|
|
|
|
|
|
|
|
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());
|
|
|
|
assert!(fixture.client.try_get_notification().is_none());
|
|
|
|
fixture.transport.mock_peer_message(message.clone());
|
|
|
|
assert!(fixture.client.try_get_notification().is_none());
|
|
|
|
|
|
|
|
fixture.pool.run_until_stalled();
|
|
|
|
|
|
|
|
let notification = fixture.client.try_get_notification();
|
2020-01-29 23:30:09 +03:00
|
|
|
assert_eq!(notification.is_none(), false);
|
|
|
|
assert_eq!(notification, Some(mock_notif));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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();
|
|
|
|
assert!(fixture.client.try_get_notification().is_none());
|
|
|
|
fixture.transport.mock_peer_message_text(other_notification);
|
|
|
|
assert!(fixture.client.try_get_notification().is_none());
|
|
|
|
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
}
|