#![cfg(feature = "reqwest")]
use std::time::Duration;
use assert_matches::assert_matches;
use reqwest::Url;
use thegraph_graphql_http::{
graphql::IntoDocument,
http::request::{IntoRequestParameters, RequestParameters},
http_client::{ReqwestExt, ResponseError},
};
const TEST_SERVER_URL: &str = "https://graphql.org/graphql/";
#[tokio::test]
async fn send_valid_graphql_request_no_variables() {
let client = reqwest::Client::new();
let server_url: Url = TEST_SERVER_URL.parse().unwrap();
let query = indoc::indoc! {
r#"{
allFilms {
films {
title
director
releaseDate
}
}
}"#
};
#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct QueryResponse {
all_films: AllFilms,
}
#[derive(Debug, serde::Deserialize)]
struct AllFilms {
films: Vec<Film>,
}
#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct Film {
title: String,
director: String,
release_date: String,
}
let req_fut = client.post(server_url).send_graphql::<QueryResponse>(query);
let response = tokio::time::timeout(Duration::from_secs(30), req_fut)
.await
.expect("Request timed out")
.expect("Request failed");
assert_matches!(response, Ok(QueryResponse { all_films: AllFilms { films } }) => {
assert_eq!(films.len(), 6);
assert_matches!(films.iter().find(|film| film.title == "A New Hope"), Some(film) => {
assert_eq!(film.title, "A New Hope");
assert_eq!(film.director, "George Lucas");
assert_eq!(film.release_date, "1977-05-25");
});
assert_matches!(films.iter().find(|film| film.title == "The Empire Strikes Back"), Some(film) => {
assert_eq!(film.title, "The Empire Strikes Back");
assert_eq!(film.director, "Irvin Kershner");
assert_eq!(film.release_date, "1980-05-17");
});
assert_matches!(films.iter().find(|film| film.title == "Return of the Jedi"), Some(film) => {
assert_eq!(film.title, "Return of the Jedi");
assert_eq!(film.director, "Richard Marquand");
assert_eq!(film.release_date, "1983-05-25");
});
});
}
#[tokio::test]
async fn send_valid_graphql_request_with_variables() {
let client = reqwest::Client::new();
let server_url: Url = TEST_SERVER_URL.parse().unwrap();
#[derive(Debug)]
struct FilmRequest {
id: String,
}
impl FilmRequest {
fn new(id: u64) -> Self {
Self { id: id.to_string() }
}
}
impl IntoRequestParameters for FilmRequest {
fn into_request_parameters(self) -> RequestParameters {
let query = indoc::indoc! {
r#"query filmByFilmId($id: ID!) {
film(filmID: $id) {
title
director
releaseDate
}
}"#
};
RequestParameters {
query: query.into_document(),
operation_name: None,
variables: serde_json::Map::from_iter([("id".to_string(), self.id.into())]),
extensions: Default::default(),
}
}
}
#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct QueryResponse {
film: Film,
}
#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct Film {
title: String,
director: String,
release_date: String,
}
let req_fut = client
.post(server_url)
.send_graphql::<QueryResponse>(FilmRequest::new(1));
let response = tokio::time::timeout(Duration::from_secs(30), req_fut)
.await
.expect("Request timed out")
.expect("Request failed");
assert_matches!(response, Ok(QueryResponse { film }) => {
assert_eq!(film.title, "A New Hope");
assert_eq!(film.director, "George Lucas");
assert_eq!(film.release_date, "1977-05-25");
});
}
#[tokio::test]
async fn send_invalid_request_document_parsing_failure() {
let client = reqwest::Client::new();
let server_url: Url = TEST_SERVER_URL.parse().unwrap();
let query = "{";
#[derive(Debug, serde::Deserialize)]
struct QueryResponse {}
let req_fut = client.post(server_url).send_graphql::<QueryResponse>(query);
let response = tokio::time::timeout(Duration::from_secs(30), req_fut)
.await
.expect("Request timed out")
.expect("Request failed");
assert_matches!(response, Err(err) => {
assert!(err.to_string().contains("Syntax Error"));
});
}
#[tokio::test]
async fn send_invalid_request_field_errors_encountered_during_execution_failure() {
let client = reqwest::Client::new();
let server_url: Url = TEST_SERVER_URL.parse().unwrap();
let query = indoc::indoc! {
r#"{
allFilms {
films {
title
director
releaseDate
invalidField
}
}
}"#
};
#[derive(Debug, serde::Deserialize)]
struct QueryResponse {}
let req_fut = client.post(server_url).send_graphql::<QueryResponse>(query);
let response = tokio::time::timeout(Duration::from_secs(30), req_fut)
.await
.expect("Request timed out")
.expect("Request failed");
assert_matches!(response, Err(ResponseError::Failure { errors }) => {
assert_eq!(errors.len(), 1);
assert!(errors[0].message.contains(r#"Cannot query field "invalidField" on type "Film""#));
});
}
#[tokio::test]
async fn send_invalid_request_operation_cannot_be_determined_failure_null_operation_name() {
let client = reqwest::Client::new();
let server_url: Url = TEST_SERVER_URL.parse().unwrap();
let query = indoc::indoc! {
r#"
query filmsWithDirector {
allFilms {
films {
title
director
}
}
}
query filsmWithReleaseDate {
allFilms {
films {
title
releaseDate
}
}
}
"#
};
let request_params = RequestParameters {
query: query.into_document(),
operation_name: None, variables: Default::default(),
extensions: Default::default(),
};
#[derive(Debug, serde::Deserialize)]
struct QueryResponse {}
let req_fut = client
.post(server_url)
.send_graphql::<QueryResponse>(request_params);
let response = tokio::time::timeout(Duration::from_secs(30), req_fut)
.await
.expect("Request timed out")
.expect("Request failed");
assert_matches!(response, Err(err) => {
assert!(err.to_string().contains(r#"Unable to detect operation AST"#));
});
}
#[tokio::test]
async fn send_invalid_request_operation_cannot_be_determined_failure_invalid_operation_name() {
let client = reqwest::Client::new();
let server_url: Url = TEST_SERVER_URL.parse().unwrap();
let query = indoc::indoc! {
r#"
query filmsWithDirector {
allFilms {
films {
title
director
}
}
}
query filsmWithReleaseDate {
allFilms {
films {
title
releaseDate
}
}
}
"#
};
let request_params = RequestParameters {
query: query.into_document(),
operation_name: Some("invalidOperationName".to_string()), variables: Default::default(),
extensions: Default::default(),
};
#[derive(Debug, serde::Deserialize)]
struct QueryResponse {}
let req_fut = client
.post(server_url)
.send_graphql::<QueryResponse>(request_params);
let response = tokio::time::timeout(Duration::from_secs(30), req_fut)
.await
.expect("Request timed out")
.expect("Request failed");
assert_matches!(response, Err(ResponseError::Failure { errors }) => {
assert_eq!(errors.len(), 1);
assert!(errors[0].message.contains(r#"Unable to detect operation AST"#));
});
}
#[tokio::test]
async fn send_invalid_request_variable_coercion_failure() {
let client = reqwest::Client::new();
let server_url: Url = TEST_SERVER_URL.parse().unwrap();
let query = indoc::indoc! {
r#"query filmByFilmId($id: ID!) {
film(filmID: $id) {
title
director
releaseDate
}
}"#
};
let request_params = RequestParameters {
query: query.into_document(),
operation_name: None,
variables: serde_json::Map::from_iter([("id".to_string(), serde_json::Value::Null)]),
extensions: Default::default(),
};
#[derive(Debug, serde::Deserialize)]
struct QueryResponse {}
let req_fut = client
.post(server_url)
.send_graphql::<QueryResponse>(request_params);
let response = tokio::time::timeout(Duration::from_secs(30), req_fut)
.await
.expect("Request timed out")
.expect("Request failed");
assert_matches!(response, Err(ResponseError::Failure { errors }) => {
assert_eq!(errors.len(), 1);
assert!(errors[0].message.contains(r#"Variable "$id" of non-null type "ID!" must not be null"#));
});
}