Skip to content

Commit 0620585

Browse files
authored
Simple testing utility. (#217)
* Simple testing utility. * Add missing doc.
1 parent 8f921ed commit 0620585

File tree

6 files changed

+169
-4
lines changed

6 files changed

+169
-4
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ members = [
99
"pubsub/more-examples",
1010
"server-utils",
1111
"tcp",
12-
"ws"
12+
"test",
13+
"ws",
1314
]

core/src/calls.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::fmt;
12
use std::sync::Arc;
23
use types::{Params, Value, Error};
34
use futures::{Future, IntoFuture};
@@ -44,6 +45,17 @@ pub enum RemoteProcedure<T: Metadata> {
4445
Alias(String),
4546
}
4647

48+
impl<T: Metadata> fmt::Debug for RemoteProcedure<T> {
49+
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
50+
use self::RemoteProcedure::*;
51+
match *self {
52+
Method(..) => write!(fmt, "<method>"),
53+
Notification(..) => write!(fmt, "<notification>"),
54+
Alias(ref alias) => write!(fmt, "alias => {:?}", alias)
55+
}
56+
}
57+
}
58+
4759
impl<F: Send + Sync + 'static, X: Send + 'static, I> RpcMethodSimple for F where
4860
F: Fn(Params) -> I,
4961
X: Future<Item = Value, Error = Error>,

core/src/io.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub type FutureOutput = future::Either<
2626
>;
2727

