Skip to main content

apollo_router/plugin/test/
service.rs

1#![allow(dead_code, unreachable_pub)]
2#![allow(missing_docs)] // FIXME
3
4#[cfg(test)]
5use std::sync::Arc;
6
7use futures::Future;
8use http::Request as HyperRequest;
9use http::Response as HyperResponse;
10
11use crate::services::ExecutionRequest;
12use crate::services::ExecutionResponse;
13#[cfg(test)]
14use crate::services::HasSchema;
15use crate::services::RouterRequest;
16use crate::services::RouterResponse;
17use crate::services::SubgraphRequest;
18use crate::services::SubgraphResponse;
19use crate::services::SupergraphRequest;
20use crate::services::SupergraphResponse;
21use crate::services::router::Body;
22#[cfg(test)]
23use crate::spec::Schema;
24
25/// Build a mock service handler for the router pipeline.
26macro_rules! mock_service {
27    ($name:ident, $request_type:ty, $response_type:ty) => {
28        paste::item! {
29            mockall::mock! {
30                #[derive(Debug)]
31                #[allow(dead_code)]
32                pub [<$name Service>] {
33                    pub fn call(&mut self, req: $request_type) -> Result<$response_type, tower::BoxError>;
34                }
35
36                #[allow(dead_code)]
37                impl Clone for [<$name Service>] {
38                    fn clone(&self) -> [<Mock $name Service>];
39                }
40            }
41
42            // mockall does not handle well the lifetime on Context
43            impl tower::Service<$request_type> for [<Mock $name Service>] {
44                type Response = $response_type;
45                type Error = tower::BoxError;
46                type Future = futures::future::BoxFuture<'static, Result<Self::Response, Self::Error>>;
47
48                fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), tower::BoxError>> {
49                    std::task::Poll::Ready(Ok(()))
50                }
51                fn call(&mut self, req: $request_type) -> Self::Future {
52                    let r  = self.call(req);
53                    Box::pin(async move { r })
54                }
55            }
56        }
57    };
58}
59
60macro_rules! mock_async_service {
61    ($name:ident, $request_type:tt < $req_generic:tt > , $response_type:tt < $res_generic:tt >) => {
62        paste::item! {
63            mockall::mock! {
64                #[derive(Debug)]
65                #[allow(dead_code)]
66                pub [<$name Service>] {
67                    pub fn call(&mut self, req: $request_type<$req_generic>) -> impl Future<Output = Result<$response_type<$res_generic>, tower::BoxError>> + Send + 'static;
68                }
69
70                #[allow(dead_code)]
71                impl Clone for [<$name Service>] {
72                    fn clone(&self) -> [<Mock $name Service>];
73                }
74            }
75
76
77            // mockall does not handle well the lifetime on Context
78            impl tower::Service<$request_type<$req_generic>> for [<Mock $name Service>] {
79                type Response = $response_type<$res_generic>;
80                type Error = tower::BoxError;
81                type Future = futures::future::BoxFuture<'static, Result<Self::Response, Self::Error>>;
82
83                fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), tower::BoxError>> {
84                    std::task::Poll::Ready(Ok(()))
85                }
86                fn call(&mut self, req: $request_type<$req_generic>) -> Self::Future {
87                    Box::pin(self.call(req))
88                }
89            }
90        }
91    };
92}
93
94#[cfg(test)]
95impl HasSchema for MockSupergraphService {
96    fn schema(&self) -> Arc<crate::spec::Schema> {
97        Arc::new(
98            Schema::parse(
99                include_str!("../../testdata/supergraph.graphql"),
100                &Default::default(),
101            )
102            .unwrap(),
103        )
104    }
105}
106
107mock_service!(Router, RouterRequest, RouterResponse);
108mock_service!(Supergraph, SupergraphRequest, SupergraphResponse);
109mock_service!(Execution, ExecutionRequest, ExecutionResponse);
110mock_service!(Subgraph, SubgraphRequest, SubgraphResponse);
111mock_async_service!(HttpClient, HyperRequest<Body>, HyperResponse<Body>);
112
113// This type is introduced to update internal uses of mocked http services, because the HttpClientService
114// defined above is part of the public API
115// TODO Router 2: remove this type and replace HttpClientService
116#[cfg(test)]
117pub(crate) use internal::MockInternalHttpClientService;
118#[cfg(test)]
119mod internal {
120    use futures::Future;
121    use http::Request as HyperRequest;
122    use http::Response as HyperResponse;
123
124    use crate::services::router::body::RouterBody;
125
126    mock_async_service!(
127        InternalHttpClient,
128        HyperRequest<RouterBody>,
129        HyperResponse<RouterBody>
130    );
131}