use async_trait::async_trait;
use capnweb_core::{CallId, CapId, Message, Outcome, RpcError, Target};
use capnweb_server::{RpcTarget, Server, ServerConfig};
use serde_json::{json, Value};
use std::sync::Arc;
struct TestCalculator;
#[async_trait]
impl RpcTarget for TestCalculator {
async fn call(&self, member: &str, args: Vec<Value>) -> Result<Value, RpcError> {
match member {
"add" => {
let a = args[0].as_f64().unwrap_or(0.0);
let b = args[1].as_f64().unwrap_or(0.0);
Ok(json!(a + b))
}
_ => Err(RpcError::not_found("Method not found")),
}
}
}
#[tokio::test]
async fn test_server_batch_endpoint() {
let config = ServerConfig {
port: 0, host: "127.0.0.1".to_string(),
max_batch_size: 10,
};
let server = Server::new(config);
server.register_capability(CapId::new(1), Arc::new(TestCalculator));
let batch_request = vec![
Message::call(
CallId::new(1),
Target::cap(CapId::new(1)),
"add".to_string(),
vec![json!(5), json!(3)],
),
Message::call(
CallId::new(2),
Target::cap(CapId::new(1)),
"add".to_string(),
vec![json!(10), json!(20)],
),
];
let mut responses = Vec::new();
for msg in batch_request {
let response = match msg {
Message::Call { call } => {
let result = match &call.target {
Target::Cap { cap } => match server.cap_table().lookup(&cap.id) {
Some(cap_target) => {
match cap_target.call(&call.member, call.args.clone()).await {
Ok(value) => Outcome::Success { value },
Err(error) => Outcome::Error { error },
}
}
None => Outcome::Error {
error: RpcError::not_found("Capability not found"),
},
},
Target::Special { .. } => Outcome::Error {
error: RpcError::not_found("Special target not implemented"),
},
};
Message::result(call.id, result)
}
_ => msg,
};
responses.push(response);
}
assert_eq!(responses.len(), 2);
match &responses[0] {
Message::Result { result } => {
assert_eq!(result.id, CallId::new(1));
match &result.outcome {
Outcome::Success { value } => assert_eq!(*value, json!(8.0)),
_ => panic!("Expected success outcome"),
}
}
_ => panic!("Expected Result message"),
}
match &responses[1] {
Message::Result { result } => {
assert_eq!(result.id, CallId::new(2));
match &result.outcome {
Outcome::Success { value } => assert_eq!(*value, json!(30.0)),
_ => panic!("Expected success outcome"),
}
}
_ => panic!("Expected Result message"),
}
}
#[tokio::test]
async fn test_dispose_in_batch() {
let server = Server::new(ServerConfig::default());
let cap_id = CapId::new(42);
server.register_capability(cap_id, Arc::new(TestCalculator));
assert!(server.cap_table().lookup(&cap_id).is_some());
let batch_request = vec![
Message::call(
CallId::new(1),
Target::cap(cap_id),
"add".to_string(),
vec![json!(1), json!(2)],
),
Message::dispose(vec![cap_id]),
];
for msg in batch_request {
if let Message::Dispose { dispose } = msg {
for id in &dispose.caps {
server.cap_table().remove(id);
}
}
}
assert!(server.cap_table().lookup(&cap_id).is_none());
}
#[tokio::test]
async fn test_batch_size_limit() {
let config = ServerConfig {
port: 0,
host: "127.0.0.1".to_string(),
max_batch_size: 2,
};
let _server = Server::new(config);
let batch: Vec<Message> = (0..3)
.map(|i| {
Message::call(
CallId::new(i),
Target::cap(CapId::new(1)),
"test".to_string(),
vec![],
)
})
.collect();
assert_eq!(batch.len(), 3);
}