Add mocking check support

This commit is contained in:
Matteo Settenvini 2023-05-02 16:54:26 +02:00
parent 6dc58f998c
commit b2ecb115c0
Signed by: matteo
GPG Key ID: CCF27A3AD054D593
3 changed files with 167 additions and 19 deletions

117
Cargo.lock generated
View File

@ -2,6 +2,15 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
dependencies = [
"memchr",
]
[[package]]
name = "anyhow"
version = "1.0.70"
@ -122,6 +131,18 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "difflib"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
[[package]]
name = "downcast"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1"
[[package]]
name = "either"
version = "1.8.1"
@ -164,12 +185,27 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "float-cmp"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
dependencies = [
"num-traits",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "fragile"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa"
[[package]]
name = "futures"
version = "0.3.28"
@ -276,6 +312,7 @@ version = "0.1.0"
dependencies = [
"async-stream",
"futures",
"mockall",
"prost",
"tokio",
"tokio-stream",
@ -492,12 +529,54 @@ dependencies = [
"windows-sys 0.45.0",
]
[[package]]
name = "mockall"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96"
dependencies = [
"cfg-if",
"downcast",
"fragile",
"lazy_static",
"mockall_derive",
"predicates",
"predicates-tree",
]
[[package]]
name = "mockall_derive"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb"
dependencies = [
"cfg-if",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "multimap"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
[[package]]
name = "normalize-line-endings"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.17.1"
@ -558,6 +637,36 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "predicates"
version = "2.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd"
dependencies = [
"difflib",
"float-cmp",
"itertools",
"normalize-line-endings",
"predicates-core",
"regex",
]
[[package]]
name = "predicates-core"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174"
[[package]]
name = "predicates-tree"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf"
dependencies = [
"predicates-core",
"termtree",
]
[[package]]
name = "prettyplease"
version = "0.1.25"
@ -685,6 +794,8 @@ version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
@ -780,6 +891,12 @@ dependencies = [
"windows-sys 0.45.0",
]
[[package]]
name = "termtree"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
[[package]]
name = "tokio"
version = "1.28.0"

View File

@ -17,5 +17,8 @@ tokio-stream = { version = "0.1", features = ["sync"] }
tonic = { version = "0.9" }
prost = { version = "0.11" }
[dev-dependencies]
mockall = "0.11"
[build-dependencies]
tonic-build = { version = "0.9" }

View File

@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: 2023 Matteo Settenvini <matteo.settenvini@montecristosoftware.eu>
// SPDX-License-Identifier: AGPL-3.0-or-later
use tonic::transport::server::TcpIncoming;
mod pb {
tonic::include_proto!("package");
}
@ -14,7 +16,6 @@ use {
std::pin::Pin,
std::sync::{Arc, Mutex, Weak},
tokio::sync::broadcast,
tokio::task::JoinHandle,
tokio_stream::wrappers::BroadcastStream,
tonic::{Request, Response, Status, Streaming},
};
@ -24,18 +25,22 @@ const RENDEZVOUS: &'static str = "[::1]:10000";
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn Error>> {
let server_handle = run_server().await;
Ok(server_handle.await.unwrap()?)
let url = RENDEZVOUS.parse()?;
let incoming = tonic::transport::server::TcpIncoming::new(url, true, None)
.expect("Cannot bind server socket");
run_server(incoming).await?;
Ok(())
}
async fn run_server() -> JoinHandle<Result<(), tonic::transport::Error>> {
fn run_server(
incoming: TcpIncoming,
) -> impl std::future::Future<Output = Result<(), tonic::transport::Error>> {
use tonic::transport::Server;
let addr = RENDEZVOUS.parse().unwrap();
let service = MessageService::default();
let svc_adapter = ServiceServer::new(service);
let builder = Server::builder().add_service(svc_adapter);
tokio::spawn(builder.serve(addr))
builder.serve_with_incoming(incoming)
}
#[derive(Default, Debug)]
@ -94,24 +99,32 @@ mod test {
super::pb::service_client::ServiceClient,
super::run_server,
super::{Message, RENDEZVOUS},
mockall::automock,
tonic::Request,
};
#[automock]
trait MessageChecker {
fn check_contents(&self, msg: &Message);
}
#[tokio::test]
async fn bidi_streaming() -> Result<(), Box<dyn std::error::Error>> {
let _server_handle = run_server().await;
let url = RENDEZVOUS.parse()?;
let incoming = tonic::transport::server::TcpIncoming::new(url, true, None)
.expect("Cannot bind server socket");
tokio::spawn(run_server(incoming));
// FIXME: avoid sleep waiting for server to start
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
const N_MESSAGES: usize = 20;
const CLIENT_IDS: &[&str] = &["client AAA", "client BBB", "client CCC"];
async fn client(id: &str) {
let mut client = ServiceClient::connect(format!("http://{}", RENDEZVOUS))
.await
.unwrap();
async fn client(id: &str, mock: &MockMessageChecker) {
let addr = format!("http://{}", RENDEZVOUS);
let mut client = ServiceClient::connect(addr).await.unwrap();
let client_id = String::from(id);
let outbound = async_stream::stream! {
for i in 1..21 {
for i in 1..(N_MESSAGES+1) {
tokio::task::yield_now().await;
let message = Message {
contents: format!("{}: {}", client_id, i),
@ -123,16 +136,31 @@ mod test {
let response = client.broadcaster(Request::new(outbound)).await.unwrap();
let mut inbound = response.into_inner();
while let Some(msg) = inbound.message().await.unwrap() {
println!("{} received {:?}", id, msg);
mock.check_contents(&msg);
}
}
let c1 = client("client AAA");
let c2 = client("client BBB");
let c3 = client("client CCC");
let _ = tokio::join!(c1, c2, c3);
let mut mock = MockMessageChecker::new();
let mut clients = vec![];
for client_id in CLIENT_IDS.iter() {
for i in 1..(N_MESSAGES + 1) {
let expected = format!("{}: {}", client_id, i);
mock.expect_check_contents()
.times(CLIENT_IDS.len())
.withf(move |msg| msg.contents == expected)
.return_const(());
}
}
for client_id in CLIENT_IDS {
let c = client(client_id, &mock);
clients.push(c);
}
let _ = futures::future::join_all(clients).await;
println!("Exiting.");
Ok(())
}