From b673f5bd1fa9effd98a1fb24cb3796fa9f5e0d1a Mon Sep 17 00:00:00 2001 From: Magnus Hallin Date: Thu, 15 Jun 2017 10:36:07 +0200 Subject: [PATCH] Validate scalar values passed as variables Related to #49 - prevents panics in later stages of execution --- src/executor_tests/variables.rs | 131 ++++++++++++++++++++++++++++++++ src/validation/input_value.rs | 11 +++ 2 files changed, 142 insertions(+) diff --git a/src/executor_tests/variables.rs b/src/executor_tests/variables.rs index 5ec1b6b1..7204ddad 100644 --- a/src/executor_tests/variables.rs +++ b/src/executor_tests/variables.rs @@ -108,6 +108,14 @@ graphql_object!(TestType: () |&self| { field input_with_defaults(arg: InputWithDefaults) -> String { format!("a: {:?}", arg.a) } + + field integer_input(value: i64) -> String { + format!("value: {}", value) + } + + field float_input(value: f64) -> String { + format!("value: {}", value) + } }); fn run_variable_query(query: &str, vars: Variables, f: F) @@ -945,3 +953,126 @@ fn input_object_with_default_values() { Some(&Value::string(r#"a: 2"#))); }); } + + +mod integers { + use super::*; + + #[test] + fn positive_and_negative_should_work() { + run_variable_query( + r#"query q($var: Int!) { integerInput(value: $var) }"#, + vec![ + ("var".to_owned(), InputValue::int(1)), + ].into_iter().collect(), + |result| { + assert_eq!( + result.get("integerInput"), + Some(&Value::string(r#"value: 1"#))); + }); + + run_variable_query( + r#"query q($var: Int!) { integerInput(value: $var) }"#, + vec![ + ("var".to_owned(), InputValue::int(-1)), + ].into_iter().collect(), + |result| { + assert_eq!( + result.get("integerInput"), + Some(&Value::string(r#"value: -1"#))); + }); + } + + #[test] + fn does_not_coerce_from_float() { + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + + let query = r#"query q($var: Int!) { integerInput(value: $var) }"#; + let vars = vec![ + ("var".to_owned(), InputValue::float(10.0)), + ].into_iter().collect(); + + let error = ::execute(query, None, &schema, &vars, &()) + .unwrap_err(); + + assert_eq!(error, ValidationError(vec![ + RuleError::new( + r#"Variable "$var" got invalid value. Expected "Int"."#, + &[SourcePosition::new(8, 0, 8)], + ), + ])); + } + + #[test] + fn does_not_coerce_from_string() { + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + + let query = r#"query q($var: Int!) { integerInput(value: $var) }"#; + let vars = vec![ + ("var".to_owned(), InputValue::string("10")), + ].into_iter().collect(); + + let error = ::execute(query, None, &schema, &vars, &()) + .unwrap_err(); + + assert_eq!(error, ValidationError(vec![ + RuleError::new( + r#"Variable "$var" got invalid value. Expected "Int"."#, + &[SourcePosition::new(8, 0, 8)], + ), + ])); + } +} + + +mod floats { + use super::*; + + #[test] + fn float_values_should_work() { + run_variable_query( + r#"query q($var: Float!) { floatInput(value: $var) }"#, + vec![ + ("var".to_owned(), InputValue::float(10.0)), + ].into_iter().collect(), + |result| { + assert_eq!( + result.get("floatInput"), + Some(&Value::string(r#"value: 10"#))); + }); + } + + #[test] + fn coercion_from_integers_should_work() { + run_variable_query( + r#"query q($var: Float!) { floatInput(value: $var) }"#, + vec![ + ("var".to_owned(), InputValue::int(-1)), + ].into_iter().collect(), + |result| { + assert_eq!( + result.get("floatInput"), + Some(&Value::string(r#"value: -1"#))); + }); + } + + #[test] + fn does_not_coerce_from_string() { + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + + let query = r#"query q($var: Float!) { floatInput(value: $var) }"#; + let vars = vec![ + ("var".to_owned(), InputValue::string("10")), + ].into_iter().collect(); + + let error = ::execute(query, None, &schema, &vars, &()) + .unwrap_err(); + + assert_eq!(error, ValidationError(vec![ + RuleError::new( + r#"Variable "$var" got invalid value. Expected "Float"."#, + &[SourcePosition::new(8, 0, 8)], + ), + ])); + } +} diff --git a/src/validation/input_value.rs b/src/validation/input_value.rs index 02a19a80..766f2d18 100644 --- a/src/validation/input_value.rs +++ b/src/validation/input_value.rs @@ -133,6 +133,17 @@ fn unify_scalar<'a>( errors: &mut Vec, path: &Path<'a>, ) { + if !(meta.try_parse_fn)(value) { + push_unification_error( + errors, + var_name, + var_pos, + path, + &format!(r#"Expected "{}""#, meta.name), + ); + return; + } + match *value { InputValue::List(_) => push_unification_error(