View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.junit.bct;
18  
19  import static org.apache.juneau.commons.lang.TriState.*;
20  import static org.apache.juneau.junit.bct.BctAssertions.*;
21  import static org.apache.juneau.junit.bct.BctConfiguration.*;
22  import static org.junit.jupiter.api.Assertions.*;
23  
24  import org.apache.juneau.*;
25  import org.apache.juneau.junit.bct.annotations.*;
26  import org.junit.jupiter.api.*;
27  
28  /**
29   * Unit tests for {@link BctConfig} annotation and extension.
30   */
31  class BctConfig_Test extends TestBase {
32  
33  	// ====================================================================================================
34  	// Class-level annotation tests
35  	// ====================================================================================================
36  
37  	@Nested
38  	@BctConfig(sortMaps = TRUE)
39  	class A_classLevelAnnotation extends TestBase {
40  
41  		@Test
42  		void a01_sortMapsEnabled() {
43  			// Verify sortMaps is enabled from class-level annotation
44  			assertTrue(BctConfiguration.get(BCT_SORT_MAPS, false));
45  		}
46  
47  		@Test
48  		void a02_sortCollectionsNotEnabled() {
49  			// Verify sortCollections is not enabled (default UNSET)
50  			assertFalse(BctConfiguration.get(BCT_SORT_COLLECTIONS, false));
51  		}
52  	}
53  
54  	@Nested
55  	@BctConfig(sortMaps = TRUE, sortCollections = TRUE)
56  	class B_classLevelBothEnabled extends TestBase {
57  
58  		@Test
59  		void b01_bothEnabled() {
60  			// Verify both are enabled from class-level annotation
61  			assertTrue(BctConfiguration.get(BCT_SORT_MAPS, false));
62  			assertTrue(BctConfiguration.get(BCT_SORT_COLLECTIONS, false));
63  		}
64  	}
65  
66  	// ====================================================================================================
67  	// Method-level annotation tests
68  	// ====================================================================================================
69  
70  	@Nested
71  	class C_methodLevelAnnotation extends TestBase {
72  
73  		@Test
74  		@BctConfig(sortMaps = TRUE)
75  		void c01_sortMapsEnabled() {
76  			// Verify sortMaps is enabled from method-level annotation
77  			assertTrue(BctConfiguration.get(BCT_SORT_MAPS, false));
78  		}
79  
80  		@Test
81  		@BctConfig(sortCollections = TRUE)
82  		void c02_sortCollectionsEnabled() {
83  			// Verify sortCollections is enabled from method-level annotation
84  			assertTrue(BctConfiguration.get(BCT_SORT_COLLECTIONS, false));
85  		}
86  
87  		@Test
88  		void c03_noAnnotation() {
89  			// Verify no settings are enabled when no annotation is present
90  			assertFalse(BctConfiguration.get(BCT_SORT_MAPS, false));
91  			assertFalse(BctConfiguration.get(BCT_SORT_COLLECTIONS, false));
92  		}
93  	}
94  
95  	// ====================================================================================================
96  	// Method-level overriding class-level tests
97  	// ====================================================================================================
98  
99  	@Nested
100 	@BctConfig(sortMaps = TRUE, sortCollections = TRUE)
101 	class D_methodOverridesClass extends TestBase {
102 
103 		@Test
104 		@BctConfig(sortMaps = FALSE)
105 		void d01_methodOverridesSortMaps() {
106 			// Method-level should override class-level for sortMaps
107 			assertFalse(BctConfiguration.get(BCT_SORT_MAPS, false));
108 			// sortCollections should still be true from class-level (UNSET in method inherits from class)
109 			assertTrue(BctConfiguration.get(BCT_SORT_COLLECTIONS, false));
110 		}
111 
112 		@Test
113 		@BctConfig(sortCollections = FALSE)
114 		void d02_methodOverridesSortCollections() {
115 			// sortMaps should still be true from class-level (UNSET in method inherits from class)
116 			assertTrue(BctConfiguration.get(BCT_SORT_MAPS, false));
117 			// Method-level should override class-level for sortCollections
118 			assertFalse(BctConfiguration.get(BCT_SORT_COLLECTIONS, false));
119 		}
120 
121 		@Test
122 		@BctConfig(sortMaps = FALSE, sortCollections = FALSE)
123 		void d03_methodOverridesBoth() {
124 			// Method-level should override both from class-level
125 			assertFalse(BctConfiguration.get(BCT_SORT_MAPS, false));
126 			assertFalse(BctConfiguration.get(BCT_SORT_COLLECTIONS, false));
127 		}
128 
129 		@Test
130 		void d04_inheritsFromClass() {
131 			// No method annotation, should inherit from class
132 			assertTrue(BctConfiguration.get(BCT_SORT_MAPS, false));
133 			assertTrue(BctConfiguration.get(BCT_SORT_COLLECTIONS, false));
134 		}
135 	}
136 
137 	// ====================================================================================================
138 	// BeanConverter annotation tests
139 	// ====================================================================================================
140 
141 	/**
142 	 * Custom converter for testing.
143 	 */
144 	static class TestBeanConverter extends BasicBeanConverter {
145 		public TestBeanConverter() {
146 			super(BasicBeanConverter.builder().defaultSettings());
147 		}
148 	}
149 
150 	@Nested
151 	class E_beanConverterAnnotation extends TestBase {
152 
153 		@Test
154 		@BctConfig(beanConverter = TestBeanConverter.class)
155 		void e01_customConverterSet() {
156 			// Verify custom converter is set by using it in an assertion
157 			// The converter should be used by assertBean
158 			var person = new TestPerson("Alice", 25);
159 			assertDoesNotThrow(() -> assertBean(person, "name", "Alice"));
160 			// Verify it's actually the custom converter by checking the class
161 			var converter = BctConfiguration.getConverter();
162 			assertNotNull(converter);
163 			assertEquals(TestBeanConverter.class, converter.getClass());
164 		}
165 
166 		@Test
167 		void e02_defaultConverter() {
168 			// Verify default converter is used when no annotation
169 			var converter = BctConfiguration.getConverter();
170 			assertNotNull(converter);
171 			assertEquals(BasicBeanConverter.class, converter.getClass());
172 		}
173 	}
174 
175 	@Nested
176 	@BctConfig(beanConverter = TestBeanConverter.class)
177 	class F_classLevelBeanConverter extends TestBase {
178 
179 		@Test
180 		void f01_inheritsConverterFromClass() {
181 			// Verify converter is inherited from class-level annotation
182 			var converter = BctConfiguration.getConverter();
183 			assertNotNull(converter);
184 			assertEquals(TestBeanConverter.class, converter.getClass());
185 		}
186 
187 		@Test
188 		@BctConfig(beanConverter = BeanConverter.class)
189 		void f02_methodFallsBackToClassConverter() {
190 			// Method-level with BeanConverter.class (default) falls back to class-level converter
191 			var converter = BctConfiguration.getConverter();
192 			assertNotNull(converter);
193 			assertEquals(TestBeanConverter.class, converter.getClass());
194 		}
195 
196 		@Test
197 		@BctConfig(beanConverter = BasicBeanConverter.class)
198 		void f03_methodExplicitlyUsesDefaultConverter() {
199 			// Method-level with BasicBeanConverter.class explicitly uses default converter,
200 			// overriding class-level TestBeanConverter
201 			var converter = BctConfiguration.getConverter();
202 			assertNotNull(converter);
203 			assertEquals(BasicBeanConverter.class, converter.getClass());
204 		}
205 	}
206 
207 	// ====================================================================================================
208 	// Combined settings tests
209 	// ====================================================================================================
210 
211 	@Nested
212 	@BctConfig(sortMaps = TRUE, beanConverter = TestBeanConverter.class)
213 	class G_combinedSettings extends TestBase {
214 
215 		@Test
216 		void g01_bothSettingsApplied() {
217 			// Verify both sortMaps and converter are set
218 			assertTrue(BctConfiguration.get(BCT_SORT_MAPS, false));
219 			var converter = BctConfiguration.getConverter();
220 			assertEquals(TestBeanConverter.class, converter.getClass());
221 		}
222 
223 		@Test
224 		@BctConfig(sortCollections = TRUE)
225 		void g02_methodAddsToClass() {
226 			// Method adds sortCollections, inherits sortMaps and converter from class
227 			assertTrue(BctConfiguration.get(BCT_SORT_MAPS, false));
228 			assertTrue(BctConfiguration.get(BCT_SORT_COLLECTIONS, false));
229 			var converter = BctConfiguration.getConverter();
230 			assertEquals(TestBeanConverter.class, converter.getClass());
231 		}
232 	}
233 
234 	// ====================================================================================================
235 	// Clearing after test tests
236 	// ====================================================================================================
237 
238 	@Nested
239 	class H_clearingAfterTest extends TestBase {
240 
241 		@Test
242 		@BctConfig(sortMaps = TRUE, sortCollections = TRUE)
243 		void h01_settingsClearedAfterTest() {
244 			// Settings should be active during test
245 			assertTrue(BctConfiguration.get(BCT_SORT_MAPS, false));
246 			assertTrue(BctConfiguration.get(BCT_SORT_COLLECTIONS, false));
247 		}
248 
249 		@AfterEach
250 		void verifyCleared() {
251 			// After test, settings should be cleared
252 			// Note: This runs after the extension's afterEach, so we need to check in the next test
253 		}
254 
255 		@Test
256 		void h02_verifyPreviousTestCleared() {
257 			// This test runs after h01, so settings should be cleared
258 			assertFalse(BctConfiguration.get(BCT_SORT_MAPS, false));
259 			assertFalse(BctConfiguration.get(BCT_SORT_COLLECTIONS, false));
260 		}
261 	}
262 
263 	// ====================================================================================================
264 	// Error handling tests
265 	// ====================================================================================================
266 
267 	@Nested
268 	class I_errorHandling extends TestBase {
269 
270 		/**
271 		 * Converter without no-arg constructor (should fail).
272 		 */
273 		static class InvalidConverter extends BasicBeanConverter {
274 			public InvalidConverter(String arg) {
275 				super(BasicBeanConverter.builder().defaultSettings());
276 			}
277 		}
278 	}
279 
280 	// ====================================================================================================
281 	// Test helper classes
282 	// ====================================================================================================
283 
284 	/**
285 	 * Test person class for testing.
286 	 */
287 	static class TestPerson {
288 		private final String name;
289 		private final int age;
290 
291 		TestPerson(String name, int age) {
292 			this.name = name;
293 			this.age = age;
294 		}
295 
296 		String getName() { return name; }
297 
298 		int getAge() { return age; }
299 
300 		@Override
301 		public String toString() {
302 			return "TestPerson{name='" + name + "', age=" + age + "}";
303 		}
304 	}
305 }
306