Skip to content

Commit 3f277ba

Browse files
committed
Add Rocket integration to the HTTP test suite
1 parent 565f14c commit 3f277ba

File tree

3 files changed

+155
-5
lines changed

3 files changed

+155
-5
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ script:
3838
- export TEST_FEATURES="iron-handlers expose-test-schema"
3939
- |
4040
if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then
41-
export TEST_FEATURES="$TEST_FEATURES rocket-handlers"
41+
export TEST_FEATURES="$TEST_FEATURES rocket-handlers rocket/testing"
4242
fi
4343
4444
- cargo test --verbose --features "$TEST_FEATURES"

examples/rocket-server.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,20 @@ fn graphiql() -> content::HTML<String> {
1919
rocket_handlers::graphiql_source("/graphql")
2020
}
2121

22+
#[get("/graphql?<request>")]
23+
fn get_graphql_handler(
24+
context: State<Database>,
25+
request: rocket_handlers::GraphQLRequest,
26+
schema: State<Schema>,
27+
) -> rocket_handlers::GraphQLResponse {
28+
request.execute(&schema, &context)
29+
}
30+
2231
#[post("/graphql", data="<request>")]
2332
fn post_graphql_handler(
2433
context: State<Database>,
2534
request: rocket_handlers::GraphQLRequest,
26-
schema: State<Schema>
35+
schema: State<Schema>,
2736
) -> rocket_handlers::GraphQLResponse {
2837
request.execute(&schema, &context)
2938
}
@@ -32,6 +41,6 @@ fn main() {
3241
rocket::ignite()
3342
.manage(Database::new())
3443
.manage(Schema::new(Database::new(), EmptyMutation::<Database>::new()))
35-
.mount("/", routes![graphiql, post_graphql_handler])
44+
.mount("/", routes![graphiql, get_graphql_handler, post_graphql_handler])
3645
.launch();
3746
}

src/integrations/rocket_handlers.rs

Lines changed: 143 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
use std::io::{Cursor, Read};
2+
use std::error::Error;
23

34
use serde_json;
45

56
use rocket::Request;
6-
use rocket::data::{FromData, Outcome};
7+
use rocket::request::{FromForm, FormItems, FromFormValue};
8+
use rocket::data::{FromData, Outcome as FromDataOutcome};
79
use rocket::response::{Responder, Response, content};
810
use rocket::http::{ContentType, Status};
911
use rocket::Data;
1012
use rocket::Outcome::{Forward, Failure, Success};
1113

14+
use ::InputValue;
1215
use ::http;
1316

1417
use types::base::GraphQLType;
@@ -39,10 +42,62 @@ impl GraphQLRequest {
3942
}
4043
}
4144

45+
impl<'f> FromForm<'f> for GraphQLRequest {
46+
type Error = String;
47+
48+
fn from_form_items(form_items: &mut FormItems<'f>) -> Result<Self, String> {
49+
let mut query = None;
50+
let mut operation_name = None;
51+
let mut variables = None;
52+
53+
for (key, value) in form_items {
54+
match key {
55+
"query" => {
56+
if query.is_some() {
57+
return Err("Query parameter must not occur more than once".to_owned());
58+
}
59+
else {
60+
query = Some(String::from_form_value(value)?);
61+
}
62+
}
63+
"operation_name" => {
64+
if operation_name.is_some() {
65+
return Err("Operation name parameter must not occur more than once".to_owned());
66+
}
67+
else {
68+
operation_name = Some(String::from_form_value(value)?);
69+
}
70+
}
71+
"variables" => {
72+
if variables.is_some() {
73+
return Err("Variables parameter must not occur more than once".to_owned());
74+
}
75+
else {
76+
variables = Some(serde_json::from_str::<InputValue>(&String::from_form_value(value)?)
77+
.map_err(|err| err.description().to_owned())?);
78+
}
79+
}
80+
_ => {}
81+
}
82+
}
83+
84+
if let Some(query) = query {
85+
Ok(GraphQLRequest(http::GraphQLRequest::new(
86+
query,
87+
operation_name,
88+
variables
89+
)))
90+
}
91+
else {
92+
Err("Query parameter missing".to_owned())
93+
}
94+
}
95+
}
96+
4297
impl FromData for GraphQLRequest {
4398
type Error = String;
4499

45-
fn from_data(request: &Request, data: Data) -> Outcome<Self, String> {
100+
fn from_data(request: &Request, data: Data) -> FromDataOutcome<Self, String> {
46101
if !request.content_type().map_or(false, |ct| ct.is_json()) {
47102
return Forward(data);
48103
}
@@ -72,3 +127,89 @@ impl<'r> Responder<'r> for GraphQLResponse {
72127
.finalize())
73128
}
74129
}
130+
131+
#[cfg(test)]
132+
mod tests {
133+
use rocket;
134+
use rocket::Rocket;
135+
use rocket::http::{ContentType, Method};
136+
use rocket::State;
137+
use rocket::testing::MockRequest;
138+
139+
use ::RootNode;
140+
use ::tests::model::Database;
141+
use ::http::tests as http_tests;
142+
use types::scalars::EmptyMutation;
143+
144+
type Schema = RootNode<'static, Database, EmptyMutation<Database>>;
145+
146+
#[get("/?<request>")]
147+
fn get_graphql_handler(
148+
context: State<Database>,
149+
request: super::GraphQLRequest,
150+
schema: State<Schema>,
151+
) -> super::GraphQLResponse {
152+
request.execute(&schema, &context)
153+
}
154+
155+
#[post("/", data="<request>")]
156+
fn post_graphql_handler(
157+
context: State<Database>,
158+
request: super::GraphQLRequest,
159+
schema: State<Schema>,
160+
) -> super::GraphQLResponse {
161+
request.execute(&schema, &context)
162+
}
163+
164+
struct TestRocketIntegration {
165+
rocket: Rocket,
166+
}
167+
168+
impl http_tests::HTTPIntegration for TestRocketIntegration
169+
{
170+
fn get(&self, url: &str) -> http_tests::TestResponse {
171+
make_test_response(&self.rocket, MockRequest::new(
172+
Method::Get,
173+
url))
174+
}
175+
176+
fn post(&self, url: &str, body: &str) -> http_tests::TestResponse {
177+
make_test_response(
178+
&self.rocket,
179+
MockRequest::new(
180+
Method::Post,
181+
url,
182+
).header(ContentType::JSON).body(body))
183+
}
184+
}
185+
186+
#[test]
187+
fn test_rocket_integration() {
188+
let integration = TestRocketIntegration {
189+
rocket: make_rocket(),
190+
};
191+
192+
http_tests::run_http_test_suite(&integration);
193+
}
194+
195+
fn make_rocket() -> Rocket {
196+
rocket::ignite()
197+
.manage(Database::new())
198+
.manage(Schema::new(Database::new(), EmptyMutation::<Database>::new()))
199+
.mount("/", routes![post_graphql_handler, get_graphql_handler])
200+
}
201+
202+
fn make_test_response<'r>(rocket: &'r Rocket, mut request: MockRequest<'r>) -> http_tests::TestResponse {
203+
let mut response = request.dispatch_with(&rocket);
204+
let status_code = response.status().code as i32;
205+
let content_type = response.header_values("content-type").collect::<Vec<_>>().into_iter().next()
206+
.expect("No content type header from handler").to_owned();
207+
let body = response.body().expect("No body returned from GraphQL handler").into_string();
208+
209+
http_tests::TestResponse {
210+
status_code: status_code,
211+
body: body,
212+
content_type: content_type,
213+
}
214+
}
215+
}

0 commit comments

Comments
 (0)