From 12735737349823881b25672575fddf89037dde72 Mon Sep 17 00:00:00 2001 From: ethanoroshiba Date: Fri, 27 Sep 2024 08:26:49 -0500 Subject: [PATCH 1/6] Restructure and add mock grpc tests --- Cargo.lock | 1 + crates/astria-grpc-mock-test/Cargo.toml | 1 + .../astria-grpc-mock-test/proto/health.proto | 3 +- .../src/generated/grpc.health.v1.rs | 13 +- .../src/generated/grpc.health.v1.serde.rs | 17 ++ .../src/generated/grpc_health_v1.bin | Bin 2875 -> 2950 bytes .../tests/health/main.rs | 271 +----------------- .../tests/health/test_matcher.rs | 150 ++++++++++ .../tests/health/test_mock.rs | 144 ++++++++++ .../tests/health/test_response.rs | 137 +++++++++ .../tests/health/test_utils.rs | 77 +++++ 11 files changed, 544 insertions(+), 270 deletions(-) create mode 100644 crates/astria-grpc-mock-test/tests/health/test_matcher.rs create mode 100644 crates/astria-grpc-mock-test/tests/health/test_mock.rs create mode 100644 crates/astria-grpc-mock-test/tests/health/test_response.rs create mode 100644 crates/astria-grpc-mock-test/tests/health/test_utils.rs diff --git a/Cargo.lock b/Cargo.lock index 2559a1f088..8488fa2260 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -761,6 +761,7 @@ name = "astria-grpc-mock-test" version = "0.1.0" dependencies = [ "astria-grpc-mock", + "futures", "prost", "serde", "tokio", diff --git a/crates/astria-grpc-mock-test/Cargo.toml b/crates/astria-grpc-mock-test/Cargo.toml index 75193f992e..3077c5501c 100644 --- a/crates/astria-grpc-mock-test/Cargo.toml +++ b/crates/astria-grpc-mock-test/Cargo.toml @@ -15,6 +15,7 @@ tokio = { workspace = true, features = [ "time", ] } tonic.workspace = true +futures = { workspace = true } [dev-dependencies] astria-grpc-mock = { path = "../astria-grpc-mock" } diff --git a/crates/astria-grpc-mock-test/proto/health.proto b/crates/astria-grpc-mock-test/proto/health.proto index 38843ff1e7..a0d21fecd8 100644 --- a/crates/astria-grpc-mock-test/proto/health.proto +++ b/crates/astria-grpc-mock-test/proto/health.proto @@ -26,7 +26,8 @@ option java_outer_classname = "HealthProto"; option java_package = "io.grpc.health.v1"; message HealthCheckRequest { - string service = 1; + string name = 1; + string service = 2; } message HealthCheckResponse { diff --git a/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.rs b/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.rs index b0d329d3c4..984a63a610 100644 --- a/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.rs +++ b/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.rs @@ -1,14 +1,20 @@ +// This file is @generated by prost-build. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct HealthCheckRequest { #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] pub service: ::prost::alloc::string::String, } impl ::prost::Name for HealthCheckRequest { const NAME: &'static str = "HealthCheckRequest"; const PACKAGE: &'static str = "grpc.health.v1"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("grpc.health.v1.{}", Self::NAME) + "grpc.health.v1.HealthCheckRequest".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/grpc.health.v1.HealthCheckRequest".into() } } #[allow(clippy::derive_partial_eq_without_eq)] @@ -67,7 +73,10 @@ impl ::prost::Name for HealthCheckResponse { const NAME: &'static str = "HealthCheckResponse"; const PACKAGE: &'static str = "grpc.health.v1"; fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("grpc.health.v1.{}", Self::NAME) + "grpc.health.v1.HealthCheckResponse".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/grpc.health.v1.HealthCheckResponse".into() } } /// Generated client implementations. diff --git a/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.serde.rs b/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.serde.rs index cbfbf6c6f1..5202e6d7a7 100644 --- a/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.serde.rs +++ b/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.serde.rs @@ -6,10 +6,16 @@ impl serde::Serialize for HealthCheckRequest { { use serde::ser::SerializeStruct; let mut len = 0; + if !self.name.is_empty() { + len += 1; + } if !self.service.is_empty() { len += 1; } let mut struct_ser = serializer.serialize_struct("grpc.health.v1.HealthCheckRequest", len)?; + if !self.name.is_empty() { + struct_ser.serialize_field("name", &self.name)?; + } if !self.service.is_empty() { struct_ser.serialize_field("service", &self.service)?; } @@ -23,11 +29,13 @@ impl<'de> serde::Deserialize<'de> for HealthCheckRequest { D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ + "name", "service", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { + Name, Service, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -50,6 +58,7 @@ impl<'de> serde::Deserialize<'de> for HealthCheckRequest { E: serde::de::Error, { match value { + "name" => Ok(GeneratedField::Name), "service" => Ok(GeneratedField::Service), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } @@ -70,9 +79,16 @@ impl<'de> serde::Deserialize<'de> for HealthCheckRequest { where V: serde::de::MapAccess<'de>, { + let mut name__ = None; let mut service__ = None; while let Some(k) = map_.next_key()? { match k { + GeneratedField::Name => { + if name__.is_some() { + return Err(serde::de::Error::duplicate_field("name")); + } + name__ = Some(map_.next_value()?); + } GeneratedField::Service => { if service__.is_some() { return Err(serde::de::Error::duplicate_field("service")); @@ -82,6 +98,7 @@ impl<'de> serde::Deserialize<'de> for HealthCheckRequest { } } Ok(HealthCheckRequest { + name: name__.unwrap_or_default(), service: service__.unwrap_or_default(), }) } diff --git a/crates/astria-grpc-mock-test/src/generated/grpc_health_v1.bin b/crates/astria-grpc-mock-test/src/generated/grpc_health_v1.bin index 52efb312248858eba8a5024742a401fbc68b32fb..edeb21622df9661bd6c51c1b88774c970a68529d 100644 GIT binary patch delta 664 zcmYk4%TB^T6oxx9hbbHoa6rHdrrbm^^%{4kCTgN?OmyYSsHBSVj>yiiF!2R=2lvLM z@8Hs<&*GVpR(A9Czx2#$KXb47BYeqYqUE1ncUlYg?YqvMN?x?@n+ti*EDyxrA==Dh2LBrZG>ydf`jc5{&5u~}l%e{#MXYbtUw0x&+zIflY z_vc+1*Hzj?6S#@#fT-A=TgM-18mP|uc(YH$nimH29=tHXXi9HxaS&$|_m;NKr1=3? Fe*spkMJ@mU delta 570 zcmYjPyG{Z@6y13+ix&hOkf+M>5JTL6#277H8cftuD=VUsRg5MeAT6Dx%@)SbFf=Cq zfL~zgKllZDXMu%o&z{HJduE;!pYbF3h(oY#Up)-km2R&y=*ZA*uiL6je+_H-3dj?e zZ?x^!{ki@4WcLR$33}h|4ewfZlDuDhlciS)Lh==@KHjItjI28&Uy7_cZ@$bZ0zvR` zjcQD?wP*|gC?}F+Q, - local_addr: SocketAddr, - mocked: astria_grpc_mock::MockServer, -} - -async fn start_mock_server() -> MockServer { - let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); - let local_addr = listener.local_addr().unwrap(); - let mock_server = astria_grpc_mock::MockServer::new(); - let server = tokio::spawn({ - let mock_server = mock_server.clone(); - async move { - let _ = Server::builder() - .add_service(HealthServer::new(HealthService { - mock_server, - })) - .serve_with_incoming(TcpListenerStream::new(listener)) - .await; - } - }); - MockServer { - _server: server, - local_addr, - mocked: mock_server, - } -} - -struct HealthService { - mock_server: astria_grpc_mock::MockServer, -} - -#[tonic::async_trait] -impl Health for HealthService { - type WatchStream = - Pin> + Send + 'static>>; - - async fn check( - self: Arc, - request: Request, - ) -> Result, Status> { - self.mock_server.handle_request("check", request).await - } - - async fn watch( - self: Arc, - _request: Request, - ) -> Result, Status> { - unimplemented!() - } -} - -#[tokio::test] -async fn default_response_works() { - let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) - .await - .unwrap(); - let mock = Mock::for_rpc_given("check", matcher::message_type::()) - .respond_with(response::default_response::()); - server.mocked.register(mock).await; - let rsp = client - .check(HealthCheckRequest { - service: "helloworld".to_string(), - }) - .await - .unwrap(); - assert_eq!(&HealthCheckResponse::default(), rsp.get_ref()); -} - -#[tokio::test] -async fn constant_response_works() { - let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) - .await - .unwrap(); - let expected_response = HealthCheckResponse { - status: 1, - }; - let mock = Mock::for_rpc_given("check", matcher::message_type::()) - .respond_with(response::constant_response(expected_response.clone())); - server.mocked.register(mock).await; - let rsp = client - .check(HealthCheckRequest { - service: "helloworld".to_string(), - }) - .await - .unwrap(); - assert_eq!(&expected_response, rsp.get_ref()); -} - -#[tokio::test] -async fn constant_response_expect_two_works() { - let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) - .await - .unwrap(); - let expected_response = HealthCheckResponse { - status: 1, - }; - let mock = Mock::for_rpc_given("check", matcher::message_type::()) - .respond_with(response::constant_response(expected_response.clone())) - .expect(2); - - let guard = server.mocked.register_as_scoped(mock).await; - let two_checks = async move { - let res_one = client - .check(HealthCheckRequest { - service: "helloworld".to_string(), - }) - .await?; - - let res_two = client - .check(HealthCheckRequest { - service: "helloworld".to_string(), - }) - .await?; - Ok::<_, tonic::Status>((res_one, res_two)) - }; - - let ((), res) = join!(guard.wait_until_satisfied(), two_checks); - let res = res.unwrap(); - assert_eq!(&expected_response, res.0.get_ref()); - assert_eq!(&expected_response, res.1.get_ref()); -} - -#[tokio::test] -async fn constant_response_guard_works() { - let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) - .await - .unwrap(); - let expected_response = HealthCheckResponse { - status: 1, - }; - let mock = Mock::for_rpc_given("check", matcher::message_type::()) - .respond_with(response::constant_response(expected_response.clone())) - .expect(1); - - let guard = server.mocked.register_as_scoped(mock).await; - let check = client.check(HealthCheckRequest { - service: "helloworld".to_string(), - }); - - let ((), check_res) = join!(guard.wait_until_satisfied(), check); - let rsp = check_res.unwrap(); - assert_eq!(&expected_response, rsp.get_ref()); -} - -#[tokio::test] -async fn exact_pbjson_match_works() { - let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) - .await - .unwrap(); - let expected_request = HealthCheckRequest { - service: "helloworld".to_string(), - }; - let expected_response = HealthCheckResponse { - status: 1, - }; - let mock = Mock::for_rpc_given("check", matcher::message_exact_pbjson(&expected_request)) - .respond_with(response::constant_response(expected_response.clone())); - server.mocked.register(mock).await; - let rsp = client - .check(HealthCheckRequest { - service: "helloworld".to_string(), - }) - .await - .unwrap(); - assert_eq!(&expected_response, rsp.get_ref()); -} - -#[tokio::test] -async fn partial_pbjson_match_works() { - let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) - .await - .unwrap(); - let expected_request = HealthCheckRequest { - service: "helloworld".to_string(), - }; - let expected_response = HealthCheckResponse { - status: 1, - }; - // FIXME: Right now this is equivalent to an exact check because the request only has one field. - let mock = Mock::for_rpc_given("check", matcher::message_partial_pbjson(&expected_request)) - .respond_with(response::constant_response(expected_response.clone())); - server.mocked.register(mock).await; - let rsp = client - .check(HealthCheckRequest { - service: "helloworld".to_string(), - }) - .await - .unwrap(); - assert_eq!(&expected_response, rsp.get_ref()); -} - -#[tokio::test] -#[should_panic] -async fn incorrect_mock_response_fails_server() { - let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) - .await - .unwrap(); - let mock = Mock::for_rpc_given("check", matcher::message_type::()) - .respond_with(response::default_response::()); - server.mocked.register(mock).await; - let _ = client - .check(HealthCheckRequest { - service: "helloworld".to_string(), - }) - .await; -} - -#[tokio::test] -#[should_panic] -async fn incorrect_mock_response_fails_guard() { - let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) - .await - .unwrap(); - let mock = Mock::for_rpc_given("check", matcher::message_type::()) - .respond_with(response::default_response::()); - - let guard = server.mocked.register_as_scoped(mock).await; - let check = client.check(HealthCheckRequest { - service: "helloworld".to_string(), - }); - - let _ = join!(guard.wait_until_satisfied(), check); -} +mod test_matcher; +mod test_mock; +mod test_response; +mod test_utils; diff --git a/crates/astria-grpc-mock-test/tests/health/test_matcher.rs b/crates/astria-grpc-mock-test/tests/health/test_matcher.rs new file mode 100644 index 0000000000..47baed3e1d --- /dev/null +++ b/crates/astria-grpc-mock-test/tests/health/test_matcher.rs @@ -0,0 +1,150 @@ +use astria_grpc_mock::{ + matcher, + response, + Mock, +}; +use astria_grpc_mock_test::health::{ + health_client::HealthClient, + HealthCheckRequest, + HealthCheckResponse, +}; + +use crate::test_utils::start_mock_server; + +#[tokio::test] +async fn exact_pbjson_match_works() { + let server = start_mock_server().await; + let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + .await + .unwrap(); + let expected_request = HealthCheckRequest { + name: "helloworld".to_string(), + service: "helloworld".to_string(), + }; + let expected_response = HealthCheckResponse { + status: 1, + }; + let mock = Mock::for_rpc_given("check", matcher::message_exact_pbjson(&expected_request)) + .respond_with(response::constant_response(expected_response.clone())); + server.mocked.register(mock).await; + let rsp = client + .check(HealthCheckRequest { + name: "helloworld".to_string(), + service: "helloworld".to_string(), + }) + .await + .unwrap(); + assert_eq!(&expected_response, rsp.get_ref()); +} + +#[tokio::test] +async fn partial_pbjson_match_works() { + let server = start_mock_server().await; + let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + .await + .unwrap(); + let expected_request = HealthCheckRequest { + name: "helloworld".to_string(), + service: String::new(), + }; + let expected_response = HealthCheckResponse { + status: 1, + }; + let mock = Mock::for_rpc_given("check", matcher::message_partial_pbjson(&expected_request)) + .respond_with(response::constant_response(expected_response.clone())); + server.mocked.register(mock).await; + let rsp = client + .check(HealthCheckRequest { + name: "helloworld".to_string(), + service: "helloworld".to_string(), + }) + .await + .unwrap(); + assert_eq!(&expected_response, rsp.get_ref()); +} + +#[tokio::test] +async fn and_combinator_works_with_partial_pbjson() { + let server = start_mock_server().await; + let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + .await + .unwrap(); + let expected_request_1 = HealthCheckRequest { + name: "helloworld".to_string(), + service: String::new(), + }; + let expected_request_2 = HealthCheckRequest { + name: String::new(), + service: "helloworld".to_string(), + }; + let expected_response = HealthCheckResponse { + status: 1, + }; + let mock = Mock::for_rpc_given( + "check", + matcher::message_partial_pbjson(&expected_request_1), + ) + .and(matcher::message_partial_pbjson(&expected_request_2)) + .respond_with(response::constant_response(expected_response.clone())); + server.mocked.register(mock).await; + let rsp = client + .check(HealthCheckRequest { + name: "helloworld".to_string(), + service: "helloworld".to_string(), + }) + .await + .unwrap(); + assert_eq!(&expected_response, rsp.get_ref()); +} + +#[tokio::test] +async fn exact_pbjson_matcher_doesnt_match_incorrect_request() { + let server = start_mock_server().await; + let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + .await + .unwrap(); + let expected_request = HealthCheckRequest { + name: "helloworld".to_string(), + service: "helloworld".to_string(), + }; + let expected_response = HealthCheckResponse { + status: 1, + }; + let mock = Mock::for_rpc_given("check", matcher::message_exact_pbjson(&expected_request)) + .respond_with(response::constant_response(expected_response.clone())); + server.mocked.register(mock).await; + let err_rsp = client + .check(HealthCheckRequest { + name: "helloworld_wrong".to_string(), + service: "helloworld_wrong".to_string(), + }) + .await + .unwrap_err(); + assert_eq!(err_rsp.code(), tonic::Code::NotFound); +} + +#[tokio::test] +async fn partial_pbjson_match_doesnt_match_incorrect_request() { + let server = start_mock_server().await; + let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + .await + .unwrap(); + let expected_request = HealthCheckRequest { + name: "helloworld".to_string(), + service: String::new(), + }; + let expected_response = HealthCheckResponse { + status: 1, + }; + let mock = Mock::for_rpc_given("check", matcher::message_partial_pbjson(&expected_request)) + .respond_with(response::constant_response(expected_response.clone())); + server.mocked.register(mock).await; + let err_rsp = client + .check(HealthCheckRequest { + name: "helloworld_wrong".to_string(), + service: "helloworld".to_string(), + }) + .await + .unwrap_err(); + assert_eq!(err_rsp.code(), tonic::Code::NotFound); +} diff --git a/crates/astria-grpc-mock-test/tests/health/test_mock.rs b/crates/astria-grpc-mock-test/tests/health/test_mock.rs new file mode 100644 index 0000000000..da897a6e2f --- /dev/null +++ b/crates/astria-grpc-mock-test/tests/health/test_mock.rs @@ -0,0 +1,144 @@ +use astria_grpc_mock::{ + matcher, + response, + Mock, +}; +use astria_grpc_mock_test::health::{ + health_client::HealthClient, + HealthCheckRequest, + HealthCheckResponse, +}; +use tokio::join; + +use crate::test_utils::start_mock_server; + +#[tokio::test] +async fn mock_expect_two_works() { + let server = start_mock_server().await; + let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + .await + .unwrap(); + let expected_response = HealthCheckResponse { + status: 1, + }; + let mock = Mock::for_rpc_given("check", matcher::message_type::()) + .respond_with(response::constant_response(expected_response.clone())) + .expect(2); + + let guard = server.mocked.register_as_scoped(mock).await; + let two_checks = async move { + let res_one = client + .check(HealthCheckRequest { + name: "helloworld".to_string(), + service: "helloworld".to_string(), + }) + .await?; + + let res_two = client + .check(HealthCheckRequest { + name: "helloworld".to_string(), + service: "helloworld".to_string(), + }) + .await?; + Ok::<_, tonic::Status>((res_one, res_two)) + }; + + let ((), res) = join!(guard.wait_until_satisfied(), two_checks); + let res = res.unwrap(); + assert_eq!(&expected_response, res.0.get_ref()); + assert_eq!(&expected_response, res.1.get_ref()); +} + +#[tokio::test] +async fn response_guard_wait_until_satisfied_works() { + let server = start_mock_server().await; + let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + .await + .unwrap(); + let expected_response = HealthCheckResponse { + status: 1, + }; + let mock = Mock::for_rpc_given("check", matcher::message_type::()) + .respond_with(response::constant_response(expected_response.clone())) + .expect(1); + + let guard = server.mocked.register_as_scoped(mock).await; + let check = client.check(HealthCheckRequest { + name: "helloworld".to_string(), + service: "helloworld".to_string(), + }); + + let ((), check_res) = join!(guard.wait_until_satisfied(), check); + let rsp = check_res.unwrap(); + assert_eq!(&expected_response, rsp.get_ref()); +} + +#[tokio::test] +async fn up_to_n_times_works_as_expected() { + let server = start_mock_server().await; + let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + .await + .unwrap(); + let expected_response = HealthCheckResponse { + status: 1, + }; + let mock = Mock::for_rpc_given("check", matcher::message_type::()) + .respond_with(response::constant_response(expected_response.clone())) + .up_to_n_times(1); + + let guard = server.mocked.register_as_scoped(mock).await; + let check = client.check(HealthCheckRequest { + name: "helloworld".to_string(), + service: "helloworld".to_string(), + }); + + let ((), check_res) = join!(guard.wait_until_satisfied(), check); + let rsp = check_res.unwrap(); + assert_eq!(&expected_response, rsp.get_ref()); + + let err_rsp = client + .check(HealthCheckRequest { + name: "helloworld".to_string(), + service: "helloworld".to_string(), + }) + .await + .unwrap_err(); + assert_eq!(err_rsp.code(), tonic::Code::NotFound); +} + +#[tokio::test] +#[should_panic] +async fn incorrect_mock_response_fails_server() { + let server = start_mock_server().await; + let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + .await + .unwrap(); + let mock = Mock::for_rpc_given("check", matcher::message_type::()) + .respond_with(response::default_response::()); + server.mocked.register(mock).await; + let _ = client + .check(HealthCheckRequest { + name: "helloworld".to_string(), + service: "helloworld".to_string(), + }) + .await; +} + +#[tokio::test] +#[should_panic] +async fn incorrect_mock_response_fails_guard() { + let server = start_mock_server().await; + let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + .await + .unwrap(); + let mock = Mock::for_rpc_given("check", matcher::message_type::()) + .respond_with(response::default_response::()); + + let guard = server.mocked.register_as_scoped(mock).await; + let check = client.check(HealthCheckRequest { + name: "helloworld".to_string(), + service: "helloworld".to_string(), + }); + + let _ = join!(guard.wait_until_satisfied(), check); +} diff --git a/crates/astria-grpc-mock-test/tests/health/test_response.rs b/crates/astria-grpc-mock-test/tests/health/test_response.rs new file mode 100644 index 0000000000..2653f634d8 --- /dev/null +++ b/crates/astria-grpc-mock-test/tests/health/test_response.rs @@ -0,0 +1,137 @@ +use std::time::Duration; + +use astria_grpc_mock::{ + matcher, + response, + Mock, +}; +use astria_grpc_mock_test::health::{ + health_client::HealthClient, + HealthCheckRequest, + HealthCheckResponse, +}; +use futures::future::join; +use tokio::time::timeout; + +use crate::test_utils::start_mock_server; + +#[tokio::test] +async fn default_response_works() { + let server = start_mock_server().await; + let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + .await + .unwrap(); + let mock = Mock::for_rpc_given("check", matcher::message_type::()) + .respond_with(response::default_response::()); + server.mocked.register(mock).await; + let rsp = client + .check(HealthCheckRequest { + name: "helloworld".to_string(), + service: "helloworld".to_string(), + }) + .await + .unwrap(); + assert_eq!(&HealthCheckResponse::default(), rsp.get_ref()); +} + +#[tokio::test] +async fn constant_response_works() { + let server = start_mock_server().await; + let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + .await + .unwrap(); + let expected_response = HealthCheckResponse { + status: 1, + }; + let mock = Mock::for_rpc_given("check", matcher::message_type::()) + .respond_with(response::constant_response(expected_response.clone())); + server.mocked.register(mock).await; + let rsp = client + .check(HealthCheckRequest { + name: "helloworld".to_string(), + service: "helloworld".to_string(), + }) + .await + .unwrap(); + assert_eq!(&expected_response, rsp.get_ref()); +} + +#[tokio::test] +async fn dynamic_response_works() { + let server = start_mock_server().await; + let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + .await + .unwrap(); + let mut expected_response = HealthCheckResponse { + status: 1, + }; + + let mock = Mock::for_rpc_given("check", matcher::message_type::()) + .respond_with(response::dynamic_response(dynamic_responder)); + server.mocked.register(mock).await; + let rsp_1 = client + .check(HealthCheckRequest { + name: "helloworld".to_string(), + service: "1".to_string(), + }) + .await + .unwrap(); + assert_eq!(&expected_response, rsp_1.get_ref()); + + expected_response.status = 2; + let rsp_2 = client + .check(HealthCheckRequest { + name: "helloworld".to_string(), + service: "2".to_string(), + }) + .await + .unwrap(); + assert_eq!(&expected_response, rsp_2.get_ref()); +} + +fn dynamic_responder(request: &HealthCheckRequest) -> HealthCheckResponse { + let status_return = request + .service + .chars() + .next() + .unwrap() + .to_digit(10) + .unwrap(); + HealthCheckResponse { + status: i32::try_from(status_return).unwrap(), + } +} + +#[tokio::test] +async fn response_delay_works_as_expected() { + let server = start_mock_server().await; + let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + .await + .unwrap(); + let mock = Mock::for_rpc_given("check", matcher::message_type::()) + .respond_with( + response::default_response::() + .set_delay(Duration::from_millis(250)), + ); + let mock_guard = server.mocked.register_as_scoped(mock).await; + let rsp_fut = client.check(HealthCheckRequest { + name: "helloworld".to_string(), + service: "helloworld".to_string(), + }); + + timeout( + Duration::from_millis(250), + join(mock_guard.wait_until_satisfied(), rsp_fut), + ) + .await + .unwrap_err(); + + let rsp = client + .check(HealthCheckRequest { + name: "helloworld".to_string(), + service: "helloworld".to_string(), + }) + .await + .unwrap(); + assert_eq!(&HealthCheckResponse::default(), rsp.get_ref()); +} diff --git a/crates/astria-grpc-mock-test/tests/health/test_utils.rs b/crates/astria-grpc-mock-test/tests/health/test_utils.rs new file mode 100644 index 0000000000..442ca83be1 --- /dev/null +++ b/crates/astria-grpc-mock-test/tests/health/test_utils.rs @@ -0,0 +1,77 @@ +use std::{ + net::SocketAddr, + pin::Pin, + sync::Arc, +}; + +use astria_grpc_mock_test::health::{ + health_server::{ + Health, + HealthServer, + }, + HealthCheckRequest, + HealthCheckResponse, +}; +use tokio::task::JoinHandle; +use tokio_stream::{ + wrappers::TcpListenerStream, + Stream, +}; +use tonic::{ + transport::Server, + Request, + Response, + Status, +}; + +pub(crate) struct MockServer { + _server: JoinHandle<()>, + pub(crate) local_addr: SocketAddr, + pub(crate) mocked: astria_grpc_mock::MockServer, +} + +pub(crate) async fn start_mock_server() -> MockServer { + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let local_addr = listener.local_addr().unwrap(); + let mock_server = astria_grpc_mock::MockServer::new(); + let server = tokio::spawn({ + let mock_server = mock_server.clone(); + async move { + let _ = Server::builder() + .add_service(HealthServer::new(HealthService { + mock_server, + })) + .serve_with_incoming(TcpListenerStream::new(listener)) + .await; + } + }); + MockServer { + _server: server, + local_addr, + mocked: mock_server, + } +} + +struct HealthService { + mock_server: astria_grpc_mock::MockServer, +} + +#[tonic::async_trait] +impl Health for HealthService { + type WatchStream = + Pin> + Send + 'static>>; + + async fn check( + self: Arc, + request: Request, + ) -> Result, Status> { + self.mock_server.handle_request("check", request).await + } + + async fn watch( + self: Arc, + _request: Request, + ) -> Result, Status> { + unimplemented!() + } +} From 4b15aa0b66ce6fc4181630618038418860396d35 Mon Sep 17 00:00:00 2001 From: ethanoroshiba Date: Fri, 4 Oct 2024 14:58:02 -0500 Subject: [PATCH 2/6] Requested changes --- Cargo.lock | 1 + crates/astria-grpc-mock-test/Cargo.toml | 1 + .../astria-grpc-mock-test/proto/health.proto | 3 +- .../src/generated/grpc.health.v1.rs | 2 - .../src/generated/grpc.health.v1.serde.rs | 17 ----- .../src/generated/grpc_health_v1.bin | Bin 2950 -> 2875 bytes .../tests/health/test_matcher.rs | 65 ++++++++---------- .../tests/health/test_mock.rs | 7 -- .../tests/health/test_response.rs | 41 +++++------ .../tests/health/test_utils.rs | 43 ++++++++++++ 10 files changed, 91 insertions(+), 89 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8488fa2260..21eb05ba10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -764,6 +764,7 @@ dependencies = [ "futures", "prost", "serde", + "serde_json", "tokio", "tokio-stream", "tonic 0.10.2", diff --git a/crates/astria-grpc-mock-test/Cargo.toml b/crates/astria-grpc-mock-test/Cargo.toml index 3077c5501c..df96cd6181 100644 --- a/crates/astria-grpc-mock-test/Cargo.toml +++ b/crates/astria-grpc-mock-test/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] prost.workspace = true serde.workspace = true +serde_json.workspace = true tokio = { workspace = true, features = [ "macros", "rt", diff --git a/crates/astria-grpc-mock-test/proto/health.proto b/crates/astria-grpc-mock-test/proto/health.proto index a0d21fecd8..38843ff1e7 100644 --- a/crates/astria-grpc-mock-test/proto/health.proto +++ b/crates/astria-grpc-mock-test/proto/health.proto @@ -26,8 +26,7 @@ option java_outer_classname = "HealthProto"; option java_package = "io.grpc.health.v1"; message HealthCheckRequest { - string name = 1; - string service = 2; + string service = 1; } message HealthCheckResponse { diff --git a/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.rs b/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.rs index 984a63a610..85dd8cadff 100644 --- a/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.rs +++ b/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.rs @@ -3,8 +3,6 @@ #[derive(Clone, PartialEq, ::prost::Message)] pub struct HealthCheckRequest { #[prost(string, tag = "1")] - pub name: ::prost::alloc::string::String, - #[prost(string, tag = "2")] pub service: ::prost::alloc::string::String, } impl ::prost::Name for HealthCheckRequest { diff --git a/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.serde.rs b/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.serde.rs index 5202e6d7a7..cbfbf6c6f1 100644 --- a/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.serde.rs +++ b/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.serde.rs @@ -6,16 +6,10 @@ impl serde::Serialize for HealthCheckRequest { { use serde::ser::SerializeStruct; let mut len = 0; - if !self.name.is_empty() { - len += 1; - } if !self.service.is_empty() { len += 1; } let mut struct_ser = serializer.serialize_struct("grpc.health.v1.HealthCheckRequest", len)?; - if !self.name.is_empty() { - struct_ser.serialize_field("name", &self.name)?; - } if !self.service.is_empty() { struct_ser.serialize_field("service", &self.service)?; } @@ -29,13 +23,11 @@ impl<'de> serde::Deserialize<'de> for HealthCheckRequest { D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "name", "service", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - Name, Service, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -58,7 +50,6 @@ impl<'de> serde::Deserialize<'de> for HealthCheckRequest { E: serde::de::Error, { match value { - "name" => Ok(GeneratedField::Name), "service" => Ok(GeneratedField::Service), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } @@ -79,16 +70,9 @@ impl<'de> serde::Deserialize<'de> for HealthCheckRequest { where V: serde::de::MapAccess<'de>, { - let mut name__ = None; let mut service__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::Name => { - if name__.is_some() { - return Err(serde::de::Error::duplicate_field("name")); - } - name__ = Some(map_.next_value()?); - } GeneratedField::Service => { if service__.is_some() { return Err(serde::de::Error::duplicate_field("service")); @@ -98,7 +82,6 @@ impl<'de> serde::Deserialize<'de> for HealthCheckRequest { } } Ok(HealthCheckRequest { - name: name__.unwrap_or_default(), service: service__.unwrap_or_default(), }) } diff --git a/crates/astria-grpc-mock-test/src/generated/grpc_health_v1.bin b/crates/astria-grpc-mock-test/src/generated/grpc_health_v1.bin index edeb21622df9661bd6c51c1b88774c970a68529d..52efb312248858eba8a5024742a401fbc68b32fb 100644 GIT binary patch delta 570 zcmYjPyG{Z@6y13+ix&hOkf+M>5JTL6#277H8cftuD=VUsRg5MeAT6Dx%@)SbFf=Cq zfL~zgKllZDXMu%o&z{HJduE;!pYbF3h(oY#Up)-km2R&y=*ZA*uiL6je+_H-3dj?e zZ?x^!{ki@4WcLR$33}h|4ewfZlDuDhlciS)Lh==@KHjItjI28&Uy7_cZ@$bZ0zvR` zjcQD?wP*|gC?}F+QyiiF!2R=2lvLM z@8Hs<&*GVpR(A9Czx2#$KXb47BYeqYqUE1ncUlYg?YqvMN?x?@n+ti*EDyxrA==Dh2LBrZG>ydf`jc5{&5u~}l%e{#MXYbtUw0x&+zIflY z_vc+1*Hzj?6S#@#fT-A=TgM-18mP|uc(YH$nimH29=tHXXi9HxaS&$|_m;NKr1=3? Fe*spkMJ@mU diff --git a/crates/astria-grpc-mock-test/tests/health/test_matcher.rs b/crates/astria-grpc-mock-test/tests/health/test_matcher.rs index 47baed3e1d..f0d64f3475 100644 --- a/crates/astria-grpc-mock-test/tests/health/test_matcher.rs +++ b/crates/astria-grpc-mock-test/tests/health/test_matcher.rs @@ -9,7 +9,10 @@ use astria_grpc_mock_test::health::{ HealthCheckResponse, }; -use crate::test_utils::start_mock_server; +use crate::test_utils::{ + start_mock_server, + MockMessage, +}; #[tokio::test] async fn exact_pbjson_match_works() { @@ -18,7 +21,6 @@ async fn exact_pbjson_match_works() { .await .unwrap(); let expected_request = HealthCheckRequest { - name: "helloworld".to_string(), service: "helloworld".to_string(), }; let expected_response = HealthCheckResponse { @@ -29,7 +31,6 @@ async fn exact_pbjson_match_works() { server.mocked.register(mock).await; let rsp = client .check(HealthCheckRequest { - name: "helloworld".to_string(), service: "helloworld".to_string(), }) .await @@ -40,24 +41,20 @@ async fn exact_pbjson_match_works() { #[tokio::test] async fn partial_pbjson_match_works() { let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) - .await - .unwrap(); - let expected_request = HealthCheckRequest { - name: "helloworld".to_string(), - service: String::new(), + let expected_request = MockMessage { + field_one: "helloworld".to_string(), + field_two: String::new(), }; - let expected_response = HealthCheckResponse { - status: 1, + let expected_response = MockMessage { + field_one: "helloworld".to_string(), + field_two: "helloworld".to_string(), }; let mock = Mock::for_rpc_given("check", matcher::message_partial_pbjson(&expected_request)) .respond_with(response::constant_response(expected_response.clone())); server.mocked.register(mock).await; - let rsp = client - .check(HealthCheckRequest { - name: "helloworld".to_string(), - service: "helloworld".to_string(), - }) + let rsp = server + .mocked + .handle_request("check", tonic::Request::new(expected_response.clone())) .await .unwrap(); assert_eq!(&expected_response, rsp.get_ref()); @@ -66,19 +63,17 @@ async fn partial_pbjson_match_works() { #[tokio::test] async fn and_combinator_works_with_partial_pbjson() { let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) - .await - .unwrap(); - let expected_request_1 = HealthCheckRequest { - name: "helloworld".to_string(), - service: String::new(), + let expected_request_1 = MockMessage { + field_one: "helloworld".to_string(), + field_two: String::new(), }; - let expected_request_2 = HealthCheckRequest { - name: String::new(), - service: "helloworld".to_string(), + let expected_request_2 = MockMessage { + field_one: String::new(), + field_two: "helloworld".to_string(), }; - let expected_response = HealthCheckResponse { - status: 1, + let expected_response = MockMessage { + field_one: "helloworld".to_string(), + field_two: "helloworld".to_string(), }; let mock = Mock::for_rpc_given( "check", @@ -87,11 +82,9 @@ async fn and_combinator_works_with_partial_pbjson() { .and(matcher::message_partial_pbjson(&expected_request_2)) .respond_with(response::constant_response(expected_response.clone())); server.mocked.register(mock).await; - let rsp = client - .check(HealthCheckRequest { - name: "helloworld".to_string(), - service: "helloworld".to_string(), - }) + let rsp = server + .mocked + .handle_request("check", tonic::Request::new(expected_response.clone())) .await .unwrap(); assert_eq!(&expected_response, rsp.get_ref()); @@ -104,7 +97,6 @@ async fn exact_pbjson_matcher_doesnt_match_incorrect_request() { .await .unwrap(); let expected_request = HealthCheckRequest { - name: "helloworld".to_string(), service: "helloworld".to_string(), }; let expected_response = HealthCheckResponse { @@ -115,7 +107,6 @@ async fn exact_pbjson_matcher_doesnt_match_incorrect_request() { server.mocked.register(mock).await; let err_rsp = client .check(HealthCheckRequest { - name: "helloworld_wrong".to_string(), service: "helloworld_wrong".to_string(), }) .await @@ -130,8 +121,7 @@ async fn partial_pbjson_match_doesnt_match_incorrect_request() { .await .unwrap(); let expected_request = HealthCheckRequest { - name: "helloworld".to_string(), - service: String::new(), + service: "helloworld".to_string(), }; let expected_response = HealthCheckResponse { status: 1, @@ -141,8 +131,7 @@ async fn partial_pbjson_match_doesnt_match_incorrect_request() { server.mocked.register(mock).await; let err_rsp = client .check(HealthCheckRequest { - name: "helloworld_wrong".to_string(), - service: "helloworld".to_string(), + service: "hello".to_string(), }) .await .unwrap_err(); diff --git a/crates/astria-grpc-mock-test/tests/health/test_mock.rs b/crates/astria-grpc-mock-test/tests/health/test_mock.rs index da897a6e2f..e0ec35aecf 100644 --- a/crates/astria-grpc-mock-test/tests/health/test_mock.rs +++ b/crates/astria-grpc-mock-test/tests/health/test_mock.rs @@ -29,14 +29,12 @@ async fn mock_expect_two_works() { let two_checks = async move { let res_one = client .check(HealthCheckRequest { - name: "helloworld".to_string(), service: "helloworld".to_string(), }) .await?; let res_two = client .check(HealthCheckRequest { - name: "helloworld".to_string(), service: "helloworld".to_string(), }) .await?; @@ -64,7 +62,6 @@ async fn response_guard_wait_until_satisfied_works() { let guard = server.mocked.register_as_scoped(mock).await; let check = client.check(HealthCheckRequest { - name: "helloworld".to_string(), service: "helloworld".to_string(), }); @@ -88,7 +85,6 @@ async fn up_to_n_times_works_as_expected() { let guard = server.mocked.register_as_scoped(mock).await; let check = client.check(HealthCheckRequest { - name: "helloworld".to_string(), service: "helloworld".to_string(), }); @@ -98,7 +94,6 @@ async fn up_to_n_times_works_as_expected() { let err_rsp = client .check(HealthCheckRequest { - name: "helloworld".to_string(), service: "helloworld".to_string(), }) .await @@ -118,7 +113,6 @@ async fn incorrect_mock_response_fails_server() { server.mocked.register(mock).await; let _ = client .check(HealthCheckRequest { - name: "helloworld".to_string(), service: "helloworld".to_string(), }) .await; @@ -136,7 +130,6 @@ async fn incorrect_mock_response_fails_guard() { let guard = server.mocked.register_as_scoped(mock).await; let check = client.check(HealthCheckRequest { - name: "helloworld".to_string(), service: "helloworld".to_string(), }); diff --git a/crates/astria-grpc-mock-test/tests/health/test_response.rs b/crates/astria-grpc-mock-test/tests/health/test_response.rs index 2653f634d8..aa14fb07db 100644 --- a/crates/astria-grpc-mock-test/tests/health/test_response.rs +++ b/crates/astria-grpc-mock-test/tests/health/test_response.rs @@ -10,7 +10,6 @@ use astria_grpc_mock_test::health::{ HealthCheckRequest, HealthCheckResponse, }; -use futures::future::join; use tokio::time::timeout; use crate::test_utils::start_mock_server; @@ -26,7 +25,6 @@ async fn default_response_works() { server.mocked.register(mock).await; let rsp = client .check(HealthCheckRequest { - name: "helloworld".to_string(), service: "helloworld".to_string(), }) .await @@ -48,7 +46,6 @@ async fn constant_response_works() { server.mocked.register(mock).await; let rsp = client .check(HealthCheckRequest { - name: "helloworld".to_string(), service: "helloworld".to_string(), }) .await @@ -71,7 +68,6 @@ async fn dynamic_response_works() { server.mocked.register(mock).await; let rsp_1 = client .check(HealthCheckRequest { - name: "helloworld".to_string(), service: "1".to_string(), }) .await @@ -81,7 +77,6 @@ async fn dynamic_response_works() { expected_response.status = 2; let rsp_2 = client .check(HealthCheckRequest { - name: "helloworld".to_string(), service: "2".to_string(), }) .await @@ -105,7 +100,10 @@ fn dynamic_responder(request: &HealthCheckRequest) -> HealthCheckResponse { #[tokio::test] async fn response_delay_works_as_expected() { let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + let mut err_client = HealthClient::connect(format!("http://{}", server.local_addr)) + .await + .unwrap(); + let mut ok_client = HealthClient::connect(format!("http://{}", server.local_addr)) .await .unwrap(); let mock = Mock::for_rpc_given("check", matcher::message_type::()) @@ -113,25 +111,22 @@ async fn response_delay_works_as_expected() { response::default_response::() .set_delay(Duration::from_millis(250)), ); - let mock_guard = server.mocked.register_as_scoped(mock).await; - let rsp_fut = client.check(HealthCheckRequest { - name: "helloworld".to_string(), + mock.mount(&server.mocked).await; + + let rsp_fut_expect_err = err_client.check(HealthCheckRequest { + service: "helloworld".to_string(), + }); + let rsp_fut_expect_ok = ok_client.check(HealthCheckRequest { service: "helloworld".to_string(), }); - timeout( - Duration::from_millis(250), - join(mock_guard.wait_until_satisfied(), rsp_fut), - ) - .await - .unwrap_err(); - - let rsp = client - .check(HealthCheckRequest { - name: "helloworld".to_string(), - service: "helloworld".to_string(), - }) + timeout(Duration::from_millis(200), rsp_fut_expect_err) .await - .unwrap(); - assert_eq!(&HealthCheckResponse::default(), rsp.get_ref()); + .unwrap_err(); // should be error + let ok_rsp = timeout(Duration::from_millis(300), rsp_fut_expect_ok) + .await + .unwrap(); // should be ok + + assert!(ok_rsp.is_ok()); + assert_eq!(&HealthCheckResponse::default(), ok_rsp.unwrap().get_ref()); } diff --git a/crates/astria-grpc-mock-test/tests/health/test_utils.rs b/crates/astria-grpc-mock-test/tests/health/test_utils.rs index 442ca83be1..c96f7ba689 100644 --- a/crates/astria-grpc-mock-test/tests/health/test_utils.rs +++ b/crates/astria-grpc-mock-test/tests/health/test_utils.rs @@ -75,3 +75,46 @@ impl Health for HealthService { unimplemented!() } } + +#[derive(::prost::Message, Clone, PartialEq)] +pub(crate) struct MockMessage { + #[prost(string, tag = "1")] + pub(crate) field_one: String, + #[prost(string, tag = "2")] + pub(crate) field_two: String, +} + +impl ::prost::Name for MockMessage { + const NAME: &'static str = "MockMessage"; + const PACKAGE: &'static str = "test_utils"; + + fn full_name() -> String { + "test_utils.MockMessage".to_string() + } +} + +impl serde::Serialize for MockMessage { + #[allow(clippy::arithmetic_side_effects)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.field_one.is_empty() { + len += 1; + } + if !self.field_two.is_empty() { + len += 1; + } + let mut struct_ser = + serializer.serialize_struct("grpc.health.v1.HealthCheckRequest", len)?; + if !self.field_one.is_empty() { + struct_ser.serialize_field("field_one", &self.field_one)?; + } + if !self.field_two.is_empty() { + struct_ser.serialize_field("field_two", &self.field_two)?; + } + struct_ser.end() + } +} From ed9013b9f53b09d58991fe10f76bf0813e5e6373 Mon Sep 17 00:00:00 2001 From: ethanoroshiba Date: Fri, 4 Oct 2024 15:01:58 -0500 Subject: [PATCH 3/6] Fix changes from merge --- crates/astria-grpc-mock-test/tests/health/test_utils.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/astria-grpc-mock-test/tests/health/test_utils.rs b/crates/astria-grpc-mock-test/tests/health/test_utils.rs index c96f7ba689..91f8f1cf5f 100644 --- a/crates/astria-grpc-mock-test/tests/health/test_utils.rs +++ b/crates/astria-grpc-mock-test/tests/health/test_utils.rs @@ -94,7 +94,10 @@ impl ::prost::Name for MockMessage { } impl serde::Serialize for MockMessage { - #[allow(clippy::arithmetic_side_effects)] + #[expect( + clippy::arithmetic_side_effects, + reason = "will never overflow for test purposes" + )] fn serialize(&self, serializer: S) -> std::result::Result where S: serde::Serializer, From 8774037753e83a398679be045c8c3de8580d2a9e Mon Sep 17 00:00:00 2001 From: ethanoroshiba Date: Fri, 4 Oct 2024 15:04:20 -0500 Subject: [PATCH 4/6] removed dependency added for testing --- Cargo.lock | 1 - crates/astria-grpc-mock-test/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d62a18090b..e707998e12 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -760,7 +760,6 @@ dependencies = [ "futures", "prost", "serde", - "serde_json", "tokio", "tokio-stream", "tonic 0.10.2", diff --git a/crates/astria-grpc-mock-test/Cargo.toml b/crates/astria-grpc-mock-test/Cargo.toml index 58a2fe8804..3fa58798c5 100644 --- a/crates/astria-grpc-mock-test/Cargo.toml +++ b/crates/astria-grpc-mock-test/Cargo.toml @@ -9,7 +9,6 @@ rust-version = "1.81.0" [dependencies] prost.workspace = true serde.workspace = true -serde_json.workspace = true tokio = { workspace = true, features = [ "macros", "rt", From 7cebad88eda0aefc2efee0dce0dfbbf80a6d3ab4 Mon Sep 17 00:00:00 2001 From: ethanoroshiba Date: Tue, 15 Oct 2024 09:52:40 -0500 Subject: [PATCH 5/6] requested changes --- .../tests/health/test_response.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/astria-grpc-mock-test/tests/health/test_response.rs b/crates/astria-grpc-mock-test/tests/health/test_response.rs index aa14fb07db..2711317517 100644 --- a/crates/astria-grpc-mock-test/tests/health/test_response.rs +++ b/crates/astria-grpc-mock-test/tests/health/test_response.rs @@ -99,6 +99,9 @@ fn dynamic_responder(request: &HealthCheckRequest) -> HealthCheckResponse { #[tokio::test] async fn response_delay_works_as_expected() { + const DELAY: Duration = Duration::from_millis(250); + const FIFTY_MILLIS: Duration = Duration::from_millis(50); + let server = start_mock_server().await; let mut err_client = HealthClient::connect(format!("http://{}", server.local_addr)) .await @@ -107,10 +110,7 @@ async fn response_delay_works_as_expected() { .await .unwrap(); let mock = Mock::for_rpc_given("check", matcher::message_type::()) - .respond_with( - response::default_response::() - .set_delay(Duration::from_millis(250)), - ); + .respond_with(response::default_response::().set_delay(DELAY)); mock.mount(&server.mocked).await; let rsp_fut_expect_err = err_client.check(HealthCheckRequest { @@ -120,12 +120,12 @@ async fn response_delay_works_as_expected() { service: "helloworld".to_string(), }); - timeout(Duration::from_millis(200), rsp_fut_expect_err) + timeout(DELAY - FIFTY_MILLIS, rsp_fut_expect_err) .await - .unwrap_err(); // should be error - let ok_rsp = timeout(Duration::from_millis(300), rsp_fut_expect_ok) + .unwrap_err(); + let ok_rsp = timeout(DELAY + FIFTY_MILLIS, rsp_fut_expect_ok) .await - .unwrap(); // should be ok + .unwrap(); assert!(ok_rsp.is_ok()); assert_eq!(&HealthCheckResponse::default(), ok_rsp.unwrap().get_ref()); From 8a4b360b8212d6caec62ff177d7c4cc8345ddc58 Mon Sep 17 00:00:00 2001 From: ethanoroshiba Date: Wed, 11 Dec 2024 14:13:34 -0600 Subject: [PATCH 6/6] requested changes --- .../astria-grpc-mock-test-codegen/src/main.rs | 4 +- .../{health.proto => mock_service.proto} | 19 +-- ...c.health.v1.rs => grpc.mock_service.v1.rs} | 124 +++++++++--------- ...serde.rs => grpc.mock_service.v1.serde.rs} | 68 ++++++---- ...health_v1.bin => grpc_mock_service_v1.bin} | Bin 2875 -> 2761 bytes crates/astria-grpc-mock-test/src/lib.rs | 6 +- .../tests/health/main.rs | 8 +- .../health/{test_matcher.rs => matcher.rs} | 73 ++++++----- .../tests/health/{test_mock.rs => mock.rs} | 61 +++++---- .../health/{test_response.rs => response.rs} | 62 +++++---- .../tests/health/test_utils.rs | 123 ----------------- .../tests/health/utils.rs | 76 +++++++++++ 12 files changed, 300 insertions(+), 324 deletions(-) rename crates/astria-grpc-mock-test/proto/{health.proto => mock_service.proto} (80%) rename crates/astria-grpc-mock-test/src/generated/{grpc.health.v1.rs => grpc.mock_service.v1.rs} (83%) rename crates/astria-grpc-mock-test/src/generated/{grpc.health.v1.serde.rs => grpc.mock_service.v1.serde.rs} (76%) rename crates/astria-grpc-mock-test/src/generated/{grpc_health_v1.bin => grpc_mock_service_v1.bin} (58%) rename crates/astria-grpc-mock-test/tests/health/{test_matcher.rs => matcher.rs} (65%) rename crates/astria-grpc-mock-test/tests/health/{test_mock.rs => mock.rs} (67%) rename crates/astria-grpc-mock-test/tests/health/{test_response.rs => response.rs} (62%) delete mode 100644 crates/astria-grpc-mock-test/tests/health/test_utils.rs create mode 100644 crates/astria-grpc-mock-test/tests/health/utils.rs diff --git a/crates/astria-grpc-mock-test-codegen/src/main.rs b/crates/astria-grpc-mock-test-codegen/src/main.rs index efd08cb032..fb10eea6c2 100644 --- a/crates/astria-grpc-mock-test-codegen/src/main.rs +++ b/crates/astria-grpc-mock-test-codegen/src/main.rs @@ -5,11 +5,11 @@ fn main() { .parent() .unwrap() .join("astria-grpc-mock-test"); - let protos = &[root_dir.join("proto/health.proto")]; + let protos = &[root_dir.join("proto/mock_service.proto")]; let includes = &[root_dir.join("proto")]; let out_dir = root_dir.join("src/generated"); - let file_descriptor_set_path = root_dir.join("src/generated/grpc_health_v1.bin"); + let file_descriptor_set_path = root_dir.join("src/generated/grpc_mock_service_v1.bin"); let mut prost_config = prost_build::Config::new(); prost_config.enable_type_names(); diff --git a/crates/astria-grpc-mock-test/proto/health.proto b/crates/astria-grpc-mock-test/proto/mock_service.proto similarity index 80% rename from crates/astria-grpc-mock-test/proto/health.proto rename to crates/astria-grpc-mock-test/proto/mock_service.proto index 38843ff1e7..7350c047b9 100644 --- a/crates/astria-grpc-mock-test/proto/health.proto +++ b/crates/astria-grpc-mock-test/proto/mock_service.proto @@ -17,19 +17,14 @@ syntax = "proto3"; -package grpc.health.v1; +package grpc.mock_service.v1; -option csharp_namespace = "Grpc.Health.V1"; -option go_package = "google.golang.org/grpc/health/grpc_health_v1"; -option java_multiple_files = true; -option java_outer_classname = "HealthProto"; -option java_package = "io.grpc.health.v1"; - -message HealthCheckRequest { +message MockRequest { string service = 1; + string additional_info = 2; } -message HealthCheckResponse { +message MockResponse { enum ServingStatus { UNKNOWN = 0; SERVING = 1; @@ -39,10 +34,10 @@ message HealthCheckResponse { ServingStatus status = 1; } -service Health { +service Service { // If the requested service is unknown, the call will fail with status // NOT_FOUND. - rpc Check(HealthCheckRequest) returns (HealthCheckResponse); + rpc Check(MockRequest) returns (MockResponse); // Performs a watch for the serving status of the requested service. // The server will immediately send back a message indicating the current @@ -59,5 +54,5 @@ service Health { // should assume this method is not supported and should not retry the // call. If the call terminates with any other status (including OK), // clients should retry the call with appropriate exponential backoff. - rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse); + rpc Watch(MockRequest) returns (stream MockResponse); } diff --git a/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.rs b/crates/astria-grpc-mock-test/src/generated/grpc.mock_service.v1.rs similarity index 83% rename from crates/astria-grpc-mock-test/src/generated/grpc.health.v1.rs rename to crates/astria-grpc-mock-test/src/generated/grpc.mock_service.v1.rs index 85dd8cadff..3ad4e3238a 100644 --- a/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.rs +++ b/crates/astria-grpc-mock-test/src/generated/grpc.mock_service.v1.rs @@ -1,28 +1,30 @@ // This file is @generated by prost-build. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct HealthCheckRequest { +pub struct MockRequest { #[prost(string, tag = "1")] pub service: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub additional_info: ::prost::alloc::string::String, } -impl ::prost::Name for HealthCheckRequest { - const NAME: &'static str = "HealthCheckRequest"; - const PACKAGE: &'static str = "grpc.health.v1"; +impl ::prost::Name for MockRequest { + const NAME: &'static str = "MockRequest"; + const PACKAGE: &'static str = "grpc.mock_service.v1"; fn full_name() -> ::prost::alloc::string::String { - "grpc.health.v1.HealthCheckRequest".into() + "grpc.mock_service.v1.MockRequest".into() } fn type_url() -> ::prost::alloc::string::String { - "/grpc.health.v1.HealthCheckRequest".into() + "/grpc.mock_service.v1.MockRequest".into() } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct HealthCheckResponse { - #[prost(enumeration = "health_check_response::ServingStatus", tag = "1")] +pub struct MockResponse { + #[prost(enumeration = "mock_response::ServingStatus", tag = "1")] pub status: i32, } -/// Nested message and enum types in `HealthCheckResponse`. -pub mod health_check_response { +/// Nested message and enum types in `MockResponse`. +pub mod mock_response { #[derive( Clone, Copy, @@ -67,26 +69,26 @@ pub mod health_check_response { } } } -impl ::prost::Name for HealthCheckResponse { - const NAME: &'static str = "HealthCheckResponse"; - const PACKAGE: &'static str = "grpc.health.v1"; +impl ::prost::Name for MockResponse { + const NAME: &'static str = "MockResponse"; + const PACKAGE: &'static str = "grpc.mock_service.v1"; fn full_name() -> ::prost::alloc::string::String { - "grpc.health.v1.HealthCheckResponse".into() + "grpc.mock_service.v1.MockResponse".into() } fn type_url() -> ::prost::alloc::string::String { - "/grpc.health.v1.HealthCheckResponse".into() + "/grpc.mock_service.v1.MockResponse".into() } } /// Generated client implementations. -pub mod health_client { +pub mod service_client { #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] use tonic::codegen::*; use tonic::codegen::http::Uri; #[derive(Debug, Clone)] - pub struct HealthClient { + pub struct ServiceClient { inner: tonic::client::Grpc, } - impl HealthClient { + impl ServiceClient { /// Attempt to create a new client by connecting to a given endpoint. pub async fn connect(dst: D) -> Result where @@ -97,7 +99,7 @@ pub mod health_client { Ok(Self::new(conn)) } } - impl HealthClient + impl ServiceClient where T: tonic::client::GrpcService, T::Error: Into, @@ -115,7 +117,7 @@ pub mod health_client { pub fn with_interceptor( inner: T, interceptor: F, - ) -> HealthClient> + ) -> ServiceClient> where F: tonic::service::Interceptor, T::ResponseBody: Default, @@ -129,7 +131,7 @@ pub mod health_client { http::Request, >>::Error: Into + Send + Sync, { - HealthClient::new(InterceptedService::new(inner, interceptor)) + ServiceClient::new(InterceptedService::new(inner, interceptor)) } /// Compress requests with the given encoding. /// @@ -166,11 +168,8 @@ pub mod health_client { /// NOT_FOUND. pub async fn check( &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await @@ -182,11 +181,11 @@ pub mod health_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/grpc.health.v1.Health/Check", + "/grpc.mock_service.v1.Service/Check", ); let mut req = request.into_request(); req.extensions_mut() - .insert(GrpcMethod::new("grpc.health.v1.Health", "Check")); + .insert(GrpcMethod::new("grpc.mock_service.v1.Service", "Check")); self.inner.unary(req, path, codec).await } /// Performs a watch for the serving status of the requested service. @@ -206,9 +205,9 @@ pub mod health_client { /// clients should retry the call with appropriate exponential backoff. pub async fn watch( &mut self, - request: impl tonic::IntoRequest, + request: impl tonic::IntoRequest, ) -> std::result::Result< - tonic::Response>, + tonic::Response>, tonic::Status, > { self.inner @@ -222,34 +221,31 @@ pub mod health_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/grpc.health.v1.Health/Watch", + "/grpc.mock_service.v1.Service/Watch", ); let mut req = request.into_request(); req.extensions_mut() - .insert(GrpcMethod::new("grpc.health.v1.Health", "Watch")); + .insert(GrpcMethod::new("grpc.mock_service.v1.Service", "Watch")); self.inner.server_streaming(req, path, codec).await } } } /// Generated server implementations. -pub mod health_server { +pub mod service_server { #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] use tonic::codegen::*; - /// Generated trait containing gRPC methods that should be implemented for use with HealthServer. + /// Generated trait containing gRPC methods that should be implemented for use with ServiceServer. #[async_trait] - pub trait Health: Send + Sync + 'static { + pub trait Service: Send + Sync + 'static { /// If the requested service is unknown, the call will fail with status /// NOT_FOUND. async fn check( self: std::sync::Arc, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; /// Server streaming response type for the Watch method. type WatchStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result, + Item = std::result::Result, > + Send + 'static; @@ -270,11 +266,11 @@ pub mod health_server { /// clients should retry the call with appropriate exponential backoff. async fn watch( self: std::sync::Arc, - request: tonic::Request, + request: tonic::Request, ) -> std::result::Result, tonic::Status>; } #[derive(Debug)] - pub struct HealthServer { + pub struct ServiceServer { inner: _Inner, accept_compression_encodings: EnabledCompressionEncodings, send_compression_encodings: EnabledCompressionEncodings, @@ -282,7 +278,7 @@ pub mod health_server { max_encoding_message_size: Option, } struct _Inner(Arc); - impl HealthServer { + impl ServiceServer { pub fn new(inner: T) -> Self { Self::from_arc(Arc::new(inner)) } @@ -334,9 +330,9 @@ pub mod health_server { self } } - impl tonic::codegen::Service> for HealthServer + impl tonic::codegen::Service> for ServiceServer where - T: Health, + T: Service, B: Body + Send + 'static, B::Error: Into + Send + 'static, { @@ -352,25 +348,23 @@ pub mod health_server { fn call(&mut self, req: http::Request) -> Self::Future { let inner = self.inner.clone(); match req.uri().path() { - "/grpc.health.v1.Health/Check" => { + "/grpc.mock_service.v1.Service/Check" => { #[allow(non_camel_case_types)] - struct CheckSvc(pub Arc); - impl< - T: Health, - > tonic::server::UnaryService + struct CheckSvc(pub Arc); + impl tonic::server::UnaryService for CheckSvc { - type Response = super::HealthCheckResponse; + type Response = super::MockResponse; type Future = BoxFuture< tonic::Response, tonic::Status, >; fn call( &mut self, - request: tonic::Request, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::check(inner, request).await + ::check(inner, request).await }; Box::pin(fut) } @@ -398,14 +392,14 @@ pub mod health_server { }; Box::pin(fut) } - "/grpc.health.v1.Health/Watch" => { + "/grpc.mock_service.v1.Service/Watch" => { #[allow(non_camel_case_types)] - struct WatchSvc(pub Arc); + struct WatchSvc(pub Arc); impl< - T: Health, - > tonic::server::ServerStreamingService + T: Service, + > tonic::server::ServerStreamingService for WatchSvc { - type Response = super::HealthCheckResponse; + type Response = super::MockResponse; type ResponseStream = T::WatchStream; type Future = BoxFuture< tonic::Response, @@ -413,11 +407,11 @@ pub mod health_server { >; fn call( &mut self, - request: tonic::Request, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::watch(inner, request).await + ::watch(inner, request).await }; Box::pin(fut) } @@ -460,7 +454,7 @@ pub mod health_server { } } } - impl Clone for HealthServer { + impl Clone for ServiceServer { fn clone(&self) -> Self { let inner = self.inner.clone(); Self { @@ -472,7 +466,7 @@ pub mod health_server { } } } - impl Clone for _Inner { + impl Clone for _Inner { fn clone(&self) -> Self { Self(Arc::clone(&self.0)) } @@ -482,7 +476,7 @@ pub mod health_server { write!(f, "{:?}", self.0) } } - impl tonic::server::NamedService for HealthServer { - const NAME: &'static str = "grpc.health.v1.Health"; + impl tonic::server::NamedService for ServiceServer { + const NAME: &'static str = "grpc.mock_service.v1.Service"; } } diff --git a/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.serde.rs b/crates/astria-grpc-mock-test/src/generated/grpc.mock_service.v1.serde.rs similarity index 76% rename from crates/astria-grpc-mock-test/src/generated/grpc.health.v1.serde.rs rename to crates/astria-grpc-mock-test/src/generated/grpc.mock_service.v1.serde.rs index cbfbf6c6f1..6f2ad9024c 100644 --- a/crates/astria-grpc-mock-test/src/generated/grpc.health.v1.serde.rs +++ b/crates/astria-grpc-mock-test/src/generated/grpc.mock_service.v1.serde.rs @@ -1,4 +1,4 @@ -impl serde::Serialize for HealthCheckRequest { +impl serde::Serialize for MockRequest { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -9,14 +9,20 @@ impl serde::Serialize for HealthCheckRequest { if !self.service.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("grpc.health.v1.HealthCheckRequest", len)?; + if !self.additional_info.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("grpc.mock_service.v1.MockRequest", len)?; if !self.service.is_empty() { struct_ser.serialize_field("service", &self.service)?; } + if !self.additional_info.is_empty() { + struct_ser.serialize_field("additional_info", &self.additional_info)?; + } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for HealthCheckRequest { +impl<'de> serde::Deserialize<'de> for MockRequest { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where @@ -24,11 +30,14 @@ impl<'de> serde::Deserialize<'de> for HealthCheckRequest { { const FIELDS: &[&str] = &[ "service", + "additional_info", + "additionalInfo", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { Service, + AdditionalInfo, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -51,6 +60,7 @@ impl<'de> serde::Deserialize<'de> for HealthCheckRequest { { match value { "service" => Ok(GeneratedField::Service), + "additionalInfo" | "additional_info" => Ok(GeneratedField::AdditionalInfo), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -60,17 +70,18 @@ impl<'de> serde::Deserialize<'de> for HealthCheckRequest { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = HealthCheckRequest; + type Value = MockRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct grpc.health.v1.HealthCheckRequest") + formatter.write_str("struct grpc.mock_service.v1.MockRequest") } - fn visit_map(self, mut map_: V) -> std::result::Result + fn visit_map(self, mut map_: V) -> std::result::Result where V: serde::de::MapAccess<'de>, { let mut service__ = None; + let mut additional_info__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Service => { @@ -79,17 +90,24 @@ impl<'de> serde::Deserialize<'de> for HealthCheckRequest { } service__ = Some(map_.next_value()?); } + GeneratedField::AdditionalInfo => { + if additional_info__.is_some() { + return Err(serde::de::Error::duplicate_field("additionalInfo")); + } + additional_info__ = Some(map_.next_value()?); + } } } - Ok(HealthCheckRequest { + Ok(MockRequest { service: service__.unwrap_or_default(), + additional_info: additional_info__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("grpc.health.v1.HealthCheckRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("grpc.mock_service.v1.MockRequest", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for HealthCheckResponse { +impl serde::Serialize for MockResponse { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -100,16 +118,16 @@ impl serde::Serialize for HealthCheckResponse { if self.status != 0 { len += 1; } - let mut struct_ser = serializer.serialize_struct("grpc.health.v1.HealthCheckResponse", len)?; + let mut struct_ser = serializer.serialize_struct("grpc.mock_service.v1.MockResponse", len)?; if self.status != 0 { - let v = health_check_response::ServingStatus::try_from(self.status) + let v = mock_response::ServingStatus::try_from(self.status) .map_err(|_| serde::ser::Error::custom(format!("Invalid variant {}", self.status)))?; struct_ser.serialize_field("status", &v)?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for HealthCheckResponse { +impl<'de> serde::Deserialize<'de> for MockResponse { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where @@ -153,13 +171,13 @@ impl<'de> serde::Deserialize<'de> for HealthCheckResponse { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = HealthCheckResponse; + type Value = MockResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct grpc.health.v1.HealthCheckResponse") + formatter.write_str("struct grpc.mock_service.v1.MockResponse") } - fn visit_map(self, mut map_: V) -> std::result::Result + fn visit_map(self, mut map_: V) -> std::result::Result where V: serde::de::MapAccess<'de>, { @@ -170,19 +188,19 @@ impl<'de> serde::Deserialize<'de> for HealthCheckResponse { if status__.is_some() { return Err(serde::de::Error::duplicate_field("status")); } - status__ = Some(map_.next_value::()? as i32); + status__ = Some(map_.next_value::()? as i32); } } } - Ok(HealthCheckResponse { + Ok(MockResponse { status: status__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("grpc.health.v1.HealthCheckResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("grpc.mock_service.v1.MockResponse", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for health_check_response::ServingStatus { +impl serde::Serialize for mock_response::ServingStatus { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -197,7 +215,7 @@ impl serde::Serialize for health_check_response::ServingStatus { serializer.serialize_str(variant) } } -impl<'de> serde::Deserialize<'de> for health_check_response::ServingStatus { +impl<'de> serde::Deserialize<'de> for mock_response::ServingStatus { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where @@ -213,7 +231,7 @@ impl<'de> serde::Deserialize<'de> for health_check_response::ServingStatus { struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = health_check_response::ServingStatus; + type Value = mock_response::ServingStatus; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(formatter, "expected one of: {:?}", &FIELDS) @@ -248,10 +266,10 @@ impl<'de> serde::Deserialize<'de> for health_check_response::ServingStatus { E: serde::de::Error, { match value { - "UNKNOWN" => Ok(health_check_response::ServingStatus::Unknown), - "SERVING" => Ok(health_check_response::ServingStatus::Serving), - "NOT_SERVING" => Ok(health_check_response::ServingStatus::NotServing), - "SERVICE_UNKNOWN" => Ok(health_check_response::ServingStatus::ServiceUnknown), + "UNKNOWN" => Ok(mock_response::ServingStatus::Unknown), + "SERVING" => Ok(mock_response::ServingStatus::Serving), + "NOT_SERVING" => Ok(mock_response::ServingStatus::NotServing), + "SERVICE_UNKNOWN" => Ok(mock_response::ServingStatus::ServiceUnknown), _ => Err(serde::de::Error::unknown_variant(value, FIELDS)), } } diff --git a/crates/astria-grpc-mock-test/src/generated/grpc_health_v1.bin b/crates/astria-grpc-mock-test/src/generated/grpc_mock_service_v1.bin similarity index 58% rename from crates/astria-grpc-mock-test/src/generated/grpc_health_v1.bin rename to crates/astria-grpc-mock-test/src/generated/grpc_mock_service_v1.bin index 52efb312248858eba8a5024742a401fbc68b32fb..5d038ba20c98a32bc80a528416d6b18767e800b1 100644 GIT binary patch delta 965 zcma)5J#X4T81|iyGrUkB2N=pn04Ea35SKKH>dal5N{Lc3uq7gn6Hr=Au#>IwZ;0|I zT6O4M_e?A&txfR+XTd7`laeRoU#%BVVBh2qP z-ozbyj(7vy^9TN&S0QM~zU-R+y2@Pkn5VMa`1&;T^|<%&wI6&TpoQnQBaR{Qdg$0U zqP;5Y_RjJAsgIym1!fQ2-?cAjYuw4nQ;Ln z=quOiVGSZ!RWW8Yq^z1OqKq*j3)W~$4NZuUt7%1e23&HoWS!+93)&1KU1#Y9riin- zSdMfflJ})uUe#FA6Vx}B^a$5kF^;1sUuU@mmb@syi>M6_)O`HNcY`J?#3B_%O}!kh zhseThTANKn6)sV3jxfEQ*XIbs+Rv+UI~;egsT_pc;c^3;$sC*kO|8(yUk;SBm delta 1054 zcmb7EO>YuW6n$^z@t9t4ppUPXR-pq$(Qzm?x)K&8M%#tKE;Mn`2Fb{nk`BVO+xZPm z7ybtqCZ-!>e}QZFZu}$O_XfslQWJOgoO|xM_ni!UD_j;G_)>(l=MKh!=S-%4;0tp! zoeZ5hb=tNZ5YJS<=ecLwwVw5vLM~>8NBiz<;vdgku?u__41$x{3a!#R=7YBLr;`&8 zoqe5#pD6H%5Pe9X_wYzq*G|Ya?@!!WP_F$`Ge2b?60Ziq(7WC-#C`u`xkYydu>8*d zYx_6~!!@G}DPW9fMCj{U0n(6=AR&>lK&V__$}W(OD3@1$ zRIm!U9r;mIRg5)MF-GO4s#iQhb{;(5|$Rs-P2*o=)&DPw2 OqCsb?_lB##f$d+DLA9O$ diff --git a/crates/astria-grpc-mock-test/src/lib.rs b/crates/astria-grpc-mock-test/src/lib.rs index 6dad4f20cb..8e0dc36be0 100644 --- a/crates/astria-grpc-mock-test/src/lib.rs +++ b/crates/astria-grpc-mock-test/src/lib.rs @@ -10,7 +10,7 @@ clippy::allow_attributes_without_reason, reason = "cannot prevent generated files from having allow attributes" )] -pub mod health { - include!("generated/grpc.health.v1.rs"); - include!("generated/grpc.health.v1.serde.rs"); +pub mod service { + include!("generated/grpc.mock_service.v1.rs"); + include!("generated/grpc.mock_service.v1.serde.rs"); } diff --git a/crates/astria-grpc-mock-test/tests/health/main.rs b/crates/astria-grpc-mock-test/tests/health/main.rs index 995290f72c..41f2622bf3 100644 --- a/crates/astria-grpc-mock-test/tests/health/main.rs +++ b/crates/astria-grpc-mock-test/tests/health/main.rs @@ -3,7 +3,7 @@ reason = "just make the tests work for now" )] -mod test_matcher; -mod test_mock; -mod test_response; -mod test_utils; +mod matcher; +mod mock; +mod response; +mod utils; diff --git a/crates/astria-grpc-mock-test/tests/health/test_matcher.rs b/crates/astria-grpc-mock-test/tests/health/matcher.rs similarity index 65% rename from crates/astria-grpc-mock-test/tests/health/test_matcher.rs rename to crates/astria-grpc-mock-test/tests/health/matcher.rs index f0d64f3475..22645d3d22 100644 --- a/crates/astria-grpc-mock-test/tests/health/test_matcher.rs +++ b/crates/astria-grpc-mock-test/tests/health/matcher.rs @@ -3,35 +3,34 @@ use astria_grpc_mock::{ response, Mock, }; -use astria_grpc_mock_test::health::{ - health_client::HealthClient, - HealthCheckRequest, - HealthCheckResponse, +use astria_grpc_mock_test::service::{ + service_client::ServiceClient, + MockRequest, + MockResponse, }; -use crate::test_utils::{ - start_mock_server, - MockMessage, -}; +use crate::utils::start_mock_server; #[tokio::test] async fn exact_pbjson_match_works() { let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + let mut client = ServiceClient::connect(format!("http://{}", server.local_addr)) .await .unwrap(); - let expected_request = HealthCheckRequest { + let expected_request = MockRequest { service: "helloworld".to_string(), + additional_info: String::new(), }; - let expected_response = HealthCheckResponse { + let expected_response = MockResponse { status: 1, }; let mock = Mock::for_rpc_given("check", matcher::message_exact_pbjson(&expected_request)) .respond_with(response::constant_response(expected_response.clone())); server.mocked.register(mock).await; let rsp = client - .check(HealthCheckRequest { + .check(MockRequest { service: "helloworld".to_string(), + additional_info: String::new(), }) .await .unwrap(); @@ -41,13 +40,13 @@ async fn exact_pbjson_match_works() { #[tokio::test] async fn partial_pbjson_match_works() { let server = start_mock_server().await; - let expected_request = MockMessage { - field_one: "helloworld".to_string(), - field_two: String::new(), + let expected_request = MockRequest { + service: "helloworld".to_string(), + additional_info: String::new(), }; - let expected_response = MockMessage { - field_one: "helloworld".to_string(), - field_two: "helloworld".to_string(), + let expected_response = MockRequest { + service: "helloworld".to_string(), + additional_info: "helloworld".to_string(), }; let mock = Mock::for_rpc_given("check", matcher::message_partial_pbjson(&expected_request)) .respond_with(response::constant_response(expected_response.clone())); @@ -63,17 +62,17 @@ async fn partial_pbjson_match_works() { #[tokio::test] async fn and_combinator_works_with_partial_pbjson() { let server = start_mock_server().await; - let expected_request_1 = MockMessage { - field_one: "helloworld".to_string(), - field_two: String::new(), + let expected_request_1 = MockRequest { + service: "helloworld".to_string(), + additional_info: String::new(), }; - let expected_request_2 = MockMessage { - field_one: String::new(), - field_two: "helloworld".to_string(), + let expected_request_2 = MockRequest { + service: String::new(), + additional_info: "helloworld".to_string(), }; - let expected_response = MockMessage { - field_one: "helloworld".to_string(), - field_two: "helloworld".to_string(), + let expected_response = MockRequest { + service: "helloworld".to_string(), + additional_info: "helloworld".to_string(), }; let mock = Mock::for_rpc_given( "check", @@ -93,21 +92,23 @@ async fn and_combinator_works_with_partial_pbjson() { #[tokio::test] async fn exact_pbjson_matcher_doesnt_match_incorrect_request() { let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + let mut client = ServiceClient::connect(format!("http://{}", server.local_addr)) .await .unwrap(); - let expected_request = HealthCheckRequest { + let expected_request = MockRequest { service: "helloworld".to_string(), + additional_info: String::new(), }; - let expected_response = HealthCheckResponse { + let expected_response = MockResponse { status: 1, }; let mock = Mock::for_rpc_given("check", matcher::message_exact_pbjson(&expected_request)) .respond_with(response::constant_response(expected_response.clone())); server.mocked.register(mock).await; let err_rsp = client - .check(HealthCheckRequest { + .check(MockRequest { service: "helloworld_wrong".to_string(), + additional_info: String::new(), }) .await .unwrap_err(); @@ -117,21 +118,23 @@ async fn exact_pbjson_matcher_doesnt_match_incorrect_request() { #[tokio::test] async fn partial_pbjson_match_doesnt_match_incorrect_request() { let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + let mut client = ServiceClient::connect(format!("http://{}", server.local_addr)) .await .unwrap(); - let expected_request = HealthCheckRequest { + let expected_request = MockRequest { service: "helloworld".to_string(), + additional_info: String::new(), }; - let expected_response = HealthCheckResponse { + let expected_response = MockResponse { status: 1, }; let mock = Mock::for_rpc_given("check", matcher::message_partial_pbjson(&expected_request)) .respond_with(response::constant_response(expected_response.clone())); server.mocked.register(mock).await; let err_rsp = client - .check(HealthCheckRequest { + .check(MockRequest { service: "hello".to_string(), + additional_info: String::new(), }) .await .unwrap_err(); diff --git a/crates/astria-grpc-mock-test/tests/health/test_mock.rs b/crates/astria-grpc-mock-test/tests/health/mock.rs similarity index 67% rename from crates/astria-grpc-mock-test/tests/health/test_mock.rs rename to crates/astria-grpc-mock-test/tests/health/mock.rs index e0ec35aecf..406c841332 100644 --- a/crates/astria-grpc-mock-test/tests/health/test_mock.rs +++ b/crates/astria-grpc-mock-test/tests/health/mock.rs @@ -3,39 +3,41 @@ use astria_grpc_mock::{ response, Mock, }; -use astria_grpc_mock_test::health::{ - health_client::HealthClient, - HealthCheckRequest, - HealthCheckResponse, +use astria_grpc_mock_test::service::{ + service_client::ServiceClient, + MockRequest, + MockResponse, }; use tokio::join; -use crate::test_utils::start_mock_server; +use crate::utils::start_mock_server; #[tokio::test] async fn mock_expect_two_works() { let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + let mut client = ServiceClient::connect(format!("http://{}", server.local_addr)) .await .unwrap(); - let expected_response = HealthCheckResponse { + let expected_response = MockResponse { status: 1, }; - let mock = Mock::for_rpc_given("check", matcher::message_type::()) + let mock = Mock::for_rpc_given("check", matcher::message_type::()) .respond_with(response::constant_response(expected_response.clone())) .expect(2); let guard = server.mocked.register_as_scoped(mock).await; let two_checks = async move { let res_one = client - .check(HealthCheckRequest { + .check(MockRequest { service: "helloworld".to_string(), + additional_info: String::new(), }) .await?; let res_two = client - .check(HealthCheckRequest { + .check(MockRequest { service: "helloworld".to_string(), + additional_info: String::new(), }) .await?; Ok::<_, tonic::Status>((res_one, res_two)) @@ -50,19 +52,20 @@ async fn mock_expect_two_works() { #[tokio::test] async fn response_guard_wait_until_satisfied_works() { let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + let mut client = ServiceClient::connect(format!("http://{}", server.local_addr)) .await .unwrap(); - let expected_response = HealthCheckResponse { + let expected_response = MockResponse { status: 1, }; - let mock = Mock::for_rpc_given("check", matcher::message_type::()) + let mock = Mock::for_rpc_given("check", matcher::message_type::()) .respond_with(response::constant_response(expected_response.clone())) .expect(1); let guard = server.mocked.register_as_scoped(mock).await; - let check = client.check(HealthCheckRequest { + let check = client.check(MockRequest { service: "helloworld".to_string(), + additional_info: String::new(), }); let ((), check_res) = join!(guard.wait_until_satisfied(), check); @@ -73,19 +76,20 @@ async fn response_guard_wait_until_satisfied_works() { #[tokio::test] async fn up_to_n_times_works_as_expected() { let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + let mut client = ServiceClient::connect(format!("http://{}", server.local_addr)) .await .unwrap(); - let expected_response = HealthCheckResponse { + let expected_response = MockResponse { status: 1, }; - let mock = Mock::for_rpc_given("check", matcher::message_type::()) + let mock = Mock::for_rpc_given("check", matcher::message_type::()) .respond_with(response::constant_response(expected_response.clone())) .up_to_n_times(1); let guard = server.mocked.register_as_scoped(mock).await; - let check = client.check(HealthCheckRequest { + let check = client.check(MockRequest { service: "helloworld".to_string(), + additional_info: String::new(), }); let ((), check_res) = join!(guard.wait_until_satisfied(), check); @@ -93,8 +97,9 @@ async fn up_to_n_times_works_as_expected() { assert_eq!(&expected_response, rsp.get_ref()); let err_rsp = client - .check(HealthCheckRequest { + .check(MockRequest { service: "helloworld".to_string(), + additional_info: String::new(), }) .await .unwrap_err(); @@ -105,15 +110,16 @@ async fn up_to_n_times_works_as_expected() { #[should_panic] async fn incorrect_mock_response_fails_server() { let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + let mut client = ServiceClient::connect(format!("http://{}", server.local_addr)) .await .unwrap(); - let mock = Mock::for_rpc_given("check", matcher::message_type::()) - .respond_with(response::default_response::()); + let mock = Mock::for_rpc_given("check", matcher::message_type::()) + .respond_with(response::default_response::()); server.mocked.register(mock).await; let _ = client - .check(HealthCheckRequest { + .check(MockRequest { service: "helloworld".to_string(), + additional_info: String::new(), }) .await; } @@ -122,15 +128,16 @@ async fn incorrect_mock_response_fails_server() { #[should_panic] async fn incorrect_mock_response_fails_guard() { let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + let mut client = ServiceClient::connect(format!("http://{}", server.local_addr)) .await .unwrap(); - let mock = Mock::for_rpc_given("check", matcher::message_type::()) - .respond_with(response::default_response::()); + let mock = Mock::for_rpc_given("check", matcher::message_type::()) + .respond_with(response::default_response::()); let guard = server.mocked.register_as_scoped(mock).await; - let check = client.check(HealthCheckRequest { + let check = client.check(MockRequest { service: "helloworld".to_string(), + additional_info: String::new(), }); let _ = join!(guard.wait_until_satisfied(), check); diff --git a/crates/astria-grpc-mock-test/tests/health/test_response.rs b/crates/astria-grpc-mock-test/tests/health/response.rs similarity index 62% rename from crates/astria-grpc-mock-test/tests/health/test_response.rs rename to crates/astria-grpc-mock-test/tests/health/response.rs index 2711317517..d14d93ab4b 100644 --- a/crates/astria-grpc-mock-test/tests/health/test_response.rs +++ b/crates/astria-grpc-mock-test/tests/health/response.rs @@ -5,48 +5,50 @@ use astria_grpc_mock::{ response, Mock, }; -use astria_grpc_mock_test::health::{ - health_client::HealthClient, - HealthCheckRequest, - HealthCheckResponse, +use astria_grpc_mock_test::service::{ + service_client::ServiceClient, + MockRequest, + MockResponse, }; use tokio::time::timeout; -use crate::test_utils::start_mock_server; +use crate::utils::start_mock_server; #[tokio::test] async fn default_response_works() { let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + let mut client = ServiceClient::connect(format!("http://{}", server.local_addr)) .await .unwrap(); - let mock = Mock::for_rpc_given("check", matcher::message_type::()) - .respond_with(response::default_response::()); + let mock = Mock::for_rpc_given("check", matcher::message_type::()) + .respond_with(response::default_response::()); server.mocked.register(mock).await; let rsp = client - .check(HealthCheckRequest { + .check(MockRequest { service: "helloworld".to_string(), + additional_info: String::new(), }) .await .unwrap(); - assert_eq!(&HealthCheckResponse::default(), rsp.get_ref()); + assert_eq!(&MockResponse::default(), rsp.get_ref()); } #[tokio::test] async fn constant_response_works() { let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + let mut client = ServiceClient::connect(format!("http://{}", server.local_addr)) .await .unwrap(); - let expected_response = HealthCheckResponse { + let expected_response = MockResponse { status: 1, }; - let mock = Mock::for_rpc_given("check", matcher::message_type::()) + let mock = Mock::for_rpc_given("check", matcher::message_type::()) .respond_with(response::constant_response(expected_response.clone())); server.mocked.register(mock).await; let rsp = client - .check(HealthCheckRequest { + .check(MockRequest { service: "helloworld".to_string(), + additional_info: String::new(), }) .await .unwrap(); @@ -56,19 +58,20 @@ async fn constant_response_works() { #[tokio::test] async fn dynamic_response_works() { let server = start_mock_server().await; - let mut client = HealthClient::connect(format!("http://{}", server.local_addr)) + let mut client = ServiceClient::connect(format!("http://{}", server.local_addr)) .await .unwrap(); - let mut expected_response = HealthCheckResponse { + let mut expected_response = MockResponse { status: 1, }; - let mock = Mock::for_rpc_given("check", matcher::message_type::()) + let mock = Mock::for_rpc_given("check", matcher::message_type::()) .respond_with(response::dynamic_response(dynamic_responder)); server.mocked.register(mock).await; let rsp_1 = client - .check(HealthCheckRequest { + .check(MockRequest { service: "1".to_string(), + additional_info: String::new(), }) .await .unwrap(); @@ -76,15 +79,16 @@ async fn dynamic_response_works() { expected_response.status = 2; let rsp_2 = client - .check(HealthCheckRequest { + .check(MockRequest { service: "2".to_string(), + additional_info: String::new(), }) .await .unwrap(); assert_eq!(&expected_response, rsp_2.get_ref()); } -fn dynamic_responder(request: &HealthCheckRequest) -> HealthCheckResponse { +fn dynamic_responder(request: &MockRequest) -> MockResponse { let status_return = request .service .chars() @@ -92,7 +96,7 @@ fn dynamic_responder(request: &HealthCheckRequest) -> HealthCheckResponse { .unwrap() .to_digit(10) .unwrap(); - HealthCheckResponse { + MockResponse { status: i32::try_from(status_return).unwrap(), } } @@ -103,21 +107,23 @@ async fn response_delay_works_as_expected() { const FIFTY_MILLIS: Duration = Duration::from_millis(50); let server = start_mock_server().await; - let mut err_client = HealthClient::connect(format!("http://{}", server.local_addr)) + let mut err_client = ServiceClient::connect(format!("http://{}", server.local_addr)) .await .unwrap(); - let mut ok_client = HealthClient::connect(format!("http://{}", server.local_addr)) + let mut ok_client = ServiceClient::connect(format!("http://{}", server.local_addr)) .await .unwrap(); - let mock = Mock::for_rpc_given("check", matcher::message_type::()) - .respond_with(response::default_response::().set_delay(DELAY)); + let mock = Mock::for_rpc_given("check", matcher::message_type::()) + .respond_with(response::default_response::().set_delay(DELAY)); mock.mount(&server.mocked).await; - let rsp_fut_expect_err = err_client.check(HealthCheckRequest { + let rsp_fut_expect_err = err_client.check(MockRequest { service: "helloworld".to_string(), + additional_info: String::new(), }); - let rsp_fut_expect_ok = ok_client.check(HealthCheckRequest { + let rsp_fut_expect_ok = ok_client.check(MockRequest { service: "helloworld".to_string(), + additional_info: String::new(), }); timeout(DELAY - FIFTY_MILLIS, rsp_fut_expect_err) @@ -128,5 +134,5 @@ async fn response_delay_works_as_expected() { .unwrap(); assert!(ok_rsp.is_ok()); - assert_eq!(&HealthCheckResponse::default(), ok_rsp.unwrap().get_ref()); + assert_eq!(&MockResponse::default(), ok_rsp.unwrap().get_ref()); } diff --git a/crates/astria-grpc-mock-test/tests/health/test_utils.rs b/crates/astria-grpc-mock-test/tests/health/test_utils.rs deleted file mode 100644 index 91f8f1cf5f..0000000000 --- a/crates/astria-grpc-mock-test/tests/health/test_utils.rs +++ /dev/null @@ -1,123 +0,0 @@ -use std::{ - net::SocketAddr, - pin::Pin, - sync::Arc, -}; - -use astria_grpc_mock_test::health::{ - health_server::{ - Health, - HealthServer, - }, - HealthCheckRequest, - HealthCheckResponse, -}; -use tokio::task::JoinHandle; -use tokio_stream::{ - wrappers::TcpListenerStream, - Stream, -}; -use tonic::{ - transport::Server, - Request, - Response, - Status, -}; - -pub(crate) struct MockServer { - _server: JoinHandle<()>, - pub(crate) local_addr: SocketAddr, - pub(crate) mocked: astria_grpc_mock::MockServer, -} - -pub(crate) async fn start_mock_server() -> MockServer { - let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); - let local_addr = listener.local_addr().unwrap(); - let mock_server = astria_grpc_mock::MockServer::new(); - let server = tokio::spawn({ - let mock_server = mock_server.clone(); - async move { - let _ = Server::builder() - .add_service(HealthServer::new(HealthService { - mock_server, - })) - .serve_with_incoming(TcpListenerStream::new(listener)) - .await; - } - }); - MockServer { - _server: server, - local_addr, - mocked: mock_server, - } -} - -struct HealthService { - mock_server: astria_grpc_mock::MockServer, -} - -#[tonic::async_trait] -impl Health for HealthService { - type WatchStream = - Pin> + Send + 'static>>; - - async fn check( - self: Arc, - request: Request, - ) -> Result, Status> { - self.mock_server.handle_request("check", request).await - } - - async fn watch( - self: Arc, - _request: Request, - ) -> Result, Status> { - unimplemented!() - } -} - -#[derive(::prost::Message, Clone, PartialEq)] -pub(crate) struct MockMessage { - #[prost(string, tag = "1")] - pub(crate) field_one: String, - #[prost(string, tag = "2")] - pub(crate) field_two: String, -} - -impl ::prost::Name for MockMessage { - const NAME: &'static str = "MockMessage"; - const PACKAGE: &'static str = "test_utils"; - - fn full_name() -> String { - "test_utils.MockMessage".to_string() - } -} - -impl serde::Serialize for MockMessage { - #[expect( - clippy::arithmetic_side_effects, - reason = "will never overflow for test purposes" - )] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.field_one.is_empty() { - len += 1; - } - if !self.field_two.is_empty() { - len += 1; - } - let mut struct_ser = - serializer.serialize_struct("grpc.health.v1.HealthCheckRequest", len)?; - if !self.field_one.is_empty() { - struct_ser.serialize_field("field_one", &self.field_one)?; - } - if !self.field_two.is_empty() { - struct_ser.serialize_field("field_two", &self.field_two)?; - } - struct_ser.end() - } -} diff --git a/crates/astria-grpc-mock-test/tests/health/utils.rs b/crates/astria-grpc-mock-test/tests/health/utils.rs new file mode 100644 index 0000000000..221f6d6bed --- /dev/null +++ b/crates/astria-grpc-mock-test/tests/health/utils.rs @@ -0,0 +1,76 @@ +use std::{ + net::SocketAddr, + pin::Pin, + sync::Arc, +}; + +use astria_grpc_mock_test::service::{ + service_server::{ + Service, + ServiceServer, + }, + MockRequest, + MockResponse, +}; +use tokio::task::JoinHandle; +use tokio_stream::{ + wrappers::TcpListenerStream, + Stream, +}; +use tonic::{ + transport::Server, + Request, + Response, + Status, +}; + +pub(crate) struct MockServer { + _server: JoinHandle<()>, + pub(crate) local_addr: SocketAddr, + pub(crate) mocked: astria_grpc_mock::MockServer, +} + +pub(crate) async fn start_mock_server() -> MockServer { + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let local_addr = listener.local_addr().unwrap(); + let mock_server = astria_grpc_mock::MockServer::new(); + let server = tokio::spawn({ + let mock_server = mock_server.clone(); + async move { + let _ = Server::builder() + .add_service(ServiceServer::new(MockGRPCService { + mock_server, + })) + .serve_with_incoming(TcpListenerStream::new(listener)) + .await; + } + }); + MockServer { + _server: server, + local_addr, + mocked: mock_server, + } +} + +struct MockGRPCService { + mock_server: astria_grpc_mock::MockServer, +} + +#[tonic::async_trait] +impl Service for MockGRPCService { + type WatchStream = Pin> + Send + 'static>>; + + async fn check( + self: Arc, + request: Request, + ) -> Result, Status> { + self.mock_server.handle_request("check", request).await + } + + async fn watch( + self: Arc, + _request: Request, + ) -> Result, Status> { + unimplemented!() + } +}