Add Rocket integration to the HTTP test suite
This commit is contained in:
parent
565f14ceef
commit
3f277ba8b4
3 changed files with 155 additions and 5 deletions
|
@ -38,7 +38,7 @@ script:
|
||||||
- export TEST_FEATURES="iron-handlers expose-test-schema"
|
- export TEST_FEATURES="iron-handlers expose-test-schema"
|
||||||
- |
|
- |
|
||||||
if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then
|
if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then
|
||||||
export TEST_FEATURES="$TEST_FEATURES rocket-handlers"
|
export TEST_FEATURES="$TEST_FEATURES rocket-handlers rocket/testing"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- cargo test --verbose --features "$TEST_FEATURES"
|
- cargo test --verbose --features "$TEST_FEATURES"
|
||||||
|
|
|
@ -19,11 +19,20 @@ fn graphiql() -> content::HTML<String> {
|
||||||
rocket_handlers::graphiql_source("/graphql")
|
rocket_handlers::graphiql_source("/graphql")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/graphql?<request>")]
|
||||||
|
fn get_graphql_handler(
|
||||||
|
context: State<Database>,
|
||||||
|
request: rocket_handlers::GraphQLRequest,
|
||||||
|
schema: State<Schema>,
|
||||||
|
) -> rocket_handlers::GraphQLResponse {
|
||||||
|
request.execute(&schema, &context)
|
||||||
|
}
|
||||||
|
|
||||||
#[post("/graphql", data="<request>")]
|
#[post("/graphql", data="<request>")]
|
||||||
fn post_graphql_handler(
|
fn post_graphql_handler(
|
||||||
context: State<Database>,
|
context: State<Database>,
|
||||||
request: rocket_handlers::GraphQLRequest,
|
request: rocket_handlers::GraphQLRequest,
|
||||||
schema: State<Schema>
|
schema: State<Schema>,
|
||||||
) -> rocket_handlers::GraphQLResponse {
|
) -> rocket_handlers::GraphQLResponse {
|
||||||
request.execute(&schema, &context)
|
request.execute(&schema, &context)
|
||||||
}
|
}
|
||||||
|
@ -32,6 +41,6 @@ fn main() {
|
||||||
rocket::ignite()
|
rocket::ignite()
|
||||||
.manage(Database::new())
|
.manage(Database::new())
|
||||||
.manage(Schema::new(Database::new(), EmptyMutation::<Database>::new()))
|
.manage(Schema::new(Database::new(), EmptyMutation::<Database>::new()))
|
||||||
.mount("/", routes![graphiql, post_graphql_handler])
|
.mount("/", routes![graphiql, get_graphql_handler, post_graphql_handler])
|
||||||
.launch();
|
.launch();
|
||||||
}
|
}
|
|
@ -1,14 +1,17 @@
|
||||||
use std::io::{Cursor, Read};
|
use std::io::{Cursor, Read};
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
|
||||||
use rocket::Request;
|
use rocket::Request;
|
||||||
use rocket::data::{FromData, Outcome};
|
use rocket::request::{FromForm, FormItems, FromFormValue};
|
||||||
|
use rocket::data::{FromData, Outcome as FromDataOutcome};
|
||||||
use rocket::response::{Responder, Response, content};
|
use rocket::response::{Responder, Response, content};
|
||||||
use rocket::http::{ContentType, Status};
|
use rocket::http::{ContentType, Status};
|
||||||
use rocket::Data;
|
use rocket::Data;
|
||||||
use rocket::Outcome::{Forward, Failure, Success};
|
use rocket::Outcome::{Forward, Failure, Success};
|
||||||
|
|
||||||
|
use ::InputValue;
|
||||||
use ::http;
|
use ::http;
|
||||||
|
|
||||||
use types::base::GraphQLType;
|
use types::base::GraphQLType;
|
||||||
|
@ -39,10 +42,62 @@ impl GraphQLRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'f> FromForm<'f> for GraphQLRequest {
|
||||||
|
type Error = String;
|
||||||
|
|
||||||
|
fn from_form_items(form_items: &mut FormItems<'f>) -> Result<Self, String> {
|
||||||
|
let mut query = None;
|
||||||
|
let mut operation_name = None;
|
||||||
|
let mut variables = None;
|
||||||
|
|
||||||
|
for (key, value) in form_items {
|
||||||
|
match key {
|
||||||
|
"query" => {
|
||||||
|
if query.is_some() {
|
||||||
|
return Err("Query parameter must not occur more than once".to_owned());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
query = Some(String::from_form_value(value)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"operation_name" => {
|
||||||
|
if operation_name.is_some() {
|
||||||
|
return Err("Operation name parameter must not occur more than once".to_owned());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
operation_name = Some(String::from_form_value(value)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"variables" => {
|
||||||
|
if variables.is_some() {
|
||||||
|
return Err("Variables parameter must not occur more than once".to_owned());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
variables = Some(serde_json::from_str::<InputValue>(&String::from_form_value(value)?)
|
||||||
|
.map_err(|err| err.description().to_owned())?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(query) = query {
|
||||||
|
Ok(GraphQLRequest(http::GraphQLRequest::new(
|
||||||
|
query,
|
||||||
|
operation_name,
|
||||||
|
variables
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Err("Query parameter missing".to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromData for GraphQLRequest {
|
impl FromData for GraphQLRequest {
|
||||||
type Error = String;
|
type Error = String;
|
||||||
|
|
||||||
fn from_data(request: &Request, data: Data) -> Outcome<Self, String> {
|
fn from_data(request: &Request, data: Data) -> FromDataOutcome<Self, String> {
|
||||||
if !request.content_type().map_or(false, |ct| ct.is_json()) {
|
if !request.content_type().map_or(false, |ct| ct.is_json()) {
|
||||||
return Forward(data);
|
return Forward(data);
|
||||||
}
|
}
|
||||||
|
@ -72,3 +127,89 @@ impl<'r> Responder<'r> for GraphQLResponse {
|
||||||
.finalize())
|
.finalize())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use rocket;
|
||||||
|
use rocket::Rocket;
|
||||||
|
use rocket::http::{ContentType, Method};
|
||||||
|
use rocket::State;
|
||||||
|
use rocket::testing::MockRequest;
|
||||||
|
|
||||||
|
use ::RootNode;
|
||||||
|
use ::tests::model::Database;
|
||||||
|
use ::http::tests as http_tests;
|
||||||
|
use types::scalars::EmptyMutation;
|
||||||
|
|
||||||
|
type Schema = RootNode<'static, Database, EmptyMutation<Database>>;
|
||||||
|
|
||||||
|
#[get("/?<request>")]
|
||||||
|
fn get_graphql_handler(
|
||||||
|
context: State<Database>,
|
||||||
|
request: super::GraphQLRequest,
|
||||||
|
schema: State<Schema>,
|
||||||
|
) -> super::GraphQLResponse {
|
||||||
|
request.execute(&schema, &context)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/", data="<request>")]
|
||||||
|
fn post_graphql_handler(
|
||||||
|
context: State<Database>,
|
||||||
|
request: super::GraphQLRequest,
|
||||||
|
schema: State<Schema>,
|
||||||
|
) -> super::GraphQLResponse {
|
||||||
|
request.execute(&schema, &context)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestRocketIntegration {
|
||||||
|
rocket: Rocket,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl http_tests::HTTPIntegration for TestRocketIntegration
|
||||||
|
{
|
||||||
|
fn get(&self, url: &str) -> http_tests::TestResponse {
|
||||||
|
make_test_response(&self.rocket, MockRequest::new(
|
||||||
|
Method::Get,
|
||||||
|
url))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post(&self, url: &str, body: &str) -> http_tests::TestResponse {
|
||||||
|
make_test_response(
|
||||||
|
&self.rocket,
|
||||||
|
MockRequest::new(
|
||||||
|
Method::Post,
|
||||||
|
url,
|
||||||
|
).header(ContentType::JSON).body(body))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rocket_integration() {
|
||||||
|
let integration = TestRocketIntegration {
|
||||||
|
rocket: make_rocket(),
|
||||||
|
};
|
||||||
|
|
||||||
|
http_tests::run_http_test_suite(&integration);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_rocket() -> Rocket {
|
||||||
|
rocket::ignite()
|
||||||
|
.manage(Database::new())
|
||||||
|
.manage(Schema::new(Database::new(), EmptyMutation::<Database>::new()))
|
||||||
|
.mount("/", routes![post_graphql_handler, get_graphql_handler])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_test_response<'r>(rocket: &'r Rocket, mut request: MockRequest<'r>) -> http_tests::TestResponse {
|
||||||
|
let mut response = request.dispatch_with(&rocket);
|
||||||
|
let status_code = response.status().code as i32;
|
||||||
|
let content_type = response.header_values("content-type").collect::<Vec<_>>().into_iter().next()
|
||||||
|
.expect("No content type header from handler").to_owned();
|
||||||
|
let body = response.body().expect("No body returned from GraphQL handler").into_string();
|
||||||
|
|
||||||
|
http_tests::TestResponse {
|
||||||
|
status_code: status_code,
|
||||||
|
body: body,
|
||||||
|
content_type: content_type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue