Problem
I'm learning about Tonic. I'm trying to write an automated test where I spin up a server in the background, call it via a generated client, and then stop the server.
Attempts
What I have currently looks like this:
//...
pub fn build(inv: impl Inventory) -> Result<Router, Box<dyn Error>> {
let reflection_service = tonic_reflection::server::Builder::configure()
.register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
.build()?;
let srv = Server::builder()
.add_service(InventoryServer::new(inv))
.add_service(reflection_service);
Ok(srv)
}
#[tokio::test]
async fn works() -> Result<(), Box<dyn Error>> {
let addr = "127.0.0.1:9001".parse()?;
let inventory = InMemoryInventory::default();
let (handle, registration) = AbortHandle::new_pair();
_ = Abortable::new(build(inventory)?.serve(addr), registration); // does not start
let sku = "test".to_string();
let add_res = add::add(AddOptions { sku: sku.clone(), price: 0.0, quantity: 0, name: None, description: None }).await?;
println!("{:?}", add_res);
let get_res = get::get(GetOptions { sku }).await?;
println!("{:?}", get_res);
handle.abort();
Ok(())
}
I understand that the problem here comes from the fact that the future for serve
is not triggered, thus the server cannot start. If I append await
, the server starts on the current thread and blocks it.
I tried to create a new thread with tokio::spawn
:
_ = Abortable::new(tokio::spawn(async move {
let addr = "127.0.0.1:9001".parse().unwrap();
let inventory = InMemoryInventory::default();
build(inventory).unwrap().serve(addr).await
}), registration);
But then I get the following error:
error: future cannot be sent between threads safely
--> svc-store/src/main.rs:36:37
|
36 | _ = Abortable::new(tokio::spawn(async move {
| _____________________________________^
37 | | let addr = "127.0.0.1:9001".parse().unwrap();
38 | | let inventory = InMemoryInventory::default();
39 | | build(inventory).unwrap().serve(addr).await
40 | | }), registration);
| |_____^ future created by async block is not `Send`
|
= help: the trait `std::marker::Send` is not implemented for `dyn std::error::Error`
Which sends me even deeper down the rabbit hole of difficult terms. I just want to spin up a background task with dependencies that can be safely stored in that background thread, no need for any thread-to-thread communication.
Questions
- What is the easiest way to achieve what I need?
- Why doesn't the second approach work how a "Go programmer" would expect?