Skip to main content

juniper/validation/rules/
default_values_of_correct_type.rs

1use std::fmt;
2
3use crate::{
4    ast::VariableDefinition,
5    parser::Spanning,
6    types::utilities::validate_literal_value,
7    validation::{ValidatorContext, Visitor},
8    value::ScalarValue,
9};
10
11pub struct DefaultValuesOfCorrectType;
12
13pub fn factory() -> DefaultValuesOfCorrectType {
14    DefaultValuesOfCorrectType
15}
16
17impl<'a, S> Visitor<'a, S> for DefaultValuesOfCorrectType
18where
19    S: ScalarValue,
20{
21    fn enter_variable_definition(
22        &mut self,
23        ctx: &mut ValidatorContext<'a, S>,
24        (var_name, var_def): &'a (Spanning<&'a str>, VariableDefinition<S>),
25    ) {
26        if let Some(Spanning {
27            item: ref var_value,
28            ref span,
29        }) = var_def.default_value
30        {
31            if var_def.var_type.item.is_non_null() {
32                ctx.report_error(
33                    &non_null_error_message(var_name.item, &var_def.var_type.item),
34                    &[span.start],
35                )
36            } else {
37                let meta_type = ctx.schema.make_type(&var_def.var_type.item);
38
39                if let Some(err) = validate_literal_value(ctx.schema, &meta_type, var_value) {
40                    ctx.report_error(
41                        &type_error_message(var_name.item, &var_def.var_type.item, err),
42                        &[span.start],
43                    );
44                }
45            }
46        }
47    }
48}
49
50fn type_error_message(
51    arg_name: impl fmt::Display,
52    type_name: impl fmt::Display,
53    reason: impl fmt::Display,
54) -> String {
55    format!(
56        "Invalid default value for argument \"{arg_name}\", expected type \"{type_name}\", \
57         reason: {reason}",
58    )
59}
60
61fn non_null_error_message(arg_name: impl fmt::Display, type_name: impl fmt::Display) -> String {
62    format!(
63        "Argument \"{arg_name}\" has type \"{type_name}\" and is not nullable, \
64         so it can't have a default value",
65    )
66}
67
68#[cfg(test)]
69mod tests {
70    use super::{factory, non_null_error_message, type_error_message};
71
72    use crate::{
73        parser::SourcePosition,
74        types::utilities::error,
75        validation::{RuleError, expect_fails_rule, expect_passes_rule},
76        value::DefaultScalarValue,
77    };
78
79    #[test]
80    fn variables_with_no_default_values() {
81        expect_passes_rule::<_, _, DefaultScalarValue>(
82            factory,
83            r#"
84            query NullableValues($a: Int, $b: String, $c: ComplexInput) {
85                dog { name }
86            }
87            "#,
88        );
89    }
90
91    #[test]
92    fn required_variables_without_default_values() {
93        expect_passes_rule::<_, _, DefaultScalarValue>(
94            factory,
95            r#"
96            query RequiredValues($a: Int!, $b: String!) {
97                dog { name }
98            }
99            "#,
100        );
101    }
102
103    #[test]
104    fn variables_with_valid_default_values() {
105        expect_passes_rule::<_, _, DefaultScalarValue>(
106            factory,
107            r#"
108            query WithDefaultValues(
109                $a: Int = 1,
110                $b: String = "ok",
111                $c: ComplexInput = { requiredField: true, intField: 3 }
112            ) {
113                dog { name }
114            }
115            "#,
116        );
117    }
118
119    #[test]
120    fn no_required_variables_with_default_values() {
121        expect_fails_rule::<_, _, DefaultScalarValue>(
122            factory,
123            r#"
124            query UnreachableDefaultValues($a: Int! = 3, $b: String! = "default") {
125                dog { name }
126            }
127            "#,
128            &[
129                RuleError::new(
130                    &non_null_error_message("a", "Int!"),
131                    &[SourcePosition::new(55, 1, 54)],
132                ),
133                RuleError::new(
134                    &non_null_error_message("b", "String!"),
135                    &[SourcePosition::new(72, 1, 71)],
136                ),
137            ],
138        );
139    }
140
141    #[test]
142    fn variables_with_invalid_default_values() {
143        expect_fails_rule::<_, _, DefaultScalarValue>(
144            factory,
145            r#"
146            query InvalidDefaultValues(
147                $a: Int = "one",
148                $b: String = 4,
149                $c: ComplexInput = "notverycomplex"
150            ) {
151                dog { name }
152            }
153            "#,
154            &[
155                RuleError::new(
156                    &type_error_message("a", "Int", error::type_value("\"one\"", "Int")),
157                    &[SourcePosition::new(67, 2, 26)],
158                ),
159                RuleError::new(
160                    &type_error_message("b", "String", error::type_value("4", "String")),
161                    &[SourcePosition::new(103, 3, 29)],
162                ),
163                RuleError::new(
164                    &type_error_message(
165                        "c",
166                        "ComplexInput",
167                        error::type_value("\"notverycomplex\"", "ComplexInput"),
168                    ),
169                    &[SourcePosition::new(141, 4, 35)],
170                ),
171            ],
172        );
173    }
174
175    #[test]
176    fn complex_variables_missing_required_field() {
177        expect_fails_rule::<_, _, DefaultScalarValue>(
178            factory,
179            r#"
180            query MissingRequiredField($a: ComplexInput = {intField: 3}) {
181                dog { name }
182            }
183            "#,
184            &[RuleError::new(
185                &type_error_message(
186                    "a",
187                    "ComplexInput",
188                    error::missing_fields("ComplexInput", "\"requiredField\""),
189                ),
190                &[SourcePosition::new(59, 1, 58)],
191            )],
192        );
193    }
194
195    #[test]
196    fn list_variables_with_invalid_item() {
197        expect_fails_rule::<_, _, DefaultScalarValue>(
198            factory,
199            r#"
200            query InvalidItem($a: [String] = ["one", 2]) {
201                dog { name }
202            }
203            "#,
204            &[RuleError::new(
205                &type_error_message("a", "[String]", error::type_value("2", "String")),
206                &[SourcePosition::new(46, 1, 45)],
207            )],
208        );
209    }
210}