Skip to main content

graphql_tools/validation/rules/
variables_in_allowed_position.rs

1use std::collections::{HashMap, HashSet};
2
3use crate::{
4    ast::{AstNodeWithName, OperationVisitor, OperationVisitorContext},
5    static_graphql::query::{Type, Value, VariableDefinition},
6    validation::utils::{ValidationError, ValidationErrorContext},
7};
8
9use super::ValidationRule;
10
11/// Variables in allowed position
12///
13/// Variable usages must be compatible with the arguments they are passed to.
14///
15/// See https://spec.graphql.org/draft/#sec-All-Variable-Usages-are-Allowed
16#[derive(Default)]
17pub struct VariablesInAllowedPosition<'doc> {
18    spreads: HashMap<Scope<'doc>, HashSet<&'doc str>>,
19    variable_usages: HashMap<Scope<'doc>, Vec<(&'doc str, &'doc Type, bool)>>,
20    variable_defs: HashMap<Scope<'doc>, Vec<&'doc VariableDefinition>>,
21    current_scope: Option<Scope<'doc>>,
22}
23
24impl<'doc> VariablesInAllowedPosition<'doc> {
25    pub fn new() -> Self {
26        VariablesInAllowedPosition {
27            spreads: HashMap::new(),
28            variable_usages: HashMap::new(),
29            variable_defs: HashMap::new(),
30            current_scope: None,
31        }
32    }
33
34    fn collect_incorrect_usages(
35        &self,
36        from: &Scope<'doc>,
37        var_defs: &[&VariableDefinition],
38        visitor_context: &mut OperationVisitorContext,
39        user_context: &mut ValidationErrorContext,
40        visited: &mut HashSet<Scope<'doc>>,
41    ) {
42        if visited.contains(from) {
43            return;
44        }
45
46        visited.insert(from.clone());
47
48        let usages = match self.variable_usages.get(from) {
49            Some(usages) => usages.as_slice(),
50            None => &[],
51        };
52        for (var_name, location_type, has_default) in usages {
53            let Some(var_def) = var_defs.iter().find(|var_def| var_def.name == *var_name) else {
54                continue;
55            };
56
57            let has_non_null_default = var_def
58                .default_value
59                .as_ref()
60                .is_some_and(|v| !matches!(v, Value::Null));
61
62            // https://spec.graphql.org/draft/#sec-All-Variable-Usages-are-Allowed
63            // If a variable definition has a non-null default value (e.g., $foo: String = "hi"),
64            // the variable can be used where a non-null type is expected,
65            // because when the variable is omitted, the default kicks in.
66            // An explicit null default ($foo: String = null) doesn't get this treatment,
67            // since null doesn't satisfy a non-null position.
68            let variable_type = match &var_def.var_type {
69                Type::NonNullType(_) => var_def.var_type.clone(),
70                t if has_non_null_default => Type::NonNullType(Box::new(t.clone())),
71                t => t.clone(),
72            };
73
74            // A default at the usage location permits a nullable variable in a
75            // non-null position, so ignore the location's outer nullability.
76            let effective_location_type = match (has_default, location_type) {
77                (true, Type::NonNullType(inner)) => inner.as_ref(),
78                _ => location_type,
79            };
80
81            if !visitor_context
82                .schema
83                .is_subtype(&variable_type, effective_location_type)
84            {
85                user_context.report_error(ValidationError {
86                    error_code: self.error_code(),
87                    message: format!(
88                        "Variable \"${}\" of type \"{}\" used in position expecting type \"{}\".",
89                        var_name, variable_type, location_type,
90                    ),
91                    locations: vec![var_def.position],
92                });
93            }
94        }
95
96        if let Some(spreads) = self.spreads.get(from) {
97            for spread in spreads {
98                self.collect_incorrect_usages(
99                    &Scope::Fragment(spread),
100                    var_defs,
101                    visitor_context,
102                    user_context,
103                    visited,
104                );
105            }
106        }
107    }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq, Hash)]
111pub enum Scope<'doc> {
112    Operation(Option<&'doc str>),
113    Fragment(&'doc str),
114}
115
116impl<'doc> OperationVisitor<'doc, ValidationErrorContext> for VariablesInAllowedPosition<'doc> {
117    fn leave_document(
118        &mut self,
119        visitor_context: &mut OperationVisitorContext<'doc>,
120        user_context: &mut ValidationErrorContext,
121        _: &crate::static_graphql::query::Document,
122    ) {
123        for (op_scope, var_defs) in &self.variable_defs {
124            self.collect_incorrect_usages(
125                op_scope,
126                var_defs,
127                visitor_context,
128                user_context,
129                &mut HashSet::new(),
130            );
131        }
132    }
133
134    fn enter_fragment_definition(
135        &mut self,
136        _: &mut OperationVisitorContext<'doc>,
137        _: &mut ValidationErrorContext,
138        fragment_definition: &'doc crate::static_graphql::query::FragmentDefinition,
139    ) {
140        self.current_scope = Some(Scope::Fragment(&fragment_definition.name));
141    }
142
143    fn enter_operation_definition(
144        &mut self,
145        _: &mut OperationVisitorContext<'doc>,
146        _: &mut ValidationErrorContext,
147        operation_definition: &'doc crate::static_graphql::query::OperationDefinition,
148    ) {
149        self.current_scope = Some(Scope::Operation(operation_definition.node_name()));
150    }
151
152    fn enter_fragment_spread(
153        &mut self,
154        _: &mut OperationVisitorContext<'doc>,
155        _: &mut ValidationErrorContext,
156        fragment_spread: &'doc crate::static_graphql::query::FragmentSpread,
157    ) {
158        if let Some(scope) = &self.current_scope {
159            self.spreads
160                .entry(scope.clone())
161                .or_default()
162                .insert(&fragment_spread.fragment_name);
163        }
164    }
165
166    fn enter_variable_definition(
167        &mut self,
168        _: &mut OperationVisitorContext<'doc>,
169        _: &mut ValidationErrorContext,
170        variable_definition: &'doc VariableDefinition,
171    ) {
172        if let Some(ref scope) = self.current_scope {
173            self.variable_defs
174                .entry(scope.clone())
175                .or_default()
176                .push(variable_definition);
177        }
178    }
179
180    fn enter_variable_value(
181        &mut self,
182        visitor_context: &mut OperationVisitorContext<'doc>,
183        _: &mut ValidationErrorContext,
184        variable_name: &'doc str,
185    ) {
186        if let (Some(scope), Some(input_type)) = (
187            &self.current_scope,
188            visitor_context.current_input_type_literal(),
189        ) {
190            let has_default = visitor_context.current_input_type_has_default();
191            self.variable_usages
192                .entry(scope.clone())
193                .or_default()
194                .push((variable_name, input_type, has_default));
195        }
196    }
197}
198
199impl ValidationRule for VariablesInAllowedPosition<'_> {
200    fn error_code(&self) -> &'static str {
201        "VariablesInAllowedPosition"
202    }
203
204    fn visitor<'doc>(&self) -> super::ValidationVisitor<'doc> {
205        Box::new(VariablesInAllowedPosition::new())
206    }
207}
208
209#[test]
210fn boolean_to_boolean() {
211    use crate::validation::test_utils::*;
212
213    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
214    let errors = test_operation_with_schema(
215        "query Query($booleanArg: Boolean)
216        {
217          complicatedArgs {
218            booleanArgField(booleanArg: $booleanArg)
219          }
220        }",
221        TEST_SCHEMA,
222        &mut plan,
223    );
224
225    assert_eq!(get_messages(&errors).len(), 0);
226}
227
228#[test]
229fn boolean_to_boolean_within_fragment() {
230    use crate::validation::test_utils::*;
231
232    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
233    let errors = test_operation_with_schema(
234        "fragment booleanArgFrag on ComplicatedArgs {
235          booleanArgField(booleanArg: $booleanArg)
236        }
237        query Query($booleanArg: Boolean)
238        {
239          complicatedArgs {
240            ...booleanArgFrag
241          }
242        }",
243        TEST_SCHEMA,
244        &mut plan,
245    );
246
247    assert_eq!(get_messages(&errors).len(), 0);
248
249    let errors = test_operation_with_schema(
250        "query Query($booleanArg: Boolean)
251      {
252        complicatedArgs {
253          ...booleanArgFrag
254        }
255      }
256      fragment booleanArgFrag on ComplicatedArgs {
257        booleanArgField(booleanArg: $booleanArg)
258      }",
259        TEST_SCHEMA,
260        &mut plan,
261    );
262
263    assert_eq!(get_messages(&errors).len(), 0);
264}
265
266#[test]
267fn boolean_nonnull_to_boolean() {
268    use crate::validation::test_utils::*;
269
270    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
271    let errors = test_operation_with_schema(
272        "query Query($nonNullBooleanArg: Boolean!)
273        {
274          complicatedArgs {
275            booleanArgField(booleanArg: $nonNullBooleanArg)
276          }
277        }",
278        TEST_SCHEMA,
279        &mut plan,
280    );
281
282    assert_eq!(get_messages(&errors).len(), 0);
283}
284
285#[test]
286fn string_list_to_string_list() {
287    use crate::validation::test_utils::*;
288
289    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
290    let errors = test_operation_with_schema(
291        "query Query($stringListVar: [String])
292        {
293          complicatedArgs {
294            stringListArgField(stringListArg: $stringListVar)
295          }
296        }",
297        TEST_SCHEMA,
298        &mut plan,
299    );
300
301    assert_eq!(get_messages(&errors).len(), 0);
302}
303
304#[test]
305fn string_list_nonnull_to_string_list() {
306    use crate::validation::test_utils::*;
307
308    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
309    let errors = test_operation_with_schema(
310        "query Query($stringListVar: [String!])
311        {
312          complicatedArgs {
313            stringListArgField(stringListArg: $stringListVar)
314          }
315        }",
316        TEST_SCHEMA,
317        &mut plan,
318    );
319
320    assert_eq!(get_messages(&errors).len(), 0);
321}
322
323#[test]
324fn string_to_string_list_in_item_position() {
325    use crate::validation::test_utils::*;
326
327    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
328    let errors = test_operation_with_schema(
329        "query Query($stringVar: String)
330        {
331          complicatedArgs {
332            stringListArgField(stringListArg: [$stringVar])
333          }
334        }",
335        TEST_SCHEMA,
336        &mut plan,
337    );
338
339    assert_eq!(get_messages(&errors).len(), 0);
340}
341
342#[test]
343fn string_nonnull_to_string_list_in_item_position() {
344    use crate::validation::test_utils::*;
345
346    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
347    let errors = test_operation_with_schema(
348        "query Query($stringVar: String!)
349        {
350          complicatedArgs {
351            stringListArgField(stringListArg: [$stringVar])
352          }
353        }",
354        TEST_SCHEMA,
355        &mut plan,
356    );
357
358    assert_eq!(get_messages(&errors).len(), 0);
359}
360
361#[test]
362fn complexinput_to_complexinput() {
363    use crate::validation::test_utils::*;
364
365    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
366    let errors = test_operation_with_schema(
367        "query Query($complexVar: ComplexInput)
368        {
369          complicatedArgs {
370            complexArgField(complexArg: $complexVar)
371          }
372        }",
373        TEST_SCHEMA,
374        &mut plan,
375    );
376
377    assert_eq!(get_messages(&errors).len(), 0);
378}
379
380#[test]
381fn complexinput_to_complexinput_in_field_position() {
382    use crate::validation::test_utils::*;
383
384    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
385    let errors = test_operation_with_schema(
386        "query Query($boolVar: Boolean = false)
387        {
388          complicatedArgs {
389            complexArgField(complexArg: { requiredArg: $boolVar })
390          }
391        }",
392        TEST_SCHEMA,
393        &mut plan,
394    );
395
396    let messages = get_messages(&errors);
397    assert_eq!(messages.len(), 0);
398}
399
400#[test]
401fn boolean_nonnull_to_boolean_nonnull_in_directive() {
402    use crate::validation::test_utils::*;
403
404    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
405    let errors = test_operation_with_schema(
406        "query Query($boolVar: Boolean!)
407        {
408          dog @include(if: $boolVar)
409        }",
410        TEST_SCHEMA,
411        &mut plan,
412    );
413
414    assert_eq!(get_messages(&errors).len(), 0);
415}
416
417#[test]
418fn int_to_int_nonnull() {
419    use crate::validation::test_utils::*;
420
421    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
422    let errors = test_operation_with_schema(
423        "query Query($intArg: Int) {
424          complicatedArgs {
425            nonNullIntArgField(nonNullIntArg: $intArg)
426          }
427        }",
428        TEST_SCHEMA,
429        &mut plan,
430    );
431
432    let messages = get_messages(&errors);
433    assert_eq!(messages.len(), 1);
434    assert_eq!(
435        messages,
436        vec!["Variable \"$intArg\" of type \"Int\" used in position expecting type \"Int!\"."]
437    )
438}
439
440#[test]
441fn int_to_int_nonnull_within_fragment() {
442    use crate::validation::test_utils::*;
443
444    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
445    let errors = test_operation_with_schema(
446        "fragment nonNullIntArgFieldFrag on ComplicatedArgs {
447          nonNullIntArgField(nonNullIntArg: $intArg)
448        }
449        query Query($intArg: Int) {
450          complicatedArgs {
451            ...nonNullIntArgFieldFrag
452          }
453        }",
454        TEST_SCHEMA,
455        &mut plan,
456    );
457
458    let messages = get_messages(&errors);
459    assert_eq!(messages.len(), 1);
460    assert_eq!(
461        messages,
462        vec!["Variable \"$intArg\" of type \"Int\" used in position expecting type \"Int!\"."]
463    )
464}
465
466#[test]
467fn int_to_int_nonnull_within_nested_fragment() {
468    use crate::validation::test_utils::*;
469
470    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
471    let errors = test_operation_with_schema(
472        "fragment outerFrag on ComplicatedArgs {
473          ...nonNullIntArgFieldFrag
474        }
475        fragment nonNullIntArgFieldFrag on ComplicatedArgs {
476          nonNullIntArgField(nonNullIntArg: $intArg)
477        }
478        query Query($intArg: Int) {
479          complicatedArgs {
480            ...outerFrag
481          }
482        }",
483        TEST_SCHEMA,
484        &mut plan,
485    );
486
487    let messages = get_messages(&errors);
488    assert_eq!(messages.len(), 1);
489    assert_eq!(
490        messages,
491        vec!["Variable \"$intArg\" of type \"Int\" used in position expecting type \"Int!\"."]
492    )
493}
494
495#[test]
496fn string_over_boolean() {
497    use crate::validation::test_utils::*;
498
499    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
500    let errors = test_operation_with_schema(
501        "query Query($stringVar: String) {
502          complicatedArgs {
503            booleanArgField(booleanArg: $stringVar)
504          }
505        }",
506        TEST_SCHEMA,
507        &mut plan,
508    );
509
510    let messages = get_messages(&errors);
511    assert_eq!(messages.len(), 1);
512    assert_eq!(
513        messages,
514        vec![
515      "Variable \"$stringVar\" of type \"String\" used in position expecting type \"Boolean\"."
516    ]
517    )
518}
519
520#[test]
521fn string_over_string_list() {
522    use crate::validation::test_utils::*;
523
524    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
525    let errors = test_operation_with_schema(
526        "query Query($stringVar: String) {
527          complicatedArgs {
528            stringListArgField(stringListArg: $stringVar)
529          }
530        }",
531        TEST_SCHEMA,
532        &mut plan,
533    );
534
535    let messages = get_messages(&errors);
536    assert_eq!(messages.len(), 1);
537    assert_eq!(
538        messages,
539        vec![
540      "Variable \"$stringVar\" of type \"String\" used in position expecting type \"[String]\"."
541    ]
542    )
543}
544
545#[test]
546fn boolean_to_boolean_nonnull_in_directive() {
547    use crate::validation::test_utils::*;
548
549    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
550    let errors = test_operation_with_schema(
551        "query Query($boolVar: Boolean) {
552          dog @include(if: $boolVar)
553        }",
554        TEST_SCHEMA,
555        &mut plan,
556    );
557
558    let messages = get_messages(&errors);
559    assert_eq!(messages.len(), 1);
560    assert_eq!(
561        messages,
562        vec![
563      "Variable \"$boolVar\" of type \"Boolean\" used in position expecting type \"Boolean!\"."
564    ]
565    )
566}
567
568#[test]
569fn string_to_boolean_nonnull_in_directive() {
570    use crate::validation::test_utils::*;
571
572    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
573    let errors = test_operation_with_schema(
574        "query Query($stringVar: String) {
575          dog @include(if: $stringVar)
576        }",
577        TEST_SCHEMA,
578        &mut plan,
579    );
580
581    let messages = get_messages(&errors);
582    assert_eq!(messages.len(), 1);
583    assert_eq!(
584        messages,
585        vec![
586      "Variable \"$stringVar\" of type \"String\" used in position expecting type \"Boolean!\"."
587    ]
588    )
589}
590
591#[test]
592fn string_list_to_string_nonnull_list() {
593    use crate::validation::test_utils::*;
594
595    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
596    let errors = test_operation_with_schema(
597        "query Query($stringListVar: [String])
598        {
599          complicatedArgs {
600            stringListNonNullArgField(stringListNonNullArg: $stringListVar)
601          }
602        }",
603        TEST_SCHEMA,
604        &mut plan,
605    );
606
607    let messages = get_messages(&errors);
608    assert_eq!(messages.len(), 1);
609    assert_eq!(messages, vec![
610      "Variable \"$stringListVar\" of type \"[String]\" used in position expecting type \"[String!]\"."
611    ])
612}
613
614#[test]
615fn int_to_int_non_null_with_null_default_value() {
616    use crate::validation::test_utils::*;
617
618    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
619    let errors = test_operation_with_schema(
620        "query Query($intVar: Int = null) {
621          complicatedArgs {
622            nonNullIntArgField(nonNullIntArg: $intVar)
623          }
624        }",
625        TEST_SCHEMA,
626        &mut plan,
627    );
628
629    let messages = get_messages(&errors);
630    assert_eq!(messages.len(), 1);
631    assert_eq!(
632        messages,
633        vec!["Variable \"$intVar\" of type \"Int\" used in position expecting type \"Int!\"."]
634    )
635}
636
637#[test]
638fn int_to_int_non_null_with_default_value() {
639    use crate::validation::test_utils::*;
640
641    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
642    let errors = test_operation_with_schema(
643        "query Query($intVar: Int = 1) {
644          complicatedArgs {
645            nonNullIntArgField(nonNullIntArg: $intVar)
646          }
647        }",
648        TEST_SCHEMA,
649        &mut plan,
650    );
651
652    let messages = get_messages(&errors);
653    assert_eq!(messages.len(), 0);
654}
655
656#[test]
657fn int_to_int_non_null_where_argument_with_default_value() {
658    use crate::validation::test_utils::*;
659
660    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
661    let errors = test_operation_with_schema(
662        "query Query($intVar: Int) {
663          complicatedArgs {
664            nonNullFieldWithDefault(arg: $intVar)
665          }
666        }",
667        TEST_SCHEMA,
668        &mut plan,
669    );
670
671    let messages = get_messages(&errors);
672    assert_eq!(messages.len(), 0);
673}
674
675#[test]
676fn list_of_non_null_enum_with_single_enum_default_value() {
677    use crate::validation::test_utils::*;
678
679    // A single-value default on a list-typed variable is valid per
680    // GraphQL spec (single values coerce to a one-item list). The router used to
681    // reject this with the malformed type "T!!" because the expected-type
682    // computation dropped the list wrapper and double-wrapped NonNull.
683    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
684    let errors = test_operation_with_schema(
685        "query Query($enumListVar: [FurColor!] = BROWN) {
686          complicatedArgs {
687            enumListArgField(enumListArg: $enumListVar)
688          }
689        }",
690        // Augment the schema with a list-of-non-null-enum argument.
691        &(TEST_SCHEMA.replace(
692            "enumArgField(enumArg: FurColor): String",
693            "enumArgField(enumArg: FurColor): String\n  enumListArgField(enumListArg: [FurColor!]): String",
694        )),
695        &mut plan,
696    );
697
698    let messages = get_messages(&errors);
699    assert_eq!(messages, Vec::<&String>::new());
700}
701
702#[test]
703fn list_of_non_null_with_default_value_used_in_non_null_list_position() {
704    use crate::validation::test_utils::*;
705
706    // A list variable with a non-null default value should be usable in a
707    // non-null list position. Before the fix, the expected type for the
708    // subtype check was "T!!" (invalid) instead of "[T!]!".
709    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
710    let errors = test_operation_with_schema(
711        "query Query($enumListVar: [FurColor!] = BROWN) {
712          complicatedArgs {
713            requiredEnumListArgField(enumListArg: $enumListVar)
714          }
715        }",
716        &(TEST_SCHEMA.replace(
717            "enumArgField(enumArg: FurColor): String",
718            "enumArgField(enumArg: FurColor): String\n  requiredEnumListArgField(enumListArg: [FurColor!]!): String",
719        )),
720        &mut plan,
721    );
722
723    let messages = get_messages(&errors);
724    assert_eq!(messages, Vec::<&String>::new());
725}
726
727#[test]
728fn boolean_to_boolean_non_null_with_default_value() {
729    use crate::validation::test_utils::*;
730
731    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
732    let errors = test_operation_with_schema(
733        "query Query($boolVar: Boolean = false) {
734          dog @include(if: $boolVar)
735        }",
736        TEST_SCHEMA,
737        &mut plan,
738    );
739
740    let messages = get_messages(&errors);
741    assert_eq!(messages.len(), 0);
742}
743
744#[test]
745fn nullable_enum_to_non_null_enum_with_default_on_argument() {
746    use crate::validation::test_utils::*;
747
748    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
749    let errors = test_operation_with_schema(
750        "query Query($currency: FurColor) {
751          complicatedArgs {
752            enumArgFieldWithDefault(enumArg: $currency)
753          }
754        }",
755        &(TEST_SCHEMA.replace(
756            "enumArgField(enumArg: FurColor): String",
757            "enumArgField(enumArg: FurColor): String\n  enumArgFieldWithDefault(enumArg: FurColor! = BROWN): String",
758        )),
759        &mut plan,
760    );
761
762    let messages = get_messages(&errors);
763    assert_eq!(messages.len(), 0);
764}
765
766#[test]
767fn nullable_int_to_non_null_int_with_default_on_argument() {
768    use crate::validation::test_utils::*;
769
770    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
771    let errors = test_operation_with_schema(
772        "query Query($intVar: Int) {
773          complicatedArgs {
774            nonNullFieldWithDefault(arg: $intVar)
775          }
776        }",
777        TEST_SCHEMA,
778        &mut plan,
779    );
780
781    let messages = get_messages(&errors);
782    assert_eq!(messages.len(), 0);
783}
784
785#[test]
786fn nullable_int_to_non_null_input_field_with_default() {
787    use crate::validation::test_utils::*;
788
789    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
790    let errors = test_operation_with_schema(
791        "query Query($intVar: Int) {
792          set(input: { value: $intVar })
793        }",
794        "input Input {
795          value: Int! = 1
796        }
797        type Query {
798          set(input: Input): String
799        }",
800        &mut plan,
801    );
802
803    let messages = get_messages(&errors);
804    assert_eq!(messages.len(), 0);
805}
806
807#[test]
808fn string_to_non_null_int_with_default_on_argument() {
809    use crate::validation::test_utils::*;
810
811    let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
812    let errors = test_operation_with_schema(
813        "query Query($stringVar: String) {
814          complicatedArgs {
815            nonNullFieldWithDefault(arg: $stringVar)
816          }
817        }",
818        TEST_SCHEMA,
819        &mut plan,
820    );
821
822    let messages = get_messages(&errors);
823    assert_eq!(messages.len(), 1);
824    assert_eq!(
825        messages,
826        vec![
827            "Variable \"$stringVar\" of type \"String\" used in position expecting type \"Int!\"."
828        ]
829    )
830}