Skip to content

Commit

Permalink
Simple testing utility. (#217)
Browse files Browse the repository at this point in the history
* Simple testing utility.

* Add missing doc.
  • Loading branch information
tomusdrw authored Nov 20, 2017
1 parent 8f921ed commit 0620585
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 4 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ members = [
"pubsub/more-examples",
"server-utils",
"tcp",
"ws"
"test",
"ws",
]
12 changes: 12 additions & 0 deletions core/src/calls.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::fmt;
use std::sync::Arc;
use types::{Params, Value, Error};
use futures::{Future, IntoFuture};
Expand Down Expand Up @@ -44,6 +45,17 @@ pub enum RemoteProcedure<T: Metadata> {
Alias(String),
}

impl<T: Metadata> fmt::Debug for RemoteProcedure<T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
use self::RemoteProcedure::*;
match *self {
Method(..) => write!(fmt, "<method>"),
Notification(..) => write!(fmt, "<notification>"),
Alias(ref alias) => write!(fmt, "alias => {:?}", alias)
}
}
}

impl<F: Send + Sync + 'static, X: Send + 'static, I> RpcMethodSimple for F where
F: Fn(Params) -> I,
X: Future<Item = Value, Error = Error>,
Expand Down
5 changes: 3 additions & 2 deletions core/src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub type FutureOutput = future::Either<
>;

/// `IoHandler` json-rpc protocol compatibility
#[derive(Clone, Copy)]
#[derive(Debug, Clone, Copy)]
pub enum Compatibility {
/// Compatible only with JSON-RPC 1.x
V1,
Expand Down Expand Up @@ -63,6 +63,7 @@ impl Compatibility {
/// Request handler
///
/// By default compatible only with jsonrpc v2
#[derive(Debug)]
pub struct MetaIoHandler<T: Metadata, S: Middleware<T> = middleware::Noop> {
middleware: S,
compatibility: Compatibility,
Expand Down Expand Up @@ -270,7 +271,7 @@ impl<T: Metadata, S: Middleware<T>> MetaIoHandler<T, S> {
}

/// Simplified `IoHandler` with no `Metadata` associated with each request.
#[derive(Default)]
#[derive(Debug, Default)]
pub struct IoHandler<M: Metadata = ()>(MetaIoHandler<M>);

// Type inference helper
Expand Down
2 changes: 1 addition & 1 deletion core/src/middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub trait Middleware<M: Metadata>: Send + Sync + 'static {
}

/// No-op middleware implementation
#[derive(Default)]
#[derive(Debug, Default)]
pub struct Noop;
impl<M: Metadata> Middleware<M> for Noop {
type Future = Box<Future<Item=Option<Response>, Error=()> + Send>;
Expand Down
12 changes: 12 additions & 0 deletions test/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "jsonrpc-test"
version = "8.0.0"
authors = ["Tomasz Drwięga <tomasz@parity.io>"]

[dependencies]
jsonrpc-core = { path = "../core", version = "8.0" }
serde = "1.0"
serde_json = "1.0"

[dev-dependencies]
jsonrpc-macros = { path = "../macros", version = "8.0" }
139 changes: 139 additions & 0 deletions test/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
//! An utility package to test jsonrpc-core based projects.
//!
//! ```
//! #[macro_use]
//! extern crate jsonrpc_macros;
//!
//! extern crate jsonrpc_core as core;
//! extern crate jsonrpc_test as test;
//!
//! use core::Result;
//!
//! build_rpc_trait! {
//! pub trait Test {
//! #[rpc(name = "rpc_some_method")]
//! fn some_method(&self, u64) -> Result<u64>;
//! }
//! }
//!
//! struct Dummy;
//! impl Test for Dummy {
//! fn some_method(&self, x: u64) -> Result<u64> {
//! Ok(x * 2)
//! }
//! }
//!
//! fn main() {
//! // Initialize new instance of test environment
//! let rpc = test::Rpc::new(Dummy.to_delegate());
//!
//! // make a request and verify the response as a pretty-printed string
//! assert_eq!(rpc.request("rpc_some_method", &[5]), r#"10"#);
//!
//! // You can also test RPC created without macros:
//! let rpc = {
//! let mut io = core::IoHandler::new();
//! io.add_method("rpc_test_method", |_| {
//! Err(core::Error::internal_error())
//! });
//! test::Rpc::from(io)
//! };
//!
//! assert_eq!(rpc.request("rpc_test_method", &()), r#"{
//! "code": -32603,
//! "message": "Internal error"
//! }"#);
//! }
//! ```

#[warn(missing_docs)]

extern crate jsonrpc_core as rpc;
extern crate serde;
extern crate serde_json;

use std::collections::HashMap;

/// Test RPC options.
#[derive(Default, Debug)]
pub struct Options {
/// Disable printing requests and responses.
pub no_print: bool,
}

#[derive(Default, Debug)]
/// RPC instance.
pub struct Rpc {
/// Underlying `IoHandler`.
pub io: rpc::IoHandler,
/// Options
pub options: Options,
}

impl From<rpc::IoHandler> for Rpc {
fn from(io: rpc::IoHandler) -> Self {
Rpc { io, ..Default::default() }
}
}

impl Rpc {
/// Create a new RPC instance from a single delegate.
pub fn new<D>(delegate: D) -> Self where
D: Into<HashMap<String, rpc::RemoteProcedure<()>>>,
{
let mut io = rpc::IoHandler::new();
io.extend_with(delegate);
io.into()
}

/// Perform a single, synchronous method call.
pub fn request<T>(&self, method: &str, params: &T) -> String where
T: serde::Serialize,
{
use self::rpc::types::response;

let request = format!(
"{{ \"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"{}\", \"params\": {} }}",
method,
serde_json::to_string_pretty(params).expect("Serialization should be infallible."),
);

let response = self.io
.handle_request_sync(&request)
.expect("We are sending a method call not notification.");

// extract interesting part from the response
let extracted = match serde_json::from_str(&response).expect("We will always get a single output.") {
response::Output::Success(response::Success { result, .. }) => serde_json::to_string_pretty(&result),
response::Output::Failure(response::Failure { error, .. }) => serde_json::to_string_pretty(&error),
}.expect("Serialization is infallible; qed");


println!("\n{}\n --> {}\n", request, extracted);

extracted
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn should_test_simple_method() {
// given
let rpc = {
let mut io = rpc::IoHandler::new();
io.add_method("test_method", |_| {
Ok(rpc::Value::Number(5.into()))
});
Rpc::from(io)
};

// when
assert_eq!(
rpc.request("test_method", &[5u64]),
r#"5"#
);
}
}

0 comments on commit 0620585

Please sign in to comment.