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