2828
/// `IoHandler` json-rpc protocol compatibility
29-
#[derive(Clone, Copy)]
29+
#[derive(Debug, Clone, Copy)]
3030
pub enum Compatibility {
3131
/// Compatible only with JSON-RPC 1.x
3232
V1,
@@ -63,6 +63,7 @@ impl Compatibility {
6363
/// Request handler
6464
///
6565
/// By default compatible only with jsonrpc v2
66+
#[derive(Debug)]
6667
pub struct MetaIoHandler<T: Metadata, S: Middleware<T> = middleware::Noop> {
6768
middleware: S,
6869
compatibility: Compatibility,
@@ -270,7 +271,7 @@ impl<T: Metadata, S: Middleware<T>> MetaIoHandler<T, S> {
270271
}
271272

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

276277
// Type inference helper

core/src/middleware.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub trait Middleware<M: Metadata>: Send + Sync + 'static {
1818
}
1919

2020
/// No-op middleware implementation
21-
#[derive(Default)]
21+
#[derive(Debug, Default)]
2222
pub struct Noop;
2323
impl<M: Metadata> Middleware<M> for Noop {
2424
type Future = Box<Future<Item=Option<Response>, Error=()> + Send>;

test/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "jsonrpc-test"
3+
version = "8.0.0"
4+
authors = ["Tomasz Drwięga <tomasz@parity.io>"]
5+
6+
[dependencies]
7+
jsonrpc-core = { path = "../core", version = "8.0" }
8+
serde = "1.0"
9+
serde_json = "1.0"
10+
11+
[dev-dependencies]
12+
jsonrpc-macros = { path = "../macros", version = "8.0" }

test/src/lib.rs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
//! An utility package to test jsonrpc-core based projects.
2+
//!
3+
//! ```
4+
//! #[macro_use]
5+
//! extern crate jsonrpc_macros;
6+
//!
7+
//! extern crate jsonrpc_core as core;
8+
//! extern crate jsonrpc_test as test;
9+
//!
10+
//! use core::Result;
11+
//!
12+
//! build_rpc_trait! {
13+
//! pub trait Test {
14+
//! #[rpc(name = "rpc_some_method")]
15+
//! fn some_method(&self, u64) -> Result<u64>;
16+
//! }
17+
//! }
18+
//!
19+
//! struct Dummy;
20+
//! impl Test for Dummy {
21+
//! fn some_method(&self, x: u64) -> Result<u64> {
22+
//! Ok(x * 2)
23+
//! }
24+
//! }
25+
//!
26+
//! fn main() {
27+
//! // Initialize new instance of test environment
28+
//! let rpc = test::Rpc::new(Dummy.to_delegate());
29+
//!
30+
//! // make a request and verify the response as a pretty-printed string
31+
//! assert_eq!(rpc.request("rpc_some_method", &[5]), r#"10"#);
32+
//!
33+
//! // You can also test RPC created without macros:
34+
//! let rpc = {
35+
//! let mut io = core::IoHandler::new();
36+
//! io.add_method("rpc_test_method", |_| {
37+
//! Err(core::Error::internal_error())
38+
//! });
39+
//! test::Rpc::from(io)
40+
//! };
41+
//!
42+
//! assert_eq!(rpc.request("rpc_test_method", &()), r#"{
43+
//! "code": -32603,
44+
//! "message": "Internal error"
45+
//! }"#);
46+
//! }
47+
//! ```
48+
49+
#[warn(missing_docs)]
50+
51+
extern crate jsonrpc_core as rpc;
52+
extern crate serde;
53+
extern crate serde_json;
54+
55+
use std::collections::HashMap;
56+
57+
/// Test RPC options.
58+
#[derive(Default, Debug)]
59+
pub struct Options {
60+
/// Disable printing requests and responses.
61+
pub no_print: bool,
62+
}
63+
64+
#[derive(Default, Debug)]
65+
/// RPC instance.
66+
pub struct Rpc {
67+
/// Underlying `IoHandler`.
68+
pub io: rpc::IoHandler,
69+
/// Options
70+
pub options: Options,
71+
}
72+
73+
impl From<rpc::IoHandler> for Rpc {
74+
fn from(io: rpc::IoHandler) -> Self {
75+
Rpc { io, ..Default::default() }
76+
}
77+
}
78+
79+
impl Rpc {
80+
/// Create a new RPC instance from a single delegate.
81+
pub fn new<D>(delegate: D) -> Self where
82+
D: Into<HashMap<String, rpc::RemoteProcedure<()>>>,
83+
{
84+
let mut io = rpc::IoHandler::new();
85+
io.extend_with(delegate);
86+
io.into()
87+
}
88+
89+
/// Perform a single, synchronous method call.
90+
pub fn request<T>(&self, method: &str, params: &T) -> String where
91+
T: serde::Serialize,
92+
{
93+
use self::rpc::types::response;
94+
95+
let request = format!(
96+
"{{ \"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"{}\", \"params\": {} }}",
97+
method,
98+
serde_json::to_string_pretty(params).expect("Serialization should be infallible."),
99+
);
100+
101+
let response = self.io
102+
.handle_request_sync(&request)
103+
.expect("We are sending a method call not notification.");
104+
105+
// extract interesting part from the response
106+
let extracted = match serde_json::from_str(&response).expect("We will always get a single output.") {
107+
response::Output::Success(response::Success { result, .. }) => serde_json::to_string_pretty(&result),
108+
response::Output::Failure(response::Failure { error, .. }) => serde_json::to_string_pretty(&error),
109+
}.expect("Serialization is infallible; qed");
110+
111+
112+
println!("\n{}\n --> {}\n", request, extracted);
113+
114+
extracted
115+
}
116+
}
117+
118+
#[cfg(test)]
119+
mod tests {
120+
use super::*;
121+
122+
#[test]
123+
fn should_test_simple_method() {
124+
// given
125+
let rpc = {
126+
let mut io = rpc::IoHandler::new();
127+
io.add_method("test_method", |_| {
128+
Ok(rpc::Value::Number(5.into()))
129+
});
130+
Rpc::from(io)
131+
};
132+
133+
// when
134+
assert_eq!(
135+
rpc.request("test_method", &[5u64]),
136+
r#"5"#
137+
);
138+
}
139+
}

0 commit comments

Comments
 (0)