Add Rocket integration to the HTTP test suite

This commit is contained in:
Magnus Hallin 2017-06-14 18:21:05 +02:00
parent 565f14ceef
commit 3f277ba8b4
3 changed files with 155 additions and 5 deletions

View file

@ -38,7 +38,7 @@ script:
- export TEST_FEATURES="iron-handlers expose-test-schema"
- |
if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then
export TEST_FEATURES="$TEST_FEATURES rocket-handlers"
export TEST_FEATURES="$TEST_FEATURES rocket-handlers rocket/testing"
fi
- cargo test --verbose --features "$TEST_FEATURES"

View file

@ -19,11 +19,20 @@ fn graphiql() -> content::HTML<String> {
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>")]
fn post_graphql_handler(
context: State<Database>,
request: rocket_handlers::GraphQLRequest,
schema: State<Schema>
schema: State<Schema>,
) -> rocket_handlers::GraphQLResponse {
request.execute(&schema, &context)
}
@ -32,6 +41,6 @@ fn main() {
rocket::ignite()
.manage(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();
}

View file

@ -1,14 +1,17 @@
use std::io::{Cursor, Read};
use std::error::Error;
use serde_json;
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::http::{ContentType, Status};
use rocket::Data;
use rocket::Outcome::{Forward, Failure, Success};
use ::InputValue;
use ::http;
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 {
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()) {
return Forward(data);
}
@ -72,3 +127,89 @@ impl<'r> Responder<'r> for GraphQLResponse {
.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,
}
}
}