Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[rpc module] test helper for calling and converting types to JSON-RPC params #458

Merged
merged 12 commits into from Sep 13, 2021
3 changes: 1 addition & 2 deletions utils/Cargo.toml
Expand Up @@ -9,7 +9,6 @@ license = "MIT"
[dependencies]
beef = { version = "0.5.1", features = ["impl_serde"] }
thiserror = { version = "1", optional = true }
tokio = { version = "1", features = ["macros"], optional = true }
futures-channel = { version = "0.3.14", default-features = false, optional = true }
futures-util = { version = "0.3.14", default-features = false, optional = true }
hyper = { version = "0.14.10", default-features = false, features = ["stream"], optional = true }
Expand All @@ -35,8 +34,8 @@ server = [
"log",
"parking_lot",
"rand",
"tokio"
]

[dev-dependencies]
serde_json = "1.0"
tokio = { version = "1", features = ["macros", "rt"] }
71 changes: 71 additions & 0 deletions utils/src/server/rpc_module.rs
Expand Up @@ -180,6 +180,18 @@ impl Methods {
}
}

/// Helper alternative to `execute`, useful for writing unit tests without having to spin
/// a server up.
///
/// Converts the params to an array for you if it's not already serialized to a sequence.
pub async fn call_with<T: Serialize>(&self, method: &str, params: &T) -> Option<String> {
niklasad1 marked this conversation as resolved.
Show resolved Hide resolved
let params = serde_json::to_string(params).ok().map(|json| {
let json = if json.starts_with("[") && json.ends_with("]") { json } else { format!("[{}]", json) };
niklasad1 marked this conversation as resolved.
Show resolved Hide resolved
RawValue::from_string(json).expect("valid JSON string above; qed")
});
self.call(method, params).await
}

/// Helper alternative to `execute`, useful for writing unit tests without having to spin
/// a server up.
pub async fn call(&self, method: &str, params: Option<Box<RawValue>>) -> Option<String> {
Expand Down Expand Up @@ -540,6 +552,8 @@ fn subscription_closed_err(sub_id: u64) -> Error {
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;

#[test]
fn rpc_modules_with_different_contexts_can_be_merged() {
let cx = Vec::<u8>::new();
Expand Down Expand Up @@ -574,4 +588,61 @@ mod tests {
assert!(module.method("hello_world").is_some());
assert!(module.method("hello_foobar").is_some());
}

#[tokio::test]
async fn calling_method_without_server() {
// Call sync method with no params
let mut module = RpcModule::new(());
module.register_method("boo", |_: RpcParams, _| Ok(String::from("boo!"))).unwrap();
let result = &module.call_with("boo", &None::<()>).await.unwrap();
niklasad1 marked this conversation as resolved.
Show resolved Hide resolved
assert_eq!(result.as_ref(), String::from(r#"{"jsonrpc":"2.0","result":"boo!","id":0}"#));

// Call sync method with params
module
.register_method("foo", |params, _| {
let n: u16 = params.one().expect("valid params please");
Ok(n * 2)
})
.unwrap();
let result = &module.call_with("foo", &3).await.unwrap();
assert_eq!(result.as_ref(), String::from(r#"{"jsonrpc":"2.0","result":6,"id":0}"#));
let result = &module.call_with("foo", &[3]).await.unwrap();
assert_eq!(result.as_ref(), String::from(r#"{"jsonrpc":"2.0","result":6,"id":0}"#));

// Call async method with params and context
struct MyContext;
impl MyContext {
fn roo(&self, things: Vec<u8>) -> u16 {
things.iter().sum::<u8>().into()
}
}
let mut module = RpcModule::new(MyContext);
module
.register_async_method("roo", |params, ctx| {
let ns: Vec<u8> = params.parse().expect("valid params please");
async move { Ok(ctx.roo(ns)) }.boxed()
})
.unwrap();

module
.register_async_method("many_args", |params, _ctx| {
let mut seq = params.sequence();

let one: Vec<usize> = seq.next().unwrap();
let two: String = seq.next().unwrap();
let three: usize = seq.optional_next().unwrap().unwrap_or(0);

let res = one.iter().sum::<usize>() + two.as_bytes().len() + three;

async move { Ok(res) }.boxed()
})
.unwrap();

let result = &module.call_with("roo", &[12, 13]).await.unwrap();
assert_eq!(result.as_ref(), String::from(r#"{"jsonrpc":"2.0","result":25,"id":0}"#));

let json = vec![json!([1, 3, 7]), json!("oooh")];
let result = &module.call_with("many_args", &json).await.unwrap();
assert_eq!(result.as_ref(), String::from(r#"{"jsonrpc":"2.0","result":15,"id":0}"#));
}
}