26
26
27
27
package test .org .springdoc .api .v30 .app233 ;
28
28
29
+ import java .util .Collection ;
30
+ import java .util .List ;
31
+ import java .util .Optional ;
32
+ import java .util .Set ;
33
+ import java .util .stream .Collectors ;
34
+ import java .util .stream .Stream ;
35
+
36
+ import com .jayway .jsonpath .JsonPath ;
37
+ import net .minidev .json .JSONArray ;
38
+ import org .junit .jupiter .api .Test ;
39
+ import org .junit .jupiter .params .ParameterizedTest ;
40
+ import org .junit .jupiter .params .provider .CsvSource ;
41
+ import org .springdoc .core .utils .Constants ;
29
42
import test .org .springdoc .api .v30 .AbstractSpringDocV30Test ;
30
43
31
44
import org .springframework .boot .autoconfigure .SpringBootApplication ;
45
+ import org .springframework .test .web .servlet .MvcResult ;
46
+ import org .springframework .test .web .servlet .request .MockHttpServletRequestBuilder ;
47
+ import org .springframework .validation .BindingResult ;
48
+ import org .springframework .web .bind .MethodArgumentNotValidException ;
49
+
50
+ import static org .assertj .core .api .Assertions .assertThat ;
51
+ import static org .springframework .test .web .servlet .request .MockMvcRequestBuilders .get ;
52
+ import static org .springframework .test .web .servlet .result .MockMvcResultMatchers .status ;
32
53
33
54
/**
34
- * @author bnasslahsen
55
+ * @author bnasslahsen, michael.clarke
35
56
*/
36
- public class SpringDocApp233Test extends AbstractSpringDocV30Test {
57
+ class SpringDocApp233Test extends AbstractSpringDocV30Test {
58
+
59
+ @ CsvSource ({"requiredNotNullParameterObject.requiredNotNullField, true" ,
60
+ "requiredNotNullParameterObject.requiredNoValidationField, true" ,
61
+ "requiredNotNullParameterObject.notRequiredNotNullField, false" ,
62
+ "requiredNotNullParameterObject.notRequiredNoValidationField, false" ,
63
+ "requiredNotNullParameterObject.noSchemaNotNullField, true" ,
64
+ "requiredNotNullParameterObject.noSchemaNoValidationField, false" ,
65
+ "requiredNoValidationParameterObject.requiredNotNullField, true" ,
66
+ "requiredNoValidationParameterObject.requiredNoValidationField, true" ,
67
+ "requiredNoValidationParameterObject.notRequiredNotNullField, false" ,
68
+ "requiredNoValidationParameterObject.notRequiredNoValidationField, false" ,
69
+ "requiredNoValidationParameterObject.noSchemaNotNullField, true" ,
70
+ "requiredNoValidationParameterObject.noSchemaNoValidationField, false" ,
71
+ "notRequiredNotNullParameterObject.requiredNotNullField, false" ,
72
+ "notRequiredNotNullParameterObject.requiredNoValidationField, false" ,
73
+ "notRequiredNotNullParameterObject.notRequiredNotNullField, false" ,
74
+ "notRequiredNotNullParameterObject.notRequiredNoValidationField, false" ,
75
+ "notRequiredNotNullParameterObject.noSchemaNotNullField, false" ,
76
+ "notRequiredNotNullParameterObject.noSchemaNoValidationField, false" ,
77
+ "notRequiredNoValidationParameterObject.requiredNotNullField, false" ,
78
+ "notRequiredNoValidationParameterObject.requiredNoValidationField, false" ,
79
+ "notRequiredNoValidationParameterObject.notRequiredNotNullField, false" ,
80
+ "notRequiredNoValidationParameterObject.notRequiredNoValidationField, false" ,
81
+ "notRequiredNoValidationParameterObject.noSchemaNotNullField, false" ,
82
+ "notRequiredNoValidationParameterObject.noSchemaNoValidationField, false" ,
83
+ "noSchemaNotNullParameterObject.requiredNotNullField, true" ,
84
+ "noSchemaNotNullParameterObject.requiredNoValidationField, true" ,
85
+ "noSchemaNotNullParameterObject.notRequiredNotNullField, false" ,
86
+ "noSchemaNotNullParameterObject.notRequiredNoValidationField, false" ,
87
+ "noSchemaNotNullParameterObject.noSchemaNotNullField, true" ,
88
+ "noSchemaNotNullParameterObject.noSchemaNoValidationField, false" ,
89
+ "noSchemaNoValidationParameterObject.requiredNotNullField, false" ,
90
+ "noSchemaNoValidationParameterObject.requiredNoValidationField, false" ,
91
+ "noSchemaNoValidationParameterObject.notRequiredNotNullField, false" ,
92
+ "noSchemaNoValidationParameterObject.notRequiredNoValidationField, false" ,
93
+ "noSchemaNoValidationParameterObject.noSchemaNotNullField, false" ,
94
+ "noSchemaNoValidationParameterObject.noSchemaNoValidationField, false" })
95
+ @ ParameterizedTest
96
+ void shouldHaveCorrectRequireStatus (String field , String required ) throws Exception {
97
+ MvcResult mockMvcResult = mockMvc .perform (get (Constants .DEFAULT_API_DOCS_URL )).andExpect (status ().isOk ()).andReturn ();
98
+ String result = mockMvcResult .getResponse ().getContentAsString ();
99
+
100
+ String requiredMode = ((JSONArray ) JsonPath .parse (result ).read ("$.paths.['/optional-parent'].get.parameters[?(@.name == '" + field + "')].required" )).get (0 ).toString ();
101
+ assertThat (requiredMode ).isEqualTo (required );
102
+ }
103
+
104
+ @ Test
105
+ void verifySwaggerFieldRequirementsMatchJavaValidation () throws Exception {
106
+ MvcResult mockMvcResult = mockMvc .perform (get (Constants .DEFAULT_API_DOCS_URL )).andExpect (status ().isOk ()).andReturn ();
107
+ String result = mockMvcResult .getResponse ().getContentAsString ();
108
+
109
+ JSONArray allFieldsJsonArray = JsonPath .parse (result ).read ("$.paths.['/optional-parent'].get.parameters[*].name" );
110
+ List <String > allFields = allFieldsJsonArray .stream ().map (Object ::toString ).toList ();
111
+
112
+ // check we get no validation failures when all mandatory fields are present
113
+ verifySwaggerFieldRequirementsMatchJavaValidation (allFields , List .of ());
114
+
115
+ JSONArray mandatoryFieldsJsonArray = JsonPath .parse (result ).read ("$.paths.['/optional-parent'].get.parameters[?(@.required == true)].name" );
116
+ List <String > mandatoryFields = mandatoryFieldsJsonArray .stream ().map (Object ::toString ).toList ();
117
+
118
+ // check validation failures when each individual mandatory field is missing
119
+ for (String mandatoryField : mandatoryFields ) {
120
+ List <String > filteredFields = allFields .stream ()
121
+ .filter (field -> !field .equals (mandatoryField ))
122
+ .toList ();
123
+
124
+ List <String > expectedErrors = Stream .of (mandatoryField )
125
+ // Fields using Swagger annotations to drive required status but not using Java validation to enforce it so don't cause validation errors
126
+ .filter (field -> !field .endsWith ("requiredNullableField" ))
127
+ .filter (field -> !field .endsWith ("requiredNoValidationField" ))
128
+ // the error is returned prefixed with the query parameter name, so add it to the expected error message
129
+ .map (field -> "multiFieldParameterObject." + field )
130
+ .toList ();
131
+ verifySwaggerFieldRequirementsMatchJavaValidation (filteredFields , expectedErrors );
132
+ }
133
+
134
+
135
+ JSONArray nonMandatoryFieldsJsonArray = JsonPath .parse (result ).read ("$.paths.['/optional-parent'].get.parameters[?(@.required == false)].name" );
136
+ List <String > nonMandatoryFields = nonMandatoryFieldsJsonArray .stream ().map (Object ::toString ).toList ();
137
+
138
+ // check validation failures for any individual non-mandatory fields being missed
139
+ for (String nonMandatoryField : nonMandatoryFields ) {
140
+ List <String > filteredFields = allFields .stream ()
141
+ .filter (field -> !field .equals (nonMandatoryField ))
142
+ .toList ();
143
+
144
+ List <String > expectedErrors = Stream .of (nonMandatoryField )
145
+ // Fields that are mandatory but either have nullable parent fields so are excluded in swagger or are marked as not required so do cause validation errors
146
+ .filter (field -> field .endsWith ("NotNullField" ))
147
+ // the error is returned prefixed with the query parameter name, so add it to the expected error message
148
+ .map (field -> "multiFieldParameterObject." + field )
149
+ .toList ();
150
+ verifySwaggerFieldRequirementsMatchJavaValidation (filteredFields , expectedErrors );
151
+ }
152
+ }
153
+
154
+ private void verifySwaggerFieldRequirementsMatchJavaValidation (Collection <String > requestFields , Collection <String > expectedErrorFields ) throws Exception {
155
+ MockHttpServletRequestBuilder request = get ("/optional-parent" );
156
+ for (String mandatoryField : requestFields ) {
157
+ request .queryParam (mandatoryField , mandatoryField + ".value" );
158
+ }
159
+
160
+ mockMvc .perform (request )
161
+ .andExpect (result -> {
162
+
163
+ Set <String > errorFields = Optional .ofNullable (result .getResolvedException ())
164
+ .map (MethodArgumentNotValidException .class ::cast )
165
+ .map (MethodArgumentNotValidException ::getBindingResult )
166
+ .map (BindingResult ::getFieldErrors )
167
+ .stream ()
168
+ .flatMap (Collection ::stream )
169
+ .map (field -> field .getObjectName () + "." + field .getField ())
170
+ .collect (Collectors .toSet ());
171
+
172
+
173
+ assertThat (errorFields ).containsExactlyElementsOf (expectedErrorFields );
174
+
175
+ assertThat (result .getResponse ().getStatus ()).isEqualTo (expectedErrorFields .isEmpty () ? 200 : 400 );
176
+ });
177
+ }
178
+
179
+
180
+ @ SpringBootApplication
181
+ static class SpringDocTestApp {
182
+
183
+ }
37
184
38
- @ SpringBootApplication
39
- static class SpringDocTestApp {
40
- }
41
185
}
0 commit comments