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. # It is not intended for manual editing.
version = 3 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]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.70" version = "1.0.70"
@ -122,6 +131,18 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 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]] [[package]]
name = "either" name = "either"
version = "1.8.1" version = "1.8.1"
@ -164,12 +185,27 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" 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]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "fragile"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa"
[[package]] [[package]]
name = "futures" name = "futures"
version = "0.3.28" version = "0.3.28"
@ -276,6 +312,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"async-stream", "async-stream",
"futures", "futures",
"mockall",
"prost", "prost",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
@ -492,12 +529,54 @@ dependencies = [
"windows-sys 0.45.0", "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]] [[package]]
name = "multimap" name = "multimap"
version = "0.8.3" version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" 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]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.17.1" version = "1.17.1"
@ -558,6 +637,36 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 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]] [[package]]
name = "prettyplease" name = "prettyplease"
version = "0.1.25" version = "0.1.25"
@ -685,6 +794,8 @@ version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
dependencies = [ dependencies = [
"aho-corasick",
"memchr",
"regex-syntax", "regex-syntax",
] ]
@ -780,6 +891,12 @@ dependencies = [
"windows-sys 0.45.0", "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]] [[package]]
name = "tokio" name = "tokio"
version = "1.28.0" version = "1.28.0"

View File

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

View File

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