graphql_tools/validation/rules/
unique_fragment_names.rs1use std::collections::HashMap;
2
3use super::ValidationRule;
4use crate::ast::{visit_document, AstNodeWithName, OperationVisitor, OperationVisitorContext};
5use crate::static_graphql::query::*;
6use crate::validation::utils::{ValidationError, ValidationErrorContext};
7
8pub struct UniqueFragmentNames<'a> {
14 findings_counter: HashMap<&'a str, i32>,
15}
16
17impl<'a> OperationVisitor<'a, ValidationErrorContext> for UniqueFragmentNames<'a> {
18 fn enter_fragment_definition(
19 &mut self,
20 _: &mut OperationVisitorContext,
21 _: &mut ValidationErrorContext,
22 fragment: &'a FragmentDefinition,
23 ) {
24 if let Some(name) = fragment.node_name() {
25 self.store_finding(name);
26 }
27 }
28}
29
30impl<'a> Default for UniqueFragmentNames<'a> {
31 fn default() -> Self {
32 Self::new()
33 }
34}
35
36impl<'a> UniqueFragmentNames<'a> {
37 pub fn new() -> Self {
38 Self {
39 findings_counter: HashMap::new(),
40 }
41 }
42
43 fn store_finding(&mut self, name: &'a str) {
44 let value = *self.findings_counter.entry(name).or_insert(0);
45 self.findings_counter.insert(name, value + 1);
46 }
47}
48
49impl<'u> ValidationRule for UniqueFragmentNames<'u> {
50 fn error_code<'a>(&self) -> &'a str {
51 "UniqueFragmentNames"
52 }
53
54 fn validate(
55 &self,
56 ctx: &mut OperationVisitorContext,
57 error_collector: &mut ValidationErrorContext,
58 ) {
59 let mut rule = UniqueFragmentNames::new();
60
61 visit_document(&mut rule, ctx.operation, ctx, error_collector);
62
63 rule.findings_counter
64 .into_iter()
65 .filter(|(_key, value)| *value > 1)
66 .for_each(|(key, _value)| {
67 error_collector.report_error(ValidationError {
68 error_code: self.error_code(),
69 message: format!("There can be only one fragment named \"{}\".", key),
70 locations: vec![],
71 })
72 })
73 }
74}
75
76#[test]
77fn no_fragments() {
78 use crate::validation::test_utils::*;
79
80 let mut plan = create_plan_from_rule(Box::new(UniqueFragmentNames::new()));
81 let errors = test_operation_with_schema(
82 "{
83 field
84 }",
85 TEST_SCHEMA,
86 &mut plan,
87 );
88
89 assert_eq!(get_messages(&errors).len(), 0);
90}
91
92#[test]
93fn one_fragment() {
94 use crate::validation::test_utils::*;
95
96 let mut plan = create_plan_from_rule(Box::new(UniqueFragmentNames::new()));
97 let errors = test_operation_with_schema(
98 "{
99 ...fragA
100 }
101 fragment fragA on Type {
102 field
103 }",
104 TEST_SCHEMA,
105 &mut plan,
106 );
107
108 assert_eq!(get_messages(&errors).len(), 0);
109}
110
111#[test]
112fn many_fragment() {
113 use crate::validation::test_utils::*;
114
115 let mut plan = create_plan_from_rule(Box::new(UniqueFragmentNames::new()));
116 let errors = test_operation_with_schema(
117 "{
118 ...fragA
119 ...fragB
120 ...fragC
121 }
122 fragment fragA on Type {
123 fieldA
124 }
125 fragment fragB on Type {
126 fieldB
127 }
128 fragment fragC on Type {
129 fieldC
130 }",
131 TEST_SCHEMA,
132 &mut plan,
133 );
134
135 assert_eq!(get_messages(&errors).len(), 0);
136}
137
138#[test]
139fn inline_fragments_are_always_unique() {
140 use crate::validation::test_utils::*;
141
142 let mut plan = create_plan_from_rule(Box::new(UniqueFragmentNames::new()));
143 let errors = test_operation_with_schema(
144 "{
145 ...on Type {
146 fieldA
147 }
148 ...on Type {
149 fieldB
150 }
151 }",
152 TEST_SCHEMA,
153 &mut plan,
154 );
155
156 assert_eq!(get_messages(&errors).len(), 0);
157}
158
159#[test]
160fn fragment_and_operation_named_the_same() {
161 use crate::validation::test_utils::*;
162
163 let mut plan = create_plan_from_rule(Box::new(UniqueFragmentNames::new()));
164 let errors = test_operation_with_schema(
165 "query Foo {
166 ...Foo
167 }
168 fragment Foo on Type {
169 field
170 }",
171 TEST_SCHEMA,
172 &mut plan,
173 );
174
175 assert_eq!(get_messages(&errors).len(), 0);
176}
177
178#[test]
179fn fragments_named_the_same() {
180 use crate::validation::test_utils::*;
181
182 let mut plan = create_plan_from_rule(Box::new(UniqueFragmentNames::new()));
183 let errors = test_operation_with_schema(
184 "{
185 ...fragA
186 }
187 fragment fragA on Type {
188 fieldA
189 }
190 fragment fragA on Type {
191 fieldB
192 }",
193 TEST_SCHEMA,
194 &mut plan,
195 );
196
197 let messages = get_messages(&errors);
198 assert_eq!(messages.len(), 1);
199 assert_eq!(
200 messages,
201 vec!["There can be only one fragment named \"fragA\"."]
202 );
203}
204
205#[test]
206fn fragments_named_the_same_without_being_referenced() {
207 use crate::validation::test_utils::*;
208
209 let mut plan = create_plan_from_rule(Box::new(UniqueFragmentNames::new()));
210 let errors = test_operation_with_schema(
211 "fragment fragA on Type {
212 fieldA
213 }
214 fragment fragA on Type {
215 fieldB
216 }",
217 TEST_SCHEMA,
218 &mut plan,
219 );
220
221 let messages = get_messages(&errors);
222 assert_eq!(messages.len(), 1);
223 assert_eq!(
224 messages,
225 vec!["There can be only one fragment named \"fragA\"."]
226 );
227}