grafbase_sdk_mock/
server.rs

1use std::{
2    net::{Ipv4Addr, SocketAddrV4},
3    sync::Arc,
4};
5
6use async_graphql_axum::{GraphQLRequest, GraphQLResponse};
7use axum::{Router, extract::State, response::IntoResponse, routing::post};
8use url::Url;
9
10/// Represents a mock GraphQL server used for testing purposes.
11pub struct MockGraphQlServer {
12    shutdown: Option<tokio::sync::oneshot::Sender<()>>,
13    state: AppState,
14    url: Url,
15    name: String,
16}
17
18impl Drop for MockGraphQlServer {
19    fn drop(&mut self) {
20        if let Some(shutdown) = self.shutdown.take() {
21            shutdown.send(()).ok();
22        }
23    }
24}
25
26#[derive(Clone)]
27struct AppState {
28    schema: Arc<(async_graphql::dynamic::Schema, String)>,
29}
30
31impl MockGraphQlServer {
32    pub(crate) async fn new(name: String, schema: Arc<(async_graphql::dynamic::Schema, String)>) -> Self {
33        let state = AppState { schema };
34
35        let app = Router::new()
36            .route("/", post(graphql_handler))
37            .with_state(state.clone());
38
39        let (shutdown_sender, shutdown_receiver) = tokio::sync::oneshot::channel::<()>();
40
41        let listen_address = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 0);
42        let listener = tokio::net::TcpListener::bind(listen_address).await.unwrap();
43
44        let listen_address = listener.local_addr().unwrap();
45
46        tokio::spawn(async move {
47            axum::serve(listener, app)
48                .with_graceful_shutdown(async move {
49                    shutdown_receiver.await.ok();
50                })
51                .await
52                .unwrap();
53        });
54
55        let url = format!("http://{listen_address}").parse().unwrap();
56
57        MockGraphQlServer {
58            shutdown: Some(shutdown_sender),
59            url,
60            state,
61            name,
62        }
63    }
64
65    /// Returns a reference to the URL of the mock GraphQL server
66    pub fn url(&self) -> &Url {
67        &self.url
68    }
69
70    /// Returns the GraphQL schema in SDL (Schema Definition Language)
71    pub fn schema(&self) -> &str {
72        &self.state.schema.1
73    }
74
75    /// Returns the name of the subgraph
76    pub fn name(&self) -> &str {
77        &self.name
78    }
79}
80
81async fn graphql_handler(State(state): State<AppState>, req: GraphQLRequest) -> axum::response::Response {
82    let req = req.into_inner();
83    let response: GraphQLResponse = state.schema.0.execute(req).await.into();
84
85    response.into_response()
86}