77import org .jspecify .annotations .NullUnmarked ;
88import org .junit .jupiter .api .Test ;
99
10- import java .util .List ;
10+ import java .lang .annotation .Retention ;
11+ import java .lang .annotation .RetentionPolicy ;
1112
12- import static org .junit .jupiter .api .Assertions .assertDoesNotThrow ;
13- import static org .junit .jupiter .api .Assertions .assertTrue ;
13+ import static org .assertj .core .api .Assertions .assertThat ;
1414
1515@ NullUnmarked
1616class NullableCustomizerTest {
@@ -52,6 +52,7 @@ public String directMethod() {
5252
5353 @ Test
5454 void shouldFindFieldInSuperClass () {
55+ // given
5556 var customizer = new NullableCustomizer ();
5657 var openApi = new OpenAPI ();
5758 var components = new Components ();
@@ -64,15 +65,19 @@ void shouldFindFieldInSuperClass() {
6465 components .addSchemas (SubClass .class .getName (), subClassSchema );
6566 openApi .setComponents (components );
6667
67- assertDoesNotThrow (() -> customizer .customise (openApi ));
68+ // when
69+ customizer .customise (openApi );
6870
69- List <String > required = subClassSchema .getRequired ();
70- assertTrue (required != null && required .contains ("subField" ), "subField should be required" );
71- assertTrue (required == null || !required .contains ("baseField" ), "baseField should NOT be required" );
71+ // then
72+ var required = subClassSchema .getRequired ();
73+
74+ assertThat (required ).as ("subField should be required" ).contains ("subField" );
75+ assertThat (required ).as ("baseField should NOT be required" ).doesNotContain ("baseField" );
7276 }
7377
7478 @ Test
7579 void shouldFindAnnotationOnGetter () {
80+ // given
7681 var customizer = new NullableCustomizer ();
7782 var openApi = new OpenAPI ();
7883 var components = new Components ();
@@ -84,14 +89,17 @@ void shouldFindAnnotationOnGetter() {
8489 components .addSchemas (MethodAnnotated .class .getName (), schema );
8590 openApi .setComponents (components );
8691
87- assertDoesNotThrow (() -> customizer .customise (openApi ));
92+ // when
93+ customizer .customise (openApi );
8894
89- List <String > required = schema .getRequired ();
90- assertTrue (required == null || !required .contains ("annotatedGetter" ), "annotatedGetter should NOT be required" );
95+ // then
96+ var required = schema .getRequired ();
97+ assertThat (required ).as ("annotatedGetter should NOT be required" ).isNullOrEmpty ();
9198 }
9299
93100 @ Test
94101 void shouldFindAnnotationOnDirectMethod () {
102+ // given
95103 var customizer = new NullableCustomizer ();
96104 var openApi = new OpenAPI ();
97105 var components = new Components ();
@@ -103,14 +111,17 @@ void shouldFindAnnotationOnDirectMethod() {
103111 components .addSchemas (DirectMethodAnnotated .class .getName (), schema );
104112 openApi .setComponents (components );
105113
106- assertDoesNotThrow (() -> customizer .customise (openApi ));
114+ // when
115+ customizer .customise (openApi );
107116
108- List <String > required = schema .getRequired ();
109- assertTrue (required == null || !required .contains ("directMethod" ), "directMethod should NOT be required" );
117+ // then
118+ var required = schema .getRequired ();
119+ assertThat (required ).as ("directMethod should NOT be required" ).isNullOrEmpty ();
110120 }
111121
112122 @ Test
113123 void shouldHandleConcatenatedFqns () {
124+ // given
114125 var customizer = new NullableCustomizer ();
115126 var openApi = new OpenAPI ();
116127 var components = new Components ();
@@ -124,17 +135,17 @@ void shouldHandleConcatenatedFqns() {
124135 components .addSchemas (concatenatedFqn , schema );
125136 openApi .setComponents (components );
126137
127- assertDoesNotThrow (() -> customizer .customise (openApi ));
138+ // when
139+ customizer .customise (openApi );
128140
129- List <String > required = schema .getRequired ();
130- assertTrue (
131- required == null || !required .contains ("baseField" ),
132- "baseField should NOT be required even with concatenated FQN"
133- );
141+ // then
142+ var required = schema .getRequired ();
143+ assertThat (required ).as ("baseField should NOT be required even with concatenated FQN" ).isNullOrEmpty ();
134144 }
135145
136146 @ Test
137147 void shouldNotThrowWhenFieldNotFound () {
148+ // given
138149 var customizer = new NullableCustomizer ();
139150 var openApi = new OpenAPI ();
140151 var components = new Components ();
@@ -146,12 +157,50 @@ void shouldNotThrowWhenFieldNotFound() {
146157 components .addSchemas (SubClass .class .getName (), schema );
147158 openApi .setComponents (components );
148159
149- assertDoesNotThrow (() -> customizer .customise (openApi ));
160+ // when
161+ customizer .customise (openApi );
162+
163+ // then
164+ var required = schema .getRequired ();
165+ assertThat (required ).as ("nonExistent field should be considered required if not found and not nullable" )
166+ .contains ("nonExistent" );
167+ }
168+
169+ @ Test
170+ void shouldFindCustomNullableAnnotation () {
171+ // given
172+ var customizer = new NullableCustomizer ();
173+ var openApi = new OpenAPI ();
174+ var components = new Components ();
175+
176+ var schema = new Schema <Object >();
177+ schema .setName (CustomNullableClass .class .getName ());
178+ schema .addProperty ("customNullableField" , new StringSchema ());
179+
180+ components .addSchemas (CustomNullableClass .class .getName (), schema );
181+ openApi .setComponents (components );
182+
183+ // when
184+ customizer .customise (openApi );
185+
186+ // then
187+ var required = schema .getRequired ();
188+ assertThat (required ).as ("customNullableField should NOT be required" ).isNullOrEmpty ();
189+ }
190+
191+ public static class Nest {
192+ @ Retention (RetentionPolicy .RUNTIME )
193+ public @interface Nullable {
194+ }
195+ }
150196
151- List <String > required = schema .getRequired ();
152- assertTrue (
153- required != null && required .contains ("nonExistent" ),
154- "nonExistent field should be considered required if not found and not nullable"
155- );
197+ public static class CustomNullableClass {
198+ @ Nest .Nullable
199+ private String customNullableField ;
200+
201+ @ SuppressWarnings ("NullAway" )
202+ public String getCustomNullableField () {
203+ return customNullableField ;
204+ }
156205 }
157206}
0 commit comments