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#[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 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 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 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 &(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 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}