Add directive tests, fix some bugs
This commit is contained in:
parent
478c7b7819
commit
251f957a7f
4 changed files with 278 additions and 8 deletions
251
src/executor_tests/directives.rs
Normal file
251
src/executor_tests/directives.rs
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use value::Value;
|
||||||
|
use ast::InputValue;
|
||||||
|
use schema::model::RootNode;
|
||||||
|
|
||||||
|
struct TestType;
|
||||||
|
|
||||||
|
graphql_object!(TestType: () |&self| {
|
||||||
|
field a() -> &str {
|
||||||
|
"a"
|
||||||
|
}
|
||||||
|
|
||||||
|
field b() -> &str {
|
||||||
|
"b"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fn run_variable_query<F>(query: &str, vars: HashMap<String, InputValue>, f: F)
|
||||||
|
where F: Fn(&HashMap<String, Value>) -> ()
|
||||||
|
{
|
||||||
|
let schema = RootNode::new(TestType, ());
|
||||||
|
|
||||||
|
let (result, errs) = ::execute(query, None, &schema, &vars, &())
|
||||||
|
.expect("Execution failed");
|
||||||
|
|
||||||
|
assert_eq!(errs, []);
|
||||||
|
|
||||||
|
println!("Result: {:?}", result);
|
||||||
|
|
||||||
|
let obj = result.as_object_value().expect("Result is not an object");
|
||||||
|
|
||||||
|
f(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_query<F>(query: &str, f: F)
|
||||||
|
where F: Fn(&HashMap<String, Value>) -> ()
|
||||||
|
{
|
||||||
|
run_variable_query(query, HashMap::new(), f);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scalar_include_true() {
|
||||||
|
run_query(
|
||||||
|
"{ a, b @include(if: true) }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), Some(&Value::string("b")));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scalar_include_false() {
|
||||||
|
run_query(
|
||||||
|
"{ a, b @include(if: false) }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), None);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scalar_skip_false() {
|
||||||
|
run_query(
|
||||||
|
"{ a, b @skip(if: false) }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), Some(&Value::string("b")));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scalar_skip_true() {
|
||||||
|
run_query(
|
||||||
|
"{ a, b @skip(if: true) }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), None);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fragment_spread_include_true() {
|
||||||
|
run_query(
|
||||||
|
"{ a, ...Frag @include(if: true) } fragment Frag on TestType { b }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), Some(&Value::string("b")));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fragment_spread_include_false() {
|
||||||
|
run_query(
|
||||||
|
"{ a, ...Frag @include(if: false) } fragment Frag on TestType { b }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), None);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fragment_spread_skip_false() {
|
||||||
|
run_query(
|
||||||
|
"{ a, ...Frag @skip(if: false) } fragment Frag on TestType { b }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), Some(&Value::string("b")));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fragment_spread_skip_true() {
|
||||||
|
run_query(
|
||||||
|
"{ a, ...Frag @skip(if: true) } fragment Frag on TestType { b }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), None);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inline_fragment_include_true() {
|
||||||
|
run_query(
|
||||||
|
"{ a, ... on TestType @include(if: true) { b } }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), Some(&Value::string("b")));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inline_fragment_include_false() {
|
||||||
|
run_query(
|
||||||
|
"{ a, ... on TestType @include(if: false) { b } }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), None);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inline_fragment_skip_false() {
|
||||||
|
run_query(
|
||||||
|
"{ a, ... on TestType @skip(if: false) { b } }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), Some(&Value::string("b")));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inline_fragment_skip_true() {
|
||||||
|
run_query(
|
||||||
|
"{ a, ... on TestType @skip(if: true) { b } }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), None);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn anonymous_inline_fragment_include_true() {
|
||||||
|
run_query(
|
||||||
|
"{ a, ... @include(if: true) { b } }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), Some(&Value::string("b")));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn anonymous_inline_fragment_include_false() {
|
||||||
|
run_query(
|
||||||
|
"{ a, ... @include(if: false) { b } }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), None);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn anonymous_inline_fragment_skip_false() {
|
||||||
|
run_query(
|
||||||
|
"{ a, ... @skip(if: false) { b } }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), Some(&Value::string("b")));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn anonymous_inline_fragment_skip_true() {
|
||||||
|
run_query(
|
||||||
|
"{ a, ... @skip(if: true) { b } }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), None);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scalar_include_true_skip_true() {
|
||||||
|
run_query(
|
||||||
|
"{ a, b @include(if: true) @skip(if: true) }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), None);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scalar_include_true_skip_false() {
|
||||||
|
run_query(
|
||||||
|
"{ a, b @include(if: true) @skip(if: false) }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), Some(&Value::string("b")));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scalar_include_false_skip_true() {
|
||||||
|
run_query(
|
||||||
|
"{ a, b @include(if: false) @skip(if: true) }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), None);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scalar_include_false_skip_false() {
|
||||||
|
run_query(
|
||||||
|
"{ a, b @include(if: false) @skip(if: false) }",
|
||||||
|
|result| {
|
||||||
|
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||||
|
assert_eq!(result.get("b"), None);
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
mod introspection;
|
mod introspection;
|
||||||
mod variables;
|
mod variables;
|
||||||
mod enums;
|
mod enums;
|
||||||
|
mod directives;
|
||||||
|
|
|
@ -171,6 +171,20 @@ fn parse_fragment<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Selec
|
||||||
directives: directives.map(|s| s.item),
|
directives: directives.map(|s| s.item),
|
||||||
})))
|
})))
|
||||||
},
|
},
|
||||||
|
Token::At => {
|
||||||
|
let directives = try!(parse_directives(parser));
|
||||||
|
let selection_set = try!(parse_selection_set(parser));
|
||||||
|
|
||||||
|
Ok(Selection::InlineFragment(
|
||||||
|
Spanning::start_end(
|
||||||
|
&start_pos.clone(),
|
||||||
|
&selection_set.end,
|
||||||
|
InlineFragment {
|
||||||
|
type_condition: None,
|
||||||
|
directives: directives.map(|s| s.item),
|
||||||
|
selection_set: selection_set.item,
|
||||||
|
})))
|
||||||
|
},
|
||||||
_ => Err(parser.next().map(ParseError::UnexpectedToken)),
|
_ => Err(parser.next().map(ParseError::UnexpectedToken)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,12 +218,12 @@ pub trait GraphQLType<CtxT>: Sized {
|
||||||
///
|
///
|
||||||
/// The executor can be used to drive selections into sub-objects.
|
/// The executor can be used to drive selections into sub-objects.
|
||||||
///
|
///
|
||||||
/// The default implementation panics through `unimplemented!()`.
|
/// The default implementation panics.
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn resolve_field(&self, field_name: &str, arguments: &Arguments, executor: &mut Executor<CtxT>)
|
fn resolve_field(&self, field_name: &str, arguments: &Arguments, executor: &mut Executor<CtxT>)
|
||||||
-> ExecutionResult
|
-> ExecutionResult
|
||||||
{
|
{
|
||||||
unimplemented!()
|
panic!("resolve_field must be implemented by object types");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve this interface or union into a concrete type
|
/// Resolve this interface or union into a concrete type
|
||||||
|
@ -231,18 +231,22 @@ pub trait GraphQLType<CtxT>: Sized {
|
||||||
/// Try to resolve the current type into the type name provided. If the
|
/// Try to resolve the current type into the type name provided. If the
|
||||||
/// type matches, pass the instance along to `executor.resolve`.
|
/// type matches, pass the instance along to `executor.resolve`.
|
||||||
///
|
///
|
||||||
/// The default implementation panics through `unimplemented()`.
|
/// The default implementation panics.
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn resolve_into_type(&self, type_name: &str, selection_set: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> ExecutionResult {
|
fn resolve_into_type(&self, type_name: &str, selection_set: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> ExecutionResult {
|
||||||
unimplemented!();
|
if Self::name().unwrap() == type_name {
|
||||||
|
Ok(self.resolve(selection_set, executor))
|
||||||
|
} else {
|
||||||
|
panic!("resolve_into_type must be implemented by unions and interfaces");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the concrete type name for this instance/union.
|
/// Return the concrete type name for this instance/union.
|
||||||
///
|
///
|
||||||
/// The default implementation panics through `unimplemented()`.
|
/// The default implementation panics.
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn concrete_type_name(&self, context: &CtxT) -> String {
|
fn concrete_type_name(&self, context: &CtxT) -> String {
|
||||||
unimplemented!();
|
panic!("concrete_type_name must be implemented by unions and interfaces");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve the provided selection set against the current object.
|
/// Resolve the provided selection set against the current object.
|
||||||
|
@ -254,7 +258,7 @@ pub trait GraphQLType<CtxT>: Sized {
|
||||||
///
|
///
|
||||||
/// The default implementation uses `resolve_field` to resolve all fields,
|
/// The default implementation uses `resolve_field` to resolve all fields,
|
||||||
/// including those through fragment expansion, for object types. For
|
/// including those through fragment expansion, for object types. For
|
||||||
/// non-object types, this method panics through `unimplemented!()`.
|
/// non-object types, this method panics.
|
||||||
fn resolve(&self, selection_set: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> Value {
|
fn resolve(&self, selection_set: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> Value {
|
||||||
if let Some(selection_set) = selection_set {
|
if let Some(selection_set) = selection_set {
|
||||||
let mut result = HashMap::new();
|
let mut result = HashMap::new();
|
||||||
|
@ -262,7 +266,7 @@ pub trait GraphQLType<CtxT>: Sized {
|
||||||
Value::object(result)
|
Value::object(result)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
unimplemented!();
|
panic!("resolve() must be implemented by non-object output types");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue