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<VariableUsage<'doc>>>,
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, one_of_parent) 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 if let Some(one_of_type) = one_of_parent {
99 if !matches!(var_def.var_type, Type::NonNullType(_)) {
100 user_context.report_error(ValidationError {
101 error_code: self.error_code(),
102 message: format!(
103 "Variable \"${}\" is of type \"{}\" but must be non-nullable to be used for OneOf Input Object \"{}\".",
104 var_name, var_def.var_type, one_of_type,
105 ),
106 locations: vec![var_def.position],
107 });
108 }
109 }
110 }
111
112 if let Some(spreads) = self.spreads.get(from) {
113 for spread in spreads {
114 self.collect_incorrect_usages(
115 &Scope::Fragment(spread),
116 var_defs,
117 visitor_context,
118 user_context,
119 visited,
120 );
121 }
122 }
123 }
124}
125
126type VariableUsage<'doc> = (&'doc str, &'doc Type, bool, Option<&'doc str>);
129
130#[derive(Debug, Clone, PartialEq, Eq, Hash)]
131pub enum Scope<'doc> {
132 Operation(Option<&'doc str>),
133 Fragment(&'doc str),
134}
135
136impl<'doc> OperationVisitor<'doc, ValidationErrorContext> for VariablesInAllowedPosition<'doc> {
137 fn leave_document(
138 &mut self,
139 visitor_context: &mut OperationVisitorContext<'doc>,
140 user_context: &mut ValidationErrorContext,
141 _: &crate::static_graphql::query::Document,
142 ) {
143 for (op_scope, var_defs) in &self.variable_defs {
144 self.collect_incorrect_usages(
145 op_scope,
146 var_defs,
147 visitor_context,
148 user_context,
149 &mut HashSet::new(),
150 );
151 }
152 }
153
154 fn enter_fragment_definition(
155 &mut self,
156 _: &mut OperationVisitorContext<'doc>,
157 _: &mut ValidationErrorContext,
158 fragment_definition: &'doc crate::static_graphql::query::FragmentDefinition,
159 ) {
160 self.current_scope = Some(Scope::Fragment(&fragment_definition.name));
161 }
162
163 fn enter_operation_definition(
164 &mut self,
165 _: &mut OperationVisitorContext<'doc>,
166 _: &mut ValidationErrorContext,
167 operation_definition: &'doc crate::static_graphql::query::OperationDefinition,
168 ) {
169 self.current_scope = Some(Scope::Operation(operation_definition.node_name()));
170 }
171
172 fn enter_fragment_spread(
173 &mut self,
174 _: &mut OperationVisitorContext<'doc>,
175 _: &mut ValidationErrorContext,
176 fragment_spread: &'doc crate::static_graphql::query::FragmentSpread,
177 ) {
178 if let Some(scope) = &self.current_scope {
179 self.spreads
180 .entry(scope.clone())
181 .or_default()
182 .insert(&fragment_spread.fragment_name);
183 }
184 }
185
186 fn enter_variable_definition(
187 &mut self,
188 _: &mut OperationVisitorContext<'doc>,
189 _: &mut ValidationErrorContext,
190 variable_definition: &'doc VariableDefinition,
191 ) {
192 if let Some(ref scope) = self.current_scope {
193 self.variable_defs
194 .entry(scope.clone())
195 .or_default()
196 .push(variable_definition);
197 }
198 }
199
200 fn enter_variable_value(
201 &mut self,
202 visitor_context: &mut OperationVisitorContext<'doc>,
203 _: &mut ValidationErrorContext,
204 variable_name: &'doc str,
205 ) {
206 if let (Some(scope), Some(input_type)) = (
207 &self.current_scope,
208 visitor_context.current_input_type_literal(),
209 ) {
210 let has_default = visitor_context.current_input_type_has_default();
211 let one_of_parent = visitor_context
212 .parent_input_type()
213 .filter(|parent| parent.is_one_of())
214 .map(|parent| parent.name());
215 self.variable_usages
216 .entry(scope.clone())
217 .or_default()
218 .push((variable_name, input_type, has_default, one_of_parent));
219 }
220 }
221}
222
223impl ValidationRule for VariablesInAllowedPosition<'_> {
224 fn error_code(&self) -> &'static str {
225 "VariablesInAllowedPosition"
226 }
227
228 fn visitor<'doc>(&self) -> super::ValidationVisitor<'doc> {
229 Box::new(VariablesInAllowedPosition::new())
230 }
231}
232
233#[test]
234fn boolean_to_boolean() {
235 use crate::validation::test_utils::*;
236
237 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
238 let errors = test_operation_with_schema(
239 "query Query($booleanArg: Boolean)
240 {
241 complicatedArgs {
242 booleanArgField(booleanArg: $booleanArg)
243 }
244 }",
245 TEST_SCHEMA,
246 &mut plan,
247 );
248
249 assert_eq!(get_messages(&errors).len(), 0);
250}
251
252#[test]
253fn boolean_to_boolean_within_fragment() {
254 use crate::validation::test_utils::*;
255
256 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
257 let errors = test_operation_with_schema(
258 "fragment booleanArgFrag on ComplicatedArgs {
259 booleanArgField(booleanArg: $booleanArg)
260 }
261 query Query($booleanArg: Boolean)
262 {
263 complicatedArgs {
264 ...booleanArgFrag
265 }
266 }",
267 TEST_SCHEMA,
268 &mut plan,
269 );
270
271 assert_eq!(get_messages(&errors).len(), 0);
272
273 let errors = test_operation_with_schema(
274 "query Query($booleanArg: Boolean)
275 {
276 complicatedArgs {
277 ...booleanArgFrag
278 }
279 }
280 fragment booleanArgFrag on ComplicatedArgs {
281 booleanArgField(booleanArg: $booleanArg)
282 }",
283 TEST_SCHEMA,
284 &mut plan,
285 );
286
287 assert_eq!(get_messages(&errors).len(), 0);
288}
289
290#[test]
291fn boolean_nonnull_to_boolean() {
292 use crate::validation::test_utils::*;
293
294 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
295 let errors = test_operation_with_schema(
296 "query Query($nonNullBooleanArg: Boolean!)
297 {
298 complicatedArgs {
299 booleanArgField(booleanArg: $nonNullBooleanArg)
300 }
301 }",
302 TEST_SCHEMA,
303 &mut plan,
304 );
305
306 assert_eq!(get_messages(&errors).len(), 0);
307}
308
309#[test]
310fn string_list_to_string_list() {
311 use crate::validation::test_utils::*;
312
313 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
314 let errors = test_operation_with_schema(
315 "query Query($stringListVar: [String])
316 {
317 complicatedArgs {
318 stringListArgField(stringListArg: $stringListVar)
319 }
320 }",
321 TEST_SCHEMA,
322 &mut plan,
323 );
324
325 assert_eq!(get_messages(&errors).len(), 0);
326}
327
328#[test]
329fn string_list_nonnull_to_string_list() {
330 use crate::validation::test_utils::*;
331
332 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
333 let errors = test_operation_with_schema(
334 "query Query($stringListVar: [String!])
335 {
336 complicatedArgs {
337 stringListArgField(stringListArg: $stringListVar)
338 }
339 }",
340 TEST_SCHEMA,
341 &mut plan,
342 );
343
344 assert_eq!(get_messages(&errors).len(), 0);
345}
346
347#[test]
348fn string_to_string_list_in_item_position() {
349 use crate::validation::test_utils::*;
350
351 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
352 let errors = test_operation_with_schema(
353 "query Query($stringVar: String)
354 {
355 complicatedArgs {
356 stringListArgField(stringListArg: [$stringVar])
357 }
358 }",
359 TEST_SCHEMA,
360 &mut plan,
361 );
362
363 assert_eq!(get_messages(&errors).len(), 0);
364}
365
366#[test]
367fn string_nonnull_to_string_list_in_item_position() {
368 use crate::validation::test_utils::*;
369
370 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
371 let errors = test_operation_with_schema(
372 "query Query($stringVar: String!)
373 {
374 complicatedArgs {
375 stringListArgField(stringListArg: [$stringVar])
376 }
377 }",
378 TEST_SCHEMA,
379 &mut plan,
380 );
381
382 assert_eq!(get_messages(&errors).len(), 0);
383}
384
385#[test]
386fn complexinput_to_complexinput() {
387 use crate::validation::test_utils::*;
388
389 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
390 let errors = test_operation_with_schema(
391 "query Query($complexVar: ComplexInput)
392 {
393 complicatedArgs {
394 complexArgField(complexArg: $complexVar)
395 }
396 }",
397 TEST_SCHEMA,
398 &mut plan,
399 );
400
401 assert_eq!(get_messages(&errors).len(), 0);
402}
403
404#[test]
405fn complexinput_to_complexinput_in_field_position() {
406 use crate::validation::test_utils::*;
407
408 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
409 let errors = test_operation_with_schema(
410 "query Query($boolVar: Boolean = false)
411 {
412 complicatedArgs {
413 complexArgField(complexArg: { requiredArg: $boolVar })
414 }
415 }",
416 TEST_SCHEMA,
417 &mut plan,
418 );
419
420 let messages = get_messages(&errors);
421 assert_eq!(messages.len(), 0);
422}
423
424#[test]
425fn boolean_nonnull_to_boolean_nonnull_in_directive() {
426 use crate::validation::test_utils::*;
427
428 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
429 let errors = test_operation_with_schema(
430 "query Query($boolVar: Boolean!)
431 {
432 dog @include(if: $boolVar)
433 }",
434 TEST_SCHEMA,
435 &mut plan,
436 );
437
438 assert_eq!(get_messages(&errors).len(), 0);
439}
440
441#[test]
442fn int_to_int_nonnull() {
443 use crate::validation::test_utils::*;
444
445 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
446 let errors = test_operation_with_schema(
447 "query Query($intArg: Int) {
448 complicatedArgs {
449 nonNullIntArgField(nonNullIntArg: $intArg)
450 }
451 }",
452 TEST_SCHEMA,
453 &mut plan,
454 );
455
456 let messages = get_messages(&errors);
457 assert_eq!(messages.len(), 1);
458 assert_eq!(
459 messages,
460 vec!["Variable \"$intArg\" of type \"Int\" used in position expecting type \"Int!\"."]
461 )
462}
463
464#[test]
465fn int_to_int_nonnull_within_fragment() {
466 use crate::validation::test_utils::*;
467
468 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
469 let errors = test_operation_with_schema(
470 "fragment nonNullIntArgFieldFrag on ComplicatedArgs {
471 nonNullIntArgField(nonNullIntArg: $intArg)
472 }
473 query Query($intArg: Int) {
474 complicatedArgs {
475 ...nonNullIntArgFieldFrag
476 }
477 }",
478 TEST_SCHEMA,
479 &mut plan,
480 );
481
482 let messages = get_messages(&errors);
483 assert_eq!(messages.len(), 1);
484 assert_eq!(
485 messages,
486 vec!["Variable \"$intArg\" of type \"Int\" used in position expecting type \"Int!\"."]
487 )
488}
489
490#[test]
491fn int_to_int_nonnull_within_nested_fragment() {
492 use crate::validation::test_utils::*;
493
494 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
495 let errors = test_operation_with_schema(
496 "fragment outerFrag on ComplicatedArgs {
497 ...nonNullIntArgFieldFrag
498 }
499 fragment nonNullIntArgFieldFrag on ComplicatedArgs {
500 nonNullIntArgField(nonNullIntArg: $intArg)
501 }
502 query Query($intArg: Int) {
503 complicatedArgs {
504 ...outerFrag
505 }
506 }",
507 TEST_SCHEMA,
508 &mut plan,
509 );
510
511 let messages = get_messages(&errors);
512 assert_eq!(messages.len(), 1);
513 assert_eq!(
514 messages,
515 vec!["Variable \"$intArg\" of type \"Int\" used in position expecting type \"Int!\"."]
516 )
517}
518
519#[test]
520fn string_over_boolean() {
521 use crate::validation::test_utils::*;
522
523 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
524 let errors = test_operation_with_schema(
525 "query Query($stringVar: String) {
526 complicatedArgs {
527 booleanArgField(booleanArg: $stringVar)
528 }
529 }",
530 TEST_SCHEMA,
531 &mut plan,
532 );
533
534 let messages = get_messages(&errors);
535 assert_eq!(messages.len(), 1);
536 assert_eq!(
537 messages,
538 vec![
539 "Variable \"$stringVar\" of type \"String\" used in position expecting type \"Boolean\"."
540 ]
541 )
542}
543
544#[test]
545fn string_over_string_list() {
546 use crate::validation::test_utils::*;
547
548 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
549 let errors = test_operation_with_schema(
550 "query Query($stringVar: String) {
551 complicatedArgs {
552 stringListArgField(stringListArg: $stringVar)
553 }
554 }",
555 TEST_SCHEMA,
556 &mut plan,
557 );
558
559 let messages = get_messages(&errors);
560 assert_eq!(messages.len(), 1);
561 assert_eq!(
562 messages,
563 vec![
564 "Variable \"$stringVar\" of type \"String\" used in position expecting type \"[String]\"."
565 ]
566 )
567}
568
569#[test]
570fn boolean_to_boolean_nonnull_in_directive() {
571 use crate::validation::test_utils::*;
572
573 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
574 let errors = test_operation_with_schema(
575 "query Query($boolVar: Boolean) {
576 dog @include(if: $boolVar)
577 }",
578 TEST_SCHEMA,
579 &mut plan,
580 );
581
582 let messages = get_messages(&errors);
583 assert_eq!(messages.len(), 1);
584 assert_eq!(
585 messages,
586 vec![
587 "Variable \"$boolVar\" of type \"Boolean\" used in position expecting type \"Boolean!\"."
588 ]
589 )
590}
591
592#[test]
593fn string_to_boolean_nonnull_in_directive() {
594 use crate::validation::test_utils::*;
595
596 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
597 let errors = test_operation_with_schema(
598 "query Query($stringVar: String) {
599 dog @include(if: $stringVar)
600 }",
601 TEST_SCHEMA,
602 &mut plan,
603 );
604
605 let messages = get_messages(&errors);
606 assert_eq!(messages.len(), 1);
607 assert_eq!(
608 messages,
609 vec![
610 "Variable \"$stringVar\" of type \"String\" used in position expecting type \"Boolean!\"."
611 ]
612 )
613}
614
615#[test]
616fn string_list_to_string_nonnull_list() {
617 use crate::validation::test_utils::*;
618
619 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
620 let errors = test_operation_with_schema(
621 "query Query($stringListVar: [String])
622 {
623 complicatedArgs {
624 stringListNonNullArgField(stringListNonNullArg: $stringListVar)
625 }
626 }",
627 TEST_SCHEMA,
628 &mut plan,
629 );
630
631 let messages = get_messages(&errors);
632 assert_eq!(messages.len(), 1);
633 assert_eq!(messages, vec![
634 "Variable \"$stringListVar\" of type \"[String]\" used in position expecting type \"[String!]\"."
635 ])
636}
637
638#[test]
639fn int_to_int_non_null_with_null_default_value() {
640 use crate::validation::test_utils::*;
641
642 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
643 let errors = test_operation_with_schema(
644 "query Query($intVar: Int = null) {
645 complicatedArgs {
646 nonNullIntArgField(nonNullIntArg: $intVar)
647 }
648 }",
649 TEST_SCHEMA,
650 &mut plan,
651 );
652
653 let messages = get_messages(&errors);
654 assert_eq!(messages.len(), 1);
655 assert_eq!(
656 messages,
657 vec!["Variable \"$intVar\" of type \"Int\" used in position expecting type \"Int!\"."]
658 )
659}
660
661#[test]
662fn int_to_int_non_null_with_default_value() {
663 use crate::validation::test_utils::*;
664
665 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
666 let errors = test_operation_with_schema(
667 "query Query($intVar: Int = 1) {
668 complicatedArgs {
669 nonNullIntArgField(nonNullIntArg: $intVar)
670 }
671 }",
672 TEST_SCHEMA,
673 &mut plan,
674 );
675
676 let messages = get_messages(&errors);
677 assert_eq!(messages.len(), 0);
678}
679
680#[test]
681fn int_to_int_non_null_where_argument_with_default_value() {
682 use crate::validation::test_utils::*;
683
684 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
685 let errors = test_operation_with_schema(
686 "query Query($intVar: Int) {
687 complicatedArgs {
688 nonNullFieldWithDefault(arg: $intVar)
689 }
690 }",
691 TEST_SCHEMA,
692 &mut plan,
693 );
694
695 let messages = get_messages(&errors);
696 assert_eq!(messages.len(), 0);
697}
698
699#[test]
700fn list_of_non_null_enum_with_single_enum_default_value() {
701 use crate::validation::test_utils::*;
702
703 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
708 let errors = test_operation_with_schema(
709 "query Query($enumListVar: [FurColor!] = BROWN) {
710 complicatedArgs {
711 enumListArgField(enumListArg: $enumListVar)
712 }
713 }",
714 &(TEST_SCHEMA.replace(
716 "enumArgField(enumArg: FurColor): String",
717 "enumArgField(enumArg: FurColor): String\n enumListArgField(enumListArg: [FurColor!]): String",
718 )),
719 &mut plan,
720 );
721
722 let messages = get_messages(&errors);
723 assert_eq!(messages, Vec::<&String>::new());
724}
725
726#[test]
727fn list_of_non_null_with_default_value_used_in_non_null_list_position() {
728 use crate::validation::test_utils::*;
729
730 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
734 let errors = test_operation_with_schema(
735 "query Query($enumListVar: [FurColor!] = BROWN) {
736 complicatedArgs {
737 requiredEnumListArgField(enumListArg: $enumListVar)
738 }
739 }",
740 &(TEST_SCHEMA.replace(
741 "enumArgField(enumArg: FurColor): String",
742 "enumArgField(enumArg: FurColor): String\n requiredEnumListArgField(enumListArg: [FurColor!]!): String",
743 )),
744 &mut plan,
745 );
746
747 let messages = get_messages(&errors);
748 assert_eq!(messages, Vec::<&String>::new());
749}
750
751#[test]
752fn boolean_to_boolean_non_null_with_default_value() {
753 use crate::validation::test_utils::*;
754
755 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
756 let errors = test_operation_with_schema(
757 "query Query($boolVar: Boolean = false) {
758 dog @include(if: $boolVar)
759 }",
760 TEST_SCHEMA,
761 &mut plan,
762 );
763
764 let messages = get_messages(&errors);
765 assert_eq!(messages.len(), 0);
766}
767
768#[test]
769fn nullable_enum_to_non_null_enum_with_default_on_argument() {
770 use crate::validation::test_utils::*;
771
772 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
773 let errors = test_operation_with_schema(
774 "query Query($currency: FurColor) {
775 complicatedArgs {
776 enumArgFieldWithDefault(enumArg: $currency)
777 }
778 }",
779 &(TEST_SCHEMA.replace(
780 "enumArgField(enumArg: FurColor): String",
781 "enumArgField(enumArg: FurColor): String\n enumArgFieldWithDefault(enumArg: FurColor! = BROWN): String",
782 )),
783 &mut plan,
784 );
785
786 let messages = get_messages(&errors);
787 assert_eq!(messages.len(), 0);
788}
789
790#[test]
791fn nullable_int_to_non_null_int_with_default_on_argument() {
792 use crate::validation::test_utils::*;
793
794 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
795 let errors = test_operation_with_schema(
796 "query Query($intVar: Int) {
797 complicatedArgs {
798 nonNullFieldWithDefault(arg: $intVar)
799 }
800 }",
801 TEST_SCHEMA,
802 &mut plan,
803 );
804
805 let messages = get_messages(&errors);
806 assert_eq!(messages.len(), 0);
807}
808
809#[test]
810fn nullable_int_to_non_null_input_field_with_default() {
811 use crate::validation::test_utils::*;
812
813 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
814 let errors = test_operation_with_schema(
815 "query Query($intVar: Int) {
816 set(input: { value: $intVar })
817 }",
818 "input Input {
819 value: Int! = 1
820 }
821 type Query {
822 set(input: Input): String
823 }",
824 &mut plan,
825 );
826
827 let messages = get_messages(&errors);
828 assert_eq!(messages.len(), 0);
829}
830
831#[test]
832fn string_to_non_null_int_with_default_on_argument() {
833 use crate::validation::test_utils::*;
834
835 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
836 let errors = test_operation_with_schema(
837 "query Query($stringVar: String) {
838 complicatedArgs {
839 nonNullFieldWithDefault(arg: $stringVar)
840 }
841 }",
842 TEST_SCHEMA,
843 &mut plan,
844 );
845
846 let messages = get_messages(&errors);
847 assert_eq!(messages.len(), 1);
848 assert_eq!(
849 messages,
850 vec![
851 "Variable \"$stringVar\" of type \"String\" used in position expecting type \"Int!\"."
852 ]
853 )
854}
855
856#[cfg(test)]
857static ONE_OF_SCHEMA: &str = "
858 directive @oneOf on INPUT_OBJECT
859 input OneOfInput @oneOf { a: String b: Int }
860 input Holder { one: OneOfInput name: String }
861 type Query { oneOf(input: OneOfInput): String holder(input: Holder): String }";
862
863#[test]
864fn non_null_variable_for_oneof_field() {
865 use crate::validation::test_utils::*;
866
867 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
868 let errors = test_operation_with_schema(
869 "query ($a: String!, $h: String) { oneOf(input: { a: $a }) holder(input: { name: $h }) }",
870 ONE_OF_SCHEMA,
871 &mut plan,
872 );
873 assert_eq!(get_messages(&errors).len(), 0);
874}
875
876#[test]
877fn nullable_variable_for_oneof_field() {
878 use crate::validation::test_utils::*;
879
880 let mut plan = create_plan_from_rule(Box::new(VariablesInAllowedPosition::new()));
881 let errors = test_operation_with_schema(
882 r#"query ($a: String, $b: Int = 1) { a: oneOf(input: { a: $a }) b: holder(input: { one: { b: $b } }) }"#,
883 ONE_OF_SCHEMA,
884 &mut plan,
885 );
886 let mut messages = get_messages(&errors);
887 messages.sort();
888 assert_eq!(
889 messages,
890 vec![
891 "Variable \"$a\" is of type \"String\" but must be non-nullable to be used for OneOf Input Object \"OneOfInput\".",
892 "Variable \"$b\" is of type \"Int\" but must be non-nullable to be used for OneOf Input Object \"OneOfInput\".",
893 ]
894 );
895